def _finalize_markov_model(self): """ Finalize a markov model """ # Derivatives states = self.states derivatives = defaultdict(lambda: sp.sympify(0.0)) rate_check = defaultdict(lambda: 0) # Build rate information and check that each rate is added in a # symetric way used_states = [0] * self.num_states for rate in self.rate_expressions: # Get the states to_state, from_state = rate.states # Add to derivatives of the two states derivatives[from_state] -= rate.sym * from_state.sym derivatives[to_state] += rate.sym * from_state.sym if isinstance(from_state, StateSolution): from_state = from_state.state if isinstance(to_state, StateSolution): to_state = to_state.state # Register rate ind_from = states.index(from_state) ind_to = states.index(to_state) ind_tuple = (min(ind_from, ind_to), max(ind_from, ind_to)) rate_check[ind_tuple] += 1 used_states[ind_from] = 1 used_states[ind_to] = 1 # Check used states if 0 in used_states: error( f"No rate registered for state {states[used_states.find(0)]}") # Check rate symetry for (ind_from, ind_to), times in list(rate_check.items()): if times != 2: error( "Only one rate between the states {0} and {1} was " "registered, expected two.".format( states[ind_from], states[ind_to], ), ) # Add derivatives for state in states: # Skip solved states if not isinstance(state, State) or state.is_solved: continue self.add_derivative(state, state.time.sym, derivatives[state])
def _add_single_rate(self, to_state, from_state, expr): """ Add a single rate expression """ if self.state_expressions: error( "A component cannot have both state expressions (derivative " "and algebraic expressions) and rate expressions.", ) check_arg(expr, scalars + (sp.Basic, ), 2, ODEComponent._add_single_rate) expr = sp.sympify(expr) to_state = self._expect_state( to_state, allow_state_solution=True, only_local_states=True, ) from_state = self._expect_state( from_state, allow_state_solution=True, only_local_states=True, ) if to_state == from_state: error("The two states cannot be the same.") if (to_state.sym, from_state.sym) in self.rates: error( f"Rate between state {from_state} and {to_state} is already registered.", ) # FIXME: It should also not be possible to include other # states in the markov model, right? syms_expr = symbols_from_expr(expr) if to_state.sym in syms_expr or from_state.sym in syms_expr: error( "The rate expression cannot be dependent on the " "states it connects.", ) # Create a RateExpression obj = RateExpression(to_state, from_state, expr) self._register_component_object(obj) # Store the rate sym in the rate dict self.rates._register_single_rate(to_state.sym, from_state.sym, obj.sym)
def _iszero(x): """Returns True if x is zero.""" x = sp.sympify(x) return x.is_zero
def __init__(self, name, expr, dependent=None): """ Create an Expression with an associated name Arguments --------- name : str The name of the Expression expr : sympy.Basic The expression dependent : ODEObject If given the count of this Expression will follow as a fractional count based on the count of the dependent object """ from modelparameters.sympytools import symbols_from_expr # Check arguments check_arg(expr, scalars + (sp.Basic,), 1, Expression) expr = sp.sympify(expr) # Deal with Subs in sympy expression for sub_expr in expr.atoms(sp.Subs): # deal with one Subs at a time subs = dict( (key, value) for key, value in zip(sub_expr.variables, sub_expr.point) ) expr = expr.subs(sub_expr, sub_expr.expr.xreplace(subs)) # Deal with im and re im_exprs = expr.atoms(sp.im) re_exprs = expr.atoms(sp.re) if im_exprs or re_exprs: replace_dict = {} for im_expr in im_exprs: replace_dict[im_expr] = sp.S.Zero for re_expr in re_exprs: replace_dict[re_expr] = re_expr.args[0] expr = expr.xreplace(replace_dict) if not symbols_from_expr(expr, include_numbers=True): error( "expected the expression to contain at least one " "Symbol or Number.", ) # Call super class with expression as the "value" super(Expression, self).__init__(name, expr, dependent) # Collect dependent symbols dependent = tuple( sorted( symbols_from_expr(expr), key=cmp_to_key(lambda a, b: cmp(sympycode(a), sympycode(b))), ), ) if dependent: self._sym = self._param.sym(*dependent) self._sym._assumptions["real"] = True self._sym._assumptions["commutative"] = True self._sym._assumptions["imaginary"] = False self._sym._assumptions["hermitian"] = True self._sym._assumptions["complex"] = True else: self._sym = self.param.sym self._dependent = set(dependent)