2222#include " ../utils/Stopwatch.h"
2323#include " ../utils/exceptions.h"
2424#include " ../utils/FileURI.h"
25+ #include " ../Infomap.h"
2526
2627#include < string>
2728#include < vector>
@@ -2138,19 +2139,20 @@ void InfomapBase::writeResult()
21382139
21392140 if (printJson) {
21402141 std::string filename = outDirectory + outName + " .json" ;
2142+ const bool writeLinks = false ;
21412143
21422144 if (!haveMemory ()) {
21432145 Log () << " Write JSON tree to " << filename << " ... " ;
2144- writeJsonTree (filename);
2146+ writeJsonTree (filename, false , writeLinks );
21452147 Log () << " done!\n " ;
21462148 } else {
21472149 // Write both physical and state level
21482150 Log () << " Write physical JSON tree to " << filename << " ... " ;
2149- writeJsonTree (filename, false );
2151+ writeJsonTree (filename, false , writeLinks );
21502152 Log () << " done!\n " ;
21512153 std::string filenameStates = outDirectory + outName + " _states.json" ;
21522154 Log () << " Write state JSON tree to " << filenameStates << " ... " ;
2153- writeJsonTree (filenameStates, true );
2155+ writeJsonTree (filenameStates, true , writeLinks );
21542156 Log () << " done!\n " ;
21552157 }
21562158 }
@@ -2236,12 +2238,12 @@ std::string InfomapBase::writeNewickTree(std::string filename, bool states)
22362238 return outputFilename;
22372239}
22382240
2239- std::string InfomapBase::writeJsonTree (std::string filename, bool states)
2241+ std::string InfomapBase::writeJsonTree (std::string filename, bool states, bool writeLinks )
22402242{
22412243 std::string outputFilename = filename.empty () ? outDirectory + outName + (haveMemory () && states ? " _states.json" : " .json" ) : filename;
22422244
22432245 SafeOutFile outFile (outputFilename);
2244- writeJsonTree (outFile, states);
2246+ writeJsonTree (outFile, states, writeLinks );
22452247
22462248 return outputFilename;
22472249}
@@ -2562,7 +2564,7 @@ void InfomapBase::writeNewickTree(std::ostream& outStream, bool states)
25622564 outStream << std::setprecision (oldPrecision);
25632565}
25642566
2565- void InfomapBase::writeJsonTree (std::ostream& outStream, bool states)
2567+ void InfomapBase::writeJsonTree (std::ostream& outStream, bool states, bool writeLinks )
25662568{
25672569 auto oldPrecision = outStream.precision ();
25682570
@@ -2572,9 +2574,9 @@ void InfomapBase::writeJsonTree(std::ostream& outStream, bool states)
25722574 << " \" args\" : \" " << parsedString << " \" ,\n "
25732575 << " \" startedAt\" : \" " << m_startDate << " \" ,\n "
25742576 << " \" completedIn\" : " << m_elapsedTime.getElapsedTimeInSec () << " ,\n "
2577+ << " \" codelength\" : " << codelength () << " ,\n "
25752578 << " \" numLevels\" : " << maxTreeDepth () << " ,\n "
25762579 << " \" numTopModules\" : " << numTopModules () << " ,\n "
2577- << " \" codelength\" : " << codelength () << " ,\n "
25782580 << " \" relativeCodelengthSavings\" : " << getRelativeCodelengthSavings () << " ,\n "
25792581 << " \" directed\" : " << (isUndirectedFlow () ? " false" : " true" ) << " ,\n " ;
25802582
@@ -2599,6 +2601,9 @@ void InfomapBase::writeJsonTree(std::ostream& outStream, bool states)
25992601 const auto shouldHideBipartiteNodes = isBipartite () && hideBipartiteNodes;
26002602 const auto bipartiteStartId = shouldHideBipartiteNodes ? m_network.bipartiteStartId () : 0 ;
26012603
2604+ // Hack to re-use getMultilevelModules from Infomap.cpp
2605+ const auto multilevelModules = static_cast <InfomapWrapper*>(this )->getMultilevelModules (states);
2606+
26022607 // don't append a comma after the last entry
26032608 auto first = true ;
26042609
@@ -2611,18 +2616,20 @@ void InfomapBase::writeJsonTree(std::ostream& outStream, bool states)
26112616 }
26122617
26132618 const auto path = io::stringify (it.path (), " , " );
2619+ const auto modules = io::stringify (multilevelModules.at (node.physicalId ), " , " );
26142620
26152621 if (first) {
26162622 first = false ;
26172623 } else {
26182624 outStream << " ,\n " ;
26192625 }
26202626
2621- outStream << " { " ;
2622- outStream << " \" path\" : [" << path << " ], " ;
2623- outStream << " \" name\" : \" " << nodeName (node) << " \" , " ;
2624- outStream << " \" flow\" : " << node.data .flow << " , " ;
2625- outStream << " \" id\" : " << node.physicalId << " }" ;
2627+ outStream << " { "
2628+ << " \" path\" : [" << path << " ], "
2629+ << " \" modules\" : [" << modules << " ], "
2630+ << " \" name\" : \" " << nodeName (node) << " \" , "
2631+ << " \" flow\" : " << node.data .flow << " , "
2632+ << " \" id\" : " << node.physicalId << " }" ;
26262633 }
26272634 }
26282635 } else {
@@ -2640,11 +2647,13 @@ void InfomapBase::writeJsonTree(std::ostream& outStream, bool states)
26402647 }
26412648
26422649 const auto path = io::stringify (it.path (), " , " );
2650+ const auto modules = io::stringify (multilevelModules.at (node.physicalId ), " , " );
26432651
2644- outStream << " { " ;
2645- outStream << " \" path\" : [" << path << " ], " ;
2646- outStream << " \" name\" : \" " << nodeName (node) << " \" , " ;
2647- outStream << " \" flow\" : " << node.data .flow << " , " ;
2652+ outStream << " { "
2653+ << " \" path\" : [" << path << " ], "
2654+ << " \" modules\" : [" << modules << " ], "
2655+ << " \" name\" : \" " << nodeName (node) << " \" , "
2656+ << " \" flow\" : " << node.data .flow << " , " ;
26482657
26492658 if (states) {
26502659 outStream << " \" stateId\" : " << node.stateId << " , " ;
@@ -2657,12 +2666,74 @@ void InfomapBase::writeJsonTree(std::ostream& outStream, bool states)
26572666 }
26582667 }
26592668
2660- outStream << " \n ]\n " ; // tree
2661- outStream << ' }' ;
2669+ outStream << " \n ],\n " ; // tree
2670+
2671+ // -------------
2672+ // Write modules
2673+ // -------------
2674+ auto moduleLinks = aggregateModuleLinks (states);
2675+
2676+ first = true ;
2677+
2678+ outStream << " \" modules\" : [\n " ;
2679+
2680+ // Use stateId to store depth on modules to optimize link aggregation
2681+ for (auto it (iterModules ()); !it.isEnd (); ++it) {
2682+ const auto parentId = io::stringify (it.path (), " :" );
2683+ const auto & module = *it;
2684+ const auto & links = moduleLinks[parentId];
2685+ const auto path = io::stringify (it.path (), " , " );
2686+
2687+
2688+ if (first) {
2689+ first = false ;
2690+ } else {
2691+ outStream << " ,\n " ;
2692+ }
2693+
2694+ outStream << " {" ;
2695+
2696+ if (writeLinks) outStream << " \n " ;
2697+
2698+ outStream << " \" path\" : [" << (parentId.empty () ? " 0" : path) << " ], "
2699+ << " \" enterFlow\" : " << module .data .enterFlow << " , "
2700+ << " \" exitFlow\" : " << module .data .exitFlow << " , "
2701+ << " \" numEdges\" : " << links.size () << " , "
2702+ << " \" numChildren\" : " << module .infomapChildDegree ();
2703+
2704+
2705+ if (writeLinks) {
2706+ outStream << " ,\n "
2707+ << " \" links\" : [\n " ;
2708+
2709+ auto firstLink = true ;
2710+
2711+ for (auto itLink : links) {
2712+ if (firstLink) {
2713+ firstLink = false ;
2714+ } else {
2715+ outStream << " ,\n " ;
2716+ }
2717+
2718+ unsigned int sourceId = itLink.first .first ;
2719+ unsigned int targetId = itLink.first .second ;
2720+ double flow = itLink.second ;
2721+ outStream << " { \" source\" : " << sourceId << " , \" target\" : " << targetId << " , \" flow\" : " << flow << " }" ;
2722+ }
2723+ outStream << " \n ]\n " ; // links
2724+ }
2725+
2726+ outStream << " }" ;
2727+ }
2728+
2729+ outStream << " \n ]\n " ; // modules
2730+
2731+ outStream << " }\n " ;
26622732
26632733 outStream << std::setprecision (oldPrecision);
26642734}
26652735
2736+
26662737void InfomapBase::writeCsvTree (std::ostream& outStream, bool states)
26672738{
26682739 auto oldPrecision = outStream.precision ();
0 commit comments