def _test_maths(self, version): # Test maths is written (in selected ``version``) # Create model m1 = cellml.Model('m', version) c1 = m1.add_component('c') p1 = c1.add_variable('p', 'mole') p1.set_initial_value(2) q1 = c1.add_variable('q', 'dimensionless') r1 = c1.add_variable('r', 'second') r1.set_initial_value(0.1) t1 = c1.add_variable('t', 'second') m1.set_variable_of_integration(t1) # Add component without maths d1 = m1.add_component('d') s1 = d1.add_variable('s', 'volt') s1.set_initial_value(1.23) # Add two equations # Note: Numbers without units become dimensionless in CellML eq1 = myokit.Equation( myokit.Name(q1), myokit.Plus(myokit.Number(3, myokit.units.mole), myokit.Name(p1))) er1 = myokit.Equation(myokit.Derivative(myokit.Name(r1)), myokit.Power(myokit.Name(q1), myokit.Number(2))) q1.set_equation(eq1) r1.set_equation(er1) # Write and read xml = cellml.write_string(m1) m2 = cellml.parse_string(xml) # Check results p2, q2, r2, s2 = m2['c']['p'], m2['c']['q'], m2['c']['r'], m2['d']['s'] subst = { myokit.Name(p1): myokit.Name(p2), myokit.Name(q1): myokit.Name(q2), myokit.Name(r1): myokit.Name(r2), } eq2 = eq1.clone(subst) er2 = er1.clone(subst) self.assertEqual(q2.equation(), eq2) self.assertEqual(r2.equation(), er2) self.assertEqual(s2.initial_value(), myokit.Number(1.23, myokit.units.volt)) self.assertFalse(p2.is_state()) self.assertFalse(q2.is_state()) self.assertTrue(r2.is_state()) self.assertFalse(s2.is_state()) self.assertIs(m2.variable_of_integration(), m2['c']['t'])
def _derive_sensitivity_equations(self, model, deps, indeps): """ Derive expressions needed to evaluate the variables we want to output partial derivatives of. """ # Derive equations needed to calculate the requested sensitivities, # assuming that the sensitivities of the state variables are known. s_output_equations = [] # First, get variables instead of LhsExpressions. Ignore Name(state), # as we already have these, and convert Derivative(Name(state)) into # variables. output_variables = [ lhs.var() for lhs in deps if not (isinstance(lhs, myokit.Name) and lhs.var().is_state()) ] # Now call expressions_for, which will return expressions to evaluate # the rhs for each variable (i.e. the dot(x) rhs for states). output_equations, _ = model.expressions_for(*output_variables) del (output_variables, _) # Gather output expressions for each parameter or initial value we want # sensitivities w.r.t. for expr in indeps: eqs = [] for eq in output_equations: rhs = eq.rhs.diff(expr, independent_states=False) if rhs.is_number(0) and eq.lhs not in deps: continue lhs = myokit.PartialDerivative(eq.lhs, expr) eqs.append(myokit.Equation(lhs, rhs)) s_output_equations.append(eqs) return s_output_equations
def test_units(self): # Test writing of units u1 = myokit.parse_unit('kg*m^2/mole^3 (0.123)') m1 = cellml.Model('mmm') m1.add_units('flibbit', u1) m1.add_units('special_volt', myokit.units.Volt) d = m1.add_component('ddd') q = d.add_variable('q', 'flibbit') q.set_equation(myokit.Equation(myokit.Name(q), myokit.Number(2, u1))) xml = cellml.write_string(m1) m2 = cellml.parse_string(xml) q = m2['ddd']['q'] self.assertEqual(q.units().name(), 'flibbit') self.assertEqual(q.units().myokit_unit(), u1) self.assertEqual( m2.find_units('special_volt').myokit_unit(), myokit.units.volt) # Dimensionless units with a multiplier u1 = myokit.parse_unit('1 (0.123)') m1 = cellml.Model('mmm') m1.add_units('flibbit', u1) xml = cellml.write_string(m1) m2 = cellml.parse_string(xml) u2 = m2.find_units('flibbit') u2 = u2.myokit_unit() self.assertEqual(u1, u2)
def test_equation(self): # Test equation writing expression = myokit.Equation(myokit.Name('a'), myokit.Number(1)) xml = '<apply><eq/><ci>a</ci><cn>1.0</cn></apply>' x = self.w.eq(expression) m = self._math.match(x) self.assertTrue(m) self.assertEqual(m.group(1), xml)
def _maths(self, parent, component): """ Adds a ``math`` element to the given ``parent`` containing the maths for the variables in ``component``. """ # Test if this component has maths has_maths = False for v in component: if v.rhs() is not None: has_maths = True break if not has_maths: return # Find free variable alias in local component # In valid models, this will always be set if states are present in # this component. free = None for v in component: if v.value_source().is_free(): free = v break # Create expression writer for this component from myokit.formats.cellml import CellMLExpressionWriter ewriter = CellMLExpressionWriter(component.model().version()) ewriter.set_lhs_function(lambda x: x.var().name()) ewriter.set_unit_function(lambda x: component.find_units_name(x)) if free is not None: ewriter.set_time_variable(free) # Reset default namespace to MathML namespace nsmap = {None: cellml.NS_MATHML} if component.model().version() == '1.0': nsmap['cellml'] = cellml.NS_CELLML_1_0 else: nsmap['cellml'] = cellml.NS_CELLML_1_1 # Create math elements math = etree.SubElement(parent, 'math', nsmap=nsmap) # Add maths for variables for variable in sorted(component, key=_name): # Check RHS rhs = variable.rhs() if rhs is None: continue # Get LHS lhs = myokit.Name(variable) if variable.is_state(): lhs = myokit.Derivative(lhs) ewriter.eq(myokit.Equation(lhs, variable.rhs()), math)
def test_model_creation(self): # Create a model m = myokit.Model('LotkaVolterra') # Add the first component X = m.add_component('X') self.assertEqual(X.qname(), 'X') self.assertEqual(X.parent(), m) self.assertIsInstance(X, myokit.Component) self.assertIn(X.qname(), m) self.assertEqual(len(m), 1) # Add variable a self.assertFalse(X.has_variable('a')) a = X.add_variable('a') self.assertTrue(X.has_variable('a')) self.assertEqual(a, a) self.assertIsInstance(a, myokit.Variable) self.assertEqual(len(X), 1) self.assertIn(a.name(), X) a.set_rhs(3) self.assertFalse(a.is_state()) self.assertFalse(a.is_intermediary()) self.assertTrue(a.is_constant()) self.assertEqual(a.lhs(), myokit.Name(a)) self.assertEqual(a.rhs(), myokit.Number(3)) self.assertEqual(a.rhs().eval(), 3) self.assertEqual(a.code(), 'a = 3\n') self.assertEqual(a.eq().code(), 'X.a = 3') self.assertEqual(a.lhs().code(), 'X.a') self.assertEqual(a.rhs().code(), '3') self.assertEqual( a.eq(), myokit.Equation(myokit.Name(a), myokit.Number(3))) # Check lhs a_name1 = myokit.Name(a) a_name2 = myokit.Name(a) self.assertEqual(a_name1, a_name1) self.assertEqual(a_name2, a_name2) self.assertEqual(a_name1, a_name2) self.assertEqual(a_name2, a_name1) # Add variable b with two temporary variables b = X.add_variable('b') self.assertIsInstance(b, myokit.Variable) self.assertEqual(len(X), 2) self.assertIn(b.name(), X) self.assertFalse(b.has_variable('b1')) b1 = b.add_variable('b1') self.assertTrue(b.has_variable('b1')) self.assertEqual(len(b), 1) self.assertIn(b1.name(), b) self.assertIsInstance(b1, myokit.Variable) b2 = b.add_variable('b2') self.assertEqual(len(b), 2) self.assertIn(b2.name(), b) self.assertIsInstance(b2, myokit.Variable) b1.set_rhs(1) b2.set_rhs( myokit.Minus( myokit.Minus(myokit.Name(a), myokit.Name(b1)), myokit.Number(1)) ) b.set_rhs(myokit.Plus(myokit.Name(b1), myokit.Name(b2))) self.assertEqual(b.rhs().eval(), 2) self.assertFalse(b.is_state()) self.assertFalse(b.is_intermediary()) self.assertTrue(b.is_constant()) self.assertEqual(b.lhs(), myokit.Name(b)) # Add state variable x x = X.add_variable('x') x.set_rhs(10) x.promote() self.assertNotEqual(x, X) self.assertIsInstance(x, myokit.Variable) self.assertEqual(len(X), 3) self.assertIn(x.name(), X) self.assertTrue(x.is_state()) self.assertFalse(x.is_intermediary()) self.assertFalse(x.is_constant()) self.assertEqual(x.lhs(), myokit.Derivative(myokit.Name(x))) self.assertEqual(x.indice(), 0) # Test demoting, promoting x.demote() self.assertFalse(x.is_state()) self.assertFalse(x.is_intermediary()) self.assertTrue(x.is_constant()) self.assertEqual(x.lhs(), myokit.Name(x)) x.promote() self.assertTrue(x.is_state()) self.assertFalse(x.is_intermediary()) self.assertFalse(x.is_constant()) self.assertEqual(x.lhs(), myokit.Derivative(myokit.Name(x))) x.demote() x.promote() x.demote() x.promote() self.assertTrue(x.is_state()) self.assertFalse(x.is_intermediary()) self.assertFalse(x.is_constant()) self.assertEqual(x.lhs(), myokit.Derivative(myokit.Name(x))) # Add second component, variables Y = m.add_component('Y') self.assertNotEqual(X, Y) self.assertEqual(len(m), 2) c = Y.add_variable('c') c.set_rhs(myokit.Minus(myokit.Name(a), myokit.Number(1))) d = Y.add_variable('d') d.set_rhs(2) y = Y.add_variable('y') y.promote() # Set rhs for x and y x.set_rhs(myokit.Minus( myokit.Multiply(myokit.Name(a), myokit.Name(x)), myokit.Multiply( myokit.Multiply(myokit.Name(b), myokit.Name(x)), myokit.Name(y) ) )) x.set_state_value(10) self.assertEqual(x.rhs().code(), 'X.a * X.x - X.b * X.x * Y.y') y.set_rhs(myokit.Plus( myokit.Multiply( myokit.PrefixMinus(myokit.Name(c)), myokit.Name(y) ), myokit.Multiply( myokit.Multiply(myokit.Name(d), myokit.Name(x)), myokit.Name(y) ) )) y.set_state_value(5) self.assertEqual(y.rhs().code(), '-Y.c * Y.y + Y.d * X.x * Y.y') # Add ano component, variables Z = m.add_component('Z') self.assertNotEqual(X, Z) self.assertNotEqual(Y, Z) self.assertEqual(len(m), 3) t = Z.add_variable('total') self.assertEqual(t.name(), 'total') self.assertEqual(t.qname(), 'Z.total') self.assertEqual(t.qname(X), 'Z.total') self.assertEqual(t.qname(Z), 'total') t.set_rhs(myokit.Plus(myokit.Name(x), myokit.Name(y))) self.assertFalse(t.is_state()) self.assertFalse(t.is_constant()) self.assertTrue(t.is_intermediary()) self.assertEqual(t.rhs().code(), 'X.x + Y.y') self.assertEqual(t.rhs().code(X), 'x + Y.y') self.assertEqual(t.rhs().code(Y), 'X.x + y') self.assertEqual(t.rhs().code(Z), 'X.x + Y.y') # Add engine component E = m.add_component('engine') self.assertNotEqual(X, E) self.assertNotEqual(Y, E) self.assertNotEqual(Z, E) self.assertEqual(len(m), 4) time = E.add_variable('time') time.set_rhs(0) self.assertIsNone(time.binding()) time.set_binding('time') self.assertIsNotNone(time.binding()) # Check state state = [i for i in m.states()] self.assertEqual(len(state), 2) self.assertIn(x, state) self.assertIn(y, state) # Test variable iterators def has(*v): for var in v: self.assertIn(var, vrs) self.assertEqual(len(vrs), len(v)) vrs = [i for i in m.variables()] has(a, b, c, d, x, y, t, time) vrs = [i for i in m.variables(deep=True)] has(a, b, c, d, x, y, t, b1, b2, time) vrs = [i for i in m.variables(const=True)] has(a, b, c, d) vrs = [i for i in m.variables(const=True, deep=True)] has(a, b, c, d, b1, b2) vrs = [i for i in m.variables(const=False)] has(x, y, t, time) vrs = [i for i in m.variables(const=False, deep=True)] has(x, y, t, time) vrs = [i for i in m.variables(state=True)] has(x, y) vrs = [i for i in m.variables(state=True, deep=True)] has(x, y) vrs = [i for i in m.variables(state=False)] has(a, b, c, d, t, time) vrs = [i for i in m.variables(state=False, deep=True)] has(a, b, c, d, t, b1, b2, time) vrs = [i for i in m.variables(inter=True)] has(t) vrs = [i for i in m.variables(inter=True, deep=True)] has(t) vrs = [i for i in m.variables(inter=False)] has(a, b, c, d, x, y, time) vrs = [i for i in m.variables(inter=False, deep=True)] has(a, b, c, d, x, y, b1, b2, time) vrs = list(m.variables(const=True, state=True)) has() vrs = list(m.variables(const=True, state=False)) has(a, b, c, d) # Test sorted variable iteration names = [v.name() for v in m.variables(deep=True, sort=True)] self.assertEqual(names, [ 'a', 'b', 'b1', 'b2', 'x', 'c', 'd', 'y', 'total', 'time']) # Test equation iteration # Deeper testing is done when testing the ``variables`` method. eq = [eq for eq in X.equations(deep=False)] self.assertEqual(len(eq), 3) self.assertEqual(len(eq), X.count_equations(deep=False)) eq = [eq for eq in X.equations(deep=True)] self.assertEqual(len(eq), 5) self.assertEqual(len(eq), X.count_equations(deep=True)) eq = [eq for eq in Y.equations(deep=False)] self.assertEqual(len(eq), 3) self.assertEqual(len(eq), Y.count_equations(deep=False)) eq = [eq for eq in Y.equations(deep=True)] self.assertEqual(len(eq), 3) self.assertEqual(len(eq), Y.count_equations(deep=True)) eq = [eq for eq in Z.equations(deep=False)] self.assertEqual(len(eq), 1) self.assertEqual(len(eq), Z.count_equations(deep=False)) eq = [eq for eq in Z.equations(deep=True)] self.assertEqual(len(eq), 1) self.assertEqual(len(eq), Z.count_equations(deep=True)) eq = [eq for eq in E.equations(deep=False)] self.assertEqual(len(eq), 1) eq = [eq for eq in E.equations(deep=True)] self.assertEqual(len(eq), 1) eq = [eq for eq in m.equations(deep=False)] self.assertEqual(len(eq), 8) eq = [eq for eq in m.equations(deep=True)] self.assertEqual(len(eq), 10) # Test dependency mapping def has(var, *dps): lst = vrs[m.get(var).lhs() if isinstance(var, basestring) else var] self.assertEqual(len(lst), len(dps)) for d in dps: d = m.get(d).lhs() if isinstance(d, basestring) else d self.assertIn(d, lst) vrs = m.map_shallow_dependencies(omit_states=False) self.assertEqual(len(vrs), 12) has('X.a') has('X.b', 'X.b.b1', 'X.b.b2') has('X.b.b1') has('X.b.b2', 'X.a', 'X.b.b1') has('X.x', 'X.a', 'X.b', myokit.Name(x), myokit.Name(y)) has(myokit.Name(x)) has('Y.c', 'X.a') has('Y.d') has('Y.y', 'Y.c', 'Y.d', myokit.Name(x), myokit.Name(y)) has(myokit.Name(y)) has('Z.total', myokit.Name(x), myokit.Name(y)) vrs = m.map_shallow_dependencies() self.assertEqual(len(vrs), 10) has('X.a') has('X.b', 'X.b.b1', 'X.b.b2') has('X.b.b1') has('X.b.b2', 'X.a', 'X.b.b1') has('X.x', 'X.a', 'X.b') has('Y.c', 'X.a') has('Y.d') has('Y.y', 'Y.c', 'Y.d') has('Z.total') vrs = m.map_shallow_dependencies(collapse=True) self.assertEqual(len(vrs), 8) has('X.a') has('X.b', 'X.a') has('X.x', 'X.a', 'X.b') has('Y.c', 'X.a') has('Y.d') has('Y.y', 'Y.c', 'Y.d') has('Z.total') # Validate m.validate() # Get solvable order order = m.solvable_order() self.assertEqual(len(order), 5) self.assertIn('*remaining*', order) self.assertIn('X', order) self.assertIn('Y', order) self.assertIn('Z', order) # Check that X comes before Y pos = dict([(name, k) for k, name in enumerate(order)]) self.assertLess(pos['X'], pos['Y']) self.assertEqual(pos['*remaining*'], 4) # Check component equation lists eqs = order['*remaining*'] self.assertEqual(len(eqs), 0) eqs = order['Z'] self.assertEqual(len(eqs), 1) self.assertEqual(eqs[0].code(), 'Z.total = X.x + Y.y') eqs = order['Y'] self.assertEqual(len(eqs), 3) self.assertEqual( eqs[2].code(), 'dot(Y.y) = -Y.c * Y.y + Y.d * X.x * Y.y') eqs = order['X'] self.assertEqual(len(eqs), 5) self.assertEqual(eqs[0].code(), 'X.a = 3') self.assertEqual(eqs[1].code(), 'b1 = 1') self.assertEqual(eqs[2].code(), 'b2 = X.a - b1 - 1') self.assertEqual(eqs[3].code(), 'X.b = b1 + b2') # Test model export and cloning code1 = m.code() code2 = m.clone().code() self.assertEqual(code1, code2)
def test_reader_writer(self): # Test using the proper reader/writer try: import sympy as sp except ImportError: print('Sympy not found, skipping test.') return # Create writer and reader w = mypy.SymPyExpressionWriter() r = mypy.SymPyExpressionReader(self._model) # Name a = self._a ca = sp.Symbol('c.a') self.assertEqual(w.ex(a), ca) self.assertEqual(r.ex(ca), a) # Number with unit b = myokit.Number('12', 'pF') cb = sp.Float(12) self.assertEqual(w.ex(b), cb) # Note: Units are lost in sympy im/ex-port! #self.assertEqual(r.ex(cb), b) # Number without unit b = myokit.Number('12') cb = sp.Float(12) self.assertEqual(w.ex(b), cb) self.assertEqual(r.ex(cb), b) # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(w.ex(x), cb) # Note: Sympy doesn't seem to have a prefix plus self.assertEqual(r.ex(cb), b) # Prefix minus # Note: SymPy treats -x as Mul(NegativeOne, x) # But for numbers just returns a number with a negative value x = myokit.PrefixMinus(b) self.assertEqual(w.ex(x), -cb) self.assertEqual(float(r.ex(-cb)), float(x)) # Plus x = myokit.Plus(a, b) self.assertEqual(w.ex(x), ca + cb) # Note: SymPy likes to re-order the operands... self.assertEqual(float(r.ex(ca + cb)), float(x)) # Minus x = myokit.Minus(a, b) self.assertEqual(w.ex(x), ca - cb) self.assertEqual(float(r.ex(ca - cb)), float(x)) # Multiply x = myokit.Multiply(a, b) self.assertEqual(w.ex(x), ca * cb) self.assertEqual(float(r.ex(ca * cb)), float(x)) # Divide x = myokit.Divide(a, b) self.assertEqual(w.ex(x), ca / cb) self.assertEqual(float(r.ex(ca / cb)), float(x)) # Quotient x = myokit.Quotient(a, b) self.assertEqual(w.ex(x), ca // cb) self.assertEqual(float(r.ex(ca // cb)), float(x)) # Remainder x = myokit.Remainder(a, b) self.assertEqual(w.ex(x), ca % cb) self.assertEqual(float(r.ex(ca % cb)), float(x)) # Power x = myokit.Power(a, b) self.assertEqual(w.ex(x), ca**cb) self.assertEqual(float(r.ex(ca**cb)), float(x)) # Sqrt x = myokit.Sqrt(a) cx = sp.sqrt(ca) self.assertEqual(w.ex(x), cx) # Note: SymPy converts sqrt to power self.assertEqual(float(r.ex(cx)), float(x)) # Exp x = myokit.Exp(a) cx = sp.exp(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Log(a) x = myokit.Log(a) cx = sp.log(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Log(a, b) x = myokit.Log(a, b) cx = sp.log(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(float(r.ex(cx)), float(x)) # Log10 x = myokit.Log10(b) cx = sp.log(cb, 10) self.assertEqual(w.ex(x), cx) self.assertAlmostEqual(float(r.ex(cx)), float(x)) # Sin x = myokit.Sin(a) cx = sp.sin(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Cos x = myokit.Cos(a) cx = sp.cos(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Tan x = myokit.Tan(a) cx = sp.tan(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # ASin x = myokit.ASin(a) cx = sp.asin(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # ACos x = myokit.ACos(a) cx = sp.acos(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # ATan x = myokit.ATan(a) cx = sp.atan(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Floor x = myokit.Floor(a) cx = sp.floor(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Ceil x = myokit.Ceil(a) cx = sp.ceiling(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Abs x = myokit.Abs(a) cx = sp.Abs(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Equal x = myokit.Equal(a, b) cx = sp.Eq(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # NotEqual x = myokit.NotEqual(a, b) cx = sp.Ne(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # More x = myokit.More(a, b) cx = sp.Gt(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Less x = myokit.Less(a, b) cx = sp.Lt(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # MoreEqual x = myokit.MoreEqual(a, b) cx = sp.Ge(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # LessEqual x = myokit.LessEqual(a, b) cx = sp.Le(ca, cb) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Not x = myokit.Not(a) cx = sp.Not(ca) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # And cond1 = myokit.More(a, b) cond2 = myokit.Less(a, b) c1 = sp.Gt(ca, cb) c2 = sp.Lt(ca, cb) x = myokit.And(cond1, cond2) cx = sp.And(c1, c2) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Or x = myokit.Or(cond1, cond2) cx = sp.Or(c1, c2) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # If # Note: sympy only does piecewise, not if x = myokit.If(cond1, a, b) cx = sp.Piecewise((ca, c1), (cb, True)) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x.piecewise()) # Piecewise c = myokit.Number(1) cc = sp.Float(1) x = myokit.Piecewise(cond1, a, cond2, b, c) cx = sp.Piecewise((ca, c1), (cb, c2), (cc, True)) self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Myokit piecewise's (like CellML's) always have a final True # condition (i.e. an 'else'). SymPy doesn't require this, so test if # we can import this --> It will add an "else 0" x = myokit.Piecewise(cond1, a, myokit.Number(0)) cx = sp.Piecewise((ca, c1)) self.assertEqual(r.ex(cx), x) # SymPy function without Myokit equivalent --> Should raise exception cu = sp.principal_branch(cx, cc) self.assertRaisesRegex(ValueError, 'Unsupported type', r.ex, cu) # Derivative m = self._model.clone() avar = m.get('c.a') r = mypy.SymPyExpressionReader(self._model) avar.promote(4) x = myokit.Derivative(self._a) cx = sp.symbols('dot(c.a)') self.assertEqual(w.ex(x), cx) self.assertEqual(r.ex(cx), x) # Equation e = myokit.Equation(a, b) ce = sp.Eq(ca, cb) self.assertEqual(w.eq(e), ce) # There's no backwards equivalent for this! # The ereader can handle it, but it becomes and Equals expression. # Test sympy division del (m, avar, x, cx, e, ce) a = self._model.get('c.a') b = self._model.get('c').add_variable('bbb') b.set_rhs('1 / a') e = b.rhs() ce = w.ex(b.rhs()) e = r.ex(ce) self.assertEqual( e, myokit.Multiply(myokit.Number(1), myokit.Power(myokit.Name(a), myokit.Number(-1)))) # Test sympy negative numbers a = self._model.get('c.a') e1 = myokit.PrefixMinus(myokit.Name(a)) ce = w.ex(e1) e2 = r.ex(ce) self.assertEqual(e1, e2)
def _parse_math(self, element, component): """ Parses a mathml:math ``element``, adding equations to the variables of the given ``component``. """ model = component.model() # Get variables from component def variable_factory(name, element): try: var = component.variable(name) except KeyError: raise CellMLParsingError( 'Variable references in equations must name a variable' ' from the local component.', element) return myokit.Name(var) # Create numbers with units attr = self._join('units') def number_factory(value, element): # Numbers not connected to a cn if element is None: return myokit.Number(value, myokit.units.dimensionless) # Get units attribute try: units = element.attrib[attr] except KeyError: raise CellMLParsingError( 'Numbers inside MathML must define a cellml:units' ' attribute.', element) # Find units in component try: units = model.find_units(units) except myokit.formats.cellml.v2.CellMLError: raise CellMLParsingError( 'Unknown unit "' + str(units) + '" referenced inside a' ' MathML equation.', element) # Create and return return myokit.Number(value, units.myokit_unit()) # Create parser p = myokit.formats.mathml.MathMLParser(variable_factory, number_factory, self._vois) # Iterate over applies. for child in element: # Check each child is in MathML namespace ns, el = split(child.tag) if ns != cellml.NS_MATHML: raise CellMLParsingError( 'The contents of a mathml:math element must be in the' ' mathml namespace, found "' + str(child.tag) + '" inside ' + str(component) + '.', child) # If it isn't these it must be an apply if el != 'apply': raise CellMLParsingError( 'Unexpected contents in mathml:math. Expecting' ' mathml:apply but found mathml:' + el + ' inside maths' ' for ' + str(component) + '.', child) # Parse eq = p.parse(child) if not isinstance(eq, myokit.Equal): raise CellMLParsingError( 'Unexpected element in MathML, expecting a list of' ' equations, got ' + self._tag(child) + '.', child) lhs, rhs = eq # Check lhs if not isinstance(lhs, myokit.LhsExpression): raise CellMLParsingError( 'Invalid expression found on the left-hand side of an' ' equation: ' + self._dae_message, child) # Check variable is undefined var = lhs.var() if var.has_equation(): raise CellMLParsingError( 'Overdefined variable: ' + str(var) + ': Two defining' ' equations.', child) # Set equations try: lhs.var().set_equation(myokit.Equation(lhs, rhs)) except myokit.formats.cellml.v2.CellMLError \ as e: # pragma: no cover (currently can't happen) raise CellMLParsingError(str(e), child)