Commit c31c9eb4 authored by Clement Frainay's avatar Clement Frainay
Browse files

[ToolBox] Advanced compound graph

parent 7a1119b7
package fr.inrae.toulouse.metexplore.met4j_toolbox.networkAnalysis;
import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite;
import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
import fr.inrae.toulouse.metexplore.met4j_core.biodata.collection.BioCollection;
import fr.inrae.toulouse.metexplore.met4j_graph.computation.connect.weighting.*;
import fr.inrae.toulouse.metexplore.met4j_graph.computation.transform.EdgeMerger;
import fr.inrae.toulouse.metexplore.met4j_graph.computation.transform.VertexContraction;
import fr.inrae.toulouse.metexplore.met4j_graph.computation.utils.ComputeAdjacencyMatrix;
import fr.inrae.toulouse.metexplore.met4j_graph.core.WeightingPolicy;
import fr.inrae.toulouse.metexplore.met4j_graph.core.compound.CompoundGraph;
import fr.inrae.toulouse.metexplore.met4j_graph.core.compound.ReactionEdge;
import fr.inrae.toulouse.metexplore.met4j_io.jsbml.reader.JsbmlReader;
import fr.inrae.toulouse.metexplore.met4j_io.jsbml.reader.Met4jSbmlReaderException;
import fr.inrae.toulouse.metexplore.met4j_io.jsbml.reader.plugin.FBCParser;
import fr.inrae.toulouse.metexplore.met4j_io.jsbml.reader.plugin.GroupPathwayParser;
import fr.inrae.toulouse.metexplore.met4j_io.jsbml.reader.plugin.NotesParser;
import fr.inrae.toulouse.metexplore.met4j_io.jsbml.reader.plugin.PackageParser;
import fr.inrae.toulouse.metexplore.met4j_mathUtils.matrix.ExportMatrix;
import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.AbstractMet4jApplication;
import org.kohsuke.args4j.Option;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.function.Function;
public class CompoundNet extends AbstractMet4jApplication {
@Option(name = "-s", usage = "input SBML file", required = true)
public String inputPath = null;
@Option(name = "-sc", usage = "input Side compound file", required = false)
public String inputSide = null;
@Option(name = "-o", usage = "output Graph file", required = true)
public String outputPath = null;
enum strategy {by_name,by_id}
@Option(name = "-mc", aliases = {"--mergecomp"}, usage = "merge compartments. " +
"Use names if consistent and unambiguous across compartments, or identifiers if compartment suffix is present (id in form \"xxx_y\" with xxx as base identifier and y as compartment label).")
public strategy mergingStrat = null;
public String idRegex = "^(\\w+)_\\w$";
@Option(name = "-me", aliases = {"--simple"}, usage = "merge parallel edges to produce a simple graph", required = false)
public boolean mergeEdges = false;
@Option(name = "-ri", aliases = {"--removeIsolatedNodes"}, usage = "remove isolated nodes", required = false)
public boolean removeIsolated = false;
@Option(name = "-dw", aliases = {"--degreeWeights"}, usage = "penalize traversal of hubs by using degree square weighting", forbids = {"-cw", "-sw"})
public Boolean degree = false;
@Option(name = "-cw", aliases = {"--customWeights"}, usage = "an optional file containing weights for compound pairs", forbids = {"-dw", "-sw"})
public String weightFile = null;
@Option(name = "-un", aliases = {"--undirected"}, usage = "create as undirected", required = false)
public boolean undirected = false;
@Option(name = "-tp", aliases = {"--transitionproba"}, usage = "set weight as random walk transition probability, normalized by reaction", required = false)
public boolean computeWeight = false;
@Option(name = "-am", aliases = {"--asmatrix"}, usage = "export as matrix (implies simple graph conversion). Default export as GML file", required = false)
public boolean asMatrix = false;
public static void main(String[] args) throws IOException, Met4jSbmlReaderException {
CompoundNet app = new CompoundNet();
public void run() throws IOException, Met4jSbmlReaderException {
System.out.print("Reading SBML...");
JsbmlReader reader = new JsbmlReader(this.inputPath);
ArrayList<PackageParser> pkgs = new ArrayList<>(Arrays.asList(
new NotesParser(false), new FBCParser(), new GroupPathwayParser()));
BioNetwork network =;
System.out.println(" Done.");
System.out.print("Buildinig Network...");
Bionetwork2BioGraph builder = new Bionetwork2BioGraph(network);
CompoundGraph graph = builder.getCompoundGraph();
System.out.println(" Done.");
//Graph processing: side compound removal [optional]
if (inputSide != null) {
System.err.println("removing side compounds...");
NodeMapping<BioMetabolite, ReactionEdge, CompoundGraph> mapper = new NodeMapping<>(graph).skipIfNotFound();
BioCollection<BioMetabolite> sideCpds =;
boolean removed = graph.removeAllVertices(sideCpds);
if (removed) System.err.println(sideCpds.size() + " compounds removed.");
//Graph processing: set weights [optional]
WeightingPolicy<BioMetabolite, ReactionEdge, CompoundGraph> wp = new DefaultWeightPolicy<>();
if (weightFile != null) {
System.err.println("Setting edge weights...");
wp = new WeightsFromFile(weightFile);
} else if (degree) {
System.err.println("Setting edge weights...");
int pow = 2;
wp = new DegreeWeightPolicy(pow);
//invert graph as undirected (copy edge weight to reversed edge)
System.out.print("Create Undirected...");
System.out.println(" Done.");
//merge compartment
System.out.print("Merging compartments...");
VertexContraction vc = new VertexContraction();
VertexContraction.Mapper merger = mergingStrat.equals(strategy.by_name) ? new VertexContraction.MapByName() : new VertexContraction.MapByIdSubString(idRegex);
graph = vc.decompartmentalize(graph, merger);
System.out.println(" Done.");
//remove isolated nodes
System.out.println("Remove isolated nodes...");
HashSet<BioMetabolite> nodes = new HashSet<>(graph.vertexSet());
for(BioMetabolite n : nodes){
System.out.println("\tremoving " + n.getName());
System.out.println(" Done.");
//compute transitions probability from weights
if(computeWeight) {
System.out.print("Compute transition matrix...");
ReactionProbabilityWeight wp2 = new ReactionProbabilityWeight();
System.out.println(" Done.");
//merge parallel edges
System.out.print("Merging edges...");
System.out.println(" Done.");
//export graph
ComputeAdjacencyMatrix adjBuilder = new ComputeAdjacencyMatrix(graph);
if(!computeWeight) adjBuilder.parallelEdgeWeightsHandling((u, v) -> Math.max(u,v));
ExportGraph.toGmlWithAttributes(graph, this.outputPath, true);
System.out.println(" Done.");
public String getLabel() {return this.getClass().getSimpleName();}
public String getLongDescription() {
return "Metabolic networks used for quantitative analysis often contain links that are irrelevant for graph-based structural analysis. For example, inclusion of side compounds or modelling artifacts such as 'biomass' nodes.\n" +
"While Carbon Skeleton Graph offer a relevant alternative topology for graph-based analysis, it requires compounds' structure information, usually not provided in model, and difficult to retrieve for model with sparse cross-reference annotations.\n" +
"In contrary to the SBML2Graph app that performs a raw conversion of the SBML content, the present app propose a fine-tuned creation of compound graph from predefined list of side compounds and degree² weighting to get relevant structure without structural data."+
"This app also enable Markov-chain based analysis of metabolic networks by computing reaction-normalized transition probabilities on the network.";
public String getShortDescription() {return "Advanced creation of a compound graph representation of a SBML file content";}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment