diff --git a/DESCRIPTION b/DESCRIPTION index aa3b30f44..42528d217 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -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="handcock@stat.ucla.edu"), diff --git a/inst/include/ergm_dyadgen.h b/inst/include/ergm_dyadgen.h index 48e93bdb4..372297699 100644 --- a/inst/include/ergm_dyadgen.h +++ b/inst/include/ergm_dyadgen.h @@ -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; @@ -32,7 +35,10 @@ typedef struct { int *el; } dyads; Dyad ndyads; - UnsrtEL *intersect; + union { + UnsrtEL *uel; + HashEL *hel; + } inter; Rboolean sleeping; } DyadGen; @@ -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){ @@ -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: @@ -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. } @@ -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: @@ -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."); } @@ -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){ diff --git a/src/ergm_dyadgen.c b/src/ergm_dyadgen.c index 645cc3437..0d4f8d4ad 100644 --- a/src/ergm_dyadgen.c +++ b/src/ergm_dyadgen.c @@ -16,23 +16,23 @@ 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{ AddOnNetworkEdgeChange(nwp, (OnNetworkEdgeChange) DyadGenUpdate, gen, INT_MAX); } @@ -43,17 +43,17 @@ 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{ AddOnWtNetworkEdgeChange(nwp, (OnWtNetworkEdgeChange) WtDyadGenUpdate, gen, INT_MAX); } @@ -64,21 +64,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: @@ -137,21 +165,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: @@ -166,13 +194,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; + } }