SNAP Library, User Reference  2012-10-15 15:06:59
SNAP, a general purpose network analysis and graph mining library
 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, p1, o1), 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