diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/CommunityNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/CommunityNetworkBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..c14270c6d0f06e00257479cee568b94c1b707682 --- /dev/null +++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/CommunityNetworkBuilder.java @@ -0,0 +1,49 @@ +package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork; + +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +public class CommunityNetworkBuilder extends MetaNetworkBuilder implements MultiNetworkBuilder{ + + private BioCompartment medium; + + public CommunityNetworkBuilder(){ + setMedium(); + } + + public CommunityNetworkBuilder(BioCompartment medium){ + this.medium=medium; + this.addNewSharedCompartment(medium); + } + + public CommunityNetworkBuilder(Set<BioNetwork> networks){ + setMedium(); + Map<BioNetwork,String> prefixes = networks.stream() + .collect(Collectors.toMap(n->n,n->n.getId()+"_")); + this.setEntityFactory(new PrefixedMetaEntityFactory(prefixes,"pool")); + for(BioNetwork bn : networks){ + this.add(bn); + } + } + + private void setMedium(){ + this.medium = new BioCompartment(UUID.randomUUID().toString(),"medium"); + this.addNewSharedCompartment(medium); + } + + public void exchangeWithMedium(BioNetwork sourceNetwork, BioCompartment sourceCompartment, BioMetabolite metabolite){ + this.exchangeWithSharedCompartment(sourceNetwork, sourceCompartment, metabolite, medium); + }; + + public void add(BioNetwork bn, BioCompartment externalComp) { + super.add(bn); + this.fuseCompartmentIntoSharedCompartment(bn,externalComp,medium); + } + +} diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaBioMetabolite.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaBioMetabolite.java new file mode 100644 index 0000000000000000000000000000000000000000..4d3bfce22439ed409274663402e56ee87c273eeb --- /dev/null +++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaBioMetabolite.java @@ -0,0 +1,16 @@ +package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork; + +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork; + +public record MetaBioMetabolite(BioMetabolite metabolite, BioCompartment sourceCompartment, BioNetwork sourceNetwork) { + public MetaBioMetabolite(BioMetabolite metabolite, BioCompartment sourceCompartment, BioNetwork sourceNetwork){ + if(!sourceNetwork.containsMetabolite(metabolite.getId())) throw new IllegalArgumentException("Source network does not contains metabolites"); + if(!sourceNetwork.containsCompartment(sourceCompartment.getId())) throw new IllegalArgumentException("Source network does not contains source compartment"); + if(sourceCompartment.getComponentsView().get(metabolite.getId())==null) throw new IllegalArgumentException("Source compartment does not contains metabolites"); + this.metabolite = metabolite; + this.sourceCompartment = sourceCompartment; + this.sourceNetwork = sourceNetwork; + } +} diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..9b709b4ab21c7a8d31743a52edd3dcb25f556f74 --- /dev/null +++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java @@ -0,0 +1,40 @@ +package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork; + +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioEntity; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork; + +import java.util.Collection; + +/** +* Factory to create new instances of BioEntity and avoid id conflicts between subnetworks in a meta-graph. +**/ +public interface MetaEntityFactory { + + /** + * Create a copy of a subnetwork's BioEntity for its MetaGraph, while guaranteeing no id conflicts if the same + * original id was used in another subnetworks. This may be done by referencing in the new id the source network + * passed as parameter. + * @param originalEntity the original entity + * @param source the source network + **/ + <E extends BioEntity> E createMetaEntity(E originalEntity, BioNetwork source); + + /** + * Create a copy of a subnetwork's BioMetabolite in a shared compartment. + * @param originalEntity the original entity + * @param sharedComp the target shared compartment + * @return + */ + BioMetabolite createSharedCompound(BioMetabolite originalEntity, BioCompartment sharedComp); + + /** + * Create a new BioMetabolite in a shared compartment, that represent a pool of compounds from different subnetworks. + * @param entities the matching entities in different subnetworks + * @param sharedComp the target shared compartment + * @return + */ + BioMetabolite createPoolCompound (Collection<BioMetabolite> entities, BioCompartment sharedComp); + +} diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..6180d7b809e2305bc6b48674fbe16f5b57725cae --- /dev/null +++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java @@ -0,0 +1,351 @@ +package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork; + +import fr.inrae.toulouse.metexplore.met4j_core.biodata.*; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.collection.BioCollection; +import lombok.Getter; +import lombok.Setter; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * A class that creates a meta-network from multiple sub-networks. A meta-network is a single model which contains several + * sub-networks that remain individualized within the meta-network (as opposed to models fusion), but which can share + * some of their components with other sub-networks through shared compartments. + */ +public class MetaNetworkBuilder implements MultiNetworkBuilder{ + + private Map<BioMetabolite,BioMetabolite> metaboliteConversion; + private Map<BioCompartment,BioCompartment> compartmentConversion; + private Map<BioReaction,BioReaction> reactionConversion; + private Map<BioProtein,BioProtein> proteinConversion; + private Map<BioEnzyme,BioEnzyme> enzymeConversion; + private Map<BioGene,BioGene> geneConversion; + + protected BioCollection<BioNetwork> networks = new BioCollection<>(); + private Map<BioCompartment, Set<MetaBioMetabolite>> metaCompComposition = new HashMap<>(); + + @Setter + protected Function<BioMetabolite,String> getSharedIdFunction = BioEntity::getName; + @Setter + private String poolReactionPrefix = "poolReaction_"; + @Setter + private String sharedTransportPrefix = "transport_"; + @Setter + private String exchangeReactionPrefix = "exchange_"; + @Setter + protected BiFunction<BioMetabolite, BioMetabolite, BioReaction> createLinkWithSharedPool = (m, pool) -> { + BioReaction r=new BioReaction(poolReactionPrefix+ m.getId()); + r.setReversible(true); + return r; + }; + + @Setter + protected Function<BioMetabolite, BioReaction> createPoolExchangeReaction = (pool) -> { + BioReaction r=new BioReaction(exchangeReactionPrefix+pool.getId()); + r.setReversible(true); + return r; + }; + + @Setter + protected BiFunction<BioMetabolite, BioCompartment, BioReaction> createTransportWithSharedComp = (m, comp) -> { + BioReaction r=new BioReaction(sharedTransportPrefix+ m.getId()+"_to_"+comp.getId()); + r.setReversible(true); + return r; + }; + @Setter + @Getter + protected MetaEntityFactory entityFactory; + protected HashMap<BioCompartment,BioCompartment> fuseMap= new HashMap<>(); + protected Boolean keepGPR = false; + @Setter + protected Boolean addExchangeReaction = true; + + /** + * enable a compartment to be shared between multiple organisms + * @param sc a compartment that will be shared between multiple bionetworks + */ + public void addNewSharedCompartment(BioCompartment sc) { + metaCompComposition.put(sc,new HashSet<>()); + } + + /** + * Fuse a sub-network's compartment into a meta-network's shared compartment. The former will be replaced by the latter during build. + * All compartment's components will be added to the shared compartment + * @param n the sub-network + * @param c the sub-network's compartment to be fused + * @param sc the shared meta-compartment that will receive the compartment's components + */ + public void fuseCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) { + if(!metaCompComposition.containsKey(sc)) throw new IllegalArgumentException("Shared meta-compartment "+sc.getId()+" not found in network"); + fuseMap.put(c,sc); + } + + /** + * Bump a sub-network's compartment into a meta-network's shared compartment. For each compartment's component, + * a copy is created within the shared compartment + * @param n the sub-network + * @param c the sub-network's compartment to be bumped + * @param sc the shared meta-compartment that will receive the compartment's components + */ + public void bumpCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) { + BioCollection<BioMetabolite> toShare = n.getMetabolitesView().stream() + .filter(x -> n.getCompartmentsOf(x).contains(c)) + .collect(BioCollection::new,BioCollection::add,BioCollection::addAll); + this.exchangeWithSharedCompartment(n,c,toShare,sc); + } + + /** + * Exchange a compound from a sub-network's compartment to a meta-network's shared compartment. A copy of the compound will be created + * @param sourceNetwork the original subnetwork of the compound + * @param sourceCompartment the original compartment of the compound in the subnetwork + * @param metabolite the compound + * @param sc the target shared meta-compartment that will receive the compound + */ + public void exchangeWithSharedCompartment(BioNetwork sourceNetwork, BioCompartment sourceCompartment, BioMetabolite metabolite, BioCompartment sc) { + if(!metaCompComposition.containsKey(sc)) throw new IllegalArgumentException("Shared meta-compartment "+sc.getId()+" not found in network"); + metaCompComposition.get(sc).add(new MetaBioMetabolite(metabolite,sourceCompartment,sourceNetwork)); + } + + /** + * Exchange compounds from a sub-network's compartment to a meta-network's shared compartment. A copy of each exchanged compound will be created + * @param sourceNetwork the original subnetwork of the compounds + * @param sourceCompartment the original compartment of the compounds in the subnetwork + * @param metabolites the compounds + * @param sc the target shared meta-compartment that will receive the compounds + */ + public void exchangeWithSharedCompartment(BioNetwork sourceNetwork, BioCompartment sourceCompartment, BioCollection<BioMetabolite> metabolites, BioCompartment sc) { + if(!metaCompComposition.containsKey(sc)) throw new IllegalArgumentException("Shared meta-compartment "+sc.getId()+" not found in network"); + Set<MetaBioMetabolite> scCompo = metaCompComposition.get(sc); + for(BioMetabolite m : metabolites ){ + scCompo.add(new MetaBioMetabolite(m,sourceCompartment,sourceNetwork)); + } + } + + @Override + public void add(BioNetwork bn) { + networks.add(bn); + } + + /** + * creates all maps for conversion (original bioentity, new meta-entity) + * instantiate meta-network as new bionetwork + * @return an empty meta-network + */ + protected BioNetwork initMetaNetwork(){ + this.metaboliteConversion = new HashMap<>(); + this.compartmentConversion = new HashMap<>(); + this.reactionConversion = new HashMap<>(); + this.proteinConversion = new HashMap<>(); + this.enzymeConversion = new HashMap<>(); + this.geneConversion = new HashMap<>(); + return new BioNetwork(); + } + + /** + * add all copied bioentity to the newly built meta-network + * @param meta the (usually empty) meta-network + */ + protected void populateMetaNetwork(BioNetwork meta){ + + for(BioNetwork sub : networks){ + + for(BioMetabolite e : sub.getMetabolitesView()){ + BioMetabolite e2 = entityFactory.createMetaEntity(e,sub); + meta.add(e2); + metaboliteConversion.put(e,e2); + } + for(BioCompartment c : sub.getCompartmentsView()){ + BioCompartment c2 = fuseMap.get(c); + if(c2==null){ + c2 = entityFactory.createMetaEntity(c,sub); + meta.add(c2); + } + compartmentConversion.put(c,c2); + BioCollection<BioMetabolite> content = c.getComponentsView().stream() + .filter((e) -> e.getClass().equals(BioMetabolite.class)) + .map(metaboliteConversion::get) + .collect(BioCollection::new,BioCollection::add,BioCollection::addAll); + + meta.affectToCompartment(c2, content); + } + + // Copy genes + if (keepGPR) { + //TODO case shared Genome + for (BioGene gene : sub.getGenesView()) { + BioGene newGene = entityFactory.createMetaEntity(gene,sub); + meta.add(newGene); + geneConversion.put(gene,newGene); + } + for (BioProtein protein : sub.getProteinsView()) { + BioProtein newProtein = entityFactory.createMetaEntity(protein,sub); + meta.add(newProtein); + proteinConversion.put(protein,newProtein); + + if (protein.getGene() != null) { + BioGene newGene = geneConversion.get(protein.getGene()); + meta.affectGeneProduct(newProtein, newGene); + } + } + for (BioEnzyme enzyme : sub.getEnzymesView()) { + + BioEnzyme newEnzyme = entityFactory.createMetaEntity(enzyme,sub); + meta.add(newEnzyme); + enzymeConversion.put(enzyme,newEnzyme); + + BioCollection<BioEnzymeParticipant> participants = enzyme.getParticipantsView(); + + for (BioEnzymeParticipant participant : participants) { + Double quantity = participant.getQuantity(); + + if (participant.getPhysicalEntity().getClass().equals(BioMetabolite.class)) { + BioMetabolite metabolite = (BioMetabolite) participant.getPhysicalEntity(); + BioMetabolite newMetabolite = metaboliteConversion.get(metabolite); + meta.affectSubUnit(newEnzyme, quantity, newMetabolite); + } else if (participant.getPhysicalEntity().getClass().equals(BioProtein.class)) { + BioProtein protein = (BioProtein) participant.getPhysicalEntity(); + BioProtein newProtein = proteinConversion.get(protein); + meta.affectSubUnit(newEnzyme, quantity, newProtein); + } + } + } + } + + for (BioReaction r : sub.getReactionsView()) { + + BioReaction newReaction = entityFactory.createMetaEntity(r,sub); + newReaction.setSpontaneous(r.isSpontaneous()); + newReaction.setReversible(r.isReversible()); + newReaction.setEcNumber(r.getEcNumber()); + + meta.add(newReaction); + reactionConversion.put(r,newReaction); + + // Copy lefts + for (BioReactant reactant : r.getLeftReactantsView()) { + BioMetabolite newMetabolite = metaboliteConversion.get(reactant.getMetabolite()); + BioCompartment newCpt = compartmentConversion.get(reactant.getLocation()); + Double sto = reactant.getQuantity(); + meta.affectLeft(newReaction, sto, newCpt, newMetabolite); + } + + // Copy rights + for (BioReactant reactant : r.getRightReactantsView()) { + BioMetabolite newMetabolite = metaboliteConversion.get(reactant.getMetabolite()); + BioCompartment newCpt = compartmentConversion.get(reactant.getLocation()); + Double sto = reactant.getQuantity(); + meta.affectRight(newReaction, sto, newCpt, newMetabolite); + } + + // Copy enzymes + if (keepGPR) { + for (BioEnzyme enzyme : r.getEnzymesView()) { + BioEnzyme newEnzyme = enzymeConversion.get(enzyme); + meta.affectEnzyme(newReaction, newEnzyme); + } + } + } + + for (BioPathway pathway : sub.getPathwaysView()) { + BioPathway newPathway = entityFactory.createMetaEntity(pathway,sub); + meta.add(newPathway); + // Add reactions into pathway + BioCollection<BioReaction> reactions = sub.getReactionsFromPathways(pathway); + + for (BioReaction reaction : reactions) { + BioReaction newReaction = reactionConversion.get(reaction); + meta.affectToPathway(newPathway, newReaction); + } + } + } + } + + /** + * Set the alias prefixes for the entities of the sub-networks. This allows to distinguish entities from different sub-networks + * that have the same name. The prefixes are added to the entity names in the meta-network. + * @param aliases a map of sub-networks and their respective prefixes + * (e.g. {subNetwork1: "prefix1", subNetwork2: "prefix2"}) + * The prefixes are added to the entity names in the meta-network. + * If a sub-network is not in the map, or is in the map but has a null or empty prefix, its entities will not be prefixed. + **/ + public void setAliasPrefixes(Map<BioNetwork,String> aliases, String poolPrefix){ + this.setEntityFactory(new PrefixedMetaEntityFactory(aliases, poolPrefix)); + } + + @Override + public BioNetwork build() { + BioNetwork meta = this.initMetaNetwork(); + //create shared compartment + this.initSharedComp(meta); + //add each subnetwork content + this.populateMetaNetwork(meta); + //add shared compartment components and exchanges + this.populateSharedComp(meta); + //link compounds in shared compartment + this.linkCompoundsInSharedComp(meta); + return meta; + } + protected void initSharedComp(BioNetwork meta) { + for (Map.Entry<BioCompartment, Set<MetaBioMetabolite>> compDescriptor : metaCompComposition.entrySet()) { + BioCompartment sc = compDescriptor.getKey(); + meta.addCompartment(sc); + } + } + + protected void populateSharedComp(BioNetwork meta){ + for(Map.Entry<BioCompartment, Set<MetaBioMetabolite>> compDescriptor : metaCompComposition.entrySet()){ + for(MetaBioMetabolite m : compDescriptor.getValue()){ + BioMetabolite m1 = metaboliteConversion.get(m.metabolite()); + BioMetabolite m2 = entityFactory.createSharedCompound(m1,compDescriptor.getKey()); + meta.add(m2); + meta.affectToCompartment(compDescriptor.getKey(),m2); + BioReaction t = createTransportWithSharedComp.apply(m1,compDescriptor.getKey()); + meta.add(t); + meta.affectLeft(t,1.0,compartmentConversion.get(m.sourceCompartment()),metaboliteConversion.get(m.metabolite())); + meta.affectLeft(t,1.0,compDescriptor.getKey(),m2); + } + } + } + + /** + * Links the source-specific metabolites representing the same compounds in the shared compartments, by creating a shared "pool" entity + * alongside their interconversion reactions. + * @param meta the meta-network + */ + protected void linkCompoundsInSharedComp(BioNetwork meta) { + //loop over each shared compartment + for(BioCompartment sharedComp : metaCompComposition.keySet()){ + //retrieve all metabolite components that has been previously set, using whole compartment fusing or individual additions + BioCollection<BioMetabolite> content = sharedComp.getComponentsView().stream() + .filter((e) -> e.getClass().equals(BioMetabolite.class)) + .map(o -> (BioMetabolite)o) + .collect(BioCollection::new,BioCollection::add,BioCollection::addAll); + + //all entities that represent the same compounds from different source are grouped + Map<String, List<BioMetabolite>> compoundGroups = content.stream().collect(Collectors.groupingBy(getSharedIdFunction)); + + //for each group, create a "pool" metabolite that represent them + for(Map.Entry<String, List<BioMetabolite>> group : compoundGroups.entrySet()){ + BioMetabolite pool = this.entityFactory.createPoolCompound(group.getValue(),sharedComp); + meta.add(pool); + meta.affectToCompartment(sharedComp,pool); + //for each member of the group, create a reversible reaction linking them to their "pool" counterpart + for(BioMetabolite e : group.getValue()){ + BioReaction r = this.createLinkWithSharedPool.apply(e,pool); + meta.add(r); + meta.affectRight(r,1.0, sharedComp,e); + meta.affectLeft(r,1.0, sharedComp,pool); + } + //if option selected, add exchange reaction to the pool for flux modeling + if(addExchangeReaction){ + BioReaction r = this.createPoolExchangeReaction.apply(pool); + meta.add(r); + meta.affectLeft(r,1.0, sharedComp,pool); + } + } + } + } +} diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..12f084efae29005006d84a27e1238f79d74b57bf --- /dev/null +++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java @@ -0,0 +1,20 @@ +package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork; + +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork; + +public interface MultiNetworkBuilder { + + /** + * add network into meta-network + * @param bn + */ + public void add(BioNetwork bn); + + /** + * create a new network which encompass all added networks + * @return + */ + public BioNetwork build(); + + +} diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..29437aa064cda40ad95fbbd32352ec69c0c394f9 --- /dev/null +++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java @@ -0,0 +1,100 @@ +package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork; + +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioEntity; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork; +import lombok.Setter; +import lombok.SneakyThrows; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; + +import static fr.inrae.toulouse.metexplore.met4j_core.utils.StringUtils.isVoid; + +/** + * Factory to create new instances of BioEntity using prefixed ids from user-defined alias, in order to avoid id conflicts between subnetworks + * in a meta-graph. + **/ +public class PrefixedMetaEntityFactory implements MetaEntityFactory { + + Map<BioNetwork,String> sourcePrefixMap; + @Setter + protected String poolPrefix; + @Setter + protected String sep = "_"; + + public PrefixedMetaEntityFactory(Map<BioNetwork,String> sourcePrefixMap, String poolPrefix){ + this.sourcePrefixMap=sourcePrefixMap; + this.poolPrefix=poolPrefix; + } + + /** + * create prefixed id. This function will be called to create a new id for a BioEntity, using the + * alias of the network it comes from (if it exists) and its original id. + */ + public BiFunction<String, BioNetwork, String> addSourcePrefix = (id, bn) -> isVoid(sourcePrefixMap.get(bn)) ? id : sourcePrefixMap.get(bn)+sep+id; + + /** + * create sharedCompartment-suffix id + */ + public BiFunction<String, BioCompartment, String> addCompSuffix = (id, comp) -> id+sep+comp.getId(); + /** + * create sharedCompartment-prefix id + */ + public BiFunction<String, BioCompartment, String> addCompPrefix = (id, comp) -> comp.getId()+sep+id; + + + /** + * remove prefix-id from network alias + */ + public Function<String, String> removeSourceSuffix = id -> { + for(String s : sourcePrefixMap.values()) { + id = id.replaceAll("^" + s + sep,""); + }; + return id; + }; + + /** + * create a pool-prefix id. This is used to identify a pool of compounds originating from different subnetworks. + */ + public Function<String, String> addPoolFlag = (id) -> poolPrefix+sep+id; + + + @SneakyThrows + @Override + public <E extends BioEntity> E createMetaEntity(E originalEntity, BioNetwork source) { + return newEntityInstance(originalEntity, this.addSourcePrefix.apply(originalEntity.getId(),source)); + } + + @SneakyThrows + @Override + public BioMetabolite createSharedCompound(BioMetabolite entity, BioCompartment sharedCompartment) { + return newEntityInstance(entity, this.addCompSuffix.apply(entity.getId(),sharedCompartment)); + } + + @SneakyThrows + @Override + public BioMetabolite createPoolCompound(Collection<BioMetabolite> entities, BioCompartment sharedCompartment) { + BioMetabolite entity = entities.iterator().next(); + return newEntityInstance(entity, removeSourceSuffix + .andThen(s -> addCompPrefix.apply(s,sharedCompartment)) + .andThen(addPoolFlag) + .apply(entity.getId())); + } + + private static <E extends BioEntity> E newEntityInstance(E entity, String newId) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + E newEntity = (E) entity.getClass().getDeclaredConstructor(String.class).newInstance(newId); + newEntity.setName(entity.getName()); + newEntity.setSynonyms(new ArrayList<>(entity.getSynonyms())); + newEntity.setComment(entity.getComment()); + newEntity.setRefs(new HashMap<>(entity.getRefs())); + newEntity.setAttributes(new HashMap<>(entity.getAttributes())); + return newEntity; + } +} diff --git a/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java b/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..31d2bbd88ddc9d2f64da168194888af75520f5eb --- /dev/null +++ b/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java @@ -0,0 +1,287 @@ +package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork; + +import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment; +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.BioReaction; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.Assert.*; + +public class TestMetaNetworkBuilder { + + + public static BioNetwork bn1, bn2, bn3, bn4; + public static BioMetabolite a1,b1,c1,c1ex,c2,c2ex,d3, d3ex, f3, d4, d4ex; + public static BioCompartment co1in, co1ex, co2in, co2ex, co3in, co3ex,co4in, co4ex,medium,medium2; + public static BioReaction r1, r3, tc1, tc2, td3,td4; + + + public static BioNetwork initBump() { + HashMap<BioNetwork,String> alias = new HashMap<>(); + alias.put(bn1,"model1"); + alias.put(bn2,"model2"); + alias.put(bn3,"model3"); + alias.put(bn4,"model4"); + medium=new BioCompartment("medium"); + medium2=new BioCompartment("medium2"); + CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium); + builder.addNewSharedCompartment(medium2); + PrefixedMetaEntityFactory factory = new PrefixedMetaEntityFactory(alias,"pool"); + factory.setSep("_"); + builder.setEntityFactory(factory); + builder.setAddExchangeReaction(false); + builder.add(bn1); + builder.add(bn2); + builder.add(bn3); + builder.add(bn4); + builder.bumpCompartmentIntoSharedCompartment(bn1,co1ex,medium); + builder.bumpCompartmentIntoSharedCompartment(bn2,co2ex,medium); + builder.bumpCompartmentIntoSharedCompartment(bn3,co3ex,medium); + builder.bumpCompartmentIntoSharedCompartment(bn4,co4ex,medium2); + builder.bumpCompartmentIntoSharedCompartment(bn4,co4ex,medium); + return builder.build(); + } + + public static BioNetwork initFuse() { + HashMap<BioNetwork,String> alias = new HashMap<>(); + alias.put(bn1,"model1"); + alias.put(bn2,"model2"); + alias.put(bn3,"model3"); + alias.put(bn4,"model4"); + medium=new BioCompartment("medium"); + medium2=new BioCompartment("medium2"); + CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium); + builder.addNewSharedCompartment(medium2); + PrefixedMetaEntityFactory factory = new PrefixedMetaEntityFactory(alias,"pool"); + factory.setSep("_"); + builder.setEntityFactory(factory); + builder.setAddExchangeReaction(false); + builder.add(bn1,co1ex); + builder.add(bn2,co2ex); + builder.add(bn3,co3ex); + builder.add(bn4); + builder.fuseCompartmentIntoSharedCompartment(bn4,co4ex,medium2); + return builder.build(); + } + + public static BioNetwork initFuseIncremental() { + HashMap<BioNetwork,String> alias = new HashMap<>(); + alias.put(bn1,"model1"); + alias.put(bn2,"model2"); + + BioCompartment medium0=new BioCompartment("medium"); + CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium0); + PrefixedMetaEntityFactory factory = new PrefixedMetaEntityFactory(alias,"pool"); + factory.setSep("_"); + builder.setEntityFactory(factory); + builder.setAddExchangeReaction(false); + builder.add(bn1,co1ex); + builder.add(bn2,co2ex); + BioNetwork firstIter = builder.build(); + + System.out.println(Arrays.toString(bn1.getMetabolitesView().stream().map(x -> x.getId()).toArray())); + System.out.println(Arrays.toString(bn1.getReactionsView().stream().map(x -> x.getId()).toArray())); + System.out.println("+"); + System.out.println(Arrays.toString(bn2.getMetabolitesView().stream().map(x -> x.getId()).toArray())); + System.out.println(Arrays.toString(bn2.getReactionsView().stream().map(x -> x.getId()).toArray())); + System.out.println("="); + System.out.println(Arrays.toString(firstIter.getMetabolitesView().stream().map(x -> x.getId()).toArray())); + System.out.println(Arrays.toString(firstIter.getReactionsView().stream().map(x -> x.getId()).toArray())); + + BioMetabolite[] pool1 = firstIter.getCompartment("medium") + .getComponentsView().stream() + .filter(e -> e instanceof BioMetabolite) + .filter(m -> m.getId().startsWith("pool_")) + .toArray(BioMetabolite[]::new); + for(BioMetabolite pool : pool1){ + firstIter.removeOnCascade(firstIter.getReactionsFromMetabolite(pool)); + } + firstIter.removeOnCascade(pool1); + + System.out.println("->"); + System.out.println(Arrays.toString(firstIter.getMetabolitesView().stream().map(x -> x.getId()).toArray())); + System.out.println(Arrays.toString(firstIter.getReactionsView().stream().map(x -> x.getId()).toArray())); + + medium=new BioCompartment("medium"); + medium2=new BioCompartment("medium2"); + builder = new CommunityNetworkBuilder(medium); + builder.addNewSharedCompartment(medium2); + alias = new HashMap<>(); + alias.put(firstIter,""); + alias.put(bn3,"model3"); + alias.put(bn4,"model4"); + builder.setEntityFactory(new PrefixedMetaEntityFactory(alias,"pool")); + builder.setAddExchangeReaction(false); + builder.add(firstIter,medium0); + builder.add(bn3,co3ex); + builder.add(bn4); + builder.fuseCompartmentIntoSharedCompartment(bn4,co4ex,medium2); + + BioNetwork output = builder.build(); + System.out.println(Arrays.toString(output.getMetabolitesView().stream().map(x -> x.getId()).toArray())); + System.out.println(Arrays.toString(output.getReactionsView().stream().map(x -> x.getId()).toArray())); + return output; + } + + @BeforeClass + public static void beforeClass() { + bn1 = new BioNetwork("bn1"); + bn2 = new BioNetwork("bn2"); + bn3 = new BioNetwork("bn3"); + bn4 = new BioNetwork("bn4"); + a1 = new BioMetabolite("a_in","a"); + b1 = new BioMetabolite("b_in","b"); + c1 = new BioMetabolite("c_in","c"); + c1ex = new BioMetabolite("c_ex","c"); + r1 = new BioReaction("r1"); + tc1 = new BioReaction("tc"); + co1in = new BioCompartment("in"); + co1ex = new BioCompartment("ex"); + bn1.add(a1,b1,c1,c1ex,r1,tc1,co1in,co1ex); + bn1.affectToCompartment(co1in,a1,b1,c1); + bn1.affectToCompartment(co1ex,c1ex); + bn1.affectLeft(r1,1.0,co1in,a1); + bn1.affectLeft(r1,1.0,co1in,b1); + bn1.affectRight(r1,2.0,co1in,c1); + bn1.affectLeft(tc1,1.0,co1in,c1); + bn1.affectRight(tc1,1.0,co1ex,c1ex); + c2 = new BioMetabolite("c_in","c"); + c2ex = new BioMetabolite("c_ex","c"); + tc2 = new BioReaction("tc"); + co2in = new BioCompartment("in"); + co2ex = new BioCompartment("ex"); + bn2.add(c2,c2ex,tc2,co2in,co2ex); + bn2.affectToCompartment(co2in,c2); + bn2.affectToCompartment(co2ex,c2ex); + bn2.affectLeft(tc2,1.0,co2in,c2); + bn2.affectRight(tc2,1.0,co2ex,c2ex); + d3ex = new BioMetabolite("d_ex","d"); + d3 = new BioMetabolite("d_in","d"); + f3 = new BioMetabolite("f_in","f"); + r3 = new BioReaction("r2"); + td3 = new BioReaction("td"); + co3in = new BioCompartment("in"); + co3ex = new BioCompartment("ex"); + bn3.add(d3ex,d3,f3,r3,td3,co3in,co3ex); + bn3.affectToCompartment(co3in,d3,f3); + bn3.affectToCompartment(co3ex,d3ex); + bn3.affectLeft(td3,1.0,co3in,d3); + bn3.affectRight(td3,1.0,co3ex,d3ex); + bn3.affectLeft(r3,1.0,co3in,d3); + bn3.affectRight(r3,1.0,co3in,f3); + d4 = new BioMetabolite("d_in","d"); + d4ex = new BioMetabolite("d_ex","d"); + td4 = new BioReaction("td"); + co4in = new BioCompartment("in"); + co4ex = new BioCompartment("ex"); + bn4.add(d4,d4ex,td4,co4in,co4ex); + bn4.affectToCompartment(co4in,d4); + bn4.affectToCompartment(co4ex,d4ex); + bn4.affectLeft(td4,1.0,co4in,d4); + bn4.affectRight(td4,1.0,co4ex,d4ex); + } + + @Test + public void testCompoundsFuse(){ + BioNetwork meta = initFuse(); + assertEquals(14,meta.getMetabolitesView().size()); + assertNotNull(meta.getMetabolite("model1_a_in")); + assertNotNull(meta.getMetabolite("model1_c_in")); + assertNotNull(meta.getMetabolite("model1_c_ex")); + assertNotNull(meta.getMetabolite("model2_c_ex")); + assertNotNull(meta.getMetabolite("model2_c_in")); + + assertNotNull(meta.getMetabolite("pool_medium_c_ex")); + assertNotNull(meta.getMetabolite("pool_medium_d_ex")); + assertNotNull(meta.getMetabolite("pool_medium2_d_ex")); + } + + @Test + public void testReactionsFuse() { + BioNetwork meta = initFuse(); + assertEquals(10, meta.getReactionsView().size()); + assertNotNull(meta.getReaction("poolReaction_model2_c_ex")); + assertNotNull(meta.getReaction("poolReaction_model1_c_ex")); + assertNotNull(meta.getReaction("poolReaction_model3_d_ex")); + } + + @Test + public void testCompartmentFuse(){ + BioNetwork meta = initFuse(); + assertEquals(6,meta.getCompartmentsView().size()); + } + + @Test + public void testCompoundsFuseIncremental(){ + BioNetwork meta = initFuseIncremental(); + assertEquals(14,meta.getMetabolitesView().size()); + assertNotNull(meta.getMetabolite("model1_a_in")); + assertNotNull(meta.getMetabolite("model1_c_in")); + assertNotNull(meta.getMetabolite("model1_c_ex")); + assertNotNull(meta.getMetabolite("model2_c_ex")); + assertNotNull(meta.getMetabolite("model2_c_in")); + + Set<String> metabolites = meta.getMetabolitesView().stream().map(x -> x.getId()).collect(Collectors.toSet()); + assertTrue(metabolites.contains("pool_medium_model1_c_ex")||metabolites.contains("pool_medium_model2_c_ex")); + assertNotNull(meta.getMetabolite("pool_medium_d_ex")); + assertNotNull(meta.getMetabolite("pool_medium2_d_ex")); + } + + @Test + public void testReactionsFuseIncremental() { + BioNetwork meta = initFuseIncremental(); + assertEquals(10, meta.getReactionsView().size()); + assertNotNull(meta.getReaction("poolReaction_model2_c_ex")); + assertNotNull(meta.getReaction("poolReaction_model1_c_ex")); + assertNotNull(meta.getReaction("poolReaction_model3_d_ex")); + } + + @Test + public void testCompartmentFuseIncremental(){ + BioNetwork meta = initFuseIncremental(); + assertEquals(6,meta.getCompartmentsView().size()); + } + + + @Test + public void testCompoundsBump(){ + BioNetwork meta = initBump(); + assertEquals(19,meta.getMetabolitesView().size()); + assertNotNull(meta.getMetabolite("model1_a_in")); + assertNotNull(meta.getMetabolite("model1_c_in")); + assertNotNull(meta.getMetabolite("model1_c_ex")); + assertNotNull(meta.getMetabolite("model2_c_ex")); + assertNotNull(meta.getMetabolite("model2_c_in")); + + assertNotNull(meta.getMetabolite("pool_medium_c_ex_medium")); + assertNotNull(meta.getMetabolite("pool_medium_d_ex_medium")); + assertNotNull(meta.getMetabolite("pool_medium2_d_ex_medium2")); + } + + @Test + public void testReactionsBump() { + BioNetwork meta = initBump(); + assertEquals(16, meta.getReactionsView().size()); + assertNotNull(meta.getReaction("poolReaction_model2_c_ex_medium")); + assertNotNull(meta.getReaction("poolReaction_model1_c_ex_medium")); + assertNotNull(meta.getReaction("poolReaction_model3_d_ex_medium")); + assertNotNull(meta.getReaction("transport_model1_c_ex_to_medium")); + assertNotNull(meta.getReaction("poolReaction_model4_d_ex_medium2")); + assertNotNull(meta.getReaction("poolReaction_model4_d_ex_medium")); + assertNotNull(meta.getReaction("transport_model4_d_ex_to_medium2")); + assertNotNull(meta.getReaction("transport_model4_d_ex_to_medium")); + } + + @Test + public void testCompartmentBump(){ + BioNetwork meta = initBump(); + assertEquals(10,meta.getCompartmentsView().size()); + } +} diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/GenerateGalaxyFiles.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/GenerateGalaxyFiles.java index ecd9317c2d8a71fd5e8c568ae9af66a7e2606d3a..1d26274c58bfe78f9e89f7f5162885c7b4aff488 100644 --- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/GenerateGalaxyFiles.java +++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/GenerateGalaxyFiles.java @@ -57,7 +57,6 @@ import java.lang.reflect.Method; import java.net.URL; import java.util.Comparator; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; public class GenerateGalaxyFiles extends AbstractMet4jApplication { diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java new file mode 100644 index 0000000000000000000000000000000000000000..a0ef3029a8ec4f2e421a4fee66a60a0e3f18d518 --- /dev/null +++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java @@ -0,0 +1,204 @@ +package fr.inrae.toulouse.metexplore.met4j_toolbox.reconstruction; + +import fr.inrae.toulouse.metexplore.met4j_core.biodata.*; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork.CommunityNetworkBuilder; +import fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork.PrefixedMetaEntityFactory; +import fr.inrae.toulouse.metexplore.met4j_io.annotations.network.NetworkAttributes; +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.*; +import fr.inrae.toulouse.metexplore.met4j_io.jsbml.units.BioUnitDefinition; +import fr.inrae.toulouse.metexplore.met4j_io.jsbml.writer.JsbmlWriter; +import fr.inrae.toulouse.metexplore.met4j_io.jsbml.writer.Met4jSbmlWriterException; +import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.AbstractMet4jApplication; +import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumFormats; +import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumParameterTypes; +import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.Format; +import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.ParameterType; +import org.kohsuke.args4j.Option; + +import java.util.*; +import java.util.function.Function; + +import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumFormats.Sbml; +import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumParameterTypes.InputFile; +import static fr.inrae.toulouse.metexplore.met4j_toolbox.reconstruction.CreateMetaNetwork.strategy.by_name; + +public class CreateMetaNetwork extends AbstractMet4jApplication { + + //arguments + @Format(name = Sbml) + @ParameterType(name = InputFile) + @Option(name = "-n1", aliases = {"--network1"}, usage = "input SBML file: path to first network, in sbml format.", required = true) + public String sbml1FilePath; + @ParameterType(name = InputFile) + @Option(name = "-n2", aliases = {"--network2"}, usage = "input SBML file: path to second network, in sbml format.", required = true) + public String sbml2FilePath; + + @Option(name = "-n1ex", aliases = {"--external1"}, usage = "external compartment identifier in first network.", required = true) + public String external1; + @Option(name = "-n2ex", aliases = {"--external2"}, usage = "external compartment identifier in second network.", required = true) + public String external2; + + @Option(name = "-n1px", aliases = {"--n1prefix"}, usage = "prefix that will be added to first network's entities identifiers", required = false) + public String n1prefix = "Net1_"; + @Option(name = "-n2px", aliases = {"--n2prefix"}, usage = "prefix that will be added to second network's entities identifiers", required = false) + public String n2prefix = "Net2_"; + + @Option(name = "-k", aliases = {"--keepCompartment"}, usage = "keep the original external compartments in the meta-network, otherwise, they will be fused into the new shared external compartment", required = false) + public boolean keepCompartment = false; + + @Option(name = "-n1meta", aliases = {"--firstAsMeta"}, usage = "Treat first network as meta-network, allowing more than two sub-models with iterative fusions. This will overwrite shared compartment and pool compounds (which must follow the \"pool_\" prefix convention) and will ignore --n1prefix argument", required = false) + public boolean firstIsMeta = false; + + enum strategy {by_metanetx, by_name, by_id} + @Option(name = "-mc", aliases = {"--mergingCriterion"}, usage = "field used to identify the same metabolites across the two different networks. " + + "\"by_name\"/\"by_id\" can be used if names/identifiers are consistent and unambiguous across source models, \"by_metanetx\" can be used if models contains MetaNetX identifiers in annotation field using standard miriam format.") + public CreateMetaNetwork.strategy mergingCriterion = by_name; + + @ParameterType(name = EnumParameterTypes.OutputFile) + @Format(name = EnumFormats.Sbml) + @Option(name = "-o", usage = "output meta-network SBML file", required = true) + public String outputPath = null; + + public static void main(String[] args) throws Met4jSbmlWriterException { + CreateMetaNetwork app = new CreateMetaNetwork(); + app.parseArguments(args); + app.run(); + } + + public void run() throws Met4jSbmlWriterException { + + if(Objects.equals(this.n1prefix, this.n2prefix)){ + System.err.println("Error: prefixes must be different"); + System.exit(1); + } + + //import networks + System.out.print("Importing network 1..."); + JsbmlReader reader = new JsbmlReader(this.sbml1FilePath); + ArrayList<PackageParser> pkgs = new ArrayList<>(Arrays.asList( + new NotesParser(false), new AnnotationParser(true), new FBCParser(), new GroupPathwayParser())); + + BioNetwork network1 = null; + try { + network1 = reader.read(pkgs); + } catch (Met4jSbmlReaderException e) { + System.err.println("Error while reading the first SBML file"); + System.err.println(e.getMessage()); + System.exit(1); + } + BioCompartment co1ex = network1.getCompartment(external1); + if(co1ex==null){ + System.err.println("Error: external compartment " + external1 + " not found in network 1"); + System.exit(1); + } + if(firstIsMeta){ + n1prefix = ""; + BioMetabolite[] pool1 = network1.getCompartment(external1) + .getComponentsView().stream() + .filter(e -> e instanceof BioMetabolite) + .filter(m -> m.getId().startsWith("pool_")) + .toArray(BioMetabolite[]::new); + for(BioMetabolite pool : pool1){ + network1.removeOnCascade(network1.getReactionsFromMetabolite(pool)); + } + network1.removeOnCascade(pool1); + } + System.out.println(" Done."); + + + System.out.print("Importing network 2..."); + reader = new JsbmlReader(this.sbml2FilePath); + BioNetwork network2 = null; + try { + network2 = reader.read(pkgs); + } catch (Met4jSbmlReaderException e) { + System.err.println("Error while reading the second SBML file"); + System.err.println(e.getMessage()); + System.exit(1); + } + BioCompartment co2ex = network2.getCompartment(external2); + if(co2ex==null){ + System.err.println("Error: external compartment " + external2 + " not found in network 2"); + System.exit(1); + } + System.out.println(" Done."); + + System.out.print("Creating meta-network..."); + //setup + BioCompartment medium=new BioCompartment("medium"); medium.setName("medium"); + HashMap<BioNetwork,String> alias = new HashMap<>(); + alias.put(network1,n1prefix);alias.put(network2,n2prefix); + CommunityNetworkBuilder builder = initMetaNetworkBuilder(medium, alias); + builder.add(network1); + builder.add(network2); + + //build meta-network + if(!keepCompartment){ + builder.fuseCompartmentIntoSharedCompartment(network1,co1ex,medium); + builder.fuseCompartmentIntoSharedCompartment(network2,co2ex,medium); + }else{ + if(firstIsMeta) { + builder.fuseCompartmentIntoSharedCompartment(network1, co1ex, medium); + }else{ + builder.bumpCompartmentIntoSharedCompartment(network1, co1ex, medium); + } + builder.bumpCompartmentIntoSharedCompartment(network2,co2ex,medium); + } + BioNetwork metaNetwork = builder.build(); + System.out.println(" Done."); + + //export the meta-network + System.out.print("Exporting MetaNetwork..."); + NetworkAttributes.addUnitDefinition(metaNetwork, new BioUnitDefinition()); + new JsbmlWriter(outputPath,metaNetwork).write(); + System.out.println(" Done."); + + } + + private CommunityNetworkBuilder initMetaNetworkBuilder(BioCompartment medium, HashMap<BioNetwork, String> alias) { + CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium); + PrefixedMetaEntityFactory factory = new PrefixedMetaEntityFactory(alias,"pool_"); + factory.addCompSuffix = (id, comp) -> id+"_"+comp.getId(); + builder.setEntityFactory(factory); + Function<BioMetabolite, String> getSharedIdFunction; + switch (mergingCriterion) { + case by_metanetx: + getSharedIdFunction = x -> { + try { + BioRef r = x.getRefs("metanetx.chemical").iterator().next(); + return r.getId(); + } catch (NoSuchElementException | NullPointerException e) { + return "unknown_"+UUID.randomUUID(); + } + }; + break; + case by_id: + getSharedIdFunction = BioEntity::getId; + break; + case by_name: + getSharedIdFunction = BioEntity::getName; + break; + default: + throw new IllegalArgumentException("Invalid merging criterion: " + mergingCriterion); + } + builder.setGetSharedIdFunction(getSharedIdFunction); + return builder; + } + + @Override + public String getLabel() {return this.getClass().getSimpleName();} + + @Override + public String getShortDescription() { + return "Create a Meta-Network from two sub-networks in SBML format."; + } + + @Override + public String getLongDescription() { + return "Create a Meta-Network from two sub-networks in SBML format. A meta-network is a single model which contains several sub-networks that remains individualized within" + + "the meta-network (as opposed to models fusion), but which can share some of their components with " + + "other sub-networks through a shared \"medium\" compartment."; + } +}