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