/****************** Network NetworkCopy *****************/ Network *NetworkCopy(Network *dest, Network *src){ Vertex nnodes = dest->nnodes = src->nnodes; dest->last_inedge = src->last_inedge; dest->last_outedge = src->last_outedge; dest->outdegree = (Vertex *) malloc((nnodes+1)*sizeof(Vertex)); memcpy(dest->outdegree, src->outdegree, (nnodes+1)*sizeof(Vertex)); dest->indegree = (Vertex *) malloc((nnodes+1)*sizeof(Vertex)); memcpy(dest->indegree, src->indegree, (nnodes+1)*sizeof(Vertex)); Vertex maxedges = dest->maxedges = src->maxedges; dest->inedges = (TreeNode *) malloc(maxedges*sizeof(TreeNode)); memcpy(dest->inedges, src->inedges, maxedges*sizeof(TreeNode)); dest->outedges = (TreeNode *) malloc(maxedges*sizeof(TreeNode)); memcpy(dest->outedges, src->outedges, maxedges*sizeof(TreeNode)); int directed_flag = dest->directed_flag = src->directed_flag; Vertex bipartite = dest->bipartite = src->bipartite; if(src->duration_info.lasttoggle){ dest->duration_info.time=src->duration_info.time; dest->duration_info.lasttoggle = (int *) calloc(DYADCOUNT(nnodes, bipartite, directed_flag), sizeof(int)); memcpy(dest->duration_info.lasttoggle, src->duration_info.lasttoggle,DYADCOUNT(nnodes, bipartite, directed_flag) * sizeof(int)); } else dest->duration_info.lasttoggle = NULL; dest->nedges = src->nedges; return dest; }
/******************** void MH_Formation Propose ONLY edges not in the reference graph ***********************/ void MH_Formation (MHProposal *MHp, Network *nwp) { static Dyad ndyads; if(MHp->ntoggles == 0) { /* Initialize */ MHp->ntoggles=1; ndyads = DYADCOUNT(nwp->nnodes, 0, nwp->directed_flag); return; } if(nwp->nedges==ndyads && MHp->discord[0]->nedges==0){ /* Attempting formation on a complete graph. */ Mtail[0]=MH_FAILED; Mhead[0]=MH_IMPOSSIBLE; return; } BD_LOOP({ /* A dyad eligible to be formed is either a nontie in nwp[0] or a tie in MHp->discord-> */ if(unif_rand() < ((double)MHp->discord[0]->nedges)/(ndyads-nwp->nedges + MHp->discord[0]->nedges)){ // Tie in MHp->discord[0]: GetRandEdge(Mtail, Mhead, MHp->discord[0]); }else{ // Nontie in nwp[0]: GetRandNonedge(Mtail, Mhead, nwp); } });
void MH_FormationPlusMLE (MHproposal *MHp, Network *nwp) { static Vertex nnodes; static Edge nedges0; static Dyad ndyads; if(MHp->ntoggles == 0) { /* Initialize */ MHp->ntoggles=1; nnodes = nwp[0].nnodes; ndyads = DYADCOUNT(nnodes, 0, nwp[0].directed_flag); nedges0 = MHp->inputs[0]; return; } if(nedges0==ndyads){ /* Attempting formation on a complete graph. */ Mtail[0]=MH_FAILED; Mhead[0]=MH_IMPOSSIBLE; return; } BD_COND_LOOP({ /* Keep trying dyads until a one that is not an edge in the reference network is found. */ /* Generate. */ GetRandDyad(Mtail, Mhead, nwp); }, !dEdgeListSearch(Mtail[0],Mhead[0],MHp->inputs), 2);
int GetRandNonedge(Vertex *tail, Vertex *head, Network *nwp) { Edge ndyads = DYADCOUNT(nwp->nnodes, nwp->bipartite, nwp->directed_flag); if(ndyads-nwp->nedges==0) return(0); /* There are two ways to get a random nonedge: 1) keep trying dyads at random until you find one that's not an edge or 2) generate i at random and find ith nonedge. Method 1 works better in sparse networks, while Method 2, which runs in deterministic time, works better in dense networks. The expected number of attempts for Method 1 is 1/(1-e/d) = d/(d-e), where e is the number of edges and d is the number of dyads. */ // FIXME: The constant maxEattempts needs to be tuned. const unsigned int maxEattempts=10; unsigned int Eattempts = ndyads/(ndyads-nwp->nedges); Edge rane; if(Eattempts>maxEattempts){ // If the network is too dense, use the deterministic-time method: rane=1 + unif_rand() * (ndyads-nwp->nedges); FindithNonedge(tail, head, rane, nwp); }else{ do{ GetRandDyad(tail, head, nwp); }while(EdgetreeSearch(*tail, *head, nwp->outedges)); } return 1; }
Network NetworkInitialize(Vertex *tails, Vertex *heads, Edge nedges, Vertex nnodes, int directed_flag, Vertex bipartite, int lasttoggle_flag, int time, int *lasttoggle) { Network nw; nw.last_inedge = nw.last_outedge = (Edge)nnodes; /* Calloc will zero the allocated memory for us, probably a lot faster. */ nw.outdegree = (Vertex *) calloc((nnodes+1),sizeof(Vertex)); nw.indegree = (Vertex *) calloc((nnodes+1),sizeof(Vertex)); nw.maxedges = MAX(nedges,1)+nnodes+2; /* Maybe larger than needed? */ nw.inedges = (TreeNode *) calloc(nw.maxedges,sizeof(TreeNode)); nw.outedges = (TreeNode *) calloc(nw.maxedges,sizeof(TreeNode)); /* GetRNGstate(); R function enabling uniform RNG */ if(lasttoggle_flag){ nw.duration_info.time=time; nw.duration_info.lasttoggle = (int *) calloc(DYADCOUNT(nnodes, bipartite, directed_flag), sizeof(int)); if(lasttoggle) memcpy(nw.duration_info.lasttoggle, lasttoggle, DYADCOUNT(nnodes, bipartite, directed_flag) * sizeof(int)); } else nw.duration_info.lasttoggle = NULL; /*Configure a Network*/ nw.nnodes = nnodes; nw.nedges = 0; /* Edges will be added one by one */ nw.directed_flag=directed_flag; nw.bipartite=bipartite; ShuffleEdges(tails,heads,nedges); /* shuffle to avoid worst-case performance */ for(Edge i = 0; i < nedges; i++) { Vertex tail=tails[i], head=heads[i]; if (!directed_flag && tail > head) AddEdgeToTrees(head,tail,&nw); /* Undir edges always have tail < head */ else AddEdgeToTrees(tail,head,&nw); } /* PutRNGstate(); */ return nw; }
int FindithNonedge (Vertex *tail, Vertex *head, Edge i, Network *nwp) { Vertex taili=1; Edge e; Edge ndyads = DYADCOUNT(nwp->nnodes, nwp->bipartite, nwp->directed_flag); // If the index is too high or too low, exit immediately. if (i > ndyads - nwp->nedges || i<=0) return 0; /* TODO: This could be speeded up by a factor of 3 or more by starting the search from the tail n rather than tail 1 if i > ndyads/2. */ Vertex nnt; while (i > (nnt = nwp->nnodes - (nwp->bipartite ? nwp->bipartite : (nwp->directed_flag?1:taili)) - nwp->outdegree[taili])) { // nnt is the number of nonties incident on taili. Note that when network is undirected, tail<head. i -= nnt; taili++; } // Now, our tail is taili. /* TODO: This could be speeded up by a factor of 3 or more by starting the search from the tree maximum rather than minimum (left over) i > outdegree[taili]. */ // If taili 1, then head cannot be 1. If undirected, the smallest it can be is taili+1. If bipartite, the smallest it can be is nwp->bipartite+1. Vertex lhead = ( nwp->bipartite ? nwp->bipartite : (nwp->directed_flag ? taili==1 : taili) ); e = EdgetreeMinimum(nwp->outedges,taili); Vertex rhead = nwp->outedges[e].value; // Note that rhead-lhead-1-(lhead<taili && taili<rhead) is the number of nonties between two successive ties. // the -(lhead<taili && taili<rhead) is because (taili,taili) is not a valid nontie and must be skipped. // Note that if taili is an isolate, rhead will be 0. while (rhead && i > rhead-lhead-1-(lhead<taili && taili<rhead)) { i -= rhead-lhead-1-(lhead<taili && taili<rhead); lhead = rhead; e = EdgetreeSuccessor(nwp->outedges, e); // If rhead was the highest-indexed head, then e is now 0. if(e) rhead = nwp->outedges[e].value; else break; // Note that we don't actually need rhead in the final step. } // Now, the head we are looking for is (left over) i after lhead. *tail = taili; *head = lhead + i + (nwp->directed_flag && lhead+i>=taili); // Skip over the (taili,taili) dyad, if the network is directed. return 1; }
/******************** void MH_BipartiteHammingTNT Tie/no tie: Gives at least 50% chance of proposing a toggle of an existing edge, as opposed to simple random toggles that rarely do so in sparse networks MSH: The name Hamming is a hack for the Hamming proposals It is no different the MH_BipartiteTNT ***********************/ void MH_BipartiteHammingTNT (MHproposal *MHp, Network *nwp) { /* *** don't forget, edges are (tail, head) now */ Vertex tail, head; Edge nddyads=nwp[1].nedges; int nd, nc; static double comp=0.5; static double odds; static Dyad ndyads; static Edge nnodes; static Edge nb1; if(MHp->ntoggles == 0) { /* Initialize */ MHp->ntoggles=1; odds = comp/(1.0-comp); nnodes = nwp[0].nnodes; nb1 = nwp[0].bipartite; ndyads = DYADCOUNT(nnodes, nb1, 0); return; } if (unif_rand() < comp && nddyads > 0) { /* Select a discordant dyad at random */ GetRandEdge(Mtail, Mhead, &nwp[1]); nd = nddyads; nc = ndyads-nd; /* Fixme! Not sure whether the ratio is calculated correctly here. Check out the similar ratio calculations for other TNT proposals. */ MHp->logratio += log((nd*1.0) / (odds*(nc+1))); /* MHp->ratio = (1.0*(nce-1)*(ncn-1)) / (nde*ndn*odds); */ /* MHp->ratio = 1.0; */ /* Rprintf("disconcord nd %d nc %d nddyads %d MHp->ratio %f\n", */ /* nd, nc, nddyads, MHp->ratio); */ }else{ /* select a concordant dyad at random */ do{ tail = 1 + unif_rand() * nb1; head = 1 + nb1 + unif_rand() * (nnodes - nb1); }while(EdgetreeSearch(tail,head,nwp[1].outedges)!=0); Mtail[0]=tail; Mhead[0]=head; nd = nddyads; nc = ndyads-nd; /* Fixme! Not sure whether the ratio is calculated correctly here. Check out the similar ratio calculations for other TNT proposals. */ MHp->logratio += log((odds*nc) / ((nd+1)*1.0)); /* Rprintf("concord nd %d nc %d nddyads %d MHp->ratio %f\n", */ /* nd, nc, nddyads, MHp->ratio); */ } /* Rprintf("h0 %d t0 %d h1 %d t1 %d\n", Mtail[0], Mhead[0], */ /* Mtail[1], Mhead[1]); */ }
WtNetwork *WtNetworkInitialize(Vertex *tails, Vertex *heads, double *weights, Edge nedges, Vertex nnodes, int directed_flag, Vertex bipartite, int lasttoggle_flag, int time, int *lasttoggle) { WtNetwork *nwp = Calloc(1, WtNetwork); nwp->last_inedge = nwp->last_outedge = (Edge)nnodes; /* Calloc will zero the allocated memory for us, probably a lot faster. */ nwp->outdegree = (Vertex *) Calloc((nnodes+1), Vertex); nwp->indegree = (Vertex *) Calloc((nnodes+1), Vertex); nwp->maxedges = MAX(nedges,1)+nnodes+2; /* Maybe larger than needed? */ nwp->inedges = (WtTreeNode *) Calloc(nwp->maxedges, WtTreeNode); nwp->outedges = (WtTreeNode *) Calloc(nwp->maxedges, WtTreeNode); if(lasttoggle_flag){ nwp->duration_info.time=time; if(lasttoggle){ nwp->duration_info.lasttoggle = (int *) Calloc(DYADCOUNT(nnodes, bipartite, directed_flag), int); memcpy(nwp->duration_info.lasttoggle, lasttoggle, DYADCOUNT(nnodes, bipartite, directed_flag) * sizeof(int)); } else nwp->duration_info.lasttoggle = NULL; }
void MCMCDynSArun_wrapper(// Observed network. int *tails, int *heads, int *time, int *lasttoggle, int *n_edges, int *n_nodes, int *dflag, int *bipartite, // Formation terms and proposals. int *F_nterms, char **F_funnames, char **F_sonames, char **F_MHproposaltype, char **F_MHproposalpackage, double *F_inputs, // Dissolution terms and proposals. int *D_nterms, char **D_funnames, char **D_sonames, char **D_MHproposaltype, char **D_MHproposalpackage, double *D_inputs, // Parameter fittig. double *eta0, int *M_nterms, char **M_funnames, char **M_sonames, double *M_inputs, double *init_dev, int *runlength, double *WinvGradient, double *jitter, double *dejitter, double *dev_guard, double *par_guard, // Degree bounds. int *attribs, int *maxout, int *maxin, int *minout, int *minin, int *condAllDegExact, int *attriblength, // MCMC settings. int *SA_burnin, int *SA_interval, int *min_MH_interval, int *max_MH_interval, double *MH_pval, double *MH_interval_add, // Space for output. int *maxedges, int *maxchanges, int *newnetworktail, int *newnetworkhead, double *opt_history, // Verbosity. int *fVerbose, int *status){ Network nw[2]; Model *F_m, *D_m, *M_m; MHproposal F_MH, D_MH; if(*lasttoggle == 0) lasttoggle = NULL; Vertex *difftime, *difftail, *diffhead; difftime = (Vertex *) calloc(*maxchanges,sizeof(Vertex)); difftail = (Vertex *) calloc(*maxchanges,sizeof(Vertex)); diffhead = (Vertex *) calloc(*maxchanges,sizeof(Vertex)); memset(newnetworktail,0,*maxedges*sizeof(int)); memset(newnetworkhead,0,*maxedges*sizeof(int)); MCMCDyn_init_common(tails, heads, *time, lasttoggle, *n_edges, *n_nodes, *dflag, *bipartite, nw, *F_nterms, *F_funnames, *F_sonames, F_inputs, &F_m, *D_nterms, *D_funnames, *D_sonames, D_inputs, &D_m, *M_nterms, *M_funnames, *M_sonames, M_inputs, &M_m, attribs, maxout, maxin, minout, minin, *condAllDegExact, *attriblength, *F_MHproposaltype, *F_MHproposalpackage, &F_MH, *D_MHproposaltype, *D_MHproposalpackage, &D_MH, *fVerbose); *status = MCMCDynSArun(nw, F_m, &F_MH, D_m, &D_MH, eta0, M_m, init_dev, *runlength, WinvGradient, jitter, dejitter, dev_guard, par_guard, *maxedges, *maxchanges, difftime, difftail, diffhead, opt_history, *SA_burnin, *SA_interval, *min_MH_interval, *max_MH_interval, *MH_pval, *MH_interval_add, *fVerbose); /* record the final network to pass back to R */ if(*status==MCMCDyn_OK){ newnetworktail[0]=newnetworkhead[0]=EdgeTree2EdgeList(newnetworktail+1,newnetworkhead+1,nw,*maxedges); *time = nw->duration_info.time; if(nw->duration_info.lasttoggle) memcpy(lasttoggle, nw->duration_info.lasttoggle, sizeof(int)*DYADCOUNT(*n_nodes, *bipartite, *dflag)); } MCMCDyn_finish_common(nw, F_m, D_m, M_m, &F_MH, &D_MH); free(difftime); free(difftail); free(diffhead); }
/******************** void MH_BipartiteHammingConstantEdges Chooses a pair of toggles - one a tie and one not. MSH: The name Hamming is a hack for the Hamming proposals It is no different the MH_BipartiteConstantEdges ***********************/ void MH_BipartiteHammingConstantEdges (MHproposal *MHp, Network *nwp) { /* *** don't forget, edges are (tail, head) now */ Vertex tail, head; Edge nedges=nwp[0].nedges, nddyads=nwp[1].nedges; int nde, ndn, nce, ncn; static double comp=0.5; static double odds; static Dyad ndyads; static Edge nnodes; static Edge nb1; if(MHp->ntoggles == 0) { /* Initialize */ MHp->ntoggles=2; odds = comp/(1.0-comp); nnodes = nwp[0].nnodes; nb1 = nwp[0].bipartite; ndyads = DYADCOUNT(nnodes, nb1, 0); return; } if (unif_rand() < comp && nddyads > 0) { /* Select a discordant pair of tie/nontie at random */ /* First, select discord edge at random */ do{ GetRandEdge(Mtail, Mhead, &nwp[1]); }while(EdgetreeSearch(Mtail[0], Mhead[0], nwp[0].outedges) == 0); tail=Mtail[0]; head=Mhead[0]; /* Next, select discord non-edge at random */ /* *** don't forget, edges are (tail, head) now */ do{ GetRandEdge(Mtail, Mhead, &nwp[1]); }while(EdgetreeSearch(Mtail[0], Mhead[0], nwp[0].outedges) != 0); Mtail[1]=Mtail[0]; Mhead[1]=Mhead[0]; Mtail[0]=tail; Mhead[0]=head; nde = nddyads / 2; ndn = nddyads / 2; nce = nedges-nde; ncn = ndyads-nedges-ndn; /* MHp->ratio = (nddyads*nddyads) / (odds*(nnodes-nddyads-2)*(nnodes-nddyads-2)); */ MHp->logratio += log((nde*ndn*1.0) / (odds*(nce+1)*(ncn+1))); /* MHp->ratio = (1.0*(nce-1)*(ncn-1)) / (nde*ndn*odds); */ /* MHp->ratio = 1.0; */ /* Rprintf("disconcord nde %d nce %d ndn %d ncn %d nddyads %d MHp->ratio %f\n", */ /* nde, nce, ndn,ncn,nddyads, MHp->ratio); */ }else{ /* First, select concordant edge at random */ do{ GetRandEdge(Mtail, Mhead, &nwp[0]); }while(EdgetreeSearch(Mtail[0], Mhead[0], nwp[1].outedges) == 0); /* Next, select concord non-edge at random */ do{ tail = 1 + unif_rand() * nb1; head = 1 + nb1 + unif_rand() * (nnodes - nb1); }while((EdgetreeSearch(tail,head,nwp[0].outedges)!=0) || (EdgetreeSearch(tail,head,nwp[1].outedges)!=0)); Mtail[1]=tail; Mhead[1]=head; nde = nddyads / 2; ndn = nddyads / 2; nce = nedges-nde; ncn = ndyads-nedges-ndn; /* MHp->ratio = ((nnodes-nddyads)*(nnodes-nddyads)) / (odds*(nddyads+2)*(nddyads+2)); */ if(nddyads > 4){ MHp->logratio += log((odds*nce*ncn) / ((nde+1)*(ndn+1)*1.0)); /* MHp->ratio = ((nde+1)*(ndn+1)*odds) / (1.0*nce*ncn); */ }else{ MHp->logratio += 100000000.0; } /* Rprintf("concord nde %d nce %d ndn %d ncn %d nddyads %d MHp->ratio %f\n", */ /* nde, nce, ndn,ncn,nddyads, MHp->ratio); */ } /* Rprintf("h0 %d t0 %d h1 %d t1 %d\n", Mtail[0], Mhead[0], */ /* Mtail[1], Mhead[1]); */ }