SNAP Library 2.2, Developer Reference  2014-03-11 19:15:55
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
cascnetinf.cpp
Go to the documentation of this file.
00001 #include "stdafx.h"
00002 #include "cascnetinf.h"
00003 
00004 double TCascade::TransProb(const int& NId1, const int& NId2) const {
00005   if (!IsNode(NId1) || !IsNode(NId2)) { return Eps.Val; }
00006   if (GetTm(NId1) >= GetTm(NId2)) { return Eps.Val; }
00007   if (Model==0)
00008     return Alpha*exp(-Alpha*(GetTm(NId2)-GetTm(NId1))); // exponential
00009   else if (Model==1)
00010     return (Alpha-1)*pow((GetTm(NId2)-GetTm(NId1)), -Alpha); // power-law
00011   else
00012     return Alpha*(GetTm(NId2)-GetTm(NId1))*exp(-0.5*Alpha*pow(GetTm(NId2)-GetTm(NId1), 2)); // rayleigh
00013 
00014   return (-1);
00015 }
00016 
00017 double TCascade::GetProb(const PNGraph& G) {
00018     double P = 0;
00019     for (int n = 0; n < Len(); n++) {
00020       const int DstNId = GetNode(n);
00021       const double DstTm = GetTm(DstNId);
00022       TNGraph::TNodeI NI = G->GetNI(DstNId);
00023       double MxProb = log(Eps);
00024       int BestParent = -1;
00025       for (int e = 0; e < NI.GetInDeg(); e++) {
00026         const int SrcNId = NI.GetInNId(e);
00027         if (IsNode(SrcNId) && GetTm(SrcNId) < DstTm) {
00028           const double Prob = log(TransProb(SrcNId, DstNId));
00029           if (MxProb < Prob) { MxProb = Prob;  BestParent = SrcNId; }
00030         }
00031       }
00032       NIdHitH.GetDat(DstNId).Parent = BestParent;
00033       P += MxProb;
00034     }
00035 
00036     return P;
00037 }
00038 
00039 // init prob of a cascade in an empty graph
00040 void TCascade::InitProb() {
00041     CurProb = log(Eps) * Len();
00042     for (int i = 0; i < Len(); i++) {
00043       NIdHitH[i].Parent = -1; }
00044 }
00045 
00046 // update the cascade probability given a new edge (N1, N2) in the graph
00047 double TCascade::UpdateProb(const int& N1, const int& N2, const bool& UpdateProb) {
00048     if (!IsNode(N1) || !IsNode(N2)) { return CurProb; }
00049     if (GetTm(N1) >= GetTm(N2)) { return CurProb; }
00050     const double P1 = log(TransProb(GetParent(N2), N2));
00051     const double P2 = log(TransProb(N1, N2)); // N1 influences N2
00052     if (P1 < P2) {
00053       if (UpdateProb) { // the edge is there, update the CurProb and best Parent
00054         CurProb = CurProb - P1 + P2;
00055         NIdHitH.GetDat(N2).Parent = N1;
00056       } else {
00057         return CurProb - P1 + P2; }
00058     }
00059     return CurProb;
00060 }
00061 
00062 void TNetInfBs::LoadCascadesTxt(TSIn& SIn, const int& Model, const double& alpha) {
00063   TStr Line;
00064   while (!SIn.Eof()) {
00065     SIn.GetNextLn(Line);
00066     if (Line=="") { break; }
00067     TStrV NIdV; Line.SplitOnAllCh(',', NIdV);
00068       AddNodeNm(NIdV[0].GetInt(), TNodeInfo(NIdV[1], 0)); 
00069   }
00070   printf("All nodes read!\n");
00071   while (!SIn.Eof()) { SIn.GetNextLn(Line); AddCasc(Line, Model, alpha); }
00072   printf("All cascades read!\n");
00073 }
00074 
00075 void TNetInfBs::LoadGroundTruthTxt(TSIn& SIn) {
00076   GroundTruth = TNGraph::New(); TStr Line;
00077 
00078   // add nodes
00079   while (!SIn.Eof()) {
00080     SIn.GetNextLn(Line);
00081     if (Line=="") { break; }
00082     TStrV NIdV; Line.SplitOnAllCh(',', NIdV);
00083     GroundTruth->AddNode(NIdV[0].GetInt());
00084   }
00085 
00086   // add edges
00087   while (!SIn.Eof()) {
00088     SIn.GetNextLn(Line);
00089     TStrV NIdV; Line.SplitOnAllCh(',', NIdV);
00090     GroundTruth->AddEdge(NIdV[0].GetInt(), NIdV[1].GetInt());
00091     if (NIdV.Len()>2) { Alphas.AddDat(TIntPr(NIdV[0].GetInt(), NIdV[1].GetInt())) = NIdV[2].GetFlt(); }
00092     else { Alphas.AddDat(TIntPr(NIdV[0].GetInt(), NIdV[1].GetInt())) = 1.0; }
00093   }
00094 
00095   printf("groundtruth nodes:%d edges:%d\n", GroundTruth->GetNodes(), GroundTruth->GetEdges());
00096 }
00097 
00098 void TNetInfBs::AddCasc(const TStr& CascStr, const int& Model, const double& alpha) {
00099     TStrV NIdV; CascStr.SplitOnAllCh(',', NIdV);
00100     TCascade C(alpha, Model);
00101     for (int i = 0; i < NIdV.Len(); i+=2) {
00102       int NId;
00103       double Tm; 
00104       NId = NIdV[i].GetInt();
00105       Tm = NIdV[i+1].GetFlt();
00106       GetNodeInfo(NId).Vol = GetNodeInfo(NId).Vol + 1;
00107       C.Add(NId, Tm);
00108     }
00109     C.Sort();
00110     CascV.Add(C);
00111 }
00112 
00113 void TNetInfBs::GenCascade(TCascade& C, const int& TModel, const double &window, TIntPrIntH& EdgesUsed, const double& delta,
00114                const double& std_waiting_time, const double& std_beta) {
00115   TIntFltH InfectedNIdH; TIntH InfectedBy;
00116   double GlobalTime; int StartNId;
00117   double alpha, beta;
00118 
00119   if (GroundTruth->GetNodes() == 0)
00120     return;
00121 
00122   while (C.Len() < 2) {
00123     C.Clr();
00124     InfectedNIdH.Clr();
00125     InfectedBy.Clr();
00126     GlobalTime = 0;
00127 
00128     StartNId = GroundTruth->GetRndNId();
00129     InfectedNIdH.AddDat(StartNId) = GlobalTime;
00130 
00131     while (true) {
00132       // sort by time & get the oldest node that did not run infection
00133       InfectedNIdH.SortByDat(true);
00134       const int& NId = InfectedNIdH.BegI().GetKey();
00135       GlobalTime = InfectedNIdH.BegI().GetDat();
00136 
00137       // all the nodes has run infection
00138       if (GlobalTime >= window)
00139         break;
00140 
00141       // add current oldest node to the network and set its time
00142       C.Add(NId, GlobalTime);
00143 
00144       // run infection from the current oldest node
00145       const TNGraph::TNodeI NI = GroundTruth->GetNI(NId);
00146       for (int e = 0; e < NI.GetOutDeg(); e++) {
00147         const int DstNId = NI.GetOutNId(e);
00148 
00149         beta = Betas.GetDat(TIntPr(NId, DstNId));
00150 
00151         // flip biased coin (set by beta)
00152         if (TInt::Rnd.GetUniDev() > beta+std_beta*TFlt::Rnd.GetNrmDev())
00153           continue;
00154 
00155         alpha = Alphas.GetDat(TIntPr(NId, DstNId));
00156 
00157         // not infecting the parent
00158         if (InfectedBy.IsKey(NId) && InfectedBy.GetDat(NId).Val == DstNId)
00159           continue;
00160 
00161         double sigmaT;
00162         switch (TModel) {
00163         case 0:
00164           // exponential with alpha parameter
00165           sigmaT = TInt::Rnd.GetExpDev(alpha);
00166           break;
00167         case 1:
00168           // power-law with alpha parameter
00169           sigmaT = TInt::Rnd.GetPowerDev(alpha);
00170           while (sigmaT < delta) { sigmaT = TInt::Rnd.GetPowerDev(alpha); }
00171           break;
00172         case 2:
00173           // rayleigh with alpha parameter
00174           sigmaT = TInt::Rnd.GetRayleigh(1/sqrt(alpha));
00175           break;
00176         default:
00177           sigmaT = 1;
00178           break;
00179         }
00180 
00181         // avoid negative time diffs in case of noise
00182         if (std_waiting_time > 0)
00183           sigmaT = TFlt::GetMx(0.0, sigmaT + std_waiting_time*TFlt::Rnd.GetNrmDev());
00184 
00185         double t1 = GlobalTime + sigmaT;
00186 
00187         if (InfectedNIdH.IsKey(DstNId)) {
00188           double t2 = InfectedNIdH.GetDat(DstNId);
00189           if (t2 > t1 && t2 != window) {
00190             InfectedNIdH.GetDat(DstNId) = t1;
00191             InfectedBy.GetDat(DstNId) = NId;
00192           }
00193         } else {
00194           InfectedNIdH.AddDat(DstNId) = t1;
00195           InfectedBy.AddDat(DstNId) = NId;
00196         }
00197       }
00198 
00199       // we cannot delete key (otherwise, we cannot sort), so we assign a big time (window cut-off)
00200       InfectedNIdH.GetDat(NId) = window;
00201     }
00202 
00203   }
00204 
00205   C.Sort();
00206 
00207   for (TIntH::TIter EI = InfectedBy.BegI(); EI < InfectedBy.EndI(); EI++) {
00208     TIntPr Edge(EI.GetDat().Val, EI.GetKey().Val);
00209 
00210     if (!EdgesUsed.IsKey(Edge)) EdgesUsed.AddDat(Edge) = 0;
00211 
00212     EdgesUsed.GetDat(Edge) += 1;
00213   }
00214 }
00215 
00216 void TNetInfBs::Init() {
00217   THash<TInt, TIntV> CascPN;
00218     Graph = TNGraph::New();
00219 
00220     // reset vectors
00221     EdgeGainV.Clr();
00222     CascPerEdge.Clr();
00223     PrecisionRecall.Clr();
00224 
00225     for (int c = 0; c < CascV.Len(); c++) {
00226       for (int i = 0; i < CascV[c].Len(); i++) {
00227         if (!Graph->IsNode(CascV[c].GetNode(i))) Graph->AddNode(CascV[c].GetNode(i));
00228         if (!CascPN.IsKey(CascV[c].GetNode(i))) CascPN.AddDat(CascV[c].GetNode(i)) = TIntV();
00229         CascPN.GetDat(CascV[c].GetNode(i)).Add(c);
00230       }
00231       CascV[c].InitProb();
00232     }
00233 
00234     // only add edges that make sense (i.e., at least once coherent in time)
00235     for (TNGraph::TNodeI NI = Graph->BegNI(); NI < Graph->EndNI(); NI++) {
00236       TIntV &Cascs = CascPN.GetDat(NI.GetId());
00237       for (int c = 0; c < Cascs.Len(); c++) {
00238         for (int i=0; i < CascV[Cascs[c]].Len(); i++) {
00239           if (CascV[Cascs[c]].GetNode(i)==NI.GetId())
00240             continue;
00241 
00242           if (CascV[Cascs[c]].GetTm(CascV[Cascs[c]].GetNode(i)) < CascV[Cascs[c]].GetTm(NI.GetId()) ) {
00243             if (!CascPerEdge.IsKey(TIntPr(CascV[Cascs[c]].GetNode(i), NI.GetId()))) {
00244               EdgeGainV.Add(TPair<TFlt, TIntPr>(TFlt::Mx, TIntPr(CascV[Cascs[c]].GetNode(i), NI.GetId())));
00245               CascPerEdge.AddDat(TIntPr(CascV[Cascs[c]].GetNode(i), NI.GetId())) = TIntV();
00246             }
00247             // Add cascade to hash of cascades per edge (to implement localized update)
00248             CascPerEdge.GetDat(TIntPr(CascV[Cascs[c]].GetNode(i), NI.GetId())).Add(Cascs[c]);
00249           }
00250         }
00251       }
00252     }
00253 }
00254 
00255 double TNetInfBs::GetAllCascProb(const int& EdgeN1, const int& EdgeN2) {
00256     double P = 0.0;
00257 
00258     if (EdgeN1==-1 && EdgeN2==-1) {
00259       for (int c = 0; c < CascV.Len(); c++) {
00260             P += CascV[c].UpdateProb(EdgeN1, EdgeN2, false); } // initial log-likelihood
00261       return P;
00262     }
00263 
00264     TIntV &CascsEdge = CascPerEdge.GetDat(TIntPr(EdgeN1, EdgeN2)); // only check cascades that contain the edge
00265 
00266     for (int c = 0; c < CascsEdge.Len(); c++) {
00267       P += (CascV[CascsEdge[c]].UpdateProb(EdgeN1, EdgeN2, false) - CascV[CascsEdge[c]].CurProb); } // marginal gain
00268     return P;
00269 }
00270 
00271 TIntPr TNetInfBs::GetBestEdge(double& CurProb, double& LastGain, bool& msort, int &attempts) {
00272   TIntPr BestE;
00273   TVec<TInt> KeysV;
00274   TVec<TPair<TFlt, TIntPr> > EdgeGainCopyToSortV;
00275   TIntV EdgeZero;
00276   double BestGain = TFlt::Mn;
00277   int BestGainIndex = -1;
00278 
00279     if (msort) {
00280       for (int i=0; i<TMath::Mn(attempts-1, EdgeGainV.Len()); i++)
00281           EdgeGainCopyToSortV.Add(EdgeGainV[i]);
00282 
00283       // printf("Sorting sublist of size %d of marginal gains!\n", EdgeGainCopyToSortV.Len());
00284 
00285       // sort this list
00286       EdgeGainCopyToSortV.Sort(false);
00287 
00288       // printf("Sublist sorted!\n");
00289 
00290       // clever way of resorting without need to copy (google interview question! :-))
00291       for (int i=0, ii=0, j=0; ii < EdgeGainCopyToSortV.Len(); j++) {
00292         if ( (i+EdgeGainCopyToSortV.Len() < EdgeGainV.Len()) && (EdgeGainCopyToSortV[ii].Val1 < EdgeGainV[i+EdgeGainCopyToSortV.Len()].Val1) ) {
00293           EdgeGainV[j] = EdgeGainV[i+EdgeGainCopyToSortV.Len()];
00294           i++;
00295         } else {
00296           EdgeGainV[j] = EdgeGainCopyToSortV[ii];
00297           ii++;
00298         }
00299       }
00300     }
00301 
00302     attempts = 0;
00303     
00304   for (int e = 0; e < EdgeGainV.Len(); e++) {
00305     const TIntPr& Edge = EdgeGainV[e].Val2;
00306     if (Graph->IsEdge(Edge.Val1, Edge.Val2)) { continue; } // if edge was already included in the graph
00307 
00308     const double EProb = GetAllCascProb(Edge.Val1, Edge.Val2);
00309     EdgeGainV[e].Val1 = EProb; // update marginal gain
00310     if (BestGain < EProb) {
00311     BestGain = EProb;
00312     BestGainIndex = e;
00313     BestE = Edge;
00314     }
00315 
00316     // if we only update one weight, we don't need to sort the list
00317     attempts++;
00318 
00319     // keep track of zero edges after sorting once the full list
00320     if (!Graph->IsEdge(Edge.Val1, Edge.Val2) && Graph->GetEdges() > 1) {
00321       if (EProb == 0)
00322         EdgeZero.Add(e);
00323     }
00324 
00325     // lazy evaluation
00326     if (e+1 == EdgeGainV.Len() || BestGain >= EdgeGainV[e+1].Val1) {
00327     CurProb += BestGain;
00328 
00329     if (BestGain == 0)
00330       return TIntPr(-1, -1);
00331 
00332     EdgeGainV.Del(BestGainIndex);
00333 
00334     // we know the edges in 0 will be in sorted order, so we start from the biggest
00335     for (int i=EdgeZero.Len()-1; i>=0; i--) {
00336       if (EdgeZero[i] > BestGainIndex)
00337         EdgeGainV.Del(EdgeZero[i]-1);
00338       else
00339         EdgeGainV.Del(EdgeZero[i]);
00340     }
00341 
00342     if (EdgeZero.Len() > 2) { attempts -= (EdgeZero.Len()-1); }
00343 
00344     msort = (attempts > 1);
00345 
00346     LastGain = BestGain;
00347 
00348     return BestE;
00349     }
00350   }
00351 
00352   printf("Edges exhausted!\n");
00353   return TIntPr(-1, -1);
00354 }
00355 
00356 double TNetInfBs::GetBound(const TIntPr& Edge, double& CurProb) {
00357   double Bound = 0;
00358   TFltV Bounds;
00359 
00360   // bound could be computed faster (using lazy evaluation, as in the optimization procedure)
00361   for (int e=0; e < EdgeGainV.Len(); e++) {
00362     const TIntPr& EE = EdgeGainV[e].Val2;
00363     if (EE != Edge && !Graph->IsEdge(EE.Val1, EE.Val2)) {
00364       const double EProb = GetAllCascProb(EE.Val1, EE.Val2);
00365       if (EProb > CurProb) Bounds.Add(EProb - CurProb); }
00366   }
00367 
00368   Bounds.Sort(false);
00369   for (int i=0; i<Graph->GetEdges() && i<Bounds.Len(); i++) Bound += Bounds[i];
00370 
00371   return Bound;
00372 }
00373 
00374 void TNetInfBs::GreedyOpt(const int& MxEdges) {
00375     double CurProb = GetAllCascProb(-1, -1);
00376     double LastGain = TFlt::Mx;
00377     int attempts = 0;
00378     bool msort = false;
00379 
00380     for (int k = 0; k < MxEdges && EdgeGainV.Len() > 0; k++) {
00381       const TIntPr BestE = GetBestEdge(CurProb, LastGain, msort, attempts);
00382       if (BestE == TIntPr(-1, -1)) // if we cannot add more edges, we stop
00383         break;
00384 
00385       if (CompareGroundTruth) {
00386         double precision = 0, recall = 0;
00387         if (PrecisionRecall.Len() > 1) {
00388           precision = PrecisionRecall[PrecisionRecall.Len()-1].Val2.Val;
00389           recall = PrecisionRecall[PrecisionRecall.Len()-1].Val1.Val;
00390         }
00391         if (GroundTruth->IsEdge(BestE.Val1, BestE.Val2)) {
00392           recall++;
00393         } else {
00394           precision++;
00395         }
00396 
00397         PrecisionRecall.Add(TPair<TFlt, TFlt>(recall, precision));
00398       }
00399 
00400       Graph->AddEdge(BestE.Val1, BestE.Val2); // add edge to network
00401 
00402       
00403       // localized update!
00404       TIntV &CascsEdge = CascPerEdge.GetDat(BestE); // only check cascades that contain the edge
00405       for (int c = 0; c < CascsEdge.Len(); c++) {
00406         CascV[CascsEdge[c]].UpdateProb(BestE.Val1, BestE.Val2, true); // update probabilities
00407       }
00408 
00409       // some extra info for the added edge
00410       TInt Vol; TFlt AverageTimeDiff; TFltV TimeDiffs;
00411       Vol = 0; AverageTimeDiff = 0;
00412       for (int i=0; i< CascV.Len(); i++) {
00413         if (CascV[i].IsNode(BestE.Val2) && CascV[i].GetParent(BestE.Val2) == BestE.Val1) {
00414           Vol += 1; TimeDiffs.Add(CascV[i].GetTm(BestE.Val2)-CascV[i].GetTm(BestE.Val1));
00415           AverageTimeDiff += TimeDiffs[TimeDiffs.Len()-1]; }
00416       }
00417       AverageTimeDiff /= Vol;
00418       if (TimeDiffs.Len() > 0)
00419         TimeDiffs.Sort();
00420       else
00421         TimeDiffs.Add(0);
00422 
00423       // compute bound only if explicitly required
00424       EdgeInfoH.AddDat(BestE) = TEdgeInfo(Vol,
00425                       LastGain,
00426                       0.0,
00427                       TimeDiffs[(int)(TimeDiffs.Len()/2)],
00428                       AverageTimeDiff);
00429     }
00430 
00431     if (CompareGroundTruth) {
00432       for (int i=0; i<PrecisionRecall.Len(); i++) {
00433         PrecisionRecall[i].Val2 = 1.0 - PrecisionRecall[i].Val2/(PrecisionRecall[i].Val2+PrecisionRecall[i].Val1);
00434         PrecisionRecall[i].Val1 /= (double)GroundTruth->GetEdges();
00435       }
00436     }
00437 }
00438 
00439 void TNetInfBs::SavePajek(const TStr& OutFNm) {
00440     TIntSet NIdSet;
00441     FILE *F = fopen(OutFNm.CStr(), "wt");
00442     fprintf(F, "*Vertices %d\r\n", NIdSet.Len());
00443     for (THash<TInt, TNodeInfo>::TIter NI = NodeNmH.BegI(); NI < NodeNmH.EndI(); NI++) {
00444       const TNodeInfo& I = NI.GetDat();
00445       fprintf(F, "%d \"%s\" ic Blue x_fact %f y_fact %f\r\n", NI.GetKey().Val,
00446         I.Name.CStr(), TMath::Mx<double>(log((double)I.Vol)-5,1), TMath::Mx<double>(log((double)I.Vol)-5,1));
00447     }
00448     fprintf(F, "*Arcs\r\n");
00449     for (TNGraph::TEdgeI EI = Graph->BegEI(); EI < Graph->EndEI(); EI++) {
00450       fprintf(F, "%d %d 1\r\n", EI.GetSrcNId(), EI.GetDstNId());
00451     }
00452     fclose(F);
00453 }
00454 
00455 void TNetInfBs::SavePlaneTextNet(const TStr& OutFNm) {
00456   TIntSet NIdSet;
00457   FILE *F = fopen(OutFNm.CStr(), "wt");
00458   for (THash<TInt, TNodeInfo>::TIter NI = NodeNmH.BegI(); NI < NodeNmH.EndI(); NI++) {
00459     fprintf(F, "%d,%d\r\n", NI.GetKey().Val, NI.GetKey().Val);
00460   }
00461 
00462   fprintf(F, "\r\n");
00463 
00464   for (TNGraph::TEdgeI EI = Graph->BegEI(); EI < Graph->EndEI(); EI++) {
00465     fprintf(F, "%d,%d\r\n", EI.GetSrcNId(), EI.GetDstNId());
00466   }
00467   fclose(F);
00468 }
00469 
00470 void TNetInfBs::SaveEdgeInfo(const TStr& OutFNm) {
00471   FILE *F = fopen(OutFNm.CStr(), "wt");
00472 
00473   fprintf(F, "src dst vol marginal_gain median_timediff average_timediff\n");
00474   for (THash<TIntPr, TEdgeInfo>::TIter EI = EdgeInfoH.BegI(); EI < EdgeInfoH.EndI(); EI++) {
00475     TEdgeInfo &EdgeInfo = EI.GetDat();
00476     fprintf(F, "%s/%s/%d/%f/%f/%f\n",
00477         NodeNmH.GetDat(EI.GetKey().Val1.Val).Name.CStr(), NodeNmH.GetDat(EI.GetKey().Val2.Val).Name.CStr(),
00478         EdgeInfo.Vol.Val, EdgeInfo.MarginalGain.Val,
00479         EdgeInfo.MedianTimeDiff.Val,
00480         EdgeInfo.AverageTimeDiff.Val);
00481   }
00482   fclose(F);
00483 }
00484 
00485 void TNetInfBs::SaveObjInfo(const TStr& OutFNm) {
00486   TGnuPlot GnuPlot(OutFNm);
00487 
00488   TFltV Objective;
00489 
00490   for (THash<TIntPr, TEdgeInfo>::TIter EI = EdgeInfoH.BegI(); EI < EdgeInfoH.EndI(); EI++) {
00491     if (Objective.Len()==0) { Objective.Add(EI.GetDat().MarginalGain); 
00492     } else {
00493       Objective.Add(Objective[Objective.Len()-1]+EI.GetDat().MarginalGain);
00494     }
00495   }
00496 
00497   GnuPlot.AddPlot(Objective, gpwLinesPoints);
00498   
00499   GnuPlot.SavePng();
00500 }
00501 
00502 void TNetInfBs::SaveGroundTruth(const TStr& OutFNm) {
00503   TFOut FOut(OutFNm);
00504 
00505   // write nodes to file
00506   for (TNGraph::TNodeI NI = GroundTruth->BegNI(); NI < GroundTruth->EndNI(); NI++) {
00507     FOut.PutStr(TStr::Fmt("%d,%d\r\n", NI.GetId(), NI.GetId())); // nodes
00508   }
00509 
00510   FOut.PutStr("\r\n");
00511 
00512   // write edges to file (not allowing self loops in the network)
00513   for (TNGraph::TEdgeI EI = GroundTruth->BegEI(); EI < GroundTruth->EndEI(); EI++) {
00514     // not allowing self loops in the Kronecker network
00515     if (EI.GetSrcNId() != EI.GetDstNId()) {
00516       if (Alphas.IsKey(TIntPr(EI.GetSrcNId(), EI.GetDstNId())))
00517         FOut.PutStr(TStr::Fmt("%d,%d,%f\r\n", EI.GetSrcNId(), EI.GetDstNId(), Alphas.GetDat(TIntPr(EI.GetSrcNId(), EI.GetDstNId())).Val));
00518       else
00519         FOut.PutStr(TStr::Fmt("%d,%d,1\r\n", EI.GetSrcNId(), EI.GetDstNId()));
00520     }
00521   }
00522 }
00523 
00524 void TNetInfBs::SaveCascades(const TStr& OutFNm) {
00525   TFOut FOut(OutFNm);
00526 
00527   // write nodes to file
00528   for (TNGraph::TNodeI NI = GroundTruth->BegNI(); NI < GroundTruth->EndNI(); NI++) {
00529     FOut.PutStr(TStr::Fmt("%d,%d\r\n", NI.GetId(), NI.GetId())); // nodes
00530   }
00531 
00532   FOut.PutStr("\r\n");
00533 
00534   // write cascades to file
00535   for (int i=0; i<CascV.Len(); i++) {
00536     TCascade &C = CascV[i];
00537     int j = 0;
00538     for (THash<TInt, THitInfo>::TIter NI = C.NIdHitH.BegI(); NI < C.NIdHitH.EndI(); NI++, j++) {
00539       if (j > 0)
00540         FOut.PutStr(TStr::Fmt(",%d,%f", NI.GetDat().NId.Val, NI.GetDat().Tm.Val));
00541       else
00542         FOut.PutStr(TStr::Fmt("%d,%f", NI.GetDat().NId.Val, NI.GetDat().Tm.Val));
00543     }
00544 
00545     if (C.Len() >= 1)
00546       FOut.PutStr(TStr::Fmt("\r\n"));
00547   }
00548 }