def gene_from_rule(rule_name, agent_ix): rule_stmt = stmt_from_rule(rule_name, model, stmts) if not rule_stmt: print("Could not get stmt for rule %s" % rule_name) return None agent = rule_stmt.agent_list()[agent_ix] gene_id = agent.db_refs.get('HGNC') gene_name = hgnc_client.get_hgnc_name(gene_id) return gene_name
def make_english_output(results, model, stmts): citations = {} citation_count = 1 for source, target, polarity, value, found_path, paths, flag in results: cond = 'How does treatment with %s %s %s?' % \ (source, 'increase' if polarity == 'positive' else 'decrease', target) print(cond) print('=' * len(cond)) if paths: path = paths[0] sentences = [] for i, (path_rule, sign) in enumerate(path): for rule in model.rules: if rule.name == path_rule: stmt = stmt_from_rule(path_rule, model, stmts) if i == 0: sentences.append('%s is a target of %s.' % (stmt.agent_list()[0].name, source)) # Make citations pmids = [ev.pmid for ev in stmt.evidence if ev.pmid] cit_nums = [] for pmid in pmids: cit_num = citations.get(pmid) if cit_num is None: citations[pmid] = citation_count cit_num = citation_count citation_count += 1 cit_nums.append(cit_num) if cit_nums: cit_nums = sorted(list(set(cit_nums))) cit_str = ' [%s]' % (','.join([str(c) for c in cit_nums])) else: cit_str = '' ea = EnglishAssembler([stmt]) sentence = ea.make_model() sentence = sentence[:-1] + cit_str + '.' sentences.append(sentence) sentences[-1] = sentences[-1][:-1] + \ ', which is measured by %s.' % target text = ' '.join(sentences) print('INDRA\'s hypothesis: ' + text) elif found_path: print('INDRA determined that there exists an explanation but' ' it is intractable to reconstruct.') else: print('INDRA couldn\'t find an explanation for this observation.') print('\n') references = 'References\n==========\n' for k, v in sorted(citations.items(), key=lambda x: x[1]): references += '[%d] https://www.ncbi.nlm.nih.gov/pubmed/%s\n' % (v, k) print(references)
def prune_influence_map_degrade_bind_positive(self, model_stmts): """Prune positive edges between X degrading and X forming a complex with Y.""" im = self.get_im() edges_to_prune = [] for r1, r2, data in im.edges(data=True): s1 = stmt_from_rule(r1, self.model, model_stmts) s2 = stmt_from_rule(r2, self.model, model_stmts) # Make sure this is a degradation/binding combo s1_is_degrad = (s1 and isinstance(s1, DecreaseAmount)) s2_is_bind = (s2 and isinstance(s2, Complex) and 'bind' in r2) if not s1_is_degrad or not s2_is_bind: continue # Make sure what is degraded is part of the complex if s1.obj.name not in [m.name for m in s2.members]: continue # Make sure we're dealing with a positive influence if data['sign'] == 1: edges_to_prune.append((r1, r2)) logger.info('Removing %d edges from influence map' % len(edges_to_prune)) im.remove_edges_from(edges_to_prune)
def export_json(results, model, stmts): """Export a set of paths in JSON format for visualization.""" json_dict = {} for drug, ab, relation, value, path_found, paths, flag in results: if json_dict.get(drug) is None: json_dict[drug] = {} if json_dict[drug].get(ab) is None: json_dict[drug][ab] = {} for idx, path in enumerate(paths): path_stmts = [] for rule_name, sign in path[:-1]: stmt = stmt_from_rule(rule_name, model, stmts) path_stmts.append(stmt.uuid) json_dict[drug][ab][idx] = path_stmts return json_dict
def add_namespace_to_influence_map(self, model_stmts): """Add a namespace to each node in influence map.""" im = self.get_im() for node, data in im.nodes(data=True): ag = None # Node is observable if node.endswith('obs'): ag = agent_from_obs(node, self.model) # Node is rule else: stmt = stmt_from_rule(node, self.model, model_stmts) if stmt: agents = [ag for ag in stmt.agent_list() if ag is not None] if agents: ag = agents[0] if ag: ns_order = default_ns_order + ['PUBCHEM', 'TEXT'] ns = ag.get_grounding(ns_order)[0] data['ns'] = ns else: logger.warning('Could not get grounding for %s' % node)
def get_nodes_to_agents(self, model_stmts, add_namespaces=False): """Return a dictionary mapping influence map nodes to INDRA agents. Parameters ---------- model_stmts : list[indra.statements.Statement] A list of INDRA statements used to assemble PySB model. add_namespaces : bool Whether to propagate namespaces to node data. Default: False. Returns ------- nodes_to_agents : dict A dictionary mapping influence map nodes to INDRA agents. """ if self.nodes_to_agents: return self.nodes_to_agents im = self.get_im() for node, data in im.nodes(data=True): ag = None # Node is observable if node.endswith('obs'): ag = agent_from_obs(node, self.model) # Node is rule else: stmt = stmt_from_rule(node, self.model, model_stmts) if stmt: agents = [ag for ag in stmt.agent_list() if ag is not None] if agents: ag = agents[0] if ag: self.nodes_to_agents[node] = ag if add_namespaces: ns_order = default_ns_order + ['PUBCHEM', 'TEXT'] ns = ag.get_grounding(ns_order)[0] data['ns'] = ns else: logger.warning('Could not get agent for %s' % node) return self.nodes_to_agents
def check_explanation(self): if self.model is None: raise ValueError('check_explanation requires a PySB model.') if self.explain is None: raise ValueError('check_explanation requires an explanation goal.') result = {} mc = PysbModelChecker(self.model, [self.explain]) try: pr = mc.check_statement(self.explain, max_paths=0) result['has_explanation'] = pr.path_found except Exception as e: logger.error("Error checking statement for paths: %s" % str(e)) result['has_explanation'] = False # If we found a path get a path if result['has_explanation']: try: pr = mc.check_statement(self.explain, max_paths=1, max_path_length=8) path_stmts = stmts_from_pysb_path(pr.paths[0], self.model, self.statements) result['explanation_path'] = path_stmts except Exception as e: logger.error("Error getting paths for statement: %s" % str(e)) # If we don't already have an explanation, see if we can propose one else: # Get the source rules associated with the statement to explain source_rules = [] subj_mps = grounded_monomer_patterns(self.model, self.explain.agent_list()[0]) for subj_mp in subj_mps: source_rules += mc._get_input_rules(subj_mp) obs_container = mc.stmt_to_obs[self.explain] # If we've got both source rules and observable names, add dummy # nodes for the source (connected to all input rules) and the # target (connected to all observables) so we only have to deal # with a single source and a single target if source_rules and obs_container: new_edges = [('SOURCE', sr) for sr in source_rules] new_edges += [(on, 'TARGET') for on, _ in obs_container.main_nodes] im = mc.get_im() im.add_edges_from(new_edges) # Now, we know that there is no path between SOURCE and TARGET. # Instead, we consider connections among all possible pairs # of nodes in the graph and count the number of nodes in # the path between source and target: best_edge = (None, 0) for u, v in itertools.permutations(im.nodes(), 2): # Add the edge to the graph im.add_edge(u, v) # Find longest path between source and target simple_paths = list( nx.all_simple_paths(im, 'SOURCE', 'TARGET')) simple_paths.sort(key=lambda p: len(p), reverse=True) if simple_paths and len(simple_paths[0]) > best_edge[1]: best_edge = ((u, v), len(simple_paths[0])) # Now remove the edge we added before going on to the next im.remove_edge(u, v) if best_edge[0]: result['connect_rules'] = best_edge[0] u_stmt = stmt_from_rule(best_edge[0][0], self.model, self.statements) v_stmt = stmt_from_rule(best_edge[0][1], self.model, self.statements) if u_stmt and v_stmt: result['connect_stmts'] = (u_stmt, v_stmt) logger.info("Model statements: %s" % str(self.statements)) logger.info("To explain %s, try connecting %s and %s" % (self.explain, u_stmt, v_stmt)) return result
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