diff --git a/.gitignore b/.gitignore
index b39dd5438..e5d2b169e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -104,6 +104,7 @@ ipython_config.py
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
+.idea/
# Gradle
.idea/**/gradle.xml
diff --git a/.idea/cobrapy.iml b/.idea/cobrapy.iml
index e246f868c..f175003e1 100644
--- a/.idea/cobrapy.iml
+++ b/.idea/cobrapy.iml
@@ -3,23 +3,13 @@
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 000000000..2fc81a0d1
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 000000000..105ce2da2
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 39251827e..60adcbf46 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..c8e807676
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/cobra/core/model.py b/src/cobra/core/model.py
index 5abf5acaf..c887b47a8 100644
--- a/src/cobra/core/model.py
+++ b/src/cobra/core/model.py
@@ -4,9 +4,12 @@
import logging
from copy import copy, deepcopy
from functools import partial
+from typing import List
from warnings import warn
import optlang
+from optlang import Constraint, Variable
+from optlang.container import Container
from optlang.symbolics import Basic, Zero
from cobra.core.configuration import Configuration
@@ -60,6 +63,8 @@ class Model(Object):
groups : DictList
A DictList where the key is the group identifier and the value a
Group
+ user_defined_const : UserDefinedConstraint
+ A Dictlist to store UserDefinedConstraints
solution : Solution
The last obtained solution from optimizing the model.
@@ -97,6 +102,8 @@ def __init__(self, id_or_model=None, name=None):
self.reactions = DictList() # A list of cobra.Reactions
self.metabolites = DictList() # A list of cobra.Metabolites
self.groups = DictList() # A list of cobra.Groups
+ self.user_defined_const = Container() # A list of UserDefinedConstraints
+ self._const_ids = set()
# genes based on their ids {Gene.id: Gene}
self._compartments = {}
self._contexts = []
@@ -326,6 +333,7 @@ def copy(self):
"notes",
"annotation",
"groups",
+ "user_defined_const",
}
for attr in self.__dict__:
if attr not in do_not_copy_by_ref:
@@ -403,6 +411,8 @@ def copy(self):
new_objects.append(new_object)
new_group.add_members(new_objects)
+ new.user_defined_const = deepcopy(self.user_defined_const)
+
try:
new._solver = deepcopy(self.solver)
# Cplex has an issue with deep copies
@@ -870,6 +880,77 @@ def get_associated_groups(self, element):
# check whether the element is associated with the model
return [g for g in self.groups if element in g.members]
+ def add_user_defined_constraints(self, constraints: List[Constraint]) -> None:
+ """Adds User defined constraints (FBC V3) to the model
+
+ Parameters
+ ----------
+ constraints: list
+ a list of UserDefinedConstraints
+ """
+
+ if isinstance(constraints, Constraint):
+ logger.warning(
+ f"The constraints passed must be inside a list: {constraints}")
+ constraints = [constraints]
+ if not isinstance(constraints, list):
+ # if single Constraint, convert to a list
+ raise TypeError(
+ f"The constraints passed must be inside a list: {constraints}"
+ )
+
+ list_of_var_cons = []
+ for constraint in constraints:
+ constraint: "Constraint"
+ if not isinstance(constraint, Constraint):
+ raise TypeError(
+ f"The user defined constraints passed must be of "
+ f"type 'Constraints': {constraint}"
+ )
+
+ variable_substituions = dict()
+ for variable in constraint.variables:
+ if self.variables.get(variable.name, None):
+ variable_substituions[variable] = self.variables.get(variable.name)
+ if variable.name in self.reactions:
+ variable_substituions[variable] = self.reactions.get_by_id(variable.name).flux_expression
+ constraint._expression = constraint.expression.xreplace(variable_substituions)
+
+ list_of_var_cons.append(constraint)
+ self.user_defined_const.has_key(constraint.name) or self.user_defined_const.append(constraint)
+ self.add_cons_vars(list_of_var_cons)
+
+ def remove_user_defined_constraints(self, constraints: List[Constraint]) -> None:
+ """Remove the constraints from the model
+
+ Parameters
+ ----------
+ constraints: list
+ a list of UserDefinedConstraints
+ """
+ if isinstance(constraints, Constraint):
+ logger.warning(
+ f"The constraints passed must be inside a list: {constraints}")
+ constraints = [constraints]
+ if not isinstance(constraints, list):
+ # if single Constraint, convert to a list
+ raise TypeError(
+ f"The constraints passed must be inside a list: {constraints}"
+ )
+
+ cons_to_remove = []
+ for constraint in constraints:
+ constraint: "Constraint"
+ if not isinstance(constraint, Constraint):
+ raise TypeError(
+ f"The user defined constraints passed must be of "
+ f"type 'Constraint': {constraint}"
+ )
+ self._const_ids.remove(constraint.name)
+ cons_to_remove = self.constraints[constraint.name]
+ self.user_defined_const.__delitem__(constraint.name)
+ self.remove_cons_vars(cons_to_remove)
+
def add_cons_vars(self, what, **kwargs):
"""Add constraints and variables to the model's mathematical problem.
diff --git a/src/cobra/io/dict.py b/src/cobra/io/dict.py
index b0f009d4c..0c0a47a15 100644
--- a/src/cobra/io/dict.py
+++ b/src/cobra/io/dict.py
@@ -1,13 +1,30 @@
"""Provide functions for cobrapy objects to generic Python objects and vice-versa."""
-
-from collections import OrderedDict
-from operator import attrgetter, itemgetter
-from typing import TYPE_CHECKING, Dict, List, Sequence, Set, Union
+import itertools
+from collections import OrderedDict, defaultdict
+from typing import TYPE_CHECKING, Dict, List, Sequence, Set, Tuple, Union
import numpy as np
-from ..core import Gene, Metabolite, Model, Reaction
+from ..core import (
+ Gene,
+ Group,
+ Metabolite,
+ Model,
+ Reaction,
+)
+from ..io.sbml import (
+ F_GENE,
+ F_GENE_REV,
+ F_GROUP,
+ F_GROUP_REV,
+ F_REACTION,
+ F_REACTION_REV,
+ F_REPLACE,
+ F_SPECIE,
+ F_SPECIE_REV,
+)
from ..util.solver import set_objective
+from optlang.interface import Constraint, Variable
if TYPE_CHECKING:
@@ -16,7 +33,6 @@
_REQUIRED_REACTION_ATTRIBUTES = [
"id",
"name",
- "metabolites",
"lower_bound",
"upper_bound",
"gene_reaction_rule",
@@ -57,6 +73,14 @@
"annotation": {},
}
+_REQUIRED_GROUP_ATTRIBUTES = ["id", "kind", "members"]
+_ORDERED_OPTIONAL_GROUP_KEYS = ["name", "notes", "annotation"]
+_OPTIONAL_GROUP_ATTRIBUTES = {
+ "name": "",
+ "notes": {},
+ "annotation": {},
+}
+
_ORDERED_OPTIONAL_MODEL_KEYS = ["name", "compartments", "notes", "annotation"]
_OPTIONAL_MODEL_ATTRIBUTES = {
"name": None,
@@ -66,10 +90,43 @@
"annotation": {},
}
+_REQUIRED_CONSTRAINT_ATTRIBUTES = ["lower_bound", "upper_bound", "constraint_comps"]
+_ORDERED_OPTIONAL_CONSTRAINT_KEYS = ["id", "name", "notes", "annotation"]
+_OPTIONAL_CONSTRAINT_ATTRIBUTES = {
+ "id": None,
+ "name": None,
+ "notes": {},
+ "annotation": {},
+}
+
+_REQUIRED_CONSTRAINT_COMP_ATTRIBUTES = ["variable", "coefficient", "variable_type"]
+_ORDERED_OPTIONAL_CONSTRAINT_COMP_KEYS = ["id", "name", "notes", "annotation"]
+_OPTIONAL_CONSTRAINT_COMP_ATTRIBUTES = {
+ "id": None,
+ "name": None,
+ "notes": {},
+ "annotation": {},
+}
+
+def flatten(list_of_lists: Union[List, Tuple]) -> List:
+ """Flatten lists of lists.
+
+ Parameters
+ ----------
+ list_of_lists: List or Tuple
+ List or Tuple of lists or tuples to flatten.
+
+ Returns
+ -------
+ list: flattened list
+
+ """
+ return list(itertools.chain.from_iterable(list_of_lists))
+
def _fix_type(
value: Union[str, np.float, np.bool, Set, Dict]
-) -> Union[str, float, bool, List, OrderedDict]:
+) -> Union[str, float, bool, List, OrderedDict, Dict]:
"""Convert possible types to correct Python types.
Parameters
@@ -86,6 +143,8 @@ def _fix_type(
# Because numpy floats can not be pickled to json
if isinstance(value, str):
return str(value)
+ if isinstance(value, np.float) and (np.isnan(value) or np.isinf(value)):
+ return str(value)
if isinstance(value, np.float):
return float(value)
if isinstance(value, np.bool):
@@ -131,19 +190,65 @@ def _update_optional(
"""
for key in ordered_keys:
default = optional_attribute_dict[key]
- value = getattr(cobra_object, key)
+ value = getattr(cobra_object, key, None)
if value is None or value == default:
continue
new_dict[key] = _fix_type(value)
-def _metabolite_to_dict(metabolite: Metabolite) -> OrderedDict:
+def _fix_id_from_dict(
+ _id_to_fix: str,
+ _class_to_fix_to: str,
+ f_replace: dict = F_REPLACE, # noqa: W0102
+):
+ if f_replace is None:
+ f_replace = {}
+ if not f_replace:
+ return _id_to_fix
+ if _class_to_fix_to == "Metabolite":
+ return F_REPLACE[F_SPECIE](_id_to_fix)
+ elif _class_to_fix_to == "Reaction":
+ return F_REPLACE[F_REACTION](_id_to_fix)
+ elif _class_to_fix_to == "Gene":
+ return F_REPLACE[F_GENE](_id_to_fix)
+ elif _class_to_fix_to == "Group":
+ return F_REPLACE[F_GROUP](_id_to_fix)
+
+
+def _fix_value_from_dict(_key: str, _value_to_fix: Union[List, str]):
+ if _key == "lower_bound" or _key == "upper_bound":
+ _value_to_fix = float(_value_to_fix)
+
+ return _value_to_fix
+
+
+def _get_by_id(
+ _id: str, _object_to_get: str, model: Model
+) -> Union[Gene, Metabolite, Group, Reaction]:
+ if _object_to_get == "Reaction":
+ return model.reactions.get_by_id(_id)
+ elif _object_to_get == "Metabolite":
+ return model.metabolites.get_by_id(_id)
+ elif _object_to_get == "Group":
+ return model.groups.get_by_id(_id)
+ elif _object_to_get == "Gene":
+ return model.genes.get_by_id(_id)
+
+
+def _metabolite_to_dict(
+ metabolite: Metabolite, f_replace: dict = F_REPLACE # noqa: W0102
+) -> OrderedDict:
"""Convert a cobra Metabolite object to dictionary.
Parameters
----------
metabolite : cobra.Metabolite
The cobra.Metabolite to convert to dictionary.
+ f_replace : dict of replacement functions for id replacement
+ Dictionary of replacement functions for gene, specie, and reaction.
+ By default, the following id changes are performed on import:
+ clip G_ from genes, clip M_ from species, clip R_ from reactions
+ If no replacements should be performed, set f_replace={} or None
Returns
-------
@@ -155,9 +260,15 @@ def _metabolite_to_dict(metabolite: Metabolite) -> OrderedDict:
_metabolite_from_dict : Convert a dictionary to cobra Metabolite object.
"""
+ if f_replace is None:
+ f_replace = {}
+
new_metabolite = OrderedDict()
for key in _REQUIRED_METABOLITE_ATTRIBUTES:
- new_metabolite[key] = _fix_type(getattr(metabolite, key))
+ if key == "id" and f_replace and F_SPECIE_REV in f_replace:
+ new_metabolite[key] = _fix_type(f_replace[F_SPECIE_REV](metabolite.id))
+ else:
+ new_metabolite[key] = _fix_type(getattr(metabolite, key))
_update_optional(
metabolite,
new_metabolite,
@@ -167,7 +278,9 @@ def _metabolite_to_dict(metabolite: Metabolite) -> OrderedDict:
return new_metabolite
-def _metabolite_from_dict(metabolite: Dict) -> Metabolite:
+def _metabolite_from_dict(
+ metabolite: Dict, f_replace: dict = F_REPLACE # noqa: W0102
+) -> Metabolite:
"""Convert a dictionary to cobra Metabolite object.
Parameters
@@ -184,9 +297,17 @@ def _metabolite_from_dict(metabolite: Dict) -> Metabolite:
_metabolite_to_dict : Convert a cobra Metabolite object to dictionary.
"""
- new_metabolite = Metabolite()
- for k, v in metabolite.items():
- setattr(new_metabolite, k, v)
+ if f_replace is None:
+ f_replace = {}
+
+ new_metabolite = Metabolite(
+ _fix_id_from_dict(metabolite["id"], "Metabolite", f_replace)
+ )
+ [
+ setattr(new_metabolite, k, _fix_value_from_dict(k, v))
+ for k, v in metabolite.items()
+ if k != "id"
+ ]
return new_metabolite
@@ -210,14 +331,17 @@ def _gene_to_dict(gene: Gene) -> OrderedDict:
"""
new_gene = OrderedDict()
for key in _REQUIRED_GENE_ATTRIBUTES:
- new_gene[key] = _fix_type(getattr(gene, key))
+ if key == "id":
+ new_gene[key] = _fix_type(F_REPLACE["F_GENE_REV"](gene.id))
+ else:
+ new_gene[key] = _fix_type(getattr(gene, key))
_update_optional(
gene, new_gene, _OPTIONAL_GENE_ATTRIBUTES, _ORDERED_OPTIONAL_GENE_KEYS
)
return new_gene
-def gene_from_dict(gene: Dict) -> Gene:
+def gene_from_dict(gene: Dict, f_replace: dict = F_REPLACE) -> Gene: # noqa: W0102
"""Convert a dictionary to cobra Gene object.
Parameters
@@ -235,9 +359,15 @@ def gene_from_dict(gene: Dict) -> Gene:
_gene_to_dict : Convert a cobra Gene object to a dictionary.
"""
- new_gene = Gene(gene["id"])
- for k, v in gene.items():
- setattr(new_gene, k, v)
+ if f_replace is None:
+ f_replace = {}
+
+ new_gene = Gene(_fix_id_from_dict(gene["id"], "Gene", f_replace))
+ [
+ setattr(new_gene, k, _fix_value_from_dict(k, v))
+ for k, v in gene.items()
+ if k != "id"
+ ]
return new_gene
@@ -261,22 +391,18 @@ def _reaction_to_dict(reaction: Reaction) -> OrderedDict:
"""
new_reaction = OrderedDict()
for key in _REQUIRED_REACTION_ATTRIBUTES:
- if key != "metabolites":
- if key == "lower_bound" and (
- np.isnan(reaction.lower_bound) or np.isinf(reaction.lower_bound)
- ):
- new_reaction[key] = str(_fix_type(getattr(reaction, key)))
- elif key == "upper_bound" and (
- np.isnan(reaction.upper_bound) or np.isinf(reaction.upper_bound)
- ):
- new_reaction[key] = str(_fix_type(getattr(reaction, key)))
- else:
- new_reaction[key] = _fix_type(getattr(reaction, key))
- continue
- mets = OrderedDict()
- for met in sorted(reaction.metabolites, key=attrgetter("id")):
- mets[str(met)] = reaction.metabolites[met]
- new_reaction["metabolites"] = mets
+ if key == "id":
+ new_reaction[key] = _fix_type(F_REPLACE[F_REACTION_REV](reaction.id))
+ else:
+ new_reaction[key] = _fix_type(getattr(reaction, key))
+ if F_REPLACE and F_SPECIE_REV in F_REPLACE:
+ mets = {
+ F_REPLACE[F_SPECIE_REV](met.id): stoic
+ for met, stoic in reaction.metabolites.items()
+ }
+ else:
+ mets = {met.id: stoic for met, stoic in reaction.metabolites.items()}
+ new_reaction["metabolites"] = OrderedDict(mets)
_update_optional(
reaction,
new_reaction,
@@ -286,7 +412,9 @@ def _reaction_to_dict(reaction: Reaction) -> OrderedDict:
return new_reaction
-def _reaction_from_dict(reaction: Dict, model: Model) -> Reaction:
+def _reaction_from_dict(
+ reaction: Dict, model: Model, f_replace: Dict = F_REPLACE # noqa: W0102
+) -> Reaction:
"""Convert a dictionary to a cobra Reaction object.
Parameters
@@ -306,26 +434,93 @@ def _reaction_from_dict(reaction: Dict, model: Model) -> Reaction:
_reaction_to_dict : Convert a cobra Reaction object to a dictionary.
"""
- new_reaction = Reaction()
- for k, v in reaction.items():
- if k in {"objective_coefficient", "reversibility", "reaction"}:
- continue
- elif k == "metabolites":
- new_reaction.add_metabolites(
- OrderedDict(
- (model.metabolites.get_by_id(str(met)), coeff)
- for met, coeff in v.items()
- )
+ if f_replace is None:
+ f_replace = {}
+
+ new_reaction = Reaction(_fix_id_from_dict(reaction["id"], "Reaction", f_replace))
+ [
+ setattr(new_reaction, k, _fix_value_from_dict(k, v))
+ for k, v in reaction.items()
+ if k
+ not in {
+ "id",
+ "objective_coefficient",
+ "reversibility",
+ "reaction",
+ "metabolites",
+ }
+ ]
+
+ new_reaction.add_metabolites(
+ OrderedDict(
+ (
+ model.metabolites.get_by_id(
+ _fix_id_from_dict(str(met), "Metabolite", f_replace)
+ ),
+ coeff,
)
- else:
- if k == "lower_bound" or k == "upper_bound":
- setattr(new_reaction, k, float(v))
- else:
- setattr(new_reaction, k, v)
+ for met, coeff in reaction.get("metabolites", {}).items()
+ )
+ )
return new_reaction
-def model_to_dict(model: Model, sort: bool = False) -> OrderedDict:
+def group_to_dict(group: "Group") -> Dict:
+ new_group = OrderedDict()
+ for key in _REQUIRED_GROUP_ATTRIBUTES:
+ if key != "members":
+ if key == "id":
+ new_group[key] = _fix_type(F_REPLACE[F_GROUP_REV](group.id))
+ else:
+ new_group[key] = _fix_type(getattr(group, key))
+ continue
+ members = []
+ for member in group.members:
+ idRef = member.id
+ if isinstance(member, Reaction):
+ idRef = F_REPLACE[F_REACTION_REV](member.id)
+ elif isinstance(member, Gene):
+ idRef = F_REPLACE[F_GENE_REV](member.id)
+ elif isinstance(member, Metabolite):
+ idRef = F_REPLACE[F_SPECIE_REV](member.id)
+ elif isinstance(member, Group):
+ idRef = F_REPLACE[F_GROUP_REV](member.id)
+ json_member = {"idRef": idRef, "type": type(member).__name__}
+ members.append(json_member)
+ new_group["members"] = members
+ _update_optional(
+ group, new_group, _OPTIONAL_GROUP_ATTRIBUTES, _ORDERED_OPTIONAL_GROUP_KEYS
+ )
+ return new_group
+
+
+def group_from_dict(
+ group: Dict, model: Model, f_replace=F_REPLACE # noqa: W0102
+) -> Group:
+ if f_replace is None:
+ f_replace = {}
+
+ new_group = Group(_fix_id_from_dict(group["id"], "Group"))
+ [
+ setattr(new_group, k, _fix_value_from_dict(k, v))
+ for k, v in group.items()
+ if k not in {"id", "members"}
+ ]
+ cobra_members = [
+ _get_by_id(
+ _fix_id_from_dict(member["idRef"], member["type"], f_replace),
+ member["type"],
+ model,
+ )
+ for member in group["members"]
+ ]
+ new_group.add_members(cobra_members)
+ return new_group
+
+
+def model_to_dict(
+ model: Model, sort: bool = False, f_replace: dict = F_REPLACE # noqa: W0102
+) -> OrderedDict:
"""Convert a cobra Model to a dictionary.
Parameters
@@ -335,6 +530,11 @@ def model_to_dict(model: Model, sort: bool = False) -> OrderedDict:
sort : bool, optional
Whether to sort the metabolites, reactions, and genes or maintain the
order defined in the model (default False).
+ f_replace : dict of replacement functions for id replacement
+ Dictionary of replacement functions for gene, specie, and reaction.
+ By default, the following id changes are performed on import:
+ clip G_ from genes, clip M_ from species, clip R_ from reactions
+ If no replacements should be performed, set f_replace={} or None
Returns
-------
@@ -349,19 +549,31 @@ def model_to_dict(model: Model, sort: bool = False) -> OrderedDict:
model_from_dict : Convert a dictionary to a cobra Model.
"""
+ if f_replace is None:
+ f_replace = {}
+
obj = OrderedDict()
obj["metabolites"] = list(map(_metabolite_to_dict, model.metabolites))
obj["reactions"] = list(map(_reaction_to_dict, model.reactions))
obj["genes"] = list(map(_gene_to_dict, model.genes))
+ obj["groups"] = list(map(group_to_dict, model.groups))
+ if model.user_defined_const:
+ obj["user_defined_constraints"] = OrderedDict()
+ variables_to_export = set()
+ [variables_to_export.update(cons.variables) for cons in model.user_defined_const.values()]
+ obj["user_defined_constraints"]['variables'] = [var.to_json() for var in variables_to_export]
+ obj["user_defined_constraints"]['constraints'] = [cons.to_json() for cons in model.user_defined_const]
+ if model.objective.variables.intersection(variables_to_export):
+ obj["user_defined_constraints"]["objective"] = model.objective.to_json()
obj["id"] = model.id
_update_optional(
model, obj, _OPTIONAL_MODEL_ATTRIBUTES, _ORDERED_OPTIONAL_MODEL_KEYS
)
if sort:
- get_id = itemgetter("id")
- obj["metabolites"].sort(key=get_id)
- obj["reactions"].sort(key=get_id)
- obj["genes"].sort(key=get_id)
+ obj["metabolites"].sort(key=lambda x: x["id"])
+ obj["reactions"].sort(key=lambda x: x["id"])
+ obj["genes"].sort(key=lambda x: x["id"])
+ obj["groups"].sort(key=lambda x: x["id"])
return obj
@@ -408,11 +620,28 @@ def model_from_dict(obj: Dict) -> Model:
rxn for rxn in obj["reactions"] if rxn.get("objective_coefficient", 0) != 0
]
coefficients = {
- model.reactions.get_by_id(rxn["id"]): rxn["objective_coefficient"]
+ model.reactions.get_by_id(F_REPLACE["F_REACTION"](rxn["id"])): rxn[
+ "objective_coefficient"
+ ]
for rxn in objective_reactions
}
+ if "groups" in obj:
+ model.add_groups([group_from_dict(group, model) for group in obj["groups"]])
set_objective(model, coefficients)
- for k, v in obj.items():
- if k in {"id", "name", "notes", "compartments", "annotation"}:
- setattr(model, k, v)
+
+ if "user_defined_constraints" in obj:
+ variables = [model.solver.interface.Variable.from_json(var_json) for var_json in
+ obj['user_defined_constraints']["variables"]]
+ var_dict = {var.name: var for var in variables}
+ constraints = [model.solver.interface.Constraint.from_json(constraint_json, var_dict) for
+ constraint_json in obj['user_defined_constraints']["constraints"]]
+ model.add_user_defined_constraints(constraints)
+ if "objective" in obj["user_defined_constraints"]:
+ model.objective = model.objective.from_json(obj["user_defined_constraints"]["objective"], var_dict)
+ [
+ setattr(model, k, _fix_value_from_dict(k, v))
+ for k, v in obj.items()
+ if k in {"id", "name", "compartments", "annotation", "notes"}
+ ]
+
return model
diff --git a/src/cobra/io/schema_v1.json b/src/cobra/io/schema_v1.json
index edc9756fb..ce4d5be1c 100644
--- a/src/cobra/io/schema_v1.json
+++ b/src/cobra/io/schema_v1.json
@@ -145,6 +145,9 @@
},
"annotation": {
"type": "object"
+ },
+ "user_defined_constraints":{
+ "type": "array"
}
},
"required": [
@@ -153,5 +156,5 @@
"metabolites",
"genes"
],
- "additionalProperties": false
+ "additionalProperties": true
}
diff --git a/tests/data/iJO1366.pickle b/tests/data/iJO1366.pickle
index c35e3fa24..4d19ff88e 100644
Binary files a/tests/data/iJO1366.pickle and b/tests/data/iJO1366.pickle differ
diff --git a/tests/data/mini.json b/tests/data/mini.json
index 658bd26c1..15cac541e 100644
--- a/tests/data/mini.json
+++ b/tests/data/mini.json
@@ -5,23 +5,23 @@
},
"genes": [
{
- "id": "b0755",
+ "id": "G_b0755",
"name": "gpmA"
},
{
- "id": "b0875",
+ "id": "G_b0875",
"name": "aqpZ"
},
{
- "id": "b1101",
+ "id": "G_b1101",
"name": "ptsG"
},
{
- "id": "b1380",
+ "id": "G_b1380",
"name": "ldhA"
},
{
- "id": "b1621",
+ "id": "G_b1621",
"name": "malX"
},
{
@@ -31,85 +31,85 @@
"GI:1652654"
]
},
- "id": "b1676",
+ "id": "G_b1676",
"name": "pykF"
},
{
- "id": "b1723",
+ "id": "G_b1723",
"name": "pfkB"
},
{
- "id": "b1773",
+ "id": "G_b1773",
"name": "ydjI"
},
{
- "id": "b1779",
+ "id": "G_b1779",
"name": "gapA"
},
{
- "id": "b1817",
+ "id": "G_b1817",
"name": "manX"
},
{
- "id": "b1818",
+ "id": "G_b1818",
"name": "manY"
},
{
- "id": "b1819",
+ "id": "G_b1819",
"name": "manZ"
},
{
- "id": "b1854",
+ "id": "G_b1854",
"name": "pykA"
},
{
- "id": "b2097",
+ "id": "G_b2097",
"name": "fbaB"
},
{
- "id": "b2133",
+ "id": "G_b2133",
"name": "dld"
},
{
- "id": "b2415",
+ "id": "G_b2415",
"name": "ptsH"
},
{
- "id": "b2416",
+ "id": "G_b2416",
"name": "ptsI"
},
{
- "id": "b2417",
+ "id": "G_b2417",
"name": "crr"
},
{
"annotation": {
"ncbigi": "GI:1653839"
},
- "id": "b2779",
+ "id": "G_b2779",
"name": "eno"
},
{
- "id": "b2925",
+ "id": "G_b2925",
"name": "fbaA"
},
{
"annotation": {
"ncbigi": "GI:1653609"
},
- "id": "b2926",
+ "id": "G_b2926",
"name": "pgk"
},
{
- "id": "b2987",
+ "id": "G_b2987",
"name": "pitB"
},
{
- "id": "b3493",
+ "id": "G_b3493",
"name": "pitA"
},
{
- "id": "b3612",
+ "id": "G_b3612",
"name": "gpmM"
},
{
@@ -119,29 +119,30 @@
"GI:1651919"
]
},
- "id": "b3916",
+ "id": "G_b3916",
"name": "pfkA"
},
{
- "id": "b3919",
+ "id": "G_b3919",
"name": "tpiA"
},
{
"annotation": {
"ncbigi": "GI:1653253"
},
- "id": "b4025",
+ "id": "G_b4025",
"name": "pgi"
},
{
- "id": "b4395",
+ "id": "G_b4395",
"name": "ytjC"
},
{
- "id": "s0001",
+ "id": "G_s0001",
"name": "G_s0001"
}
],
+ "groups": [],
"id": "mini_textbook",
"metabolites": [
{
@@ -165,7 +166,7 @@
"charge": -4,
"compartment": "c",
"formula": "C3H4O10P2",
- "id": "13dpg_c",
+ "id": "M_13dpg_c",
"name": "3-Phospho-D-glyceroyl phosphate"
},
{
@@ -195,7 +196,7 @@
"charge": -3,
"compartment": "c",
"formula": "C3H4O7P",
- "id": "2pg_c",
+ "id": "M_2pg_c",
"name": "D-Glycerate 2-phosphate"
},
{
@@ -233,7 +234,7 @@
"charge": -3,
"compartment": "c",
"formula": "C3H4O7P",
- "id": "3pg_c",
+ "id": "M_3pg_c",
"name": "3-Phospho-D-glycerate"
},
{
@@ -277,7 +278,7 @@
"charge": -3,
"compartment": "c",
"formula": "C10H12N5O10P2",
- "id": "adp_c",
+ "id": "M_adp_c",
"name": "ADP"
},
{
@@ -318,7 +319,7 @@
"charge": -4,
"compartment": "c",
"formula": "C10H12N5O13P3",
- "id": "atp_c",
+ "id": "M_atp_c",
"name": "ATP"
},
{
@@ -354,7 +355,7 @@
"charge": -2,
"compartment": "c",
"formula": "C3H5O6P",
- "id": "dhap_c",
+ "id": "M_dhap_c",
"name": "Dihydroxyacetone phosphate"
},
{
@@ -390,7 +391,7 @@
"charge": -2,
"compartment": "c",
"formula": "C6H11O9P",
- "id": "f6p_c",
+ "id": "M_f6p_c",
"name": "D-Fructose 6-phosphate"
},
{
@@ -425,7 +426,7 @@
"charge": -4,
"compartment": "c",
"formula": "C6H10O12P2",
- "id": "fdp_c",
+ "id": "M_fdp_c",
"name": "D-Fructose 1,6-bisphosphate"
},
{
@@ -455,7 +456,7 @@
"charge": -2,
"compartment": "c",
"formula": "C3H5O6P",
- "id": "g3p_c",
+ "id": "M_g3p_c",
"name": "Glyceraldehyde 3-phosphate"
},
{
@@ -496,7 +497,7 @@
"charge": -2,
"compartment": "c",
"formula": "C6H11O9P",
- "id": "g6p_c",
+ "id": "M_g6p_c",
"name": "D-Glucose 6-phosphate"
},
{
@@ -511,7 +512,7 @@
"charge": 0,
"compartment": "e",
"formula": "C6H12O6",
- "id": "glc__D_e",
+ "id": "M_glc__D_e",
"name": "D-Glucose"
},
{
@@ -596,7 +597,7 @@
"charge": 0,
"compartment": "c",
"formula": "H2O",
- "id": "h2o_c",
+ "id": "M_h2o_c",
"name": "H2O"
},
{
@@ -681,7 +682,7 @@
"charge": 0,
"compartment": "e",
"formula": "H2O",
- "id": "h2o_e",
+ "id": "M_h2o_e",
"name": "H2O"
},
{
@@ -727,7 +728,7 @@
"charge": 1,
"compartment": "c",
"formula": "H",
- "id": "h_c",
+ "id": "M_h_c",
"name": "H+"
},
{
@@ -773,7 +774,7 @@
"charge": 1,
"compartment": "e",
"formula": "H",
- "id": "h_e",
+ "id": "M_h_e",
"name": "H+"
},
{
@@ -800,7 +801,7 @@
"charge": -1,
"compartment": "c",
"formula": "C3H5O3",
- "id": "lac__D_c",
+ "id": "M_lac__D_c",
"name": "D-Lactate"
},
{
@@ -827,7 +828,7 @@
"charge": -1,
"compartment": "e",
"formula": "C3H5O3",
- "id": "lac__D_e",
+ "id": "M_lac__D_e",
"name": "D-Lactate"
},
{
@@ -866,7 +867,7 @@
"charge": -1,
"compartment": "c",
"formula": "C21H26N7O14P2",
- "id": "nad_c",
+ "id": "M_nad_c",
"name": "Nicotinamide adenine dinucleotide"
},
{
@@ -900,7 +901,7 @@
"charge": -2,
"compartment": "c",
"formula": "C21H27N7O14P2",
- "id": "nadh_c",
+ "id": "M_nadh_c",
"name": "Nicotinamide adenine dinucleotide - reduced"
},
{
@@ -933,7 +934,7 @@
"charge": -3,
"compartment": "c",
"formula": "C3H2O6P",
- "id": "pep_c",
+ "id": "M_pep_c",
"name": "Phosphoenolpyruvate"
},
{
@@ -1000,7 +1001,7 @@
"charge": -2,
"compartment": "c",
"formula": "HO4P",
- "id": "pi_c",
+ "id": "M_pi_c",
"name": "Phosphate"
},
{
@@ -1067,7 +1068,7 @@
"charge": -2,
"compartment": "e",
"formula": "HO4P",
- "id": "pi_e",
+ "id": "M_pi_e",
"name": "Phosphate"
},
{
@@ -1101,7 +1102,7 @@
"charge": -1,
"compartment": "c",
"formula": "C3H3O3",
- "id": "pyr_c",
+ "id": "M_pyr_c",
"name": "Pyruvate"
}
],
@@ -1111,14 +1112,14 @@
"bigg.reaction": "ATPM"
},
"gene_reaction_rule": "",
- "id": "ATPM",
+ "id": "R_ATPM",
"lower_bound": 8.39,
"metabolites": {
- "adp_c": 1.0,
- "atp_c": -1.0,
- "h2o_c": -1.0,
- "h_c": 1.0,
- "pi_c": 1.0
+ "M_adp_c": 1.0,
+ "M_atp_c": -1.0,
+ "M_h2o_c": -1.0,
+ "M_h_c": 1.0,
+ "M_pi_c": 1.0
},
"name": "ATP maintenance requirement",
"objective_coefficient": 1.0,
@@ -1126,7 +1127,7 @@
},
{
"gene_reaction_rule": "",
- "id": "D_LACt2",
+ "id": "R_D_LACt2",
"lower_bound": -1000.0,
"metabolites": {},
"name": "",
@@ -1137,12 +1138,12 @@
"bigg.reaction": "ENO"
},
"gene_reaction_rule": "b2779",
- "id": "ENO",
+ "id": "R_ENO",
"lower_bound": -1000.0,
"metabolites": {
- "2pg_c": -1.0,
- "h2o_c": 1.0,
- "pep_c": 1.0
+ "M_2pg_c": -1.0,
+ "M_h2o_c": 1.0,
+ "M_pep_c": 1.0
},
"name": "enolase",
"upper_bound": 1000.0
@@ -1153,10 +1154,10 @@
"sbo": "SBO:0000627"
},
"gene_reaction_rule": "",
- "id": "EX_glc__D_e",
+ "id": "R_EX_glc__D_e",
"lower_bound": -10.0,
"metabolites": {
- "glc__D_e": -1.0
+ "M_glc__D_e": -1.0
},
"name": "D-Glucose exchange",
"upper_bound": 1000.0
@@ -1167,10 +1168,10 @@
"sbo": "SBO:0000627"
},
"gene_reaction_rule": "",
- "id": "EX_h_e",
+ "id": "R_EX_h_e",
"lower_bound": -1000.0,
"metabolites": {
- "h_e": -1.0
+ "M_h_e": -1.0
},
"name": "H+ exchange",
"upper_bound": 1000.0
@@ -1181,10 +1182,10 @@
"sbo": "SBO:0000627"
},
"gene_reaction_rule": "",
- "id": "EX_lac__D_e",
+ "id": "R_EX_lac__D_e",
"lower_bound": 0.0,
"metabolites": {
- "lac__D_e": -1.0
+ "M_lac__D_e": -1.0
},
"name": "D-lactate exchange",
"upper_bound": 1000.0
@@ -1194,12 +1195,12 @@
"bigg.reaction": "FBA"
},
"gene_reaction_rule": "b1773 or b2097 or b2925",
- "id": "FBA",
+ "id": "R_FBA",
"lower_bound": -1000.0,
"metabolites": {
- "dhap_c": 1.0,
- "fdp_c": -1.0,
- "g3p_c": 1.0
+ "M_dhap_c": 1.0,
+ "M_fdp_c": -1.0,
+ "M_g3p_c": 1.0
},
"name": "fructose-bisphosphate aldolase",
"upper_bound": 1000.0
@@ -1209,15 +1210,15 @@
"bigg.reaction": "GAPD"
},
"gene_reaction_rule": "b1779",
- "id": "GAPD",
+ "id": "R_GAPD",
"lower_bound": -1000.0,
"metabolites": {
- "13dpg_c": 1.0,
- "g3p_c": -1.0,
- "h_c": 1.0,
- "nad_c": -1.0,
- "nadh_c": 1.0,
- "pi_c": -1.0
+ "M_13dpg_c": 1.0,
+ "M_g3p_c": -1.0,
+ "M_h_c": 1.0,
+ "M_nad_c": -1.0,
+ "M_nadh_c": 1.0,
+ "M_pi_c": -1.0
},
"name": "glyceraldehyde-3-phosphate dehydrogenase",
"upper_bound": 1000.0
@@ -1227,13 +1228,13 @@
"bigg.reaction": "GLCpts"
},
"gene_reaction_rule": "(b2415 and b2417 and b1101 and b2416) or (b2415 and b2417 and b1621 and b2416) or (b2415 and b1818 and b1817 and b1819 and b2416)",
- "id": "GLCpts",
+ "id": "R_GLCpts",
"lower_bound": 0.0,
"metabolites": {
- "g6p_c": 1.0,
- "glc__D_e": -1.0,
- "pep_c": -1.0,
- "pyr_c": 1.0
+ "M_g6p_c": 1.0,
+ "M_glc__D_e": -1.0,
+ "M_pep_c": -1.0,
+ "M_pyr_c": 1.0
},
"name": "D-glucose transport via PEP:Pyr PTS",
"upper_bound": 1000.0
@@ -1243,11 +1244,11 @@
"bigg.reaction": "H2Ot"
},
"gene_reaction_rule": "b0875 or s0001",
- "id": "H2Ot",
+ "id": "R_H2Ot",
"lower_bound": -1000.0,
"metabolites": {
- "h2o_c": 1.0,
- "h2o_e": -1.0
+ "M_h2o_c": 1.0,
+ "M_h2o_e": -1.0
},
"name": "R H2O transport via - diffusion",
"upper_bound": 1000.0
@@ -1268,14 +1269,14 @@
"sbo": "SBO:0000375"
},
"gene_reaction_rule": "b2133 or b1380",
- "id": "LDH_D",
+ "id": "R_LDH_D",
"lower_bound": -1000.0,
"metabolites": {
- "h_c": 1.0,
- "lac__D_c": -1.0,
- "nad_c": -1.0,
- "nadh_c": 1.0,
- "pyr_c": 1.0
+ "M_h_c": 1.0,
+ "M_lac__D_c": -1.0,
+ "M_nad_c": -1.0,
+ "M_nadh_c": 1.0,
+ "M_pyr_c": 1.0
},
"name": "D-lactate dehydrogenase",
"upper_bound": 1000.0
@@ -1285,14 +1286,14 @@
"bigg.reaction": "PFK"
},
"gene_reaction_rule": "b3916 or b1723",
- "id": "PFK",
+ "id": "R_PFK",
"lower_bound": 0.0,
"metabolites": {
- "adp_c": 1.0,
- "atp_c": -1.0,
- "f6p_c": -1.0,
- "fdp_c": 1.0,
- "h_c": 1.0
+ "M_adp_c": 1.0,
+ "M_atp_c": -1.0,
+ "M_f6p_c": -1.0,
+ "M_fdp_c": 1.0,
+ "M_h_c": 1.0
},
"name": "phosphofructokinase",
"objective_coefficient": 1.0,
@@ -1303,11 +1304,11 @@
"bigg.reaction": "PGI"
},
"gene_reaction_rule": "b4025",
- "id": "PGI",
+ "id": "R_PGI",
"lower_bound": -1000.0,
"metabolites": {
- "f6p_c": 1.0,
- "g6p_c": -1.0
+ "M_f6p_c": 1.0,
+ "M_g6p_c": -1.0
},
"name": "glucose-6-phosphate isomerase",
"upper_bound": 1000.0
@@ -1317,13 +1318,13 @@
"bigg.reaction": "PGK"
},
"gene_reaction_rule": "b2926",
- "id": "PGK",
+ "id": "R_PGK",
"lower_bound": -1000.0,
"metabolites": {
- "13dpg_c": 1.0,
- "3pg_c": -1.0,
- "adp_c": 1.0,
- "atp_c": -1.0
+ "M_13dpg_c": 1.0,
+ "M_3pg_c": -1.0,
+ "M_adp_c": 1.0,
+ "M_atp_c": -1.0
},
"name": "phosphoglycerate kinase",
"upper_bound": 1000.0
@@ -1333,11 +1334,11 @@
"bigg.reaction": "PGM"
},
"gene_reaction_rule": "b4395 or b3612 or b0755",
- "id": "PGM",
+ "id": "R_PGM",
"lower_bound": -1000.0,
"metabolites": {
- "2pg_c": -1.0,
- "3pg_c": 1.0
+ "M_2pg_c": -1.0,
+ "M_3pg_c": 1.0
},
"name": "phosphoglycerate mutase",
"upper_bound": 1000.0
@@ -1347,13 +1348,13 @@
"bigg.reaction": "PIt2r"
},
"gene_reaction_rule": "b2987 or b3493",
- "id": "PIt2r",
+ "id": "R_PIt2r",
"lower_bound": -1000.0,
"metabolites": {
- "h_c": 1.0,
- "h_e": -1.0,
- "pi_c": 1.0,
- "pi_e": -1.0
+ "M_h_c": 1.0,
+ "M_h_e": -1.0,
+ "M_pi_c": 1.0,
+ "M_pi_e": -1.0
},
"name": "R phosphate reversible transport via - symport",
"upper_bound": 1000.0
@@ -1363,14 +1364,14 @@
"bigg.reaction": "PYK"
},
"gene_reaction_rule": "b1854 or b1676",
- "id": "PYK",
+ "id": "R_PYK",
"lower_bound": 0.0,
"metabolites": {
- "adp_c": -1.0,
- "atp_c": 1.0,
- "h_c": -1.0,
- "pep_c": -1.0,
- "pyr_c": 1.0
+ "M_adp_c": -1.0,
+ "M_atp_c": 1.0,
+ "M_h_c": -1.0,
+ "M_pep_c": -1.0,
+ "M_pyr_c": 1.0
},
"name": "pyruvate kinase",
"upper_bound": 1000.0
@@ -1380,11 +1381,11 @@
"bigg.reaction": "TPI"
},
"gene_reaction_rule": "b3919",
- "id": "TPI",
+ "id": "R_TPI",
"lower_bound": -1000.0,
"metabolites": {
- "dhap_c": -1.0,
- "g3p_c": 1.0
+ "M_dhap_c": -1.0,
+ "M_g3p_c": 1.0
},
"name": "triose-phosphate isomerase",
"upper_bound": 1000.0
diff --git a/tests/data/mini.mat b/tests/data/mini.mat
index a92a1570b..f711eb82c 100644
Binary files a/tests/data/mini.mat and b/tests/data/mini.mat differ
diff --git a/tests/data/mini.pickle b/tests/data/mini.pickle
index 135ce225e..e7b2b6c92 100644
Binary files a/tests/data/mini.pickle and b/tests/data/mini.pickle differ
diff --git a/tests/data/mini.yml b/tests/data/mini.yml
index e5e9e3b05..9b09e229c 100644
--- a/tests/data/mini.yml
+++ b/tests/data/mini.yml
@@ -1,7 +1,7 @@
!!omap
- metabolites:
- !!omap
- - id: 13dpg_c
+ - id: M_13dpg_c
- name: 3-Phospho-D-glyceroyl phosphate
- compartment: c
- charge: -4
@@ -22,7 +22,7 @@
- seed.compound: cpd00203
- unipathway.compound: UPC00236
- !!omap
- - id: 2pg_c
+ - id: M_2pg_c
- name: D-Glycerate 2-phosphate
- compartment: c
- charge: -3
@@ -48,7 +48,7 @@
- seed.compound: cpd00482
- unipathway.compound: UPC00631
- !!omap
- - id: 3pg_c
+ - id: M_3pg_c
- name: 3-Phospho-D-glycerate
- compartment: c
- charge: -3
@@ -81,7 +81,7 @@
- UPC00597
- UPC00197
- !!omap
- - id: adp_c
+ - id: M_adp_c
- name: ADP
- compartment: c
- charge: -3
@@ -119,7 +119,7 @@
- seed.compound: cpd00008
- unipathway.compound: UPC00008
- !!omap
- - id: atp_c
+ - id: M_atp_c
- name: ATP
- compartment: c
- charge: -4
@@ -155,7 +155,7 @@
- seed.compound: cpd00002
- unipathway.compound: UPC00002
- !!omap
- - id: dhap_c
+ - id: M_dhap_c
- name: Dihydroxyacetone phosphate
- compartment: c
- charge: -2
@@ -185,7 +185,7 @@
- seed.compound: cpd00095
- unipathway.compound: UPC00111
- !!omap
- - id: f6p_c
+ - id: M_f6p_c
- name: D-Fructose 6-phosphate
- compartment: c
- charge: -2
@@ -215,7 +215,7 @@
- UPC05345
- UPC00085
- !!omap
- - id: fdp_c
+ - id: M_fdp_c
- name: D-Fructose 1,6-bisphosphate
- compartment: c
- charge: -4
@@ -245,7 +245,7 @@
- seed.compound: cpd00290
- unipathway.compound: UPC00354
- !!omap
- - id: g3p_c
+ - id: M_g3p_c
- name: Glyceraldehyde 3-phosphate
- compartment: c
- charge: -2
@@ -269,7 +269,7 @@
- UPC00661
- UPC00118
- !!omap
- - id: g6p_c
+ - id: M_g6p_c
- name: D-Glucose 6-phosphate
- compartment: c
- charge: -2
@@ -303,7 +303,7 @@
- seed.compound: cpd00079
- unipathway.compound: UPC00092
- !!omap
- - id: glc__D_e
+ - id: M_glc__D_e
- name: D-Glucose
- compartment: e
- charge: 0
@@ -315,7 +315,7 @@
- kegg.compound: C00031
- pubchem.substance: '3333'
- !!omap
- - id: h2o_c
+ - id: M_h2o_c
- name: H2O
- compartment: c
- charge: 0
@@ -389,7 +389,7 @@
- UPC00001
- UPC01328
- !!omap
- - id: h2o_e
+ - id: M_h2o_e
- name: H2O
- compartment: e
- charge: 0
@@ -463,7 +463,7 @@
- UPC00001
- UPC01328
- !!omap
- - id: h_c
+ - id: M_h_c
- name: H+
- compartment: c
- charge: 1
@@ -504,7 +504,7 @@
- seed.compound: cpd00067
- unipathway.compound: UPC00080
- !!omap
- - id: h_e
+ - id: M_h_e
- name: H+
- compartment: e
- charge: 1
@@ -545,7 +545,7 @@
- seed.compound: cpd00067
- unipathway.compound: UPC00080
- !!omap
- - id: lac__D_c
+ - id: M_lac__D_c
- name: D-Lactate
- compartment: c
- charge: -1
@@ -568,7 +568,7 @@
- metanetx.chemical: MNXM285
- seed.compound: cpd00221
- !!omap
- - id: lac__D_e
+ - id: M_lac__D_e
- name: D-Lactate
- compartment: e
- charge: -1
@@ -591,7 +591,7 @@
- metanetx.chemical: MNXM285
- seed.compound: cpd00221
- !!omap
- - id: nad_c
+ - id: M_nad_c
- name: Nicotinamide adenine dinucleotide
- compartment: c
- charge: -1
@@ -625,7 +625,7 @@
- seed.compound: cpd00003
- unipathway.compound: UPC00003
- !!omap
- - id: nadh_c
+ - id: M_nadh_c
- name: Nicotinamide adenine dinucleotide - reduced
- compartment: c
- charge: -2
@@ -654,7 +654,7 @@
- seed.compound: cpd00004
- unipathway.compound: UPC00004
- !!omap
- - id: pep_c
+ - id: M_pep_c
- name: Phosphoenolpyruvate
- compartment: c
- charge: -3
@@ -682,7 +682,7 @@
- seed.compound: cpd00061
- unipathway.compound: UPC00074
- !!omap
- - id: pi_c
+ - id: M_pi_c
- name: Phosphate
- compartment: c
- charge: -2
@@ -741,7 +741,7 @@
- cpd00009
- unipathway.compound: UPC00009
- !!omap
- - id: pi_e
+ - id: M_pi_e
- name: Phosphate
- compartment: e
- charge: -2
@@ -800,7 +800,7 @@
- cpd00009
- unipathway.compound: UPC00009
- !!omap
- - id: pyr_c
+ - id: M_pyr_c
- name: Pyruvate
- compartment: c
- charge: -1
@@ -830,136 +830,136 @@
- unipathway.compound: UPC00022
- reactions:
- !!omap
- - id: ATPM
+ - id: R_ATPM
- name: ATP maintenance requirement
- - metabolites: !!omap
- - adp_c: 1.0
- - atp_c: -1.0
- - h2o_c: -1.0
- - h_c: 1.0
- - pi_c: 1.0
- lower_bound: 8.39
- upper_bound: 1000.0
- gene_reaction_rule: ''
+ - metabolites: !!omap
+ - M_atp_c: -1.0
+ - M_h2o_c: -1.0
+ - M_adp_c: 1.0
+ - M_h_c: 1.0
+ - M_pi_c: 1.0
- objective_coefficient: 1.0
- annotation: !!omap
- bigg.reaction: ATPM
- !!omap
- - id: D_LACt2
+ - id: R_D_LACt2
- name: ''
- - metabolites: !!omap []
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: ''
+ - metabolites: !!omap []
- !!omap
- - id: ENO
+ - id: R_ENO
- name: enolase
- - metabolites: !!omap
- - 2pg_c: -1.0
- - h2o_c: 1.0
- - pep_c: 1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b2779
+ - metabolites: !!omap
+ - M_2pg_c: -1.0
+ - M_pep_c: 1.0
+ - M_h2o_c: 1.0
- annotation: !!omap
- bigg.reaction: ENO
- !!omap
- - id: EX_glc__D_e
+ - id: R_EX_glc__D_e
- name: D-Glucose exchange
- - metabolites: !!omap
- - glc__D_e: -1.0
- lower_bound: -10.0
- upper_bound: 1000.0
- gene_reaction_rule: ''
+ - metabolites: !!omap
+ - M_glc__D_e: -1.0
- annotation: !!omap
- bigg.reaction: glc
- sbo: SBO:0000627
- !!omap
- - id: EX_h_e
+ - id: R_EX_h_e
- name: H+ exchange
- - metabolites: !!omap
- - h_e: -1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: ''
+ - metabolites: !!omap
+ - M_h_e: -1.0
- annotation: !!omap
- bigg.reaction: h
- sbo: SBO:0000627
- !!omap
- - id: EX_lac__D_e
+ - id: R_EX_lac__D_e
- name: D-lactate exchange
- - metabolites: !!omap
- - lac__D_e: -1.0
- lower_bound: 0.0
- upper_bound: 1000.0
- gene_reaction_rule: ''
+ - metabolites: !!omap
+ - M_lac__D_e: -1.0
- annotation: !!omap
- bigg.reaction: lac__D
- sbo: SBO:0000627
- !!omap
- - id: FBA
+ - id: R_FBA
- name: fructose-bisphosphate aldolase
- - metabolites: !!omap
- - dhap_c: 1.0
- - fdp_c: -1.0
- - g3p_c: 1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b1773 or b2097 or b2925
+ - metabolites: !!omap
+ - M_fdp_c: -1.0
+ - M_dhap_c: 1.0
+ - M_g3p_c: 1.0
- annotation: !!omap
- bigg.reaction: FBA
- !!omap
- - id: GAPD
+ - id: R_GAPD
- name: glyceraldehyde-3-phosphate dehydrogenase
- - metabolites: !!omap
- - 13dpg_c: 1.0
- - g3p_c: -1.0
- - h_c: 1.0
- - nad_c: -1.0
- - nadh_c: 1.0
- - pi_c: -1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b1779
+ - metabolites: !!omap
+ - M_nad_c: -1.0
+ - M_13dpg_c: 1.0
+ - M_nadh_c: 1.0
+ - M_g3p_c: -1.0
+ - M_pi_c: -1.0
+ - M_h_c: 1.0
- annotation: !!omap
- bigg.reaction: GAPD
- !!omap
- - id: GLCpts
+ - id: R_GLCpts
- name: D-glucose transport via PEP:Pyr PTS
- - metabolites: !!omap
- - g6p_c: 1.0
- - glc__D_e: -1.0
- - pep_c: -1.0
- - pyr_c: 1.0
- lower_bound: 0.0
- upper_bound: 1000.0
- gene_reaction_rule: (b2415 and b2417 and b1101 and b2416) or (b2415 and b2417
and b1621 and b2416) or (b2415 and b1818 and b1817 and b1819 and b2416)
+ - metabolites: !!omap
+ - M_g6p_c: 1.0
+ - M_pyr_c: 1.0
+ - M_glc__D_e: -1.0
+ - M_pep_c: -1.0
- annotation: !!omap
- bigg.reaction: GLCpts
- !!omap
- - id: H2Ot
+ - id: R_H2Ot
- name: R H2O transport via - diffusion
- - metabolites: !!omap
- - h2o_c: 1.0
- - h2o_e: -1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b0875 or s0001
+ - metabolites: !!omap
+ - M_h2o_e: -1.0
+ - M_h2o_c: 1.0
- annotation: !!omap
- bigg.reaction: H2Ot
- !!omap
- - id: LDH_D
+ - id: R_LDH_D
- name: D-lactate dehydrogenase
- - metabolites: !!omap
- - h_c: 1.0
- - lac__D_c: -1.0
- - nad_c: -1.0
- - nadh_c: 1.0
- - pyr_c: 1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b2133 or b1380
+ - metabolites: !!omap
+ - M_lac__D_c: -1.0
+ - M_nad_c: -1.0
+ - M_h_c: 1.0
+ - M_nadh_c: 1.0
+ - M_pyr_c: 1.0
- annotation: !!omap
- bigg.reaction: LDH_D
- biocyc: META:DLACTDEHYDROGNAD-RXN
@@ -973,195 +973,196 @@
- '16372'
- sbo: SBO:0000375
- !!omap
- - id: PFK
+ - id: R_PFK
- name: phosphofructokinase
- - metabolites: !!omap
- - adp_c: 1.0
- - atp_c: -1.0
- - f6p_c: -1.0
- - fdp_c: 1.0
- - h_c: 1.0
- lower_bound: 0.0
- upper_bound: 1000.0
- gene_reaction_rule: b3916 or b1723
+ - metabolites: !!omap
+ - M_f6p_c: -1.0
+ - M_atp_c: -1.0
+ - M_adp_c: 1.0
+ - M_fdp_c: 1.0
+ - M_h_c: 1.0
- objective_coefficient: 1.0
- annotation: !!omap
- bigg.reaction: PFK
- !!omap
- - id: PGI
+ - id: R_PGI
- name: glucose-6-phosphate isomerase
- - metabolites: !!omap
- - f6p_c: 1.0
- - g6p_c: -1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b4025
+ - metabolites: !!omap
+ - M_g6p_c: -1.0
+ - M_f6p_c: 1.0
- annotation: !!omap
- bigg.reaction: PGI
- !!omap
- - id: PGK
+ - id: R_PGK
- name: phosphoglycerate kinase
- - metabolites: !!omap
- - 13dpg_c: 1.0
- - 3pg_c: -1.0
- - adp_c: 1.0
- - atp_c: -1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b2926
+ - metabolites: !!omap
+ - M_3pg_c: -1.0
+ - M_atp_c: -1.0
+ - M_13dpg_c: 1.0
+ - M_adp_c: 1.0
- annotation: !!omap
- bigg.reaction: PGK
- !!omap
- - id: PGM
+ - id: R_PGM
- name: phosphoglycerate mutase
- - metabolites: !!omap
- - 2pg_c: -1.0
- - 3pg_c: 1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b4395 or b3612 or b0755
+ - metabolites: !!omap
+ - M_2pg_c: -1.0
+ - M_3pg_c: 1.0
- annotation: !!omap
- bigg.reaction: PGM
- !!omap
- - id: PIt2r
+ - id: R_PIt2r
- name: R phosphate reversible transport via - symport
- - metabolites: !!omap
- - h_c: 1.0
- - h_e: -1.0
- - pi_c: 1.0
- - pi_e: -1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b2987 or b3493
+ - metabolites: !!omap
+ - M_pi_e: -1.0
+ - M_h_e: -1.0
+ - M_h_c: 1.0
+ - M_pi_c: 1.0
- annotation: !!omap
- bigg.reaction: PIt2r
- !!omap
- - id: PYK
+ - id: R_PYK
- name: pyruvate kinase
- - metabolites: !!omap
- - adp_c: -1.0
- - atp_c: 1.0
- - h_c: -1.0
- - pep_c: -1.0
- - pyr_c: 1.0
- lower_bound: 0.0
- upper_bound: 1000.0
- gene_reaction_rule: b1854 or b1676
+ - metabolites: !!omap
+ - M_adp_c: -1.0
+ - M_h_c: -1.0
+ - M_pep_c: -1.0
+ - M_atp_c: 1.0
+ - M_pyr_c: 1.0
- annotation: !!omap
- bigg.reaction: PYK
- !!omap
- - id: TPI
+ - id: R_TPI
- name: triose-phosphate isomerase
- - metabolites: !!omap
- - dhap_c: -1.0
- - g3p_c: 1.0
- lower_bound: -1000.0
- upper_bound: 1000.0
- gene_reaction_rule: b3919
+ - metabolites: !!omap
+ - M_dhap_c: -1.0
+ - M_g3p_c: 1.0
- annotation: !!omap
- bigg.reaction: TPI
- genes:
- !!omap
- - id: b0755
+ - id: G_b0755
- name: gpmA
- !!omap
- - id: b0875
+ - id: G_b0875
- name: aqpZ
- !!omap
- - id: b1101
+ - id: G_b1101
- name: ptsG
- !!omap
- - id: b1380
+ - id: G_b1380
- name: ldhA
- !!omap
- - id: b1621
+ - id: G_b1621
- name: malX
- !!omap
- - id: b1676
+ - id: G_b1676
- name: pykF
- annotation: !!omap
- ncbigi:
- GI:1208453
- GI:1652654
- !!omap
- - id: b1723
+ - id: G_b1723
- name: pfkB
- !!omap
- - id: b1773
+ - id: G_b1773
- name: ydjI
- !!omap
- - id: b1779
+ - id: G_b1779
- name: gapA
- !!omap
- - id: b1817
+ - id: G_b1817
- name: manX
- !!omap
- - id: b1818
+ - id: G_b1818
- name: manY
- !!omap
- - id: b1819
+ - id: G_b1819
- name: manZ
- !!omap
- - id: b1854
+ - id: G_b1854
- name: pykA
- !!omap
- - id: b2097
+ - id: G_b2097
- name: fbaB
- !!omap
- - id: b2133
+ - id: G_b2133
- name: dld
- !!omap
- - id: b2415
+ - id: G_b2415
- name: ptsH
- !!omap
- - id: b2416
+ - id: G_b2416
- name: ptsI
- !!omap
- - id: b2417
+ - id: G_b2417
- name: crr
- !!omap
- - id: b2779
+ - id: G_b2779
- name: eno
- annotation: !!omap
- ncbigi: GI:1653839
- !!omap
- - id: b2925
+ - id: G_b2925
- name: fbaA
- !!omap
- - id: b2926
+ - id: G_b2926
- name: pgk
- annotation: !!omap
- ncbigi: GI:1653609
- !!omap
- - id: b2987
+ - id: G_b2987
- name: pitB
- !!omap
- - id: b3493
+ - id: G_b3493
- name: pitA
- !!omap
- - id: b3612
+ - id: G_b3612
- name: gpmM
- !!omap
- - id: b3916
+ - id: G_b3916
- name: pfkA
- annotation: !!omap
- ncbigi:
- GI:1006614
- GI:1651919
- !!omap
- - id: b3919
+ - id: G_b3919
- name: tpiA
- !!omap
- - id: b4025
+ - id: G_b4025
- name: pgi
- annotation: !!omap
- ncbigi: GI:1653253
- !!omap
- - id: b4395
+ - id: G_b4395
- name: ytjC
- !!omap
- - id: s0001
+ - id: G_s0001
- name: G_s0001
+- groups: []
- id: mini_textbook
- compartments: !!omap
- c: cytosol
diff --git a/tests/data/mini_fbc2.xml.gz b/tests/data/mini_fbc2.xml.gz
index efdb5e4c5..b5994cd30 100644
Binary files a/tests/data/mini_fbc2.xml.gz and b/tests/data/mini_fbc2.xml.gz differ
diff --git a/tests/data/raven.pickle b/tests/data/raven.pickle
index 5bb891e73..f60877c59 100644
Binary files a/tests/data/raven.pickle and b/tests/data/raven.pickle differ
diff --git a/tests/data/salmonella.pickle b/tests/data/salmonella.pickle
index 644fa8ee8..11f6cf3a9 100644
Binary files a/tests/data/salmonella.pickle and b/tests/data/salmonella.pickle differ
diff --git a/tests/data/userConstraint.xml b/tests/data/userConstraint.xml
new file mode 100644
index 000000000..3a193edc7
--- /dev/null
+++ b/tests/data/userConstraint.xml
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/test_core/test_udconstraints.py b/tests/test_core/test_udconstraints.py
new file mode 100644
index 000000000..0156bf717
--- /dev/null
+++ b/tests/test_core/test_udconstraints.py
@@ -0,0 +1,203 @@
+"""
+Some documentation:
+
+Reactions: v1; v2; v3;
+Stochiometric Contraints (metabolic nework, SBML Reactions):
+v1 - v2 = 0
+
+UserDefinedConstraints:
+v1 + v2 < 2
+
+Non-constant parameters (x1: not reactions; )
+
+expression =: v1 + v2 + 1.2 x1 < 2
+v1 + v2 + 1.2 x1*x1 < 2
+
+-> examples:
+-> optimize: (v1, v2, x1); how to access the optimal solution?
+
+Test Cases:
+------------
+1. add user constraint to simple model
+A -v1-> B -v2-> C
+optimize: v2; bounds v1[0; 10]; v2[0, 5]
+optimal value: 5;
+
+UserConstraint: v1 + v2 <= 4
+optimal value: 2;
+
+2. only user constraint;
+empty cobra model
+
+x1 <= 2
+maximize: x1
+flux distribution optimal: x1=2
+"""
+
+from os.path import join
+
+import pytest
+from optlang import Variable, Constraint
+
+from cobra.core.model import Model
+from cobra.io import load_json_model, read_sbml_model, save_json_model
+
+
+def ex_model(data_directory):
+ model = read_sbml_model(join(data_directory, "userConstraint.xml"))
+ return model
+
+
+def test_user_defined_constraints(data_directory):
+ cons_model = ex_model(data_directory)
+ solution1 = cons_model.optimize()
+ assert solution1.objective_value == pytest.approx(5.0)
+
+ v1 = Variable('v1')
+ v2 = Variable('v2')
+ c1 = Constraint(v1 + v2, lb=0, ub=4, name='c1')
+ cons_model.add_user_defined_constraints(c1)
+
+ solution2 = cons_model.optimize()
+ assert solution2.objective_value == pytest.approx(2.00)
+
+
+def test_user_defined_constraints_on_single_flux(data_directory):
+ cons_model = ex_model(data_directory)
+ solution1 = cons_model.optimize()
+ assert solution1.objective_value == pytest.approx(5.0)
+
+ v2 = Variable('v2')
+ const_1 = Constraint(v2, lb=0, ub=3, name='const_1')
+ cons_model.add_user_defined_constraints([const_1])
+ solution2 = cons_model.optimize()
+ assert solution2.objective_value == pytest.approx(3.00)
+
+
+def test_user_defined_constraints_on_single_variable():
+ # an empty model
+ model = Model("model_abc")
+ cc1 = Variable("new_var")
+ c1 = Constraint(cc1, lb=0, ub=2, name='c1')
+ model.add_user_defined_constraints([c1])
+ model.objective = model.variables.new_var
+ solution = model.optimize()
+ assert solution.objective_value == pytest.approx(2.00)
+ # save json
+ # read json, make sure it is till objective
+
+
+def test_json_reading_writing(model, tmp_path):
+ cc1 = model.reactions.get_by_id('FBA').flux_expression
+ cc2 = model.reactions.get_by_id('NH4t').flux_expression
+ cc3 = Variable('difference')
+ c1 = Constraint(cc1 - cc2 - cc3, lb=0, ub=0, name='c1')
+
+ c2 = Constraint(cc1 + cc2, lb=0, ub=10, name='c2')
+ model.add_user_defined_constraints([c1, c2])
+ solution1 = model.optimize()
+
+ path_to_json = join(tmp_path, "userConstraint.json")
+ save_json_model(model, path_to_json)
+
+ model = load_json_model(path_to_json)
+ assert model.user_defined_const is not None
+ assert len(model.user_defined_const) == 2
+ const_1 = model.user_defined_const[0]
+ assert const_1.name == "c1"
+ assert len(const_1.variables) == 5
+ variable_names = {var.name for var in const_1.variables}
+ assert 'FBA' in variable_names
+ solution2 = model.optimize()
+ assert solution1.objective_value == pytest.approx(solution2.objective_value)
+
+
+def test_user_defined_constraints_read_write_json(data_directory, tmp_path):
+ cons_model = ex_model(data_directory)
+ solution1 = cons_model.optimize()
+ assert solution1.objective_value == pytest.approx(5.0)
+
+ v1 = Variable('v1')
+ v2 = Variable('v2')
+ c1 = Constraint(v1 + v2, lb=0, ub=4, name='c1')
+ cons_model.add_user_defined_constraints(c1)
+
+ solution2 = cons_model.optimize()
+ assert solution2.objective_value == pytest.approx(2.00)
+ path_to_json = join(tmp_path, "userConstraint.json")
+ save_json_model(cons_model, path_to_json)
+
+ cons_model2 = load_json_model(path_to_json)
+ solution3 = cons_model2.optimize()
+ assert solution3.objective_value == pytest.approx(2.00)
+ assert solution3.objective_value == pytest.approx(solution2.objective_value)
+
+
+def test_user_defined_constraints_on_single_variable_read_write_json(tmp_path):
+ # an empty model
+ model = Model("model_abc")
+ cc1 = Variable("new_var")
+ c1 = Constraint(cc1, lb=0, ub=2, name='c1')
+ model.add_user_defined_constraints([c1])
+ model.objective = model.variables.new_var
+ solution = model.optimize()
+ assert solution.objective_value == pytest.approx(2.00)
+ path_to_json = join(tmp_path, "userConstraint.json")
+ save_json_model(model, path_to_json)
+
+ model2 = load_json_model(path_to_json)
+ solution2 = model2.optimize()
+ assert solution2.objective_value == pytest.approx(2.00)
+ assert solution2.objective_value == pytest.approx(solution.objective_value)
+
+
+def test_user_defined_constraints_documented(model):
+ solution1 = model.optimize()
+ assert solution1.objective_value == pytest.approx(0.87392, 0.0001)
+
+ FBA = Variable("FBA")
+ NH4t = Variable("NH4t")
+ c1 = Constraint(FBA - NH4t, lb=0, ub=0, name='c1')
+ model.add_user_defined_constraints([c1])
+ solution2 = model.optimize()
+ assert solution2.fluxes["FBA"] == pytest.approx(4.66274, 0.0001)
+ assert solution2.fluxes["NH4t"] == pytest.approx(4.66274, 0.0001)
+ assert solution2.objective_value == pytest.approx(0.85511, 0.0001)
+
+
+def test_user_defined_constraints_with_variable_documented(model):
+ solution1 = model.optimize()
+ assert solution1.objective_value == pytest.approx(0.87392, 0.0001)
+
+ EX_glc__D_e = Variable("EX_glc__D_e")
+ EX_nh4_e = Variable("EX_nh4_e")
+ cc3 = Variable("difference")
+ c1 = Constraint(EX_glc__D_e - EX_nh4_e - cc3, lb=0, ub=0)
+ model.add_user_defined_constraints([c1])
+ solution2 = model.optimize()
+ assert solution2.objective_value == pytest.approx(0.87392, 0.0001)
+
+ reaction1 = model.reactions[0]
+ reaction1.knock_out()
+ model.optimize()
+ assert model.solver.variables.difference.primal == pytest.approx(-5.23468, 0.0001)
+
+ reaction2 = model.reactions[1]
+ reaction2.knock_out()
+ model.optimize()
+ assert model.solver.variables.difference.primal == pytest.approx(-5.23468, 0.0001)
+
+ reaction3 = model.reactions[2]
+ reaction3.knock_out()
+ model.optimize()
+ assert model.solver.variables.difference.primal == pytest.approx(-5.23468, 0.0001)
+
+ reaction4 = model.reactions[3]
+ reaction4.knock_out()
+ model.optimize()
+ assert model.solver.variables.difference.primal == pytest.approx(-1.86444, 0.0001)
+
+ reaction5 = model.reactions[4]
+ reaction5.knock_out()
+ model.optimize()
+ assert model.solver.variables.difference.primal == pytest.approx(-1.86444, 0.0001)
diff --git a/tests/test_manipulation/test_delete.py b/tests/test_manipulation/test_delete.py
index c745a1efb..d79f48527 100644
--- a/tests/test_manipulation/test_delete.py
+++ b/tests/test_manipulation/test_delete.py
@@ -110,7 +110,7 @@ def test_gene_knockout(salmonella: Model) -> None:
assert expected_reactions == knocked_out_reactions
with salmonella:
expected_reactions = [salmonella.reactions.get_by_id("4PEPTabcpp")]
- knocked_out_reactions = knock_out_model_genes(salmonella, ["STM1746.S"])
+ knocked_out_reactions = knock_out_model_genes(salmonella, ["STM1746_S"])
assert expected_reactions == knocked_out_reactions
knocked_out_reactions = knock_out_model_genes(salmonella, gene_list)
assert len(knocked_out_reactions) == 13