Skip to content

Commit

Permalink
Merge pull request #347 from gyorilab/metabolism
Browse files Browse the repository at this point in the history
Implement new templates to model metabolism
  • Loading branch information
bgyori authored Jul 30, 2024
2 parents 7d98f51 + ee26958 commit 264a34e
Show file tree
Hide file tree
Showing 7 changed files with 501 additions and 19 deletions.
130 changes: 129 additions & 1 deletion mira/metamodel/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,126 @@
"outcome"
]
},
"MultiConversion": {
"title": "MultiConversion",
"description": "Specifies a conversion process of multiple subjects and outcomes.",
"type": "object",
"properties": {
"rate_law": {
"title": "Rate Law",
"description": "The rate law for the template.",
"type": "string",
"example": "2*x"
},
"name": {
"title": "Name",
"description": "The name of the template.",
"type": "string"
},
"display_name": {
"title": "Display Name",
"description": "The display name of the template.",
"type": "string"
},
"type": {
"title": "Type",
"default": "MultiConversion",
"const": "MultiConversion",
"enum": [
"MultiConversion"
],
"type": "string"
},
"subjects": {
"title": "Subjects",
"description": "The subjects of the conversion.",
"type": "array",
"items": {
"$ref": "#/definitions/Concept"
}
},
"outcomes": {
"title": "Outcomes",
"description": "The outcomes of the conversion.",
"type": "array",
"items": {
"$ref": "#/definitions/Concept"
}
},
"provenance": {
"title": "Provenance",
"description": "The provenance of the conversion.",
"type": "array",
"items": {
"$ref": "#/definitions/Provenance"
}
}
},
"required": [
"subjects",
"outcomes"
]
},
"ReversibleFlux": {
"title": "ReversibleFlux",
"description": "Specifies a reversible flux between a left and right side.",
"type": "object",
"properties": {
"rate_law": {
"title": "Rate Law",
"description": "The rate law for the template.",
"type": "string",
"example": "2*x"
},
"name": {
"title": "Name",
"description": "The name of the template.",
"type": "string"
},
"display_name": {
"title": "Display Name",
"description": "The display name of the template.",
"type": "string"
},
"type": {
"title": "Type",
"default": "ReversibleFlux",
"const": "ReversibleFlux",
"enum": [
"ReversibleFlux"
],
"type": "string"
},
"left": {
"title": "Left",
"description": "The left hand side of the flux.",
"type": "array",
"items": {
"$ref": "#/definitions/Concept"
}
},
"right": {
"title": "Right",
"description": "The right hand side of the flux.",
"type": "array",
"items": {
"$ref": "#/definitions/Concept"
}
},
"provenance": {
"title": "Provenance",
"description": "The provenance of the flux.",
"type": "array",
"items": {
"$ref": "#/definitions/Provenance"
}
}
},
"required": [
"left",
"right"
]
},
"NaturalProduction": {
"title": "NaturalProduction",
"description": "A template for the production of a species at a constant rate.",
Expand Down Expand Up @@ -1177,6 +1297,7 @@
"propertyName": "type",
"mapping": {
"NaturalConversion": "#/definitions/NaturalConversion",
"MultiConversion": "#/definitions/MultiConversion",
"ControlledConversion": "#/definitions/ControlledConversion",
"NaturalDegradation": "#/definitions/NaturalDegradation",
"ControlledDegradation": "#/definitions/ControlledDegradation",
Expand All @@ -1187,13 +1308,17 @@
"GroupedControlledProduction": "#/definitions/GroupedControlledProduction",
"NaturalReplication": "#/definitions/NaturalReplication",
"ControlledReplication": "#/definitions/ControlledReplication",
"StaticConcept": "#/definitions/StaticConcept"
"StaticConcept": "#/definitions/StaticConcept",
"ReversibleFlux": "#/definitions/ReversibleFlux"
}
},
"oneOf": [
{
"$ref": "#/definitions/NaturalConversion"
},
{
"$ref": "#/definitions/MultiConversion"
},
{
"$ref": "#/definitions/ControlledConversion"
},
Expand Down Expand Up @@ -1226,6 +1351,9 @@
},
{
"$ref": "#/definitions/StaticConcept"
},
{
"$ref": "#/definitions/ReversibleFlux"
}
]
}
Expand Down
6 changes: 6 additions & 0 deletions mira/metamodel/template_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,12 @@ def _iter_concepts(template_model: TemplateModel):
yield from (template.subject, template.controller)
elif isinstance(template, StaticConcept):
yield template.subject
elif isinstance(template, MultiConversion):
yield from template.subjects
yield from template.outcomes
elif isinstance(template, ReversibleFlux):
yield from template.left
yield from template.right
else:
raise TypeError(f"could not handle template: {template}")

