SNAP Library , Developer Reference  2013-01-07 14:03:36
SNAP, a general purpose, high performance system for analysis and manipulation of large networks
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
triad.h
Go to the documentation of this file.
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