Source code for pysb.export.json

"""
Module containing a class for exporting a PySB model to JSON

For information on how to use the model exporters, see the documentation
for :py:mod:`pysb.export`.
"""

from pysb.export import Exporter
from pysb.bng import generate_equations
import json
from pysb.core import Model, MultiState, KeywordMeta, Parameter, Expression


[docs]class JsonExporter(Exporter): """A class for returning the JSON for a given PySB model. Inherits from :py:class:`pysb.export.Exporter`, which implements basic functionality for all exporters. """
[docs] def export(self, include_netgen=False): """Generate the corresponding JSON for the PySB model associated with the exporter. Parameters ---------- include_netgen: bool Include cached network generation data (reactions, species, local function-derived parameters and expressions) if True. Returns ------- string The JSON output for the model. """ return json.dumps(self.model, cls=PySBJSONWithNetworkEncoder if include_netgen else PySBJSONEncoder)
[docs]class PySBJSONEncoder(json.JSONEncoder): """ Encode a PySB model in JSON This encoder stores the model without caching the reaction network. To also store the reaction network, see :py:class:`PySBJSONWithNetworkEncoder`. Attributes correspond to their PySB equivalents (monomers, parameters, etc.) and are mostly stored verbatim, with the following exceptions. * MultiStates and the ANY and WILD state values use a special object format * References to other components are stored using the component name * Sympy expressions are encoded as strings using the default encoder The protocol number (currently: 1) specifies semantic model compatibility, and should be incremented if new features are added which affect how a model is simulated or prevent a model being loaded by :py:func:`pysb.importers.json.PySBJSONDecoder`. """ PROTOCOL = 1 @classmethod def encode_keyword(cls, keyword): return {'__object__': str(keyword)} @classmethod def encode_multistate(cls, stateval): return { '__object__': '__multistate__', 'sites': stateval.sites } @classmethod def encode_monomer(cls, mon): return { 'name': mon.name, 'sites': mon.sites, 'states': mon.site_states } @classmethod def encode_compartment(cls, cpt): return { 'name': cpt.name, 'parent': cpt.parent.name if cpt.parent else None, 'dimension': cpt.dimension, 'size': cpt.size.name if cpt.size else None } @classmethod def encode_parameter(cls, par): return { 'name': par.name, 'value': par.value } @classmethod def encode_expression(cls, expr): return { 'name': expr.name, 'expr': expr.expr.name if isinstance( expr.expr, (Parameter, Expression)) else str(expr.expr) } @classmethod def encode_monomer_pattern(cls, mp): return { 'monomer': mp.monomer.name, 'site_conditions': mp.site_conditions, 'compartment': mp.compartment.name if mp.compartment else None, 'tag': mp._tag.name if mp._tag else None } @classmethod def encode_complex_pattern(cls, cp): return { 'monomer_patterns': [cls.encode_monomer_pattern(mp) for mp in cp.monomer_patterns], 'compartment': cp.name if cp.compartment else None, 'match_once': cp.match_once, 'tag': cp._tag.name if cp._tag else None } @classmethod def encode_reaction_pattern(cls, rp): return { 'complex_patterns': [cls.encode_complex_pattern(cp) for cp in rp.complex_patterns] } @classmethod def encode_rule_expression(cls, rexp): return { 'reactant_pattern': cls.encode_reaction_pattern( rexp.reactant_pattern), 'product_pattern': cls.encode_reaction_pattern( rexp.product_pattern), 'reversible': rexp.is_reversible } @classmethod def encode_rule(cls, r): return { 'name': r.name, 'rule_expression': cls.encode_rule_expression(r.rule_expression), 'rate_forward': r.rate_forward.name, 'rate_reverse': r.rate_reverse.name if r.rate_reverse else None, 'delete_molecules': r.delete_molecules, 'move_connected': r.move_connected } @classmethod def encode_observable(cls, obs): return { 'name': obs.name, 'reaction_pattern': cls.encode_reaction_pattern( obs.reaction_pattern), 'match': obs.match } @classmethod def encode_initial(cls, init): return { 'pattern': cls.encode_complex_pattern(init.pattern), 'parameter_or_expression': init.value.name, 'fixed': init.fixed } @classmethod def encode_annotation(cls, ann): return { 'subject': 'model' if isinstance(ann.subject, Model) else ann.subject.name, 'object': str(ann.object), 'predicate': str(ann.predicate) } @classmethod def encode_tag(cls, tag): return { 'name': tag.name } @classmethod def encode_model(cls, model): d = dict(protocol=cls.PROTOCOL, name=model.name) encoders = { 'monomers': cls.encode_monomer, 'compartments': cls.encode_compartment, 'tags': cls.encode_tag, 'parameters': cls.encode_parameter, 'expressions': cls.encode_expression, 'rules': cls.encode_rule, 'observables': cls.encode_observable, 'initials': cls.encode_initial, 'annotations': cls.encode_annotation } for component_type, encoder in encoders.items(): d[component_type] = [encoder(component) for component in getattr(model, component_type)] return d
[docs] def default(self, o): if isinstance(o, Model): return self.encode_model(o) elif isinstance(o, MultiState): return self.encode_multistate(o) elif isinstance(o, KeywordMeta): return self.encode_keyword(o) return super(PySBJSONEncoder, self).default(o)
[docs]class PySBJSONWithNetworkEncoder(PySBJSONEncoder): """ Encode a PySB model and its reaction network in JSON This encoder stores the model including the cached reaction network. To encode the model without the reaction network, see :py:class:`PySBJSONEncoder`, which also includes implementation details. """ @classmethod def encode_reaction(cls, rxn): rxn = rxn.copy() rxn['rate'] = rxn['rate'].name \ if isinstance(rxn['rate'], (Parameter, Expression)) \ else str(rxn['rate']) return rxn @classmethod def encode_observable(cls, obs): o = super(PySBJSONWithNetworkEncoder, cls).encode_observable(obs) o['species'] = obs.species o['coefficients'] = obs.coefficients return o @classmethod def encode_model(cls, model): d = super(PySBJSONWithNetworkEncoder, cls).encode_model(model) # Ensure network generation has taken place generate_equations(model) additional_encoders = { '_derived_parameters': cls.encode_parameter, '_derived_expressions': cls.encode_expression, 'reactions': cls.encode_reaction, 'reactions_bidirectional': cls.encode_reaction, 'species': cls.encode_complex_pattern } for component_type, encoder in additional_encoders.items(): d[component_type] = [encoder(component) for component in getattr(model, component_type)] return d