Beispiel #1
0
def pysb_to_gromet(pysb_model, model_name, statements=None, fname=None):
    """Convert PySB model to GroMEt object and save it to a JSON file.

    Parameters
    ----------
    pysb_model : pysb.Model
        PySB model object.
    model_name : str
        A name of EMMAA model.
    statements : Optional[list[indra.statements.Statement]]
        A list of INDRA Statements a PySB model was assembled from. If
        provided the statement hashes will be propagated into GroMEt metadata.
    fname : Optional[str]
        If given, the GroMEt will be dumped into JSON file.

    Returns
    -------
    g : automates.script.gromet.gromet.Gromet
        A GroMEt object built from PySB model.
    """
    from gromet import Gromet, gromet_to_json, \
        Junction, Wire, UidJunction, UidType, UidWire, Relation, \
        UidBox, UidGromet, Literal, Val
    from gromet_metadata import IndraAgent, IndraAgentReferenceSet, \
        ReactionReference, UidMetadatum, MetadatumMethod, Provenance, \
        get_current_datetime, ModelInterface
    from pysb import Parameter, WILD
    from pysb.bng import generate_equations

    logger.info('Generating equations ...')
    generate_equations(pysb_model)

    logger.info('Creating GroMEt')
    junctions = []
    wires = []
    # Get all species values
    species_values = {}
    for initial in pysb_model.initials:
        ix = pysb_model.get_species_index(initial.pattern)
        if initial.value:
            species_values[ix] = Literal(uid=None,
                                         type=UidType("Integer"),
                                         value=Val(initial.value.value),
                                         name=None,
                                         metadata=None)

    # Get groundings for monomers
    groundings_by_monomer = {}
    # Build up db_refs for each monomer object
    for ann in pysb_model.annotations:
        if ann.predicate == 'is':
            m = ann.subject
            db_name, db_id = parse_identifiers_url(ann.object)
            if m in groundings_by_monomer:
                groundings_by_monomer[m][db_name] = db_id
            else:
                groundings_by_monomer[m] = {db_name: db_id}
    # Store species names to refer later
    species_nodes = [str(sp) for sp in pysb_model.species]
    # Add all species junctions
    for ix, sp in enumerate(pysb_model.species):
        # Map to a list of agents
        agents = []
        for mp in sp.monomer_patterns:
            mods = []
            if hasattr(mp.monomer, 'site_annotations'):
                for site, state in mp.site_conditions.items():
                    if isinstance(state, tuple) and state[1] == WILD:
                        state = state[0]
                    mod, mod_type, res, pos = None, None, None, None
                    for ann in mp.monomer.site_annotations:
                        if ann.subject == (site, state):
                            mod_type = ann.object
                        elif ann.subject == site and \
                                ann.predicate == 'is_residue':
                            res = ann.object
                        if ann.subject == site and \
                                ann.predicate == 'is_position':
                            pos = ann.object
                        if mod_type:
                            not_mod, mod = states[mod_type]
                            if state == mod:
                                is_mod = True
                            elif state == not_mod:
                                is_mod = False
                            else:
                                logger.warning('Unknown state %s for %s, '
                                               'setting as not modified' %
                                               (state, mod_type))
                                is_mod = False
                            mod = ModCondition(mod_type, res, pos, is_mod)
                    if mod:
                        mods.append(mod)
            if not mods:
                mods = None
            ag = Agent(mp.monomer.name,
                       mods=mods,
                       db_refs=groundings_by_monomer.get(mp.monomer))
            agents.append(ag)
        agent_metadata = IndraAgentReferenceSet(
            uid=UidMetadatum(f'{species_nodes[ix]}_metadata'),
            provenance=Provenance(method=MetadatumMethod('from_emmaa_model'),
                                  timestamp=get_current_datetime()),
            indra_agent_references=[IndraAgent(ag.to_json()) for ag in agents])
        junctions.append(
            Junction(uid=UidJunction(f'J:{species_nodes[ix]}'),
                     type=UidType('State'),
                     name=species_nodes[ix],
                     value=species_values.get(ix),
                     value_type=UidType('Integer'),
                     metadata=[agent_metadata]))
    # Add wires for each reaction
    rate_counts = defaultdict(int)
    for rxn in pysb_model.reactions:
        rate_params = [
            rate_term for rate_term in rxn['rate'].args
            if isinstance(rate_term, Parameter)
        ]
        assert len(rate_params) == 1
        rate = rate_params[0].name
        rate_counts[rate] += 1
        rate_node = f'{rate}:{rate_counts[rate]}'
        # Get metadata for rate node
        assert len(rxn['rule']) == 1
        assert len(rxn['reverse']) == 1
        rule = rxn['rule'][0]
        reverse = rxn['reverse'][0]
        if statements:
            stmt = stmt_from_rule(rule, pysb_model, statements)
        # Add rate junction for a reaction (uid and name are the same for now)
        reaction_metadata = ReactionReference(
            uid=UidMetadatum(f'{rate_node}_metadata'),
            provenance=Provenance(method=MetadatumMethod('from_emmaa_model'),
                                  timestamp=get_current_datetime()),
            indra_stmt_hash=stmt.get_hash(),
            reaction_rule=rule,
            is_reverse=reverse)
        wire_count = defaultdict(int)
        junctions.append(
            Junction(uid=UidJunction(f'J:{rate_node}'),
                     type=UidType('Rate'),
                     name=rate,
                     value=Literal(uid=None,
                                   type=UidType("Float"),
                                   value=Val(rate_params[0].value),
                                   name=None,
                                   metadata=None),
                     value_type=UidType('Float'),
                     metadata=[reaction_metadata]))
        # Add wires from reactant to rate
        for reactant_ix in rxn['reactants']:
            reactant = species_nodes[reactant_ix]
            wire = f'{reactant}_{rate_node}'
            wire_count[wire] += 1
            wires.append(
                Wire(uid=UidWire(f'W:{wire}:w{wire_count[wire]}'),
                     type=None,
                     value_type=None,
                     name=None,
                     value=None,
                     metadata=None,
                     src=UidJunction(f'J:{reactant}'),
                     tgt=UidJunction(f'J:{rate_node}')))
        # Add wires from rate to product
        for prod_ix in rxn['products']:
            prod = species_nodes[prod_ix]
            wire = f'{rate_node}_{prod}'
            wire_count[wire] += 1
            wires.append(
                Wire(uid=UidWire(f'W:{wire}:w{wire_count[wire]}'),
                     type=None,
                     value_type=None,
                     name=None,
                     value=None,
                     metadata=None,
                     src=UidJunction(f'J:{rate_node}'),
                     tgt=UidJunction(f'J:{prod}')))
    # Create relation
    pnc = Relation(
        uid=UidBox(model_name),
        type=UidType("PetriNetClassic"),
        name=model_name,
        ports=None,
        # contents
        junctions=[j.uid for j in junctions],
        wires=[w.uid for w in wires],
        boxes=None,
        metadata=None)
    boxes = [pnc]

    # Create model interface metadata
    model_interface = \
        ModelInterface(
            uid=UidMetadatum(f'{model_name}_model_interface'),
            provenance=Provenance(method=MetadatumMethod('from_emmaa_model'),
                                  timestamp=get_current_datetime()),
            variables=[j.uid for j in junctions],
            parameters=[j.uid for j in junctions if j.type == 'Rate'],
            initial_conditions=[j.uid for j in junctions if j.type == 'State'])
    # Create Gromet object
    g = Gromet(uid=UidGromet(f'{model_name}_pnc'),
               name=model_name,
               type=UidType("PetriNetClassic"),
               root=pnc.uid,
               types=None,
               literals=None,
               junctions=junctions,
               ports=None,
               wires=wires,
               boxes=boxes,
               variables=None,
               metadata=[model_interface])
    logger.info('Created GroMEt')
    # Optionally save Gromet to JSON file
    if fname:
        gromet_to_json(g, fname)
    return g