Skip to content

Commit 9749a05

Browse files
danieledlerAnton Eriksson
authored andcommitted
feat: Support Newick tree format in output
1 parent 95141bf commit 9749a05

File tree

4 files changed

+105
-2
lines changed

4 files changed

+105
-2
lines changed

src/core/InfomapBase.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2101,6 +2101,26 @@ void InfomapBase::writeResult()
21012101
}
21022102

21032103

2104+
if (printNewick) {
2105+
std::string filename = outDirectory + outName + ".tre";
2106+
2107+
if (!haveMemory()) {
2108+
Log() << "Write Newick tree to " << filename << "... ";
2109+
writeNewickTree(filename);
2110+
Log() << "done!\n";
2111+
} else {
2112+
// Write both physical and state level
2113+
Log() << "Write physical Newick tree to " << filename << "... ";
2114+
writeNewickTree(filename, false);
2115+
Log() << "done!\n";
2116+
std::string filenameStates = outDirectory + outName + "_states.tre";
2117+
Log() << "Write state Newick tree to " << filenameStates << "... ";
2118+
writeNewickTree(filenameStates, true);
2119+
Log() << "done!\n";
2120+
}
2121+
}
2122+
2123+
21042124
if (printClu) {
21052125
std::string filename = outDirectory + outName + ".clu";
21062126
if (!haveMemory()) {
@@ -2153,6 +2173,16 @@ std::string InfomapBase::writeFlowTree(std::string filename, bool states)
21532173
return outputFilename;
21542174
}
21552175

2176+
std::string InfomapBase::writeNewickTree(std::string filename, bool states)
2177+
{
2178+
std::string outputFilename = filename.empty() ? outDirectory + outName + (haveMemory() && states ? "_states.tre" : ".tre") : filename;
2179+
2180+
SafeOutFile outFile(outputFilename);
2181+
writeNewickTree(outFile, states);
2182+
2183+
return outputFilename;
2184+
}
2185+
21562186
std::string InfomapBase::writeClu(std::string filename, bool states, int moduleIndexLevel)
21572187
{
21582188
// unsigned int maxModuleLevel = maxTreeDepth();
@@ -2400,6 +2430,60 @@ void InfomapBase::writeTreeLinks(std::ostream& outStream, bool states)
24002430
}
24012431

24022432

2433+
void InfomapBase::writeNewickTree(std::ostream& outStream, bool states)
2434+
{
2435+
auto oldPrecision = outStream.precision();
2436+
// outStream << std::setprecision(9);
2437+
// outStream << getOutputFileHeader() << "\n";
2438+
outStream << std::setprecision(6);
2439+
2440+
auto isRoot = true;
2441+
unsigned int lastDepth = 0;
2442+
std::vector<double> flowStack;
2443+
2444+
auto writeNewickNode = [&](std::ostream& o, const InfoNode& node, unsigned int depth) {
2445+
if (depth > lastDepth || isRoot) {
2446+
outStream << "(";
2447+
flowStack.push_back(node.data.flow);
2448+
if (node.isLeaf())
2449+
outStream << (states ? node.stateId : node.physicalId) << ":" << node.data.flow;
2450+
} else if (depth == lastDepth) {
2451+
outStream << ",";
2452+
flowStack[flowStack.size()-1] = node.data.flow;
2453+
if (node.isLeaf()) {
2454+
outStream << (states ? node.stateId : node.physicalId) << ":" << node.data.flow;
2455+
}
2456+
} else {
2457+
// depth < lastDepth
2458+
while (flowStack.size() > depth + 1) {
2459+
flowStack.pop_back();
2460+
outStream << "):" << flowStack.back();
2461+
}
2462+
flowStack[flowStack.size()-1] = node.data.flow;
2463+
outStream << ",";
2464+
}
2465+
lastDepth = depth;
2466+
isRoot = false;
2467+
};
2468+
2469+
// TODO: Make a general iterator where merging physical nodes depend on a parameter rather than type to be able to DRY here
2470+
if (haveMemory() && !states) {
2471+
for (auto it(iterTreePhysical()); !it.isEnd(); ++it) {
2472+
writeNewickNode(outStream, *it, it.depth());
2473+
}
2474+
} else {
2475+
for (auto it(iterTree()); !it.isEnd(); ++it) {
2476+
writeNewickNode(outStream, *it, it.depth());
2477+
}
2478+
}
2479+
while (flowStack.size() > 1) {
2480+
flowStack.pop_back();
2481+
outStream << "):" << flowStack.back();
2482+
}
2483+
outStream << ");\n";
2484+
outStream << std::setprecision(oldPrecision);
2485+
}
2486+
24032487
unsigned int InfomapBase::printPerLevelCodelength(std::ostream& out)
24042488
{
24052489
std::vector<PerLevelStat> perLevelStats;

src/core/InfomapBase.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,15 @@ class InfomapBase : public InfomapConfig<InfomapBase> {
424424
*/
425425
std::string writeFlowTree(std::string filename = "", bool states = false);
426426

427+
/**
428+
* Write Newick tree to a .tre file.
429+
* @param filename the filename for the output file. If empty, use default
430+
* based on output directory and input file name
431+
* @param states if memory network, print the state-level network without merging physical nodes within modules
432+
* @return the filename written to
433+
*/
434+
std::string writeNewickTree(std::string filename = "", bool states = false);
435+
427436
/**
428437
* Write tree to a .clu file.
429438
* @param filename the filename for the output file. If empty, use default
@@ -470,6 +479,12 @@ class InfomapBase : public InfomapConfig<InfomapBase> {
470479
*/
471480
void writeTreeLinks(std::ostream& outStream, bool states = false);
472481

482+
/**
483+
* Write Newick tree to output stream
484+
* @param states, write state-level tree, else aggregate physical nodes within modules
485+
*/
486+
void writeNewickTree(std::ostream& outStream, bool states = false);
487+
473488
InfoNode m_root;
474489
std::vector<InfoNode*> m_leafNodes;
475490
std::vector<InfoNode*> m_moduleNodes;

src/io/Config.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ Config::Config(std::string flags, bool isCLI) : isCLI(isCLI)
7979
// "Print the network with calculated flow values.", "Output", true);
8080

8181
// -o network,states,clu,ftree
82-
api.addOptionArgument(outputFormats, 'o', "output", "Comma-separated output formats without spaces, e.g. -o clu,tree,ftree. Options: clu, tree, ftree, network, states.", ArgType::list, "Output", true);
82+
api.addOptionArgument(outputFormats, 'o', "output", "Comma-separated output formats without spaces, e.g. -o clu,tree,ftree. Options: clu, tree, ftree, newick, network, states.", ArgType::list, "Output", true);
8383

8484
api.addOptionArgument(hideBipartiteNodes, "hide-bipartite-nodes", "Project bipartite solution to unipartite.", "Output", true);
8585

@@ -239,6 +239,8 @@ void Config::adaptDefaults()
239239
printTree = true;
240240
} else if (o == "ftree") {
241241
printFlowTree = true;
242+
} else if (o == "newick") {
243+
printNewick = true;
242244
} else if (o == "network") {
243245
printPajekNetwork = true;
244246
} else if (o == "states") {

src/io/Config.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ struct Config {
142142
bool originallyUndirected = false;
143143
bool printTree = false;
144144
bool printFlowTree = false;
145+
bool printNewick = false;
145146
bool printMap = false;
146147
bool printClu = false;
147148
int cluLevel = 1; // Write modules at specified depth from root. 1, 2, ... or -1 for bottom level
@@ -249,6 +250,7 @@ struct Config {
249250
originallyUndirected = other.originallyUndirected;
250251
// printTree = other.printTree;
251252
// printFlowTree = other.printFlowTree;
253+
// printNewick = other.printNewick;
252254
// printMap = other.printMap;
253255
// printClu = other.printClu;
254256
// printNodeRanks = other.printNodeRanks;
@@ -345,7 +347,7 @@ struct Config {
345347

346348
bool haveModularResultOutput() const
347349
{
348-
return printTree || printFlowTree || printMap || printClu || printBinaryTree || printBinaryFlowTree;
350+
return printTree || printFlowTree || printNewick || printMap || printClu || printBinaryTree || printBinaryFlowTree;
349351
}
350352
};
351353

0 commit comments

Comments
 (0)