SNAP Library , Developer Reference
2013-01-07 14:03:36
SNAP, a general purpose, high performance system for analysis and manipulation of large networks
|
00001 namespace TSnap { 00002 00004 // Triads and clustering coefficient 00005 00007 template <class PGraph> double GetClustCf(const PGraph& Graph, int SampleNodes=-1); 00011 template <class PGraph> double GetClustCf(const PGraph& Graph, TFltPrV& DegToCCfV, int SampleNodes=-1); 00015 template <class PGraph> double GetClustCf(const PGraph& Graph, TFltPrV& DegToCCfV, int& ClosedTriads, int& OpenTriads, int SampleNodes=-1); 00017 template <class PGraph> double GetNodeClustCf(const PGraph& Graph, const int& NId); 00019 template <class PGraph> void GetNodeClustCf(const PGraph& Graph, TIntFltH& NIdCCfH); 00020 00023 template <class PGraph> int GetTriads(const PGraph& Graph, int SampleNodes=-1); 00026 template <class PGraph> int GetTriads(const PGraph& Graph, int& ClosedTriads, int& OpenTriads, int SampleNodes); 00030 template <class PGraph> void GetTriads(const PGraph& Graph, TIntTrV& NIdCOTriadV, int SampleNodes=-1); 00033 template <class PGraph> int GetTriadEdges(const PGraph& Graph, int SampleEdges=-1); 00034 00036 template <class PGraph> int GetNodeTriads(const PGraph& Graph, const int& NId); 00038 template <class PGraph> int GetNodeTriads(const PGraph& Graph, const int& NId, int& ClosedTriads, int& OpenTriads); 00042 template <class PGraph> int GetNodeTriads(const PUNGraph& Graph, const int& NId, const TIntSet& GroupSet, int& InGroupEdges, int& InOutGroupEdges); 00047 template <class PGraph> int GetNodeTriads(const PUNGraph& Graph, const int& NId, const TIntSet& GroupSet, int& InGroupEdges, int& InOutGroupEdges, int& OutGroup); 00049 template <class PGraph> void GetTriadParticip(const PGraph& Graph, TIntPrV& TriadCntV); 00050 00052 template<class PGraph> int GetCmnNbrs(const PGraph& Graph, const int& NId1, const int& NId2); 00054 template<class PGraph> int GetCmnNbrs(const PGraph& Graph, const int& NId1, const int& NId2, TIntV& NbrV); 00056 template<class PGraph> int GetLen2Paths(const PGraph& Graph, const int& NId1, const int& NId2); 00058 template<class PGraph> int GetLen2Paths(const PGraph& Graph, const int& NId1, const int& NId2, TIntV& NbrV); 00059 00060 00062 // Implementation 00063 00064 template <class PGraph> double GetClustCf(const PGraph& Graph, int SampleNodes) { 00065 TIntTrV NIdCOTriadV; 00066 GetTriads(Graph, NIdCOTriadV, SampleNodes); 00067 if (NIdCOTriadV.Empty()) { return 0.0; } 00068 double SumCcf = 0.0; 00069 for (int i = 0; i < NIdCOTriadV.Len(); i++) { 00070 const double OpenCnt = NIdCOTriadV[i].Val2()+NIdCOTriadV[i].Val3(); 00071 if (OpenCnt > 0) { 00072 SumCcf += NIdCOTriadV[i].Val2() / OpenCnt; } 00073 } 00074 IAssert(SumCcf>=0); 00075 return SumCcf / double(NIdCOTriadV.Len()); 00076 } 00077 00078 template <class PGraph> double GetClustCf(const PGraph& Graph, TFltPrV& DegToCCfV, int SampleNodes) { 00079 TIntTrV NIdCOTriadV; 00080 GetTriads(Graph, NIdCOTriadV, SampleNodes); 00081 THash<TInt, TFltPr> DegSumCnt; 00082 double SumCcf = 0.0; 00083 for (int i = 0; i < NIdCOTriadV.Len(); i++) { 00084 const int D = NIdCOTriadV[i].Val2()+NIdCOTriadV[i].Val3(); 00085 const double Ccf = D!=0 ? NIdCOTriadV[i].Val2() / double(D) : 0.0; 00086 TFltPr& SumCnt = DegSumCnt.AddDat(Graph->GetNI(NIdCOTriadV[i].Val1).GetDeg()); 00087 SumCnt.Val1 += Ccf; 00088 SumCnt.Val2 += 1; 00089 SumCcf += Ccf; 00090 } 00091 // get average clustering coefficient for each degree 00092 DegToCCfV.Gen(DegSumCnt.Len(), 0); 00093 for (int d = 0; d < DegSumCnt.Len(); d++) { 00094 DegToCCfV.Add(TFltPr(DegSumCnt.GetKey(d).Val, double(DegSumCnt[d].Val1()/DegSumCnt[d].Val2()))); 00095 } 00096 DegToCCfV.Sort(); 00097 return SumCcf / double(NIdCOTriadV.Len()); 00098 } 00099 00100 template <class PGraph> 00101 double GetClustCf(const PGraph& Graph, TFltPrV& DegToCCfV, int& ClosedTriads, int& OpenTriads, int SampleNodes) { 00102 TIntTrV NIdCOTriadV; 00103 GetTriads(Graph, NIdCOTriadV, SampleNodes); 00104 THash<TInt, TFltPr> DegSumCnt; 00105 double SumCcf = 0.0; 00106 uint64 closedTriads = 0; 00107 uint64 openTriads = 0; 00108 for (int i = 0; i < NIdCOTriadV.Len(); i++) { 00109 const int D = NIdCOTriadV[i].Val2()+NIdCOTriadV[i].Val3(); 00110 const double Ccf = D!=0 ? NIdCOTriadV[i].Val2() / double(D) : 0.0; 00111 closedTriads += NIdCOTriadV[i].Val2; 00112 openTriads += NIdCOTriadV[i].Val3; 00113 TFltPr& SumCnt = DegSumCnt.AddDat(Graph->GetNI(NIdCOTriadV[i].Val1).GetDeg()); 00114 SumCnt.Val1 += Ccf; 00115 SumCnt.Val2 += 1; 00116 SumCcf += Ccf; 00117 } 00118 // get average clustering coefficient for each degree 00119 DegToCCfV.Gen(DegSumCnt.Len(), 0); 00120 for (int d = 0; d < DegSumCnt.Len(); d++) { 00121 DegToCCfV.Add(TFltPr(DegSumCnt.GetKey(d).Val, DegSumCnt[d].Val1()/DegSumCnt[d].Val2())); 00122 } 00123 if(closedTriads/3 > (uint64) TInt::Mx) { WarnNotify(TStr::Fmt("[%s line %d] %g closed triads.\n", __FILE__, __LINE__, float(closedTriads/3)).CStr()); } 00124 if(openTriads > (uint64) TInt::Mx) { WarnNotify(TStr::Fmt("[%s line %d] %g open triads.\n", __FILE__, __LINE__, float(openTriads/3)).CStr()); } 00125 ClosedTriads = int(closedTriads/3); // each triad is counted 3 times 00126 OpenTriads = int(openTriads); 00127 DegToCCfV.Sort(); 00128 return SumCcf / double(NIdCOTriadV.Len()); 00129 } 00130 00131 template <class PGraph> 00132 double GetNodeClustCf(const PGraph& Graph, const int& NId) { 00133 int Open, Closed; 00134 GetNodeTriads(Graph, NId, Open, Closed); 00135 //const double Deg = Graph->GetNI(NId).GetDeg(); 00136 return (Open+Closed)==0 ? 0 : double(Open)/double(Open+Closed); 00137 } 00138 00139 template <class PGraph> 00140 void GetNodeClustCf(const PGraph& Graph, TIntFltH& NIdCCfH) { 00141 TIntTrV NIdCOTriadV; 00142 GetTriads(Graph, NIdCOTriadV); 00143 NIdCCfH.Clr(false); 00144 for (int i = 0; i < NIdCOTriadV.Len(); i++) { 00145 const int D = NIdCOTriadV[i].Val2()+NIdCOTriadV[i].Val3(); 00146 const double CCf = D!=0 ? NIdCOTriadV[i].Val2() / double(D) : 0.0; 00147 NIdCCfH.AddDat(NIdCOTriadV[i].Val1, CCf); 00148 } 00149 } 00150 00151 template <class PGraph> 00152 int GetTriads(const PGraph& Graph, int SampleNodes) { 00153 int OpenTriads, ClosedTriads; 00154 return GetTriads(Graph, ClosedTriads, OpenTriads, SampleNodes); 00155 } 00156 00157 template <class PGraph> 00158 int GetTriads(const PGraph& Graph, int& ClosedTriads, int& OpenTriads, int SampleNodes) { 00159 TIntTrV NIdCOTriadV; 00160 GetTriads(Graph, NIdCOTriadV, SampleNodes); 00161 uint64 closedTriads = 0; 00162 uint64 openTriads = 0; 00163 for (int i = 0; i < NIdCOTriadV.Len(); i++) { 00164 closedTriads += NIdCOTriadV[i].Val2; 00165 openTriads += NIdCOTriadV[i].Val3; 00166 } 00167 IAssert(closedTriads/3 < (uint64) TInt::Mx); 00168 IAssert(openTriads < (uint64) TInt::Mx); 00169 ClosedTriads = int(closedTriads/3); // each triad is counted 3 times 00170 OpenTriads = int(openTriads); 00171 return ClosedTriads; 00172 } 00173 00174 // Function pretends that the graph is undirected (count unique connected triples of nodes) 00175 template <class PGraph> 00176 void GetTriads(const PGraph& Graph, TIntTrV& NIdCOTriadV, int SampleNodes) { 00177 const bool IsDir = Graph->HasFlag(gfDirected); 00178 TIntSet NbrH; 00179 TIntV NIdV; 00180 TRnd Rnd(0); 00181 00182 Graph->GetNIdV(NIdV); 00183 NIdV.Shuffle(Rnd); 00184 if (SampleNodes == -1) { 00185 SampleNodes = Graph->GetNodes(); } 00186 NIdCOTriadV.Clr(false); 00187 NIdCOTriadV.Reserve(SampleNodes); 00188 for (int node = 0; node < SampleNodes; node++) { 00189 typename PGraph::TObj::TNodeI NI = Graph->GetNI(NIdV[node]); 00190 if (NI.GetDeg() < 2) { 00191 NIdCOTriadV.Add(TIntTr(NI.GetId(), 0, 0)); // zero triangles 00192 continue; 00193 } 00194 // find neighborhood 00195 NbrH.Clr(false); 00196 for (int e = 0; e < NI.GetOutDeg(); e++) { 00197 if (NI.GetOutNId(e) != NI.GetId()) { 00198 NbrH.AddKey(NI.GetOutNId(e)); } 00199 } 00200 if (IsDir) { 00201 for (int e = 0; e < NI.GetInDeg(); e++) { 00202 if (NI.GetInNId(e) != NI.GetId()) { 00203 NbrH.AddKey(NI.GetInNId(e)); } 00204 } 00205 } 00206 // count connected neighbors 00207 int OpenCnt=0, CloseCnt=0; 00208 for (int srcNbr = 0; srcNbr < NbrH.Len(); srcNbr++) { 00209 const typename PGraph::TObj::TNodeI SrcNode = Graph->GetNI(NbrH.GetKey(srcNbr)); 00210 for (int dstNbr = srcNbr+1; dstNbr < NbrH.Len(); dstNbr++) { 00211 const int dstNId = NbrH.GetKey(dstNbr); 00212 if (SrcNode.IsNbrNId(dstNId)) { CloseCnt++; } // is edge 00213 else { OpenCnt++; } 00214 } 00215 } 00216 IAssert(2*(OpenCnt+CloseCnt) == NbrH.Len()*(NbrH.Len()-1)); 00217 NIdCOTriadV.Add(TIntTr(NI.GetId(), CloseCnt, OpenCnt)); 00218 } 00219 } 00220 00221 // Count the number of edges that participate in at least one triad 00222 template <class PGraph> 00223 int GetTriadEdges(const PGraph& Graph, int SampleEdges) { 00224 const bool IsDir = Graph->HasFlag(gfDirected); 00225 TIntSet NbrH; 00226 int TriadEdges = 0; 00227 for(typename PGraph::TObj::TNodeI NI = Graph->BegNI(); NI < Graph->EndNI(); NI++) { 00228 NbrH.Clr(false); 00229 for (int e = 0; e < NI.GetOutDeg(); e++) { 00230 if (NI.GetOutNId(e) != NI.GetId()) { 00231 NbrH.AddKey(NI.GetOutNId(e)); } 00232 } 00233 if (IsDir) { 00234 for (int e = 0; e < NI.GetInDeg(); e++) { 00235 if (NI.GetInNId(e) != NI.GetId()) { 00236 NbrH.AddKey(NI.GetInNId(e)); } 00237 } 00238 } 00239 for (int e = 0; e < NI.GetOutDeg(); e++) { 00240 if (!IsDir && NI.GetId()<NI.GetOutNId(e)) { continue; } // for undirected graphs count each edge only once 00241 const typename PGraph::TObj::TNodeI SrcNode = Graph->GetNI(NI.GetOutNId(e)); 00242 bool Triad=false; 00243 for (int e1 = 0; e1 < SrcNode.GetOutDeg(); e1++) { 00244 if (NbrH.IsKey(SrcNode.GetOutNId(e1))) { Triad=true; break; } 00245 } 00246 if (IsDir && ! Triad) { 00247 for (int e1 = 0; e1 < SrcNode.GetInDeg(); e1++) { 00248 if (NbrH.IsKey(SrcNode.GetInNId(e1))) { Triad=true; break; } 00249 } 00250 } 00251 if (Triad) { TriadEdges++; } 00252 } 00253 } 00254 return TriadEdges; 00255 } 00256 00257 // Returns number of undirected triads a node participates in 00258 template <class PGraph> 00259 int GetNodeTriads(const PGraph& Graph, const int& NId) { 00260 int ClosedTriads=0, OpenTriads=0; 00261 return GetNodeTriads(Graph, NId, ClosedTriads, OpenTriads); 00262 } 00263 00264 // Return number of undirected triads a node participates in 00265 template <class PGraph> 00266 int GetNodeTriads(const PGraph& Graph, const int& NId, int& ClosedTriads, int& OpenTriads) { 00267 const typename PGraph::TObj::TNodeI NI = Graph->GetNI(NId); 00268 ClosedTriads=0; OpenTriads=0; 00269 if (NI.GetDeg() < 2) { return 0; } 00270 // find neighborhood 00271 TIntSet NbrSet(NI.GetDeg()); 00272 for (int e = 0; e < NI.GetOutDeg(); e++) { 00273 if (NI.GetOutNId(e) != NI.GetId()) { // exclude self edges 00274 NbrSet.AddKey(NI.GetOutNId(e)); } 00275 } 00276 if (Graph->HasFlag(gfDirected)) { 00277 for (int e = 0; e < NI.GetInDeg(); e++) { 00278 if (NI.GetInNId(e) != NI.GetId()) { // exclude self edges 00279 NbrSet.AddKey(NI.GetInNId(e)); } 00280 } 00281 } 00282 // count connected neighbors 00283 for (int srcNbr = 0; srcNbr < NbrSet.Len(); srcNbr++) { 00284 const typename PGraph::TObj::TNodeI SrcNode = Graph->GetNI(NbrSet.GetKey(srcNbr)); 00285 for (int dstNbr = srcNbr+1; dstNbr < NbrSet.Len(); dstNbr++) { 00286 const int dstNId = NbrSet.GetKey(dstNbr); 00287 if (SrcNode.IsNbrNId(dstNId)) { ClosedTriads++; } 00288 else { OpenTriads++; } 00289 } 00290 } 00291 return ClosedTriads; 00292 } 00293 00294 // Node NId and a subset of its neighbors GroupSet 00295 // InGroupEdges ... triads (NId, g1, g2), where g1 and g2 are in GroupSet 00296 // InOutGroupEdges ... triads (NId, g1, o1), where g1 in GroupSet and o1 not in GroupSet 00297 template <class PGraph> 00298 int GetNodeTriads(const PGraph& Graph, const int& NId, const TIntSet& GroupSet, int& InGroupEdges, int& InOutGroupEdges) { 00299 const typename PGraph::TObj::TNodeI NI = Graph->GetNI(NId); 00300 const bool IsDir = Graph->HasFlag(gfDirected); 00301 InGroupEdges=0; InOutGroupEdges=0; 00302 if (NI.GetDeg() < 2 || GroupSet.Empty()) { return 0; } 00303 // find neighborhood 00304 TIntSet NbrSet(NI.GetDeg()); 00305 for (int e = 0; e < NI.GetOutDeg(); e++) { 00306 if (NI.GetOutNId(e) != NI.GetId()) { // exclude self edges 00307 NbrSet.AddKey(NI.GetOutNId(e)); } 00308 } 00309 if (IsDir) { 00310 for (int e = 0; e < NI.GetInDeg(); e++) { 00311 if (NI.GetInNId(e) != NI.GetId()) { 00312 NbrSet.AddKey(NI.GetInNId(e)); } 00313 } 00314 } 00315 // count connected neighbors 00316 for (int srcGrp = 0; srcGrp < GroupSet.Len(); srcGrp++) { 00317 IAssert(NbrSet.IsKey(GroupSet[srcGrp])); 00318 const typename PGraph::TObj::TNodeI SrcNode = Graph->GetNI(GroupSet.GetKey(srcGrp)); 00319 for (int i = 0; i < SrcNode.GetOutDeg(); i++) { 00320 const int dst = SrcNode.GetOutNId(i); 00321 if (! NbrSet.IsKey(dst)) { continue; } // not triangle 00322 if (GroupSet.IsKey(dst)) { InGroupEdges++; } 00323 else { InOutGroupEdges++; } 00324 } 00325 } 00326 return InGroupEdges; 00327 } 00328 00329 // Node NId and a subset of its neighbors GroupSet 00330 // InGroupEdges ... triads (NId, g1, g2), where g1 and g2 are in GroupSet 00331 // InOutGroupEdges ... triads (NId, g1, o1), where g1 in GroupSet and o1 not in GroupSet 00332 // OutGroupEdges ... triads (NId, o1, o2), where o1 and o2 are not in GroupSet 00333 template <class PGraph> 00334 int GetNodeTriads(const PGraph& Graph, const int& NId, const TIntSet& GroupSet, int& InGroupEdges, int& InOutGroupEdges, int& OutGroupEdges) { 00335 const typename PGraph::TObj::TNodeI NI = Graph->GetNI(NId); 00336 const bool IsDir = Graph->HasFlag(gfDirected); 00337 InGroupEdges=0; InOutGroupEdges=0; OutGroupEdges=0; 00338 if (NI.GetDeg() < 2 || GroupSet.Empty()) { return 0; } 00339 // find neighborhood 00340 TIntSet NbrSet(NI.GetDeg()); 00341 for (int e = 0; e < NI.GetOutDeg(); e++) { 00342 if (NI.GetOutNId(e) != NI.GetId()) { // exclude self edges 00343 NbrSet.AddKey(NI.GetOutNId(e)); } 00344 } 00345 if (IsDir) { 00346 for (int e = 0; e < NI.GetInDeg(); e++) { 00347 if (NI.GetInNId(e) != NI.GetId()) { 00348 NbrSet.AddKey(NI.GetInNId(e)); } 00349 } 00350 } 00351 // count connected neighbors 00352 for (int srcNbr = 0; srcNbr < NbrSet.Len(); srcNbr++) { 00353 const int NbrId = NbrSet.GetKey(srcNbr); 00354 const bool NbrIn = GroupSet.IsKey(NbrId); 00355 const typename PGraph::TObj::TNodeI SrcNode = Graph->GetNI(NbrId); 00356 for (int dstNbr = srcNbr+1; dstNbr < NbrSet.Len(); dstNbr++) { 00357 const int DstNId = NbrSet.GetKey(dstNbr); 00358 if (SrcNode.IsNbrNId(DstNId)) { // triad (NId, NbrId, DstNid) 00359 bool DstIn = GroupSet.IsKey(DstNId); 00360 if (NbrIn && DstIn) { InGroupEdges++; } 00361 else if (NbrIn || DstIn) { InOutGroupEdges++; } 00362 else { OutGroupEdges++; } 00363 } 00364 } 00365 } 00366 return InGroupEdges; 00367 } 00368 00369 // For each node count how many triangles it participates in 00370 template <class PGraph> 00371 void GetTriadParticip(const PGraph& Graph, TIntPrV& TriadCntV) { 00372 TIntH TriadCntH; 00373 for (typename PGraph::TObj::TNodeI NI = Graph->BegNI(); NI < Graph->EndNI(); NI++) { 00374 const int Triads = GetNodeTriads(Graph, NI.GetId()); 00375 TriadCntH.AddDat(Triads) += 1; 00376 } 00377 TriadCntH.GetKeyDatPrV(TriadCntV); 00378 TriadCntV.Sort(); 00379 } 00380 00381 template<class PGraph> 00382 int GetCmnNbrs(const PGraph& Graph, const int& NId1, const int& NId2) { 00383 TIntV NbrV; 00384 return GetCmnNbrs(Graph, NId1, NId2, NbrV); 00385 } 00386 00387 // Get common neighbors between a pair of nodes (undirected) 00388 template<class PGraph> 00389 int GetCmnNbrs(const PGraph& Graph, const int& NId1, const int& NId2, TIntV& NbrV) { 00390 if (! Graph->IsNode(NId1) || ! Graph->IsNode(NId2)) { NbrV.Clr(false); return 0; } 00391 typename PGraph::TObj::TNodeI NI1 = Graph->GetNI(NId1); 00392 typename PGraph::TObj::TNodeI NI2 = Graph->GetNI(NId2); 00393 NbrV.Clr(false); 00394 NbrV.Reserve(TMath::Mn(NI1.GetDeg(), NI2.GetDeg())); 00395 TIntSet NSet1(NI1.GetDeg()), NSet2(NI2.GetDeg()); 00396 for (int i = 0; i < NI1.GetDeg(); i++) { 00397 const int nid = NI1.GetNbrNId(i); 00398 if (nid!=NId1 && nid!=NId2) { 00399 NSet1.AddKey(nid); } 00400 } 00401 for (int i = 0; i < NI2.GetDeg(); i++) { 00402 const int nid = NI2.GetNbrNId(i); 00403 if (NSet1.IsKey(nid)) { 00404 NSet2.AddKey(nid); 00405 } 00406 } 00407 NSet2.GetKeyV(NbrV); 00408 return NbrV.Len(); 00409 } 00410 00411 template<> 00412 inline int GetCmnNbrs<PUNGraph>(const PUNGraph& Graph, const int& NId1, const int& NId2, TIntV& NbrV) { 00413 if (! Graph->IsNode(NId1) || ! Graph->IsNode(NId2)) { NbrV.Clr(false); return 0; } 00414 const TUNGraph::TNodeI NI1 = Graph->GetNI(NId1); 00415 const TUNGraph::TNodeI NI2 = Graph->GetNI(NId2); 00416 int i=0, j=0; 00417 NbrV.Clr(false); 00418 NbrV.Reserve(TMath::Mn(NI1.GetDeg(), NI2.GetDeg())); 00419 while (i < NI1.GetDeg() && j < NI2.GetDeg()) { 00420 const int nid = NI1.GetNbrNId(i); 00421 while (j < NI2.GetDeg() && NI2.GetNbrNId(j) < nid) { j++; } 00422 if (j < NI2.GetDeg() && nid==NI2.GetNbrNId(j) && nid!=NId1 && nid!=NId2) { 00423 IAssert(NbrV.Empty() || NbrV.Last() < nid); 00424 NbrV.Add(nid); 00425 j++; 00426 } 00427 i++; 00428 } 00429 return NbrV.Len(); 00430 } 00431 00432 // get number of length 2 directed paths between a pair of nodes 00433 // for a pair of nodes (i,j): |{u: (i,u) and (u,j) }| 00434 template<class PGraph> 00435 int GetLen2Paths(const PGraph& Graph, const int& NId1, const int& NId2) { 00436 TIntV NbrV; 00437 return GetLen2Paths(Graph, NId1, NId2, NbrV); 00438 } 00439 00440 // get number of length 2 directed paths between a pair of nodes 00441 // for a pair of nodes (i,j): {u: (i,u) and (u,j) } 00442 template<class PGraph> 00443 int GetLen2Paths(const PGraph& Graph, const int& NId1, const int& NId2, TIntV& NbrV) { 00444 const typename PGraph::TNodeI NI = Graph->GetNI(NId1); 00445 NbrV.Clr(false); 00446 NbrV.Reserve(NI.GetOutDeg()); 00447 for (int e = 0; e < NI.GetOutDeg(); e++) { 00448 const typename PGraph::TNodeI MidNI = GetNI(NI.GetOutNId(e)); 00449 if (MidNI.IsOutNId(NId2)) { 00450 NbrV.Add(MidNI.GetId()); 00451 } 00452 } 00453 return NbrV.Len(); 00454 } 00455 00456 }; // mamespace TSnap 00457 00459 // Node and Edge Network Constraint (by Ron Burt) 00460 // works for directed and undirected graphs (but not for multigraphs) 00461 template <class PGraph> 00462 class TNetConstraint { 00463 public: 00464 PGraph Graph; 00465 THash<TIntPr, TFlt> NodePrCH; // pairs of nodes that have non-zero network constraint 00466 public: 00467 TNetConstraint(const PGraph& GraphPt, const bool& CalcaAll=true); 00468 int Len() const { return NodePrCH.Len(); } 00469 double GetC(const int& ConstraintN) const { return NodePrCH[ConstraintN]; } 00470 TIntPr GetNodePr(const int& ConstraintN) const { return NodePrCH.GetKey(ConstraintN); } 00471 double GetEdgeC(const int& NId1, const int& NId2) const; 00472 double GetNodeC(const int& NId) const; 00473 void AddConstraint(const int& NId1, const int& NId2); 00474 void CalcConstraints(); 00475 void CalcConstraints(const int& NId); 00476 void Dump() const; 00477 static void Test(); 00478 }; 00479 00480 template <class PGraph> 00481 TNetConstraint<PGraph>::TNetConstraint(const PGraph& GraphPt, const bool& CalcaAll) : Graph(GraphPt) { 00482 CAssert(! HasGraphFlag(typename PGraph::TObj, gfMultiGraph)); // must not be multigraph 00483 if (CalcaAll) { 00484 CalcConstraints(); 00485 } 00486 } 00487 00488 template <class PGraph> 00489 double TNetConstraint<PGraph>::GetEdgeC(const int& NId1, const int& NId2) const { 00490 if (NodePrCH.IsKey(TIntPr(NId1, NId2))) { 00491 return NodePrCH.GetDat(TIntPr(NId1, NId2)); } 00492 else { 00493 return 0.0; } 00494 } 00495 00496 template <class PGraph> 00497 double TNetConstraint<PGraph>::GetNodeC(const int& NId) const { 00498 typename PGraph::TObj::TNodeI NI1 = Graph->GetNI(NId); 00499 if (NI1.GetOutDeg() == 0) { return 0.0; } 00500 int KeyId = -1; 00501 for (int k = 0; k<NI1.GetOutDeg(); k++) { 00502 KeyId = NodePrCH.GetKeyId(TIntPr(NI1.GetId(), NI1.GetOutNId(k))); 00503 if (KeyId > -1) { break; } 00504 } 00505 if (KeyId < 0) { return 0.0; } 00506 double Constraint = NodePrCH[KeyId]; 00507 for (int i = KeyId-1; i >-1 && NodePrCH.GetKey(i).Val1()==NId; i--) { 00508 Constraint += NodePrCH[i]; 00509 } 00510 for (int i = KeyId+1; i < NodePrCH.Len() && NodePrCH.GetKey(i).Val1()==NId; i++) { 00511 Constraint += NodePrCH[i]; 00512 } 00513 return Constraint; 00514 } 00515 00516 template <class PGraph> 00517 void TNetConstraint<PGraph>::AddConstraint(const int& NId1, const int& NId2) { 00518 if (NId1==NId2 || NodePrCH.IsKey(TIntPr(NId1, NId2))) { 00519 return; 00520 } 00521 typename PGraph::TObj::TNodeI NI1 = Graph->GetNI(NId1); 00522 double Constraint = 0.0; 00523 if (NI1.IsOutNId(NId2)) { // is direct edge 00524 Constraint += 1.0/(double) NI1.GetOutDeg(); 00525 } 00526 const double SrcC = 1.0/(double) NI1.GetOutDeg(); 00527 for (int e = 0; e < NI1.GetOutDeg(); e++) { 00528 const int MidNId = NI1.GetOutNId(e); 00529 if (MidNId == NId1 || MidNId == NId2) { continue; } 00530 const typename PGraph::TObj::TNodeI MidNI = Graph->GetNI(MidNId); 00531 if (MidNI.IsOutNId(NId2)) { 00532 Constraint += SrcC * (1.0/(double)MidNI.GetOutDeg()); 00533 } 00534 } 00535 if (Constraint==0) { return; } 00536 Constraint = TMath::Sqr(Constraint); 00537 NodePrCH.AddDat(TIntPr(NId1, NId2), Constraint); 00538 } 00539 00540 template <class PGraph> 00541 void TNetConstraint<PGraph>::CalcConstraints() { 00542 // add edges 00543 for (typename PGraph::TObj::TEdgeI EI = Graph->BegEI(); EI < Graph->EndEI(); EI++) { 00544 AddConstraint(EI.GetSrcNId(), EI.GetDstNId()); 00545 AddConstraint(EI.GetDstNId(), EI.GetSrcNId()); 00546 } 00547 // add open triads 00548 for (typename PGraph::TObj::TNodeI NI = Graph->BegNI(); NI < Graph->EndNI(); NI++) { 00549 for (int i = 0; i < NI.GetDeg(); i++) { 00550 const int NId1 = NI.GetNbrNId(i); 00551 for (int j = 0; j < NI.GetDeg(); j++) { 00552 const int NId2 = NI.GetNbrNId(j); 00553 AddConstraint(NId1, NId2); 00554 } 00555 } 00556 } 00557 NodePrCH.SortByKey(); 00558 } 00559 00560 // calculate constraints around a node id 00561 template <class PGraph> 00562 void TNetConstraint<PGraph>::CalcConstraints(const int& NId) { 00563 typename PGraph::TObj::TNodeI StartNI = Graph->GetNI(NId); 00564 TIntSet SeenSet; 00565 for (int e = 0; e < StartNI.GetOutDeg(); e++) { 00566 typename PGraph::TObj::TNodeI MidNI = Graph->GetNI(StartNI.GetOutNId(e)); 00567 AddConstraint(NId, MidNI.GetId()); 00568 for (int i = 0; i < MidNI.GetOutDeg(); i++) { 00569 const int EndNId = MidNI.GetOutNId(i); 00570 if (! SeenSet.IsKey(EndNId)) { 00571 AddConstraint(NId, EndNId); 00572 SeenSet.AddKey(EndNId); 00573 } 00574 } 00575 } 00576 } 00577 00578 template <class PGraph> 00579 void TNetConstraint<PGraph>::Dump() const { 00580 printf("Edge network constraint: (%d, %d)\n", Graph->GetNodes(), Graph->GetEdges()); 00581 for (int e = 0; e < NodePrCH.Len(); e++) { 00582 printf(" %4d %4d : %f\n", NodePrCH.GetKey(e).Val1(), NodePrCH.GetKey(e).Val2(), NodePrCH[e].Val); 00583 } 00584 printf("\n"); 00585 } 00586 00587 // example from page 56 of Structural Holes by Ronald S. Burt 00588 // (http://www.amazon.com/Structural-Holes-Social-Structure-Competition) 00589 template <class PGraph> 00590 void TNetConstraint<PGraph>::Test() { 00591 PUNGraph G = TUNGraph::New(); 00592 G->AddNode(0); G->AddNode(1); G->AddNode(2); G->AddNode(3); 00593 G->AddNode(4); G->AddNode(5); G->AddNode(6); 00594 G->AddEdge(0,1); G->AddEdge(0,2); G->AddEdge(0,3); G->AddEdge(0,4); G->AddEdge(0,5); G->AddEdge(0,6); 00595 G->AddEdge(1,2); G->AddEdge(1,5); G->AddEdge(1,6); 00596 G->AddEdge(2,4); 00597 TNetConstraint<PUNGraph> NetConstraint(G, true); 00598 // NetConstraint.CalcConstraints(0); 00599 NetConstraint.Dump(); 00600 printf("middle node network constraint: %f\n", NetConstraint.GetNodeC(0)); 00601 } 00602