Source code for mira.sources.util

__all__ = ["transition_to_templates", "get_sympy", "parameter_to_mira"]

import sympy
from typing import Optional
from mira.metamodel import *


[docs]def transition_to_templates( input_concepts, output_concepts, controller_concepts, transition_rate, transition_id, transition_name=None, ): """ Return a list of templates from a transition. Parameters ---------- input_concepts : list[Concept] A list of Concepts serving as input to a transition. output_concepts : list[Concept] A list of Concepts serving as output to a transition. controller_concepts : list[Concept] A list of Concepts serving as controllers towards a transition. transition_rate : sympy.Expr The rate law associated with the transition. transition_id : str The id of the transition. transition_name : str The name of the transition. Returns ------- : list[Template] A list containing Templates. """ if not controller_concepts: if not input_concepts: for output_concept in output_concepts: yield NaturalProduction( outcome=output_concept, rate_law=transition_rate, name=transition_id, display_name=transition_name, ) elif not output_concepts: for input_concept in input_concepts: yield NaturalDegradation( subject=input_concept, rate_law=transition_rate, name=transition_id, display_name=transition_name, ) else: for input_concept in input_concepts: for output_concept in output_concepts: yield NaturalConversion( subject=input_concept, outcome=output_concept, rate_law=transition_rate, name=transition_id, display_name=transition_name, ) else: if not (len(input_concepts) == 1 and len(output_concepts) == 1): if len(input_concepts) == 1 and not output_concepts: if len(controller_concepts) > 1: yield GroupedControlledDegradation( controllers=controller_concepts, subject=input_concepts[0], rate_law=transition_rate, name=transition_id, display_name=transition_name, ) else: yield ControlledDegradation( controller=controller_concepts[0], subject=input_concepts[0], rate_law=transition_rate, name=transition_id, display_name=transition_name, ) elif len(output_concepts) == 1 and not input_concepts: if len(controller_concepts) > 1: # If we have 2 controllers and one of them is # the same as the output, we have a ControlledReplication. # Note that in principle we could generalize this to # more than 2 controllers, but we would need to # define GroupedControlledReplication. # The logic we use here is to remove one controller # matching the output - this is what potentially # replicates. Whatever else is left is considered # a non-matching controller, even if it is actually # an equivalent concept to what replicates: in that # case it is thought of a a controller. removed = False non_matching_controllers = [] for controller in controller_concepts: if not removed and \ controller.is_equal_to(output_concepts[0], with_context=True): removed = True else: non_matching_controllers.append(controller) if len(controller_concepts) == 2 and \ len(non_matching_controllers) == 1: yield ControlledReplication( controller=non_matching_controllers[0], subject=output_concepts[0], rate_law=transition_rate, name=transition_id, display_name=transition_name, ) else: yield GroupedControlledProduction( controllers=controller_concepts, outcome=output_concepts[0], rate_law=transition_rate, name=transition_id, display_name=transition_name, ) else: # Check if the controller is the same as the output if controller_concepts[0].is_equal_to(output_concepts[0], with_context=True): yield NaturalReplication( subject=output_concepts[0], rate_law=transition_rate, name=transition_id, display_name=transition_name, ) else: yield ControlledProduction( controller=controller_concepts[0], outcome=output_concepts[0], rate_law=transition_rate, name=transition_id, display_name=transition_name, ) else: return [] elif len(controller_concepts) == 1: yield ControlledConversion( controller=controller_concepts[0], subject=input_concepts[0], outcome=output_concepts[0], rate_law=transition_rate, name=transition_id, display_name=transition_name, ) else: yield GroupedControlledConversion( controllers=controller_concepts, subject=input_concepts[0], outcome=output_concepts[0], rate_law=transition_rate, display_name=transition_name, )
[docs]def parameter_to_mira(parameter) -> Parameter: """ Return a MIRA parameter from a mapping of MIRA Parameter attributes to values. Parameters ---------- parameter : Dict[str,Any] A mapping containing MIRA Parameter attributes to values. Returns ------- : The corresponding MIRA Parameter. """ distr = ( Distribution(**parameter["distribution"]) if parameter.get("distribution") else None ) data = { "name": parameter["id"], "display_name": parameter.get("name"), "description": parameter.get("description"), "value": parameter.get("value"), "distribution": distr, # Note we handle empty dict below "units": parameter.get("units") if parameter.get("units") else None, } return Parameter.from_json(data)
[docs]def get_sympy(expr_data, local_dict=None) -> Optional[sympy.Expr]: """Return a sympy expression from a dict with an expression or MathML. Sympy string expressions are prioritized over MathML. Parameters ---------- expr_data : Dict[str,Any] A dict with an expression and/or MathML. local_dict : Dict[str, Any] A dict of local variables to use when parsing the expression. Returns ------- : A sympy expression or None if no expression was found. """ if expr_data is None: return None # Sympy if expr_data.get("expression"): expr = safe_parse_expr(expr_data["expression"], local_dict=local_dict) # MathML elif expr_data.get("expression_mathml"): expr = mathml_to_expression(expr_data["expression_mathml"]) # No expression found else: expr = None return expr