Skip to content

Commit e6796c8

Browse files
author
Anton Eriksson
authored
feat: Write json multilevel modules and module information
1 parent d344520 commit e6796c8

File tree

4 files changed

+106
-20
lines changed

4 files changed

+106
-20
lines changed

interfaces/js/src/filetypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type MetaClu = CluNode<"meta">[];
1111

1212
export interface TreeNode {
1313
path: number[];
14+
modules?: number[];
1415
flow?: number;
1516
name?: string;
1617
id: number;

interfaces/js/src/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ const changelog: Changelog[] = CHANGELOG;
4747
// @ts-ignore
4848
const parameters: (Parameter | RequiredParameter)[] = PARAMETERS;
4949

50+
type Module = {
51+
path: number[];
52+
enterFlow: number;
53+
exitFlow: number;
54+
numEdges: number;
55+
numChildren: number;
56+
links?: {
57+
source: number;
58+
target: number;
59+
flow: number;
60+
}[];
61+
};
62+
5063
export interface Tree<NodeType = Required<Node>> {
5164
version: string;
5265
args: string;
@@ -59,6 +72,7 @@ export interface Tree<NodeType = Required<Node>> {
5972
directed: boolean;
6073
bipartiteStartId?: number;
6174
nodes: NodeType[];
75+
modules: Module[];
6276
}
6377

6478
export interface Result {

src/core/InfomapBase.cpp

Lines changed: 89 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
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+
26662737
void InfomapBase::writeCsvTree(std::ostream& outStream, bool states)
26672738
{
26682739
auto oldPrecision = outStream.precision();

src/core/InfomapBase.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ class InfomapBase : public InfomapConfig<InfomapBase> {
434434
*/
435435
std::string writeNewickTree(std::string filename = "", bool states = false);
436436

437-
std::string writeJsonTree(std::string filename = "", bool states = false);
437+
std::string writeJsonTree(std::string filename = "", bool states = false, bool writeLinks = false);
438438

439439
std::string writeCsvTree(std::string filename = "", bool states = false);
440440

@@ -494,7 +494,7 @@ class InfomapBase : public InfomapConfig<InfomapBase> {
494494
* Write JSON tree to output stream
495495
* @param states, write state-level tree, else aggregate physical nodes within modules
496496
*/
497-
void writeJsonTree(std::ostream& outStream, bool states = false);
497+
void writeJsonTree(std::ostream& outStream, bool states = false, bool writeLinks = false);
498498

499499
/**
500500
* Write CSV tree to output stream

0 commit comments

Comments
 (0)