Expand Down
107 changes: 106 additions & 1 deletion mira/metamodel/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
"GroupedControlledConversion",
"GroupedControlledProduction",
"GroupedControlledDegradation",
"MultiConversion",
"NaturalReplication",
"ControlledReplication",
"ReversibleFlux",
"StaticConcept",
"SpecifiedTemplate",
"templates_equal",
Expand All @@ -27,6 +29,7 @@
"is_degradation",
"is_conversion",
"is_replication",
"is_reversible",
"has_subject",
"has_outcome",
"has_controller",
Expand Down Expand Up @@ -1272,6 +1275,100 @@ def get_key(self, config: Optional[Config] = None):
)


class MultiConversion(Template):
"""Specifies a conversion process of multiple subjects and outcomes."""

type: Literal["MultiConversion"] = Field("MultiConversion", const=True)
subjects: List[Concept] = Field(..., description="The subjects of the conversion.")
outcomes: List[Concept] = Field(..., description="The outcomes of the conversion.")
provenance: List[Provenance] = Field(default_factory=list, description="The provenance of the conversion.")

concept_keys: ClassVar[List[str]] = ["subjects", "outcomes"]

def get_key(self, config: Optional[Config] = None):
return (
self.type,
*tuple(
c.get_key(config=config)
for c in sorted(self.subjects, key=lambda c: c.get_key(config=config))
),
*tuple(
c.get_key(config=config)
for c in sorted(self.outcomes, key=lambda c: c.get_key(config=config))
),
)

def get_concepts(self):
return self.subjects + self.outcomes

def with_context(
self,
do_rename=False,
exclude_concepts=None,
curie_to_name_map=None,
**context
) -> "MultiConversion":
exclude_concepts = exclude_concepts or set()
return self.__class__(
type=self.type,
subjects=[c.with_context(do_rename, curie_to_name_map=curie_to_name_map, **context)
if c.name not in exclude_concepts else c
for c in self.subjects],
outcomes=[c.with_context(do_rename, curie_to_name_map=curie_to_name_map, **context)
if c.name not in exclude_concepts else c
for c in self.outcomes],
provenance=self.provenance,
rate_law=self.rate_law,
)


class ReversibleFlux(Template):
"""Specifies a reversible flux between a left and right side."""

type: Literal["ReversibleFlux"] = Field("ReversibleFlux", const=True)
left: List[Concept] = Field(..., description="The left hand side of the flux.")
right: List[Concept] = Field(..., description="The right hand side of the flux.")
provenance: List[Provenance] = Field(default_factory=list, description="The provenance of the flux.")

concept_keys: ClassVar[List[str]] = ["left", "right"]

def get_concepts(self):
return self.left + self.right

def get_key(self, config: Optional[Config] = None):
return (
self.type,
*tuple(
c.get_key(config=config)
for c in sorted(self.left, key=lambda c: c.get_key(config=config))
),
*tuple(
c.get_key(config=config)
for c in sorted(self.right, key=lambda c: c.get_key(config=config))
),
)

def with_context(
self,
do_rename=False,
exclude_concepts=None,
curie_to_name_map=None,
**context
) -> "ReversibleFlux":
exclude_concepts = exclude_concepts or set()
return self.__class__(
type=self.type,
subjects=[c.with_context(do_rename, curie_to_name_map=curie_to_name_map, **context)
if c.name not in exclude_concepts else c
for c in self.left],
outcomes=[c.with_context(do_rename, curie_to_name_map=curie_to_name_map, **context)
if c.name not in exclude_concepts else c
for c in self.right],
provenance=self.provenance,
rate_law=self.rate_law,
)


class NaturalProduction(Template):
"""A template for the production of a species at a constant rate."""

Expand Down Expand Up @@ -1767,6 +1864,7 @@ def context_refinement(refined_context, other_context) -> bool:
SpecifiedTemplate = Annotated[
Union[
NaturalConversion,
MultiConversion,
ControlledConversion,
NaturalDegradation,
ControlledDegradation,
Expand All @@ -1778,6 +1876,7 @@ def context_refinement(refined_context, other_context) -> bool:
NaturalReplication,
ControlledReplication,
StaticConcept,
ReversibleFlux,
],
Field(description="Any child class of a Template", discriminator="type"),
]
Expand Down Expand Up @@ -1855,7 +1954,7 @@ def is_replication(template):
def is_conversion(template):
"""Return True if the template is a form of conversion."""
return isinstance(template, (NaturalConversion, ControlledConversion,
GroupedControlledConversion))
GroupedControlledConversion, MultiConversion))


def has_outcome(template):
Expand All @@ -1869,6 +1968,11 @@ def has_subject(template):
or is_replication(template))


def is_reversible(template):
"""Return True if the template is a reversible process."""
return isinstance(template, ReversibleFlux)


def num_controllers(template):
"""Return the number of controllers in the template."""
if isinstance(template, (ControlledConversion,
Expand Down Expand Up @@ -1906,6 +2010,7 @@ def get_binding_templates(a, b, c, kf, kr):


def conversion_to_deg_prod(conv_template):
# TODO: Handle multiconversion
"""Given a conversion template, compile into degradation/production templates."""
if not is_conversion(conv_template):
return [conv_template]
Expand Down
Loading

0 comments on commit 264a34e

Please sign in to comment.