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 specifies semantic compatibility of the JSON output. It should be incremented if new features are added which affect how a model is simulated or prevent the new output from being loaded by :py:func:`pysb.importers.json.PySBJSONDecoder`. See that code for documentation on the different protocol versions. This encoder only produces JSON using the highest protocol currently defined; If you need to generate output for an older protocol, use an older version of PySB in which the desired protocol number was still current. """ PROTOCOL = 2 @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, 'energy': r.energy, } @classmethod def encode_energypattern(cls, ep): return { 'name': ep.name, 'pattern': cls.encode_complex_pattern(ep.pattern), 'energy': ep.energy.name, } @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, 'energypatterns': cls.encode_energypattern, '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