Skip to content

Commit

Permalink
DyadGen that requires intersection tracking now "upgrades" UnsrtEL to…
Browse files Browse the repository at this point in the history
… HashEL after a certain (currently hardcoded) number of linear searches.

fixes #548
  • Loading branch information
krivit committed Feb 5, 2024
1 parent 10abfae commit b3e7c32
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 48 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: ergm
Version: 4.6-7325
Date: 2024-02-03
Version: 4.6-7337
Date: 2024-02-05
Title: Fit, Simulate and Diagnose Exponential-Family Models for Networks
Authors@R: c(
person(c("Mark", "S."), "Handcock", role=c("aut"), email="[email protected]"),
Expand Down
63 changes: 43 additions & 20 deletions inst/include/ergm_dyadgen.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@
#include "ergm_wtedgetree.h"
#include "ergm_rlebdm.h"
#include "ergm_edgelist.h"
#include "ergm_unsorted_edgelist.h"
#include "ergm_hash_edgelist.h"
#include "ergm_changestat.h"
#include "ergm_wtchangestat.h"

typedef enum{RandDyadGen, WtRandDyadGen, RLEBDM1DGen, WtRLEBDM1DGen, EdgeListGen, WtEdgeListGen} DyadGenType;
typedef enum{NoELDyadGen, UnsrtELDyadGen, HashELDyadGen} DyadGenInterType;
#define DYADGEN_MISSES_BEFORE_UPGRADE 8

typedef struct {
DyadGenType type;
DyadGenInterType intertype;
union {
Network *b;
WtNetwork *w;
Expand All @@ -32,7 +35,10 @@ typedef struct {
int *el;
} dyads;
Dyad ndyads;
UnsrtEL *intersect;
union {
UnsrtEL *uel;
HashEL *hel;
} inter;
Rboolean sleeping;
} DyadGen;

Expand Down Expand Up @@ -70,7 +76,7 @@ static inline void DyadGenRandDyad(Vertex *tail, Vertex *head, DyadGen *gen){
error("Undefined dyad generator type.");
}

if(gen->intersect){ /* If we are maintaining an unsorted edgelist (which also implies that we are *not* in a *RandDyadGen mode)... */
if(gen->intertype == UnsrtELDyadGen){ /* If we are maintaining an unsorted edgelist (which also implies that we are *not* in a *RandDyadGen mode)... */
/* Use the appropriate function to check if we had selected an extant edge, */
Rboolean extant;
switch(gen->type){
Expand All @@ -87,15 +93,14 @@ static inline void DyadGenRandDyad(Vertex *tail, Vertex *head, DyadGen *gen){
}

/* ... and if so, reselect it from the unsorted edgelist. */
if(extant) UnsrtELGetRand(tail, head, gen->intersect);
if(extant) UnsrtELGetRand(tail, head, gen->inter.uel);
}
}


static inline Edge DyadGenEdgecount(DyadGen *gen){
if(gen->intersect){
return gen->intersect->nedges;
}else{
switch(gen->intertype){
case NoELDyadGen:
switch(gen->type){
case RandDyadGen:
case RLEBDM1DGen:
Expand All @@ -108,7 +113,10 @@ static inline Edge DyadGenEdgecount(DyadGen *gen){
default:
error("Undefined dyad generator type.");
}
case UnsrtELDyadGen: return gen->inter.uel->nedges;
case HashELDyadGen: return gen->inter.hel->list->nedges;
}
return 0; // Fix a warning.
}


Expand All @@ -126,16 +134,22 @@ static inline void DyadGenRandEdge(Vertex *tail, Vertex *head, DyadGen *gen){
case RLEBDM1DGen:
case EdgeListGen:
{
if(gen->intersect) UnsrtELGetRand(tail, head, gen->intersect);
else GetRandEdge(tail, head, gen->nwp.b);
switch(gen->intertype){
case NoELDyadGen: GetRandEdge(tail, head, gen->nwp.b); break;
case UnsrtELDyadGen: UnsrtELGetRand(tail, head, gen->inter.uel); break;
case HashELDyadGen: HashELGetRand(tail, head, gen->inter.hel); break;
}
}
break;
case WtRLEBDM1DGen:
case WtEdgeListGen:
{
double dummy;
if(gen->intersect) UnsrtELGetRand(tail, head, gen->intersect);
else WtGetRandEdge(tail, head, &dummy, gen->nwp.w);
switch(gen->intertype){
case NoELDyadGen: WtGetRandEdge(tail, head, &dummy, gen->nwp.w); break;
case UnsrtELDyadGen: UnsrtELGetRand(tail, head, gen->inter.uel); break;
case HashELDyadGen: HashELGetRand(tail, head, gen->inter.hel); break;
}
}
break;
default:
Expand Down Expand Up @@ -190,18 +204,27 @@ static inline void DyadGenRandWtEdge(Vertex *tail, Vertex *head, double *weight,
break;
case RLEBDM1DGen:
case EdgeListGen:
if(gen->intersect) UnsrtELGetRand(tail, head, gen->intersect);
else GetRandEdge(tail, head, gen->nwp.b);
switch(gen->intertype){
case NoELDyadGen: GetRandEdge(tail, head, gen->nwp.b); break;
case UnsrtELDyadGen: UnsrtELGetRand(tail, head, gen->inter.uel); break;
case HashELDyadGen: HashELGetRand(tail, head, gen->inter.hel); break;
}
*weight = 1;
break;
case WtRLEBDM1DGen:
case WtEdgeListGen:
if(gen->intersect){
UnsrtELGetRand(tail, head, gen->intersect);
*weight = WtGetEdge(*tail, *head, gen->nwp.w);
}
else WtGetRandEdge(tail, head, weight, gen->nwp.w);
break;
switch(gen->intertype){
case NoELDyadGen: WtGetRandEdge(tail, head, weight, gen->nwp.w); break;
case UnsrtELDyadGen:
UnsrtELGetRand(tail, head, gen->inter.uel);
*weight = WtGetEdge(*tail, *head, gen->nwp.w);
break;
case HashELDyadGen:
HashELGetRand(tail, head, gen->inter.hel);
*weight = WtGetEdge(*tail, *head, gen->nwp.w);
break;
}
break;
default:
error("Undefined dyad generator type.");
}
Expand Down Expand Up @@ -232,7 +255,7 @@ static inline void DyadGenSleep(DyadGen *gen){
that it must keep track of what they are.
FIXME: Maybe we should *always* maintain an UnsrtEL? Need to benchmark. */
if(!gen->intersect) DyadGenSetUpIntersect(gen, NULL, TRUE);
if(gen->intertype != NoELDyadGen) DyadGenSetUpIntersect(gen, NULL, TRUE);
}

static inline void DyadGenWake(DyadGen *gen){
Expand Down
108 changes: 82 additions & 26 deletions src/ergm_dyadgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,25 @@ void DyadGenSetUpIntersect(DyadGen *gen, void *track_nwp, Rboolean force){
switch(gen->type){
case RandDyadGen:
case WtRandDyadGen:
gen->intersect = NULL;
gen->inter.uel = NULL;
break;
case EdgeListGen:
case RLEBDM1DGen:
{
Network *nwp = track_nwp ? track_nwp : gen->nwp.b;
gen->nwp.b = nwp;
gen->intersect = UnsrtELInitialize(0, NULL, NULL, FALSE);
gen->inter.uel = UnsrtELInitialize(0, NULL, NULL, FALSE);
EXEC_THROUGH_NET_EDGES(t, h, e, {
if(DyadGenSearch(t, h, gen)){
UnsrtELInsert(t, h, gen->intersect);
UnsrtELInsert(t, h, gen->inter.uel);
}
});

if(!force && gen->intersect->nedges==EDGECOUNT(nwp)){ // There are no ties in the initial network that are fixed.
UnsrtELDestroy(gen->intersect);
gen->intersect = NULL; // "Signal" that there is no discordance network.
if(!force && gen->inter.uel->nedges==EDGECOUNT(nwp)){ // There are no ties in the initial network that are fixed.
UnsrtELDestroy(gen->inter.uel);
gen->inter.uel = NULL; // "Signal" that there is no discordance network.
}else{
gen->intertype = UnsrtELDyadGen;
AddOnNetworkEdgeChange(nwp, (OnNetworkEdgeChange) DyadGenUpdate, gen, INT_MAX);
}
}
Expand All @@ -43,18 +44,19 @@ void DyadGenSetUpIntersect(DyadGen *gen, void *track_nwp, Rboolean force){
{
WtNetwork *nwp = track_nwp ? track_nwp : gen->nwp.w;
gen->nwp.w = nwp;
gen->intersect = UnsrtELInitialize(0, NULL, NULL, FALSE);
gen->inter.uel = UnsrtELInitialize(0, NULL, NULL, FALSE);
WtEXEC_THROUGH_NET_EDGES(t, h, w, e, {
(void) e;
if(w!=0 && DyadGenSearch(t, h, gen)){
UnsrtELInsert(t, h, gen->intersect);
UnsrtELInsert(t, h, gen->inter.uel);
}
});

if(!force && gen->intersect->nedges==EDGECOUNT(nwp)){ // There are no ties in the initial network that are fixed.
UnsrtELDestroy(gen->intersect);
gen->intersect = NULL; // "Signal" that there is no discordance network.
if(!force && gen->inter.uel->nedges==EDGECOUNT(nwp)){ // There are no ties in the initial network that are fixed.
UnsrtELDestroy(gen->inter.uel);
gen->inter.uel = NULL; // "Signal" that there is no discordance network.
}else{
gen->intertype = UnsrtELDyadGen;
AddOnWtNetworkEdgeChange(nwp, (OnWtNetworkEdgeChange) WtDyadGenUpdate, gen, INT_MAX);
}
}
Expand All @@ -64,21 +66,49 @@ void DyadGenSetUpIntersect(DyadGen *gen, void *track_nwp, Rboolean force){
}
}

void DyadGenUpgradeIntersect(DyadGen *gen){
if(gen->intertype == UnsrtELDyadGen){
Rboolean directed;
switch(gen->type){
case RandDyadGen:
case EdgeListGen:
case RLEBDM1DGen:
{
Network *nwp = gen->nwp.b;
directed = DIRECTED;
}
break;
case WtRandDyadGen:
case WtEdgeListGen:
case WtRLEBDM1DGen:
{
WtNetwork *nwp = gen->nwp.w;
directed = DIRECTED;
}
break;
default:
error("Undefined dyad generator type.");
}
gen->inter.hel = UnsrtELIntoHashEL(gen->inter.uel, directed);
gen->intertype = HashELDyadGen;
}
}

DyadGen *DyadGenInitialize(DyadGenType type, void *dyads, void *track_nwp){
DyadGen *gen = Calloc(1, DyadGen);
gen->type = type;
gen->intertype = NoELDyadGen;
gen->sleeping = FALSE;
gen->inter.uel = NULL;

switch(gen->type){
case RandDyadGen:
gen->nwp.b = dyads;
gen->ndyads = DYADCOUNT(gen->nwp.b);
gen->intersect = NULL;
break;
case WtRandDyadGen:
gen->nwp.w = dyads;
gen->ndyads = DYADCOUNT(gen->nwp.w);
gen->intersect = NULL;
break;
case RLEBDM1DGen:
case WtRLEBDM1DGen:
Expand Down Expand Up @@ -137,21 +167,21 @@ DyadGen *DyadGenInitializeR(SEXP pR, void *any_nwp, Rboolean el){


void DyadGenDestroy(DyadGen *gen){
if(gen->intersect){
if(gen->intertype != NoELDyadGen){
switch(gen->intertype){
case UnsrtELDyadGen: UnsrtELDestroy(gen->inter.uel); break;
case HashELDyadGen: HashELDestroy(gen->inter.hel); break;
case NoELDyadGen: break;
}

switch(gen->type){
case RLEBDM1DGen:
case EdgeListGen:
if(gen->intersect){
UnsrtELDestroy(gen->intersect);
DeleteOnNetworkEdgeChange(gen->nwp.b, (OnNetworkEdgeChange) DyadGenUpdate, gen);
}
DeleteOnNetworkEdgeChange(gen->nwp.b, (OnNetworkEdgeChange) DyadGenUpdate, gen);
break;
case WtRLEBDM1DGen:
case WtEdgeListGen:
if(gen->intersect){
UnsrtELDestroy(gen->intersect);
DeleteOnWtNetworkEdgeChange(gen->nwp.w, (OnWtNetworkEdgeChange) WtDyadGenUpdate, gen);
}
DeleteOnWtNetworkEdgeChange(gen->nwp.w, (OnWtNetworkEdgeChange) WtDyadGenUpdate, gen);
break;
case RandDyadGen:
case WtRandDyadGen:
Expand All @@ -166,13 +196,39 @@ void DyadGenDestroy(DyadGen *gen){

void DyadGenUpdate(Vertex tail, Vertex head, DyadGen *gen, Network *nwp, Rboolean edgestate){
if(gen->sleeping) return;
if(edgestate) UnsrtELDelete(tail, head, gen->intersect); // Deleting
else UnsrtELInsert(tail, head, gen->intersect); // Inserting

switch(gen->intertype){
case UnsrtELDyadGen:
if(edgestate) UnsrtELDelete(tail, head, gen->inter.uel); // Deleting
else UnsrtELInsert(tail, head, gen->inter.uel); // Inserting
if(gen->inter.uel->linsearch >= DYADGEN_MISSES_BEFORE_UPGRADE) DyadGenUpgradeIntersect(gen);
break;

case HashELDyadGen:
if(edgestate) HashELDelete(tail, head, gen->inter.hel); // Deleting
else HashELInsert(tail, head, gen->inter.hel); // Inserting
break;

case NoELDyadGen: break;
}
}


void WtDyadGenUpdate(Vertex tail, Vertex head, double weight, DyadGen *gen, WtNetwork *nwp, double edgestate){
if(gen->sleeping) return;
if(edgestate!=0 && weight==0) UnsrtELDelete(tail, head, gen->intersect); // Deleting
else if(edgestate==0 && weight!=0) UnsrtELInsert(tail, head, gen->intersect); // Inserting

switch(gen->intertype){
case UnsrtELDyadGen:
if(edgestate!=0 && weight==0) UnsrtELDelete(tail, head, gen->inter.uel); // Deleting
else if(edgestate==0 && weight!=0) UnsrtELInsert(tail, head, gen->inter.uel); // Inserting
if(gen->inter.uel->linsearch >= DYADGEN_MISSES_BEFORE_UPGRADE) DyadGenUpgradeIntersect(gen);
break;

case HashELDyadGen:
if(edgestate!=0 && weight==0) HashELDelete(tail, head, gen->inter.hel); // Deleting
else if(edgestate==0 && weight!=0) HashELInsert(tail, head, gen->inter.hel); // Inserting
break;

case NoELDyadGen: break;
}
}

0 comments on commit b3e7c32

Please sign in to comment.