# -*- coding: utf-8 -*-
"""
CoCoMiCo LP facts.
"""
import re
from typing import Iterable, Tuple
from clyngor.as_pyasp import Atom
from cocomico.base import (
Biomolecule,
Exchange,
Metabolite,
MetaboliteSet,
Reaction,
Seeds,
Taxon,
)
from cocomico.model import Model
# Translate SBML models into LP facts
[docs]
def seed_facts(seeds: Seeds) -> list[Atom]:
"""
Convert a set of nutritional seed metabolites in LP facts,
lexicographically ordered for clarity.
seed(Taxon)
:param seeds: a set of seed metabolites
:returns: a set of LP facts
"""
seed_atoms = [Atom("seed", [f'"{s}"']) for s in sorted(seeds)]
return seed_atoms
[docs]
def network_facts(models: dict[Taxon, Model], biomolecules_only=False) -> list[Atom]:
"""
Convert an SBML model into list LP facts, lexicographically
ordered by predicate, taxon, and reaction, for clarity.
taxon(Taxon).
biomolecule(Metabolite Name, Taxon).
reaction(Reaction Id, Taxon).
product(Metabolite Name, Reaction Id, Taxon).
reactant(Metabolite Name, Reaction Id, Taxon).
:param models: a dict mapping taxa to models
:returns: a set of LP facts
"""
taxa = sorted(models.keys())
taxon_atoms = [Atom(kind, [f'"{ taxon }"']) for taxon in taxa for kind in ["taxon"]]
# Rely on Metabolite provenance, always same as taxon
biomolecule_atoms = [
Atom(
"biomolecule",
[f'"{ metabolite.biomolecule }"', f'"{ metabolite.provenance }"'],
)
for taxon in taxa
for metabolite in models[taxon].biomolecule
]
# Ignore taxon in tuples since always determined by relations key.
# Community.relations keys are the same as Atom predicates.
relation_atoms = [
Atom(
kind,
[
f'"{ m.biomolecule if biomolecules_only else str(m) }"',
f'"{ r.name }"',
f'"{ taxon }"',
],
)
for taxon in taxa
for kind in ["product", "reactant"]
for m, r, _ in models[taxon].relations[kind]
]
reaction_atoms = [
Atom("reaction", [f'"{ rid }"', f'"{ taxon }"'])
for taxon in taxa
for rid in sorted(
# use set because reactions will have multiple produce, reactant relations
{
r.name
for kind in ["product", "reactant"]
for _, r, _ in models[taxon].relations[kind]
}
)
]
return taxon_atoms + biomolecule_atoms + reaction_atoms + relation_atoms
[docs]
def to_reaction(atoms: Iterable[Atom]) -> set[Reaction]:
"""
Decode a set of clyngor.as_pyasp.Atom of the form
reaction(Name,Taxon)
:param atoms: metabolite atoms
:returns: set[Reaction]
"""
r_reaction = re.compile(
r"""\(
(?:
(?:\'reaction\(([^,]*?),\s*(.*?)\)) # nested reaction/2
|
(?:(?!\'reaction\()([^,]*?),\s*([^,]*?),) # flattened reaction, taxon
)
.*\)""",
re.VERBOSE,
)
return {
Reaction(
name=(g.group(1) or g.group(3)).strip("\"'"),
taxon=Taxon((g.group(2) or g.group(4)).strip("\"'")),
)
for atom in atoms
if (g := r_reaction.search(str(atom))) is not None
}
[docs]
def to_exchange(tuples: Iterable[Tuple[Atom, Atom, Atom]]) -> set[Exchange]:
"""
Decode a tuple of clyngor.as_pyasp.Atom of the form
# reaction(Name,Taxon)
(Taxon, Taxon)
:param atoms: Taxon tuple
:returns: set[Tuple[Taxon, Taxon]]
"""
return set(
Exchange(Taxon(p.strip('"')), Taxon(c.strip("\"'"))) for m, p, c in tuples
)
[docs]
def to_biomolecule(atoms: Iterable[Atom]) -> set[Biomolecule]:
"""
Decode a set of clyngor.as_pyasp.Atom of the form
(Biomolecule)
:param atoms: biomolecule name atoms
:returns: set[Biomolecule]
"""
return {Biomolecule(atom[0].strip("\"'")) for atom in atoms}