def calc_observables(rxncon_sys: RxnConSystem) -> List[Observable]: def observable_complex(states: List[State]) -> Complex: builder = ComplexExprBuilder() assert all(x.is_structured for x in states) for state in states: for func in STATE_TO_COMPLEX_BUILDER_FN[type(state)]: func(state, builder) complexes = builder.build(only_reactants=False) assert len(complexes) == 1 return complexes[0] observables = [] output_rxns = [rxn for rxn in rxncon_sys.reactions if isinstance(rxn, OutputReaction)] for rxn in output_rxns: LOGGER.debug('{} : calculating observable {}'.format(current_function_name(), str(rxn))) solns = Intersection(*(x.to_venn_set() for x in rxncon_sys.contingencies_for_reaction(rxn))).calc_solutions() positive_solns = [] # type: List[List[State]] for soln in solns: positive_solns += calc_positive_solutions(rxncon_sys, soln) for index, positive_soln in enumerate(positive_solns): LOGGER.debug('{} : solution {} : {}'.format(current_function_name(), index, positive_soln)) observables.append(Observable('{}{}'.format(rxn.name, index), observable_complex(positive_soln))) return observables
def mol_defs_from_rxncon(rxncon_sys: RxnConSystem) -> List[MolDef]: mol_defs = {} for spec in rxncon_sys.components(): LOGGER.debug('{} : Creating MolDefBuilder for {}'.format(current_function_name(), str(spec))) builder = MolDefBuilder(spec) for state in rxncon_sys.states_for_component(spec): LOGGER.debug('{} : Applying State {} of type {}'.format(current_function_name(), str(state), type(state))) for func in STATE_TO_MOL_DEF_BUILDER_FN[type(state)]: func(state, builder) mol_defs[spec] = builder.build() return list(mol_defs.values())
def _load_reaction_list(self) -> None: sheet = self._xlrd_book.sheet_by_name(SHEET_REACTION_LIST) reaction_rows = [row for row in sheet.get_rows()][DATA_ROW:] for row in reaction_rows: if not row[self._column_reaction_full_name].value: logger.debug('{}: Empty row'.format(current_function_name())) continue logger.debug('{}: {}'.format(current_function_name(), row[self._column_reaction_full_name].value)) # When a verb such as 'ppi' is encountered, the function 'preprocessed_reaction_strs' # will split it into 'ppi+' and 'ppi-'. reaction_strs = split_bidirectional_reaction_str(row[self._column_reaction_full_name].value) self._reactions += [reaction_from_str(x) for x in reaction_strs]
def with_connectivity_constraints(cont_set: VennSet[State]) -> VennSet: complexes = calc_connected_complexes(cont_set.values) complex_constraints = [] for complex in complexes: # pylint: disable=redefined-builtin state_paths = calc_state_paths(complex) constraint = UniversalSet() # type: VennSet[State] for state in complex: assert not state.is_global, 'Global state {} appearing in connectivity constraints.'.format(state) if any(path == [] for path in state_paths[state]): continue state_constraints = [Complement(ValueSet(state))] # type: List[VennSet[State]] for path in state_paths[state]: state_constraints.append(Intersection(*(ValueSet(x) for x in path))) constraint = Intersection(constraint, Union(*state_constraints)) # pylint: disable=redefined-variable-type complex_constraints.append(constraint.to_simplified_set()) if complex_constraints: LOGGER.debug('{} : Complex constraints {}'.format(current_function_name(), ' XOR '.join(str(x) for x in complex_constraints))) return Intersection(cont_set, DisjunctiveUnion(*complex_constraints)) else: return cont_set
def build(self, only_reactants: bool=True) -> List[Complex]: complexes = [] LOGGER.debug('{} : Building complex with molecules {}'.format(current_function_name(), ' & '.join(str(spec) for spec in self._mol_builders.keys()))) LOGGER.debug('{} : Grouped specs are {}'.format(current_function_name(), ', '.join(str(x) for x in self._grouped_specs()))) for group in self._grouped_specs(): possible_complex = Complex([self._mol_builders[spec].build() for spec in group]) if not only_reactants or (only_reactants and possible_complex.is_reactant): complexes.append(possible_complex) LOGGER.debug('{} : Adding complex {}'.format(current_function_name(), possible_complex)) else: LOGGER.info('{} : DISCONNECTED CONTINGENCY Reaction {} / Not adding complex {}' .format(current_function_name(), self.reaction, possible_complex)) return complexes
def _dereference_boolean_contingency_effectors( self: Effector, effector_table: Dict[str, Effector], equivalence_table: Dict[str, List[Tuple[QualSpec, QualSpec]]]) -> None: if isinstance(self, _BooleanContingencyEffector): LOGGER.debug('{} : {}'.format(current_function_name(), self.expr)) LOGGER.debug('{} : {}'.format(current_function_name(), effector_table)) LOGGER.debug('{} : {}'.format(current_function_name(), self.equivs)) name = self.expr.name equivs = self.equivs self.__class__ = effector_table[self.expr.name].__class__ self.__dict__ = effector_table[self.expr.name].__dict__ self.name = name try: self.equivs.merge_with(equivs, []) LOGGER.debug('{} : Merged structure information.'.format( current_function_name())) except AttributeError: self.equivs = equivs LOGGER.debug('{} : Initialized structure information.'.format( current_function_name())) elif isinstance(self, StateEffector): pass elif isinstance(self, NotEffector): _dereference_boolean_contingency_effectors(self.expr, effector_table, equivalence_table) elif isinstance(self, OrEffector) or isinstance(self, AndEffector): try: assert self.name is not None equivs_list = equivalence_table[self.name] for equiv in equivs_list: self.equivs.add_equivalence(*equiv) except KeyError: pass for expr in self.exprs: _dereference_boolean_contingency_effectors(expr, effector_table, equivalence_table) else: raise AssertionError
def split_bidirectional_reaction_str(rxn_str: str) -> List[str]: for verb in BIDIRECTIONAL_REACTIONS: if '_{}_'.format(verb).lower() in rxn_str.lower(): verb = re.findall('(?i)_{}_'.format(verb), rxn_str)[0][1:-1] logger.info('{}: {}'.format(current_function_name(), rxn_str)) return [ rxn_str.replace('_{}_'.format(verb), '_{}+_'.format(verb)), rxn_str.replace('_{}_'.format(verb), '_{}-_'.format(verb)), ] return [rxn_str]
def to_merged_struct_effector(self, glob_equivs: StructEquivalences = None, counter: StructCounter = None, cur_namespace: List[str] = None) -> Effector: glob_equivs, counter, cur_namespace = self._init_to_struct_effector_args( glob_equivs, counter, cur_namespace) state = deepcopy(self.expr) updates = {} assert not ( state.is_homodimer and not state.is_structured ), 'Please provide structure annotation for homodimer {}'.format(state) LOGGER.debug('{} : Merging {}'.format(current_function_name(), str(self))) LOGGER.debug('{} : Equivs {}'.format(current_function_name(), glob_equivs)) for spec in state.specs: existing_spec = glob_equivs.find_unqualified_spec( QualSpec(cur_namespace, spec)) if existing_spec: updates[spec] = existing_spec else: new_spec = deepcopy(spec) new_spec.struct_index = self._generate_index( glob_equivs, counter) updates[spec] = new_spec glob_equivs.add_equivalence(QualSpec([], new_spec), QualSpec(cur_namespace, spec)) state.update_specs(updates) LOGGER.debug('{} : Result {}'.format(current_function_name(), str(state))) return StateEffector(state)
def _load_contingency_list_entries(self) -> None: sheet = self._xlrd_book.sheet_by_name(SHEET_CONTINGENCY_LIST) contingency_rows = [row for row in sheet.get_rows()][DATA_ROW:] for row in contingency_rows: if not row[self._column_contingency_target].value: continue # When a reaction with a verb such as 'ppi' is encountered, we only apply the contingency to the # positive reaction. target = row[self._column_contingency_target].value if split_bidirectional_reaction_str(target) != [target]: target = split_bidirectional_reaction_str(target)[0] logger.info('{0}: applying contingency {1} to forward target {2}' .format(current_function_name(), row[self._column_contingency_modifier].value, target)) entry = contingency_list_entry_from_strs( target, row[self._column_contingency_type].value, row[self._column_contingency_modifier].value ) self._cont_list_entries.append(entry)
def expanded_effector(effector: Effector) -> Effector: if isinstance(effector, StateEffector): if effector.expr.is_elemental: return effector else: elemental_states = [ state.to_structured_from_state(effector.expr) for state in self.states if state.is_subset_of(effector.expr) ] assert elemental_states, 'Could not find elemental states which are subset of the non-elemental ' \ 'state {}'.format(effector.expr) assert all(state.is_elemental for state in elemental_states) LOGGER.info('{}: {} -> {}'.format( current_function_name(), str(effector.expr), ' | '.join(str(x) for x in elemental_states))) if len(elemental_states) == 1: return StateEffector(elemental_states[0]) else: return OrEffector(*(StateEffector(x) for x in elemental_states), name=str(effector.expr)) elif isinstance(effector, AndEffector): return AndEffector(*(expanded_effector(x) for x in effector.exprs), name=effector.name, equivs=effector.equivs) elif isinstance(effector, OrEffector): return OrEffector(*(expanded_effector(x) for x in effector.exprs), name=effector.name, equivs=effector.equivs) elif isinstance(effector, NotEffector): return NotEffector(expanded_effector(effector.expr), name=effector.name) else: raise AssertionError
def to_structured(self) -> 'Contingency': LOGGER.debug('{}: {}'.format(current_function_name(), str(self))) if isinstance(self.effector, StateEffector) and self.effector.is_structured: # A fully structured StateEffector is fine. return self elif isinstance(self.effector, StateEffector) and not self.effector.is_structured: # For a non-structured StateEffector, assume the Specs appearing in the Effector # match those appearing in the Reaction. equivs = StructEquivalences() struct_components = { spec.to_non_struct_spec(): spec for spec in self.reaction.components_lhs_structured } for spec in self.effector.expr.specs: try: equivs.add_equivalence( QualSpec([], struct_components[spec.to_component_spec()]), QualSpec([str(self.reaction)], spec.to_component_spec())) except KeyError: pass return self.with_merged_struct_effector(equivs, None, [str(self.reaction)]) elif self.effector.is_structured: # A fully structured Boolean Effector needs to have its structure indices merged. return self.with_merged_struct_effector() else: # For a non-structured Boolean Effector, assume all Specs that could match, actually do match. struct_components = { spec.to_non_struct_spec(): spec for spec in self.reaction.components_lhs_structured } equivs = TrivialStructEquivalences(struct_components) # pylint: disable=redefined-variable-type return self.with_merged_struct_effector(equivs)
def contingency_list_entry_from_strs(subject_str: str, verb_str: str, object_str: str) -> ContingencyListEntry: subject_str, verb_str, object_str = subject_str.strip(), verb_str.lower( ).strip(), object_str.strip() LOGGER.debug('{}: {} / {} / {}'.format(current_function_name(), subject_str, verb_str, object_str)) subject = None # type: Optional[Union[Reaction, BooleanContingencyName]] verb = None # type: Optional[Union[BooleanOperator, ContingencyType]] object = None # type: Optional[Union[State, BooleanContingencyName, Tuple[QualSpec, QualSpec]]] if re.match(BOOLEAN_CONTINGENCY_REGEX, subject_str): # subject: Boolean contingency, # verb : Boolean operator, # object : State / Boolean contingency / Qual Spec pair. subject = BooleanContingencyName(subject_str) verb = BooleanOperator(verb_str) else: # subject: Reaction, # verb : Contingency type, # object : State / Boolean contingency. subject = reaction_from_str(subject_str) verb = ContingencyType(verb_str) if re.match(BOOLEAN_CONTINGENCY_REGEX, object_str) and not isinstance(subject, Reaction): # subject: Boolean contingency, # verb : Contingency type / Boolean operator, # object : Boolean contingency. object = BooleanContingencyName(object_str) elif re.match(BOOLEAN_CONTINGENCY_REGEX, object_str.split('#')[0]) and isinstance(subject, Reaction): # subject: Reaction, # verb : Contingency type, # object : Boolean contingency + '#' + reactant equivs. name = object_str.split('#')[0] equivs_strs = [s.split(',') for s in object_str.split('#')[1:]] equivs_dict = { int(i): qual_spec_from_str(qual_spec_str).with_prepended_namespace([name]) for i, qual_spec_str in equivs_strs } equivs = StructEquivalences() for index, spec in enumerate(subject.components_lhs): try: equivs.add_equivalence( QualSpec([], spec.with_struct_index(index)), equivs_dict[index]) except KeyError: pass object = BooleanContingencyNameWithEquivs(name, equivs) LOGGER.debug('{} : Created {}'.format(current_function_name(), str(object))) elif verb == BooleanOperator.op_eqv: strs = [x.strip() for x in object_str.split(',')] object = (qual_spec_from_str(strs[0]).with_prepended_namespace( [subject.name]), qual_spec_from_str( strs[1]).with_prepended_namespace([subject.name])) else: object = state_from_str(object_str) assert subject is not None assert verb is not None assert object is not None return ContingencyListEntry(subject, verb, object)
], InteractionState: [ lambda state, builder: builder.add_mol(state.first), lambda state, builder: builder.add_mol(state.second), lambda state, builder: builder.set_bond(state.first, state.second) ], SelfInteractionState: [ lambda state, builder: builder.add_mol(state.first), lambda state, builder: builder.set_bond(state.first, state.second) ], EmptyBindingState: [ lambda state, builder: builder.add_mol(state.spec), lambda state, builder: builder.set_half_bond(state.spec, None) ], GlobalState: [ lambda state, builder: LOGGER.warning('{} : IGNORING INPUT STATE {}'.format(current_function_name(), str(state))) ] } # type: ignore STATE_TO_MOL_DEF_BUILDER_FN = { ModificationState: [ lambda state, builder: builder.add_site(state.spec), lambda state, builder: builder.add_mod(state.spec, state.modifier) ], InteractionState: [ lambda state, builder: builder.add_site(state.first) if builder.spec == state.first.to_component_spec() else builder.add_site(state.second), ], SelfInteractionState: [ lambda state, builder: builder.add_site(state.first),
def test_current_function_name() -> None: assert current_function_name(colored=False) == 'test_current_function_name'
def rxngraph_from_rxncon_system(rxncon_system: RxnConSystem) -> ReactionGraph: """ Creating the reaction graph from the rxncon system. Args: rxncon_system: The reconstructed rxncon system. Returns: The reaction graph (ReactionGraph) """ def get_reaction_type(rxn: Reaction) -> EdgeType: """ Getting the type of a reaction. Args: rxn: Elemental rxncon reaction. Returns: The type of the reaction (EdgeType) """ if len(rxn.components_lhs) > len(rxn.components_rhs): return EdgeType.degradation elif len(rxn.components_lhs) < len(rxn.components_rhs): return EdgeType.synthesis elif len(rxn.produced_states) == 1: return STATES[type(rxn.produced_states[0])] elif len(rxn.produced_states) == 2: if all(STATES[type(produced_state)] == EdgeType.modification for produced_state in rxn.produced_states): return EdgeType.bimodification elif all(STATES[type(produced_state)] == EdgeType.interaction for produced_state in rxn.produced_states): return EdgeType.interaction else: return EdgeType.unknown else: return EdgeType.unknown def add_reaction_to_graph(rxn: Reaction) -> None: """ Adding a reaction to the reaction graph. Args: rxn: Elemental rxncon reaction. Returns: None """ edge_type = get_reaction_type(rxn) if edge_type is EdgeType.synthesis: for syn_comp in rxn.synthesised_components: logger.info('Adding synthesis of {0} -> {1}'.format(rxn['$x'], syn_comp)) builder.add_spec_information(rxn['$x']) builder.add_spec_information(syn_comp) builder.add_external_edge(rxn['$x'], syn_comp, edge_type) else: logger.info('Adding {2} of {0} -> {1}'.format(rxn['$x'], rxn['$y'], get_reaction_type(rxn))) builder.add_spec_information(rxn['$x']) builder.add_spec_information(rxn['$y']) builder.add_external_edge(rxn['$x'], rxn['$y'], get_reaction_type(rxn)) builder = GraphBuilder() for reaction in rxncon_system.reactions: if not isinstance(reaction, OutputReaction): logger.debug('{}: {}'.format(current_function_name(), str(reaction))) add_reaction_to_graph(reaction) return ReactionGraph(builder.get_graph())
def rule_based_model_from_rxncon(rxncon_sys: RxnConSystem) -> RuleBasedModel: # pylint: disable=too-many-locals def mol_defs_from_rxncon(rxncon_sys: RxnConSystem) -> List[MolDef]: mol_defs = {} for spec in rxncon_sys.components(): LOGGER.debug('{} : Creating MolDefBuilder for {}'.format(current_function_name(), str(spec))) builder = MolDefBuilder(spec) for state in rxncon_sys.states_for_component(spec): LOGGER.debug('{} : Applying State {} of type {}'.format(current_function_name(), str(state), type(state))) for func in STATE_TO_MOL_DEF_BUILDER_FN[type(state)]: func(state, builder) mol_defs[spec] = builder.build() return list(mol_defs.values()) def remove_global_states(solutions: List[Dict[State, bool]]) -> List[Dict[State, bool]]: filtered_solutions = [] # type: List[Dict[State, bool]] for soln in solutions: cleaned_solution = {} for state, val in soln.items(): if state.is_global: LOGGER.warning('{} : REMOVING INPUT STATE {} from contingencies.'.format(current_function_name(), state)) else: cleaned_solution[state] = val if cleaned_solution not in filtered_solutions: filtered_solutions.append(cleaned_solution) return filtered_solutions def is_satisfiable(states: Iterable[State]) -> bool: for pair in combinations(states, 2): if pair[0].is_mutually_exclusive_with(pair[1]): return False return True def calc_positive_solutions(rxncon_sys: RxnConSystem, solution: Dict[State, bool]) -> List[List[State]]: def complementary_state_combos(state: State) -> List[List[State]]: combos = product( *(rxncon_sys.complement_states_for_component(spec.to_component_spec(), state) for spec in state.specs)) return [list(combo) for combo in combos if is_satisfiable(combo)] def structure_states(states: List[State]) -> List[State]: cur_index = max(spec.struct_index for state in states for spec in state.specs if spec.is_structured) assert cur_index is not None spec_to_index = {} # type: Dict[Spec, int] struct_states = [] # type: List[State] for state in states: if state.is_structured: struct_states.append(state) continue for spec in state.specs: if spec.is_structured: continue try: state = state.to_structured_from_spec(spec.with_struct_index(spec_to_index[spec.to_component_spec()])) except KeyError: cur_index += 1 state = state.to_structured_from_spec(spec.with_struct_index(cur_index)) spec_to_index[spec.to_component_spec()] = cur_index struct_states.append(state) return struct_states ordered_solution = OrderedDict(sorted(solution.items(), key=lambda x: x[0])) trues = [state for state, val in ordered_solution.items() if val] falses = [state for state, val in ordered_solution.items() if not val and not any(state.is_mutually_exclusive_with(x) for x in trues)] if not falses: return [trues] if is_satisfiable(trues) else [] positivized_falses = [list(chain(*x)) for x in product(*(complementary_state_combos(state) for state in falses))] solutions = [] for positivized_false in positivized_falses: possible_solution = [] # type: List[State] for soln_state in structure_states(trues + positivized_false): if soln_state not in possible_solution: possible_solution.append(soln_state) if is_satisfiable(possible_solution): solutions.append(possible_solution) return solutions def calc_rule(reaction: Reaction, cont_soln: List[State]) -> Rule: def calc_complexes(terms: List[ReactionTerm], states: List[State]) -> List[Complex]: if not all(x.is_structured for x in states): unstructs = [x for x in states if not x.is_structured] raise AssertionError('Error in building rule for Reaction {}, States {} appear unstructured' .format(str(reaction), ', '.join(str(x) for x in unstructs))) if not is_satisfiable(cont_soln): raise AssertionError('Cannot satisfy contingencies {} simultaneously'.format(' & '.join(str(s) for s in cont_soln))) states = copy(states) builder = ComplexExprBuilder(reaction=reaction) struct_index = 0 for term in terms: struct_states = deepcopy(term.states) for spec in term.specs: struct_spec = copy(spec) struct_spec.struct_index = struct_index builder.add_mol(struct_spec, is_reactant=True) struct_states = [state.to_structured_from_spec(struct_spec) for state in struct_states] struct_index += 1 states += struct_states assert all(x.is_structured for x in states) for state in states: for func in STATE_TO_COMPLEX_BUILDER_FN[type(state)]: func(state, builder) return builder.build() lhs = calc_complexes(reaction.terms_lhs, cont_soln) rhs = calc_complexes(reaction.terms_rhs, cont_soln) rate = Parameter('k', '1.0') return Rule(lhs, rhs, rate, parent_reaction=reaction) def calc_initial_conditions(mol_defs: List[MolDef]) -> List[InitialCondition]: return \ [InitialCondition(mol_def.create_neutral_complex(), Parameter('Num{}'.format(mol_def.name), str(INITIAL_MOLECULE_COUNT))) for mol_def in mol_defs] def calc_observables(rxncon_sys: RxnConSystem) -> List[Observable]: def observable_complex(states: List[State]) -> Complex: builder = ComplexExprBuilder() assert all(x.is_structured for x in states) for state in states: for func in STATE_TO_COMPLEX_BUILDER_FN[type(state)]: func(state, builder) complexes = builder.build(only_reactants=False) assert len(complexes) == 1 return complexes[0] observables = [] output_rxns = [rxn for rxn in rxncon_sys.reactions if isinstance(rxn, OutputReaction)] for rxn in output_rxns: LOGGER.debug('{} : calculating observable {}'.format(current_function_name(), str(rxn))) solns = Intersection(*(x.to_venn_set() for x in rxncon_sys.contingencies_for_reaction(rxn))).calc_solutions() positive_solns = [] # type: List[List[State]] for soln in solns: positive_solns += calc_positive_solutions(rxncon_sys, soln) for index, positive_soln in enumerate(positive_solns): LOGGER.debug('{} : solution {} : {}'.format(current_function_name(), index, positive_soln)) observables.append(Observable('{}{}'.format(rxn.name, index), observable_complex(positive_soln))) return observables LOGGER.debug('{} : Entered function'.format(current_function_name())) mol_defs = mol_defs_from_rxncon(rxncon_sys) LOGGER.debug('{} : Generated MolDefs: {}'.format(current_function_name(), ', '.join(str(mol_def) for mol_def in mol_defs))) rules = [] # type: List[Rule] for reaction in (x for x in rxncon_sys.reactions if not isinstance(x, OutputReaction)): LOGGER.debug('{} : Generating rules for reaction {}'.format(current_function_name(), str(reaction))) strict_cont_set = Intersection(*(x.to_venn_set() for x in rxncon_sys.s_contingencies_for_reaction(reaction))) # type: VennSet[State] quant_contingencies = QuantContingencyConfigs(rxncon_sys.q_contingencies_for_reaction(reaction)) LOGGER.debug('{} : Strict contingencies {}'.format(current_function_name(), str(strict_cont_set))) for quant_contingency_set in quant_contingencies: LOGGER.debug('{} : quantitative contingency config: {}'.format(current_function_name(), str(quant_contingency_set))) cont_set = Intersection(strict_cont_set, quant_contingency_set) # type: VennSet[State] cont_set = with_connectivity_constraints(cont_set) solutions = cont_set.calc_solutions() solutions = remove_global_states(solutions) LOGGER.debug('{} : contingency solutions {}'.format(current_function_name(), str(solutions))) positive_solutions = [] # type: List[List[State]] for solution in solutions: positive_solutions += calc_positive_solutions(rxncon_sys, solution) for positive_solution in positive_solutions: LOGGER.debug('{} : positivized contingency solution {}' .format(current_function_name(), ' & '.join(str(x) for x in positive_solution))) rule = calc_rule(reaction, positive_solution) if not any(rule.is_equivalent_to(existing) for existing in rules): rules.append(rule) return RuleBasedModel(mol_defs, calc_initial_conditions(mol_defs), [], calc_observables(rxncon_sys), rules)
def remove_global_states(solutions: List[Dict[State, bool]]) -> List[Dict[State, bool]]: filtered_solutions = [] # type: List[Dict[State, bool]] for soln in solutions: cleaned_solution = {} for state, val in soln.items(): if state.is_global: LOGGER.warning('{} : REMOVING INPUT STATE {} from contingencies.'.format(current_function_name(), state)) else: cleaned_solution[state] = val if cleaned_solution not in filtered_solutions: filtered_solutions.append(cleaned_solution) return filtered_solutions