From b53552db3f2542c6d9a34deef1416e12ba9166c8 Mon Sep 17 00:00:00 2001 From: cfrainay <clement.frainay@inrae.fr> Date: Fri, 18 Mar 2022 17:10:48 +0100 Subject: [PATCH 1/3] [ToolBox] create advanced compound graph creation --- .../networkAnalysis/CompoundNet.java | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java new file mode 100644 index 000000000..43ee1635a --- /dev/null +++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java @@ -0,0 +1,180 @@ +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_graph.io.Bionetwork2BioGraph; +import fr.inrae.toulouse.metexplore.met4j_graph.io.ExportGraph; +import fr.inrae.toulouse.metexplore.met4j_graph.io.NodeMapping; +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.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; + +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; + + @Option(name = "-mc", aliases = {"--nocomp"}, usage = "merge compartments (requires unique compound names that are consistent across compartments)", required = false) + public boolean mergeComp = false; + + @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(); + + app.parseArguments(args); + + app.run(); + + } + + + public void run() throws IOException, Met4jSbmlReaderException { + System.out.print("Reading SBML..."); + JsbmlReader reader = new JsbmlReader(this.inputPath, false); + ArrayList<PackageParser> pkgs = new ArrayList<>(Arrays.asList( + new NotesParser(false), new FBCParser(), new GroupPathwayParser())); + BioNetwork network = reader.read(pkgs); + 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 = mapper.map(inputSide); + 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); + } + wp.setWeight(graph); + + //invert graph as undirected (copy edge weight to reversed edge) + if(undirected){ + System.out.print("Create Undirected..."); + graph.asUndirected(); + System.out.println(" Done."); + } + + //merge compartment + if(mergeComp){ + System.out.print("Merging compartments..."); + VertexContraction vc = new VertexContraction(); + graph = vc.decompartmentalize(graph, new VertexContraction.MapByName()); + System.out.println(" Done."); + } + + //remove isolated nodes + if(removeIsolated){ + System.out.println("Remove isolated nodes..."); + HashSet<BioMetabolite> nodes = new HashSet<>(graph.vertexSet()); + graph.removeIsolatedNodes(); + nodes.removeAll(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(); + wp2.setWeight(graph); + System.out.println(" Done."); + } + + //merge parallel edges + if(mergeEdges){ + System.out.print("Merging edges..."); + EdgeMerger.mergeEdgesWithOverride(graph); + System.out.println(" Done."); + } + + //export graph + System.out.print("Exporting..."); + if(asMatrix){ + ComputeAdjacencyMatrix adjBuilder = new ComputeAdjacencyMatrix(graph); + if(!computeWeight) adjBuilder.parallelEdgeWeightsHandling((u, v) -> Math.max(u,v)); + ExportMatrix.toCSV(this.outputPath,adjBuilder.getadjacencyMatrix()); + }else{ + ExportGraph.toGmlWithAttributes(graph, this.outputPath, true); + } + System.out.println(" Done."); + return; + } + + @Override + public String getLabel() {return this.getClass().getSimpleName();} + + @Override + 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."; + } + + @Override + public String getShortDescription() {return "Advanced creation of a compound graph representation of a SBML file content";} +} + -- GitLab From 1e0428ec6e1da4dff162e718a7825322156a4899 Mon Sep 17 00:00:00 2001 From: cfrainay <clement.frainay@inrae.fr> Date: Fri, 18 Mar 2022 17:17:10 +0100 Subject: [PATCH 2/3] Allow compartment merging from identifier suffix --- .../met4j_toolbox/networkAnalysis/CompoundNet.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java index 43ee1635a..bc0b5d22d 100644 --- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java +++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.function.Function; public class CompoundNet extends AbstractMet4jApplication { @@ -39,8 +40,11 @@ public class CompoundNet extends AbstractMet4jApplication { @Option(name = "-o", usage = "output Graph file", required = true) public String outputPath = null; - @Option(name = "-mc", aliases = {"--nocomp"}, usage = "merge compartments (requires unique compound names that are consistent across compartments)", required = false) - public boolean mergeComp = false; + 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; @@ -116,10 +120,11 @@ public class CompoundNet extends AbstractMet4jApplication { } //merge compartment - if(mergeComp){ + if(mergingStrat!=null){ System.out.print("Merging compartments..."); VertexContraction vc = new VertexContraction(); - graph = vc.decompartmentalize(graph, new VertexContraction.MapByName()); + VertexContraction.Mapper merger = mergingStrat.equals(strategy.by_name) ? new VertexContraction.MapByName() : new VertexContraction.MapByIdSubString(idRegex); + graph = vc.decompartmentalize(graph, merger); System.out.println(" Done."); } -- GitLab From 027b476cc26a5ff8faae74132ed1f94ea25880b3 Mon Sep 17 00:00:00 2001 From: Ludovic Cottret <ludovic.cottret@inra.fr> Date: Fri, 1 Apr 2022 17:11:23 +0200 Subject: [PATCH 3/3] Update JsbmlReader call --- .../metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java index bc0b5d22d..c09ef4d57 100644 --- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java +++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/networkAnalysis/CompoundNet.java @@ -79,7 +79,7 @@ public class CompoundNet extends AbstractMet4jApplication { public void run() throws IOException, Met4jSbmlReaderException { System.out.print("Reading SBML..."); - JsbmlReader reader = new JsbmlReader(this.inputPath, false); + JsbmlReader reader = new JsbmlReader(this.inputPath); ArrayList<PackageParser> pkgs = new ArrayList<>(Arrays.asList( new NotesParser(false), new FBCParser(), new GroupPathwayParser())); BioNetwork network = reader.read(pkgs); -- GitLab