def test_functions(self): # Tests writing basic functions # Power a = myokit.Name('a') b = myokit.Number(1) e = myokit.Power(a, b) x = '<apply><power/><ci>a</ci><cn>1.0</cn></apply>' self.assertWrite(e, x) # Sqrt e = myokit.Sqrt(b) x = '<apply><root/><cn>1.0</cn></apply>' self.assertWrite(e, x) # Exp e = myokit.Exp(a) x = '<apply><exp/><ci>a</ci></apply>' self.assertWrite(e, x) # Log(a) e = myokit.Log(b) x = '<apply><ln/><cn>1.0</cn></apply>' self.assertWrite(e, x) # Log(a, b) e = myokit.Log(a, b) x = '<apply><log/><logbase><cn>1.0</cn></logbase><ci>a</ci></apply>' self.assertWrite(e, x) # Log10 e = myokit.Log10(b) x = '<apply><log/><cn>1.0</cn></apply>' self.assertWrite(e, x) # Floor e = myokit.Floor(b) x = '<apply><floor/><cn>1.0</cn></apply>' self.assertWrite(e, x) # Ceil e = myokit.Ceil(b) x = '<apply><ceiling/><cn>1.0</cn></apply>' self.assertWrite(e, x) # Abs e = myokit.Abs(b) x = '<apply><abs/><cn>1.0</cn></apply>' self.assertWrite(e, x) # Quotient e = myokit.Quotient(a, b) x = '<apply><quotient/><ci>a</ci><cn>1.0</cn></apply>' self.assertWrite(e, x) # Remainder e = myokit.Remainder(a, b) x = '<apply><rem/><ci>a</ci><cn>1.0</cn></apply>' self.assertWrite(e, x)
def test_functions(self): # Tests writing basic functions a = myokit.Name(self.avar) b = myokit.Number('12', 'pF') ca = '<mi>c.a</mi>' cb = '<mn>12.0</mn>' # Power x = myokit.Power(a, b) self.assertWrite(x, '<msup>' + ca + cb + '</msup>') # Sqrt x = myokit.Sqrt(b) self.assertWrite( x, '<mrow><mi>root</mi><mfenced>' + cb + '</mfenced></mrow>') # Exp x = myokit.Exp(a) self.assertWrite(x, '<msup><mi>e</mi>' + ca + '</msup>') # Log(a) x = myokit.Log(b) self.assertWrite( x, '<mrow><mi>ln</mi><mfenced>' + cb + '</mfenced></mrow>') # Log(a, b) x = myokit.Log(a, b) self.assertWrite( x, '<mrow><msub><mi>log</mi>' + cb + '</msub>' '<mfenced>' + ca + '</mfenced></mrow>') # Log10 x = myokit.Log10(b) self.assertWrite( x, '<mrow><mi>log</mi><mfenced>' + cb + '</mfenced></mrow>') # Floor x = myokit.Floor(b) self.assertWrite( x, '<mrow><mi>floor</mi><mfenced>' + cb + '</mfenced></mrow>') # Ceil x = myokit.Ceil(b) self.assertWrite( x, '<mrow><mi>ceiling</mi><mfenced>' + cb + '</mfenced></mrow>') # Abs x = myokit.Abs(b) self.assertWrite( x, '<mrow><mi>abs</mi><mfenced>' + cb + '</mfenced></mrow>') # Quotient x = myokit.Quotient(a, b) self.assertWrite(x, '<mrow>' + ca + '<mo>//</mo>' + cb + '</mrow>') # Remainder x = myokit.Remainder(a, b) self.assertWrite(x, '<mrow>' + ca + '<mo>%</mo>' + cb + '</mrow>')
def _parse_log(self, element, iterator): """ Parses the elements following a ``<log>`` element. Arguments: ``element`` The ``<log>`` element. ``iterator`` An iterator pointing at the first element after ``element``. """ # Get next operands ops = [x for x in iterator] # Check for zero ops if len(ops) == 0: raise MathMLError('Expecting operand after <log> element.', element) # Check if first op is logbase if split(ops[0].tag)[1] == 'logbase': # Get logbase base = ops[0] if len(base) != 1: raise MathMLError( 'Expecting a single operand inside <logbase> element.', base) base = self._parse_atomic(base[0]) # Get main operand if len(ops) != 2: raise MathMLError( 'Expecting a single operand after the <logbase> element' ' inside a <log>.', element) op = self._parse_atomic(ops[1]) # No logbase given else: base = None if len(ops) != 1: raise MathMLError( 'Expecting a single operand (or a <logbase> followed by a' ' single operand) inside a <log> element.', element) op = self._parse_atomic(ops[0]) if base is None or base.eval() == 10: return myokit.Log10(op) else: return myokit.Log(op, base)
def test_functions_log(self): # Tests parsing logs # Log(a) a = myokit.Name('a') b = myokit.Number(1) e = myokit.Log(b) x = '<apply><ln/><cn>1.0</cn></apply>' self.assertEqual(self.p(x), e) # Log(a, b) e = myokit.Log(a, b) x = '<apply><log/><logbase><cn>1.0</cn></logbase><ci>a</ci></apply>' self.assertEqual(self.p(x), e) # Log10 e = myokit.Log10(b) x = '<apply><log/><cn>1.0</cn></apply>' self.assertEqual(self.p(x), e) # Empty log x = '<apply><log/></apply>' self.assertRaisesRegex( mathml.MathMLError, 'Expecting operand after <log>', self.p, x) # Empty logbase x = '<apply><log/><logbase/><ci>a</ci></apply>' self.assertRaisesRegex( mathml.MathMLError, 'Expecting a single', self.p, x) # Crowded logbase x = ('<apply>' '<log/><logbase><cn>1</cn><cn>2</cn></logbase><ci>a</ci>' '</apply>') self.assertRaisesRegex( mathml.MathMLError, 'Expecting a single', self.p, x) # Too many operands x = '<apply><log/><cn>3</cn><ci>a</ci></apply>' self.assertRaisesRegex( mathml.MathMLError, 'Expecting a single', self.p, x) # Too many operands after logbase x = ('<apply>' '<log/><logbase><cn>1</cn></logbase><cn>3</cn><ci>a</ci>' '</apply>') self.assertRaisesRegex( mathml.MathMLError, 'Expecting a single', self.p, x)
def test_log(self): # Tests printing the log function a = myokit.Name(self.avar) b = myokit.Number('12', 'pF') ca = '<ci>a</ci>' cb = ('<cn cellml:units="picofarad">12.0</cn>') # Log(a) x = myokit.Log(b) self.assertWrite(x, '<apply><ln/>' + cb + '</apply>') # Log(a, b) x = myokit.Log(a, b) self.assertWrite( x, '<apply><log/><logbase>' + cb + '</logbase>' + ca + '</apply>') # Log10 x = myokit.Log10(b) self.assertWrite(x, '<apply><log/>' + cb + '</apply>')
def parsex(node): """ Parses a mathml expression. """ def chain(kind, node, unary=None): """ Parses operands for chained operators (for example plus, minus, times and division). The argument ``kind`` must be the myokit expression type being parsed, ``node`` is a DOM node and ``unary``, if given, should be the unary expression type (unary Plus or unary Minus). """ ops = [] node = dom_next(node) while node: ops.append(parsex(node)) node = dom_next(node) n = len(ops) if n < 1: raise MathMLError('Operator needs at least one operand.') if n < 2: if unary: return unary(ops[0]) else: raise MathMLError('Operator needs at least two operands') ex = kind(ops[0], ops[1]) for i in range(2, n): ex = kind(ex, ops[i]) return ex # Start parsing name = node.tagName if name == 'apply': # Brackets, can be ignored in an expression tree. return parsex(dom_child(node)) elif name == 'ci': # Reference var = str(node.firstChild.data).strip() if var_table is not None: try: var = var_table[var] except KeyError: if logger: logger.warn('Unable to resolve reference to <' + str(var) + '>.') return myokit.Name(var) elif name == 'diff': # Derivative # Check time variable bvar = dom_next(node, 'bvar') if derivative_post_processor: derivative_post_processor(parsex(dom_child(bvar, 'ci'))) # Check degree, if given d = dom_child(bvar, 'degree') if d is not None: d = parsex(dom_child(d, 'cn')).eval() if not d == 1: raise MathMLError( 'Only derivatives of degree one are supported.') # Create derivative and return x = dom_next(node, 'ci') if x is None: raise MathMLError( 'Derivative of an expression found: only derivatives of' ' variables are supported.') return myokit.Derivative(parsex(x)) elif name == 'cn': # Number number = parse_mathml_number(node, logger) if number_post_processor: return number_post_processor(node, number) return number # # Algebra # elif name == 'plus': return chain(myokit.Plus, node, myokit.PrefixPlus) elif name == 'minus': return chain(myokit.Minus, node, myokit.PrefixMinus) elif name == 'times': return chain(myokit.Multiply, node) elif name == 'divide': return chain(myokit.Divide, node) # # Functions # elif name == 'exp': return myokit.Exp(parsex(dom_next(node))) elif name == 'ln': return myokit.Log(parsex(dom_next(node))) elif name == 'log': if dom_next(node).tagName != 'logbase': return myokit.Log10(parsex(dom_next(node))) else: return myokit.Log(parsex(dom_next(dom_next(node))), parsex(dom_child(dom_next(node)))) elif name == 'root': # Check degree, if given nxt = dom_next(node) if nxt.tagName == 'degree': # Degree given, return x^(1/d) unless d is 2 d = parsex(dom_child(nxt)) x = parsex(dom_next(nxt)) if d.is_literal() and d.eval() == 2: return myokit.Sqrt(x) return myokit.Power(x, myokit.Divide(myokit.Number(1), d)) else: return myokit.Sqrt(parsex(nxt)) elif name == 'power': n2 = dom_next(node) return myokit.Power(parsex(n2), parsex(dom_next(n2))) elif name == 'floor': return myokit.Floor(parsex(dom_next(node))) elif name == 'ceiling': return myokit.Ceil(parsex(dom_next(node))) elif name == 'abs': return myokit.Abs(parsex(dom_next(node))) elif name == 'quotient': n2 = dom_next(node) return myokit.Quotient(parsex(n2), parsex(dom_next(n2))) elif name == 'rem': n2 = dom_next(node) return myokit.Remainder(parsex(n2), parsex(dom_next(n2))) # # Trigonometry # elif name == 'sin': return myokit.Sin(parsex(dom_next(node))) elif name == 'cos': return myokit.Cos(parsex(dom_next(node))) elif name == 'tan': return myokit.Tan(parsex(dom_next(node))) elif name == 'arcsin': return myokit.ASin(parsex(dom_next(node))) elif name == 'arccos': return myokit.ACos(parsex(dom_next(node))) elif name == 'arctan': return myokit.ATan(parsex(dom_next(node))) # # Redundant trigonometry (CellML includes this) # elif name == 'csc': # Cosecant: csc(x) = 1 / sin(x) return myokit.Divide(myokit.Number(1), myokit.Sin(parsex(dom_next(node)))) elif name == 'sec': # Secant: sec(x) = 1 / cos(x) return myokit.Divide(myokit.Number(1), myokit.Cos(parsex(dom_next(node)))) elif name == 'cot': # Contangent: cot(x) = 1 / tan(x) return myokit.Divide(myokit.Number(1), myokit.Tan(parsex(dom_next(node)))) elif name == 'arccsc': # ArcCosecant: acsc(x) = asin(1/x) return myokit.ASin( myokit.Divide(myokit.Number(1), parsex(dom_next(node)))) elif name == 'arcsec': # ArcSecant: asec(x) = acos(1/x) return myokit.ACos( myokit.Divide(myokit.Number(1), parsex(dom_next(node)))) elif name == 'arccot': # ArcCotangent: acot(x) = atan(1/x) return myokit.ATan( myokit.Divide(myokit.Number(1), parsex(dom_next(node)))) # # Hyperbolic trigonometry (CellML again) # elif name == 'sinh': # Hyperbolic sine: sinh(x) = 0.5 * (e^x - e^-x) x = parsex(dom_next(node)) return myokit.Multiply( myokit.Number(0.5), myokit.Minus(myokit.Exp(x), myokit.Exp(myokit.PrefixMinus(x)))) elif name == 'cosh': # Hyperbolic cosine: cosh(x) = 0.5 * (e^x + e^-x) x = parsex(dom_next(node)) return myokit.Multiply( myokit.Number(0.5), myokit.Plus(myokit.Exp(x), myokit.Exp(myokit.PrefixMinus(x)))) elif name == 'tanh': # Hyperbolic tangent: tanh(x) = (e^2x - 1) / (e^2x + 1) x = parsex(dom_next(node)) e2x = myokit.Exp(myokit.Multiply(myokit.Number(2), x)) return myokit.Divide(myokit.Minus(e2x, myokit.Number(1)), myokit.Plus(e2x, myokit.Number(1))) # # Inverse hyperbolic trigonometry (CellML...) # elif name == 'arcsinh': # Inverse hyperbolic sine: asinh(x) = log(x + sqrt(1 + x*x)) x = parsex(dom_next(node)) return myokit.Log( myokit.Plus( x, myokit.Sqrt( myokit.Plus(myokit.Number(1), myokit.Multiply(x, x))))) elif name == 'arccosh': # Inverse hyperbolic cosine: # acosh(x) = log(x + sqrt(x + 1) * sqrt(x - 1)) x = parsex(dom_next(node)) return myokit.Log( myokit.Plus( x, myokit.Multiply( myokit.Sqrt(myokit.Plus(x, myokit.Number(1))), myokit.Sqrt(myokit.Minus(x, myokit.Number(1)))))) elif name == 'arctanh': # Inverse hyperbolic tangent: # atanh(x) = 0.5 * (log(1 + x) - log(1 - x)) x = parsex(dom_next(node)) return myokit.Multiply( myokit.Number(0.5), myokit.Minus(myokit.Log(myokit.Plus(myokit.Number(1), x)), myokit.Log(myokit.Minus(myokit.Number(1), x)))) # # Hyperbolic redundant trigonometry (CellML...) # elif name == 'csch': # Hyperbolic cosecant: csch(x) = 2 / (exp(x) - exp(-x)) x = parsex(dom_next(node)) return myokit.Divide( myokit.Number(2), myokit.Minus(myokit.Exp(x), myokit.Exp(myokit.PrefixMinus(x)))) elif name == 'sech': # Hyperbolic secant: sech(x) = 2 / (exp(x) + exp(-x)) x = parsex(dom_next(node)) return myokit.Divide( myokit.Number(2), myokit.Plus(myokit.Exp(x), myokit.Exp(myokit.PrefixMinus(x)))) elif name == 'coth': # Hyperbolic cotangent: # coth(x) = (exp(2*x) + 1) / (exp(2*x) - 1) x = parsex(dom_next(node)) e2x = myokit.Exp(myokit.Multiply(myokit.Number(2), x)) return myokit.Divide(myokit.Plus(e2x, myokit.Number(1)), myokit.Minus(e2x, myokit.Number(1))) # # Inverse hyperbolic redundant trigonometry (CellML has a lot to answer # for...) # elif name == 'arccsch': # Inverse hyperbolic cosecant: # arccsch(x) = log(sqrt(1/(x*x) + 1) + 1/x) x = parsex(dom_next(node)) return myokit.Log( myokit.Plus( myokit.Sqrt( myokit.Plus( myokit.Divide(myokit.Number(1), myokit.Multiply(x, x)), myokit.Number(1))), myokit.Divide(myokit.Number(1), x))) elif name == 'arcsech': # Inverse hyperbolic secant: # arcsech(x) = log(sqrt(1/(x*x) - 1) + 1/x) x = parsex(dom_next(node)) return myokit.Log( myokit.Plus( myokit.Sqrt( myokit.Minus( myokit.Divide(myokit.Number(1), myokit.Multiply(x, x)), myokit.Number(1))), myokit.Divide(myokit.Number(1), x))) elif name == 'arccoth': # Inverse hyperbolic cotangent: # arccoth(x) = 0.5 * (log(3 + 1) - log(3 - 1)) x = parsex(dom_next(node)) return myokit.Multiply( myokit.Number(0.5), myokit.Log( myokit.Divide(myokit.Plus(x, myokit.Number(1)), myokit.Minus(x, myokit.Number(1))))) # # Logic # elif name == 'and': return chain(myokit.And, node) elif name == 'or': return chain(myokit.Or, node) elif name == 'not': return chain(None, node, myokit.Not) elif name == 'eq' or name == 'equivalent': n2 = dom_next(node) return myokit.Equal(parsex(n2), parsex(dom_next(n2))) elif name == 'neq': n2 = dom_next(node) return myokit.NotEqual(parsex(n2), parsex(dom_next(n2))) elif name == 'gt': n2 = dom_next(node) return myokit.More(parsex(n2), parsex(dom_next(n2))) elif name == 'lt': n2 = dom_next(node) return myokit.Less(parsex(n2), parsex(dom_next(n2))) elif name == 'geq': n2 = dom_next(node) return myokit.MoreEqual(parsex(n2), parsex(dom_next(n2))) elif name == 'leq': n2 = dom_next(node) return myokit.LessEqual(parsex(n2), parsex(dom_next(n2))) elif name == 'piecewise': # Piecewise contains at least one piece, optionally contains an # "otherwise". Syntax doesn't ensure this statement makes sense. conds = [] funcs = [] other = None piece = dom_child(node) while piece: if piece.tagName == 'otherwise': if other is None: other = parsex(dom_child(piece)) elif logger: logger.warn( 'Multiple <otherwise> tags found in <piecewise>' ' statement.') elif piece.tagName == 'piece': n2 = dom_child(piece) funcs.append(parsex(n2)) conds.append(parsex(dom_next(n2))) elif logger: logger.warn('Unexpected tag type in <piecewise>: <' + piece.tagName + '>.') piece = dom_next(piece) if other is None: if logger: logger.warn('No <otherwise> tag found in <piecewise>') other = myokit.Number(0) # Create string of if statements args = [] f = iter(funcs) for c in conds: args.append(c) args.append(next(f)) args.append(other) return myokit.Piecewise(*args) # # Constants # elif name == 'pi': return myokit.Number('3.14159265358979323846') elif name == 'exponentiale': return myokit.Exp(myokit.Number(1)) elif name == 'true': # This is corrent, even in Python True == 1 but not True == 2 return myokit.Number(1) elif name == 'false': return myokit.Number(0) # # Unknown/unhandled elements # else: if logger: logger.warn('Unknown element: ' + name) ops = [] node = dom_child(node) if dom_child(node) else dom_next(node) while node: ops.append(parsex(node)) node = dom_next(node) return myokit.UnsupportedFunction(name, ops)
def test_all(self): w = myokit.formats.python.PythonExpressionWriter() model = myokit.Model() component = model.add_component('c') avar = component.add_variable('a') # Name a = myokit.Name(avar) self.assertEqual(w.ex(a), 'c.a') # Number with unit b = myokit.Number('12', 'pF') self.assertEqual(w.ex(b), '12.0') # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(w.ex(x), '12.0') # Prefix minus x = myokit.PrefixMinus(b) self.assertEqual(w.ex(x), '(-12.0)') # Plus x = myokit.Plus(a, b) self.assertEqual(w.ex(x), 'c.a + 12.0') # Minus x = myokit.Minus(a, b) self.assertEqual(w.ex(x), 'c.a - 12.0') # Multiply x = myokit.Multiply(a, b) self.assertEqual(w.ex(x), 'c.a * 12.0') # Divide x = myokit.Divide(a, b) self.assertEqual(w.ex(x), 'c.a / 12.0') # Quotient x = myokit.Quotient(a, b) self.assertEqual(w.ex(x), 'c.a // 12.0') # Remainder x = myokit.Remainder(a, b) self.assertEqual(w.ex(x), 'c.a % 12.0') # Power x = myokit.Power(a, b) self.assertEqual(w.ex(x), 'c.a ** 12.0') # Sqrt x = myokit.Sqrt(b) self.assertEqual(w.ex(x), 'math.sqrt(12.0)') # Exp x = myokit.Exp(a) self.assertEqual(w.ex(x), 'math.exp(c.a)') # Log(a) x = myokit.Log(b) self.assertEqual(w.ex(x), 'math.log(12.0)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(w.ex(x), 'math.log(c.a, 12.0)') # Log10 x = myokit.Log10(b) self.assertEqual(w.ex(x), 'math.log10(12.0)') # Sin x = myokit.Sin(b) self.assertEqual(w.ex(x), 'math.sin(12.0)') # Cos x = myokit.Cos(b) self.assertEqual(w.ex(x), 'math.cos(12.0)') # Tan x = myokit.Tan(b) self.assertEqual(w.ex(x), 'math.tan(12.0)') # ASin x = myokit.ASin(b) self.assertEqual(w.ex(x), 'math.asin(12.0)') # ACos x = myokit.ACos(b) self.assertEqual(w.ex(x), 'math.acos(12.0)') # ATan x = myokit.ATan(b) self.assertEqual(w.ex(x), 'math.atan(12.0)') # Floor x = myokit.Floor(b) self.assertEqual(w.ex(x), 'math.floor(12.0)') # Ceil x = myokit.Ceil(b) self.assertEqual(w.ex(x), 'math.ceil(12.0)') # Abs x = myokit.Abs(b) self.assertEqual(w.ex(x), 'abs(12.0)') # Equal x = myokit.Equal(a, b) self.assertEqual(w.ex(x), '(c.a == 12.0)') # NotEqual x = myokit.NotEqual(a, b) self.assertEqual(w.ex(x), '(c.a != 12.0)') # More x = myokit.More(a, b) self.assertEqual(w.ex(x), '(c.a > 12.0)') # Less x = myokit.Less(a, b) self.assertEqual(w.ex(x), '(c.a < 12.0)') # MoreEqual x = myokit.MoreEqual(a, b) self.assertEqual(w.ex(x), '(c.a >= 12.0)') # LessEqual x = myokit.LessEqual(a, b) self.assertEqual(w.ex(x), '(c.a <= 12.0)') # Not cond1 = myokit.parse_expression('5 > 3') cond2 = myokit.parse_expression('2 < 1') x = myokit.Not(cond1) self.assertEqual(w.ex(x), 'not ((5.0 > 3.0))') # And x = myokit.And(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) and (2.0 < 1.0))') # Or x = myokit.Or(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) or (2.0 < 1.0))') # If x = myokit.If(cond1, a, b) self.assertEqual(w.ex(x), '(c.a if (5.0 > 3.0) else 12.0)') # Piecewise c = myokit.Number(1) x = myokit.Piecewise(cond1, a, cond2, b, c) self.assertEqual( w.ex(x), '(c.a if (5.0 > 3.0) else (12.0 if (2.0 < 1.0) else 1.0))') # Test fetching using ewriter method w = myokit.formats.ewriter('python') self.assertIsInstance(w, myokit.formats.python.PythonExpressionWriter) # Test lhs method w.set_lhs_function(lambda x: 'sheep') self.assertEqual(w.ex(a), 'sheep') # Test without a Myokit expression self.assertRaisesRegex(ValueError, 'Unknown expression type', w.ex, 7)
def test_all(self): w = myokit.formats.matlab.MatlabExpressionWriter() model = myokit.Model() component = model.add_component('c') avar = component.add_variable('a') # Name a = myokit.Name(avar) self.assertEqual(w.ex(a), 'c.a') # Number with unit b = myokit.Number('12', 'pF') self.assertEqual(w.ex(b), '12.0') # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(w.ex(x), '12.0') # Prefix minus x = myokit.PrefixMinus(b) self.assertEqual(w.ex(x), '(-12.0)') # Plus x = myokit.Plus(a, b) self.assertEqual(w.ex(x), 'c.a + 12.0') # Minus x = myokit.Minus(a, b) self.assertEqual(w.ex(x), 'c.a - 12.0') # Multiply x = myokit.Multiply(a, b) self.assertEqual(w.ex(x), 'c.a * 12.0') # Divide x = myokit.Divide(a, b) self.assertEqual(w.ex(x), 'c.a / 12.0') # Quotient x = myokit.Quotient(a, b) self.assertEqual(w.ex(x), 'floor(c.a / 12.0)') # Remainder x = myokit.Remainder(a, b) self.assertEqual(w.ex(x), 'mod(c.a, 12.0)') # Power x = myokit.Power(a, b) self.assertEqual(w.ex(x), 'c.a ^ 12.0') # Sqrt x = myokit.Sqrt(b) self.assertEqual(w.ex(x), 'sqrt(12.0)') # Exp x = myokit.Exp(a) self.assertEqual(w.ex(x), 'exp(c.a)') # Log(a) x = myokit.Log(b) self.assertEqual(w.ex(x), 'log(12.0)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(w.ex(x), '(log(c.a) / log(12.0))') # Log10 x = myokit.Log10(b) self.assertEqual(w.ex(x), 'log10(12.0)') # Sin x = myokit.Sin(b) self.assertEqual(w.ex(x), 'sin(12.0)') # Cos x = myokit.Cos(b) self.assertEqual(w.ex(x), 'cos(12.0)') # Tan x = myokit.Tan(b) self.assertEqual(w.ex(x), 'tan(12.0)') # ASin x = myokit.ASin(b) self.assertEqual(w.ex(x), 'asin(12.0)') # ACos x = myokit.ACos(b) self.assertEqual(w.ex(x), 'acos(12.0)') # ATan x = myokit.ATan(b) self.assertEqual(w.ex(x), 'atan(12.0)') # Floor x = myokit.Floor(b) self.assertEqual(w.ex(x), 'floor(12.0)') # Ceil x = myokit.Ceil(b) self.assertEqual(w.ex(x), 'ceil(12.0)') # Abs x = myokit.Abs(b) self.assertEqual(w.ex(x), 'abs(12.0)') # Equal x = myokit.Equal(a, b) self.assertEqual(w.ex(x), '(c.a == 12.0)') # NotEqual x = myokit.NotEqual(a, b) self.assertEqual(w.ex(x), '(c.a != 12.0)') # More x = myokit.More(a, b) self.assertEqual(w.ex(x), '(c.a > 12.0)') # Less x = myokit.Less(a, b) self.assertEqual(w.ex(x), '(c.a < 12.0)') # MoreEqual x = myokit.MoreEqual(a, b) self.assertEqual(w.ex(x), '(c.a >= 12.0)') # LessEqual x = myokit.LessEqual(a, b) self.assertEqual(w.ex(x), '(c.a <= 12.0)') # Not cond1 = myokit.parse_expression('5 > 3') cond2 = myokit.parse_expression('2 < 1') x = myokit.Not(cond1) self.assertEqual(w.ex(x), '!((5.0 > 3.0))') # And x = myokit.And(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) && (2.0 < 1.0))') # Or x = myokit.Or(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) || (2.0 < 1.0))') # If (custom function) x = myokit.If(cond1, a, b) self.assertEqual(w.ex(x), 'ifthenelse((5.0 > 3.0), c.a, 12.0)') # Piecewise c = myokit.Number(1) x = myokit.Piecewise(cond1, a, cond2, b, c) self.assertEqual( w.ex(x), 'ifthenelse((5.0 > 3.0), c.a, ifthenelse((2.0 < 1.0), 12.0, 1.0))') # Test fetching using ewriter method w = myokit.formats.ewriter('matlab') self.assertIsInstance(w, myokit.formats.matlab.MatlabExpressionWriter) # Test without a Myokit expression self.assertRaisesRegex(ValueError, 'Unknown expression type', w.ex, 7)
def test_all(self): w = myokit.formats.latex.LatexExpressionWriter() model = myokit.Model() component = model.add_component('c') avar = component.add_variable('a') # Model needs to be validated --> sets unames avar.set_rhs(12) avar.set_binding('time') model.validate() # Name a = myokit.Name(avar) self.assertEqual(w.ex(a), '\\text{a}') # Number with unit b = myokit.Number('12', 'pF') self.assertEqual(w.ex(b), '12.0') # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(w.ex(x), '12.0') # Prefix minus x = myokit.PrefixMinus(b) self.assertEqual(w.ex(x), '\\left(-12.0\\right)') # Prefix minus with bracket x = myokit.PrefixMinus(myokit.Plus(a, b)) self.assertEqual(w.ex(x), '\\left(-\\left(\\text{a}+12.0\\right)\\right)') # Plus x = myokit.Plus(a, b) self.assertEqual(w.ex(x), '\\text{a}+12.0') # Minus x = myokit.Minus(a, b) self.assertEqual(w.ex(x), '\\text{a}-12.0') # Multiply x = myokit.Multiply(a, b) self.assertEqual(w.ex(x), '\\text{a}*12.0') # Divide x = myokit.Divide(a, b) self.assertEqual(w.ex(x), '\\frac{\\text{a}}{12.0}') # Quotient # Not supported in latex! x = myokit.Quotient(a, b) self.assertEqual( w.ex(x), '\\left\\lfloor\\frac{\\text{a}}{12.0}\\right\\rfloor') # Remainder x = myokit.Remainder(a, b) self.assertEqual(w.ex(x), '\\bmod\\left(\\text{a},12.0\\right)') # Power x = myokit.Power(a, b) self.assertEqual(w.ex(x), '\\text{a}^{12.0}') # Power with brackets x = myokit.Power(myokit.Plus(a, b), b) self.assertEqual(w.ex(x), '\\left(\\text{a}+12.0\\right)^{12.0}') # Sqrt x = myokit.Sqrt(b) self.assertEqual(w.ex(x), '\\sqrt{12.0}') # Exp x = myokit.Exp(a) self.assertEqual(w.ex(x), '\\exp\\left(\\text{a}\\right)') # Log(a) x = myokit.Log(b) self.assertEqual(w.ex(x), '\\log\\left(12.0\\right)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(w.ex(x), '\\log_{12.0}\\left(\\text{a}\\right)') # Log10 x = myokit.Log10(b) self.assertEqual(w.ex(x), '\\log_{10.0}\\left(12.0\\right)') # Sin x = myokit.Sin(b) self.assertEqual(w.ex(x), '\\sin\\left(12.0\\right)') # Cos x = myokit.Cos(b) self.assertEqual(w.ex(x), '\\cos\\left(12.0\\right)') # Tan x = myokit.Tan(b) self.assertEqual(w.ex(x), '\\tan\\left(12.0\\right)') # ASin x = myokit.ASin(b) self.assertEqual(w.ex(x), '\\arcsin\\left(12.0\\right)') # ACos x = myokit.ACos(b) self.assertEqual(w.ex(x), '\\arccos\\left(12.0\\right)') # ATan x = myokit.ATan(b) self.assertEqual(w.ex(x), '\\arctan\\left(12.0\\right)') # Floor x = myokit.Floor(b) self.assertEqual(w.ex(x), '\\left\\lfloor{12.0}\\right\\rfloor') # Ceil x = myokit.Ceil(b) self.assertEqual(w.ex(x), '\\left\\lceil{12.0}\\right\\rceil') # Abs x = myokit.Abs(b) self.assertEqual(w.ex(x), '\\lvert{12.0}\\rvert') # Equal x = myokit.Equal(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}=12.0\\right)') # NotEqual x = myokit.NotEqual(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}\\neq12.0\\right)') # More x = myokit.More(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}>12.0\\right)') # Less x = myokit.Less(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}<12.0\\right)') # MoreEqual x = myokit.MoreEqual(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}\\geq12.0\\right)') # LessEqual x = myokit.LessEqual(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}\\leq12.0\\right)') # Not cond1 = myokit.parse_expression('5 > 3') cond2 = myokit.parse_expression('2 < 1') x = myokit.Not(cond1) self.assertEqual(w.ex(x), '\\not\\left(\\left(5.0>3.0\\right)\\right)') # And x = myokit.And(cond1, cond2) self.assertEqual( w.ex(x), '\\left(\\left(5.0>3.0\\right)\\and' '\\left(2.0<1.0\\right)\\right)') # Or x = myokit.Or(cond1, cond2) self.assertEqual( w.ex(x), '\\left(\\left(5.0>3.0\\right)\\or' '\\left(2.0<1.0\\right)\\right)') # If x = myokit.If(cond1, a, b) self.assertEqual( w.ex(x), 'if\\left(\\left(5.0>3.0\\right),\\text{a},12.0\\right)') # Piecewise c = myokit.Number(1) x = myokit.Piecewise(cond1, a, cond2, b, c) self.assertEqual( w.ex(x), 'piecewise\\left(\\left(5.0>3.0\\right),\\text{a},' '\\left(2.0<1.0\\right),12.0,1.0\\right)') # Test fetching using ewriter method w = myokit.formats.ewriter('latex') self.assertIsInstance(w, myokit.formats.latex.LatexExpressionWriter) # Test without a Myokit expression self.assertRaisesRegex(ValueError, 'Unknown expression type', w.ex, 7)
def test_all(self): # Single and double precision ws = myokit.formats.cuda.CudaExpressionWriter() wd = myokit.formats.cuda.CudaExpressionWriter(myokit.DOUBLE_PRECISION) model = myokit.Model() component = model.add_component('c') avar = component.add_variable('a') # Name a = myokit.Name(avar) self.assertEqual(ws.ex(a), 'c.a') self.assertEqual(wd.ex(a), 'c.a') # Number with unit b = myokit.Number('12', 'pF') self.assertEqual(ws.ex(b), '12.0f') self.assertEqual(wd.ex(b), '12.0') # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(ws.ex(x), '12.0f') self.assertEqual(wd.ex(x), '12.0') # Prefix minus x = myokit.PrefixMinus(b) self.assertEqual(ws.ex(x), '(-12.0f)') self.assertEqual(wd.ex(x), '(-12.0)') # Plus x = myokit.Plus(a, b) self.assertEqual(ws.ex(x), 'c.a + 12.0f') self.assertEqual(wd.ex(x), 'c.a + 12.0') # Minus x = myokit.Minus(a, b) self.assertEqual(ws.ex(x), 'c.a - 12.0f') self.assertEqual(wd.ex(x), 'c.a - 12.0') # Multiply x = myokit.Multiply(a, b) self.assertEqual(ws.ex(x), 'c.a * 12.0f') self.assertEqual(wd.ex(x), 'c.a * 12.0') # Divide x = myokit.Divide(a, b) self.assertEqual(ws.ex(x), 'c.a / 12.0f') self.assertEqual(wd.ex(x), 'c.a / 12.0') # Quotient x = myokit.Quotient(a, b) self.assertEqual(ws.ex(x), 'floorf(c.a / 12.0f)') self.assertEqual(wd.ex(x), 'floor(c.a / 12.0)') # Remainder x = myokit.Remainder(a, b) self.assertEqual(ws.ex(x), 'c.a - 12.0f * (floorf(c.a / 12.0f))') self.assertEqual(wd.ex(x), 'c.a - 12.0 * (floor(c.a / 12.0))') # Power x = myokit.Power(a, b) self.assertEqual(ws.ex(x), 'powf(c.a, 12.0f)') self.assertEqual(wd.ex(x), 'pow(c.a, 12.0)') # Square x = myokit.Power(a, myokit.Number(2)) self.assertEqual(ws.ex(x), '(c.a * c.a)') self.assertEqual(wd.ex(x), '(c.a * c.a)') # Square with brackets x = myokit.Power(myokit.Plus(a, b), myokit.Number(2)) self.assertEqual(ws.ex(x), '((c.a + 12.0f) * (c.a + 12.0f))') self.assertEqual(wd.ex(x), '((c.a + 12.0) * (c.a + 12.0))') # Sqrt x = myokit.Sqrt(b) self.assertEqual(ws.ex(x), 'sqrtf(12.0f)') self.assertEqual(wd.ex(x), 'sqrt(12.0)') # Exp x = myokit.Exp(a) self.assertEqual(ws.ex(x), 'expf(c.a)') self.assertEqual(wd.ex(x), 'exp(c.a)') # Log(a) x = myokit.Log(b) self.assertEqual(ws.ex(x), 'logf(12.0f)') self.assertEqual(wd.ex(x), 'log(12.0)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(ws.ex(x), '(logf(c.a) / logf(12.0f))') self.assertEqual(wd.ex(x), '(log(c.a) / log(12.0))') # Log10 x = myokit.Log10(b) self.assertEqual(ws.ex(x), 'log10f(12.0f)') self.assertEqual(wd.ex(x), 'log10(12.0)') # Sin x = myokit.Sin(b) self.assertEqual(ws.ex(x), 'sinf(12.0f)') self.assertEqual(wd.ex(x), 'sin(12.0)') # Cos x = myokit.Cos(b) self.assertEqual(ws.ex(x), 'cosf(12.0f)') self.assertEqual(wd.ex(x), 'cos(12.0)') # Tan x = myokit.Tan(b) self.assertEqual(ws.ex(x), 'tanf(12.0f)') self.assertEqual(wd.ex(x), 'tan(12.0)') # ASin x = myokit.ASin(b) self.assertEqual(ws.ex(x), 'asinf(12.0f)') self.assertEqual(wd.ex(x), 'asin(12.0)') # ACos x = myokit.ACos(b) self.assertEqual(ws.ex(x), 'acosf(12.0f)') self.assertEqual(wd.ex(x), 'acos(12.0)') # ATan x = myokit.ATan(b) self.assertEqual(ws.ex(x), 'atanf(12.0f)') self.assertEqual(wd.ex(x), 'atan(12.0)') # Floor x = myokit.Floor(b) self.assertEqual(ws.ex(x), 'floorf(12.0f)') self.assertEqual(wd.ex(x), 'floor(12.0)') # Ceil x = myokit.Ceil(b) self.assertEqual(ws.ex(x), 'ceilf(12.0f)') self.assertEqual(wd.ex(x), 'ceil(12.0)') # Abs x = myokit.Abs(b) self.assertEqual(ws.ex(x), 'fabsf(12.0f)') self.assertEqual(wd.ex(x), 'fabs(12.0)') # Equal x = myokit.Equal(a, b) self.assertEqual(ws.ex(x), '(c.a == 12.0f)') self.assertEqual(wd.ex(x), '(c.a == 12.0)') # NotEqual x = myokit.NotEqual(a, b) self.assertEqual(ws.ex(x), '(c.a != 12.0f)') self.assertEqual(wd.ex(x), '(c.a != 12.0)') # More x = myokit.More(a, b) self.assertEqual(ws.ex(x), '(c.a > 12.0f)') self.assertEqual(wd.ex(x), '(c.a > 12.0)') # Less x = myokit.Less(a, b) self.assertEqual(ws.ex(x), '(c.a < 12.0f)') self.assertEqual(wd.ex(x), '(c.a < 12.0)') # MoreEqual x = myokit.MoreEqual(a, b) self.assertEqual(ws.ex(x), '(c.a >= 12.0f)') self.assertEqual(wd.ex(x), '(c.a >= 12.0)') # LessEqual x = myokit.LessEqual(a, b) self.assertEqual(ws.ex(x), '(c.a <= 12.0f)') self.assertEqual(wd.ex(x), '(c.a <= 12.0)') # Not cond1 = myokit.parse_expression('5 > 3') cond2 = myokit.parse_expression('2 < 1') x = myokit.Not(cond1) self.assertEqual(ws.ex(x), '!((5.0f > 3.0f))') self.assertEqual(wd.ex(x), '!((5.0 > 3.0))') # And x = myokit.And(cond1, cond2) self.assertEqual(ws.ex(x), '((5.0f > 3.0f) && (2.0f < 1.0f))') self.assertEqual(wd.ex(x), '((5.0 > 3.0) && (2.0 < 1.0))') # Or x = myokit.Or(cond1, cond2) self.assertEqual(ws.ex(x), '((5.0f > 3.0f) || (2.0f < 1.0f))') self.assertEqual(wd.ex(x), '((5.0 > 3.0) || (2.0 < 1.0))') # If x = myokit.If(cond1, a, b) self.assertEqual(ws.ex(x), '((5.0f > 3.0f) ? c.a : 12.0f)') self.assertEqual(wd.ex(x), '((5.0 > 3.0) ? c.a : 12.0)') # Piecewise c = myokit.Number(1) x = myokit.Piecewise(cond1, a, cond2, b, c) self.assertEqual( ws.ex(x), '((5.0f > 3.0f) ? c.a : ((2.0f < 1.0f) ? 12.0f : 1.0f))') self.assertEqual(wd.ex(x), '((5.0 > 3.0) ? c.a : ((2.0 < 1.0) ? 12.0 : 1.0))') # Test fetching using ewriter method w = myokit.formats.ewriter('cuda') self.assertIsInstance(w, myokit.formats.cuda.CudaExpressionWriter) # Test without a Myokit expression self.assertRaisesRegex(ValueError, 'Unknown expression type', w.ex, 7)
def test_functions(self): # Single and double precision and native maths ws = myokit.formats.opencl.OpenCLExpressionWriter() wd = myokit.formats.opencl.OpenCLExpressionWriter( myokit.DOUBLE_PRECISION) wn = myokit.formats.opencl.OpenCLExpressionWriter(native_math=False) a = myokit.Name(myokit.Model().add_component('c').add_variable('a')) b = myokit.Number('12', 'pF') # Power x = myokit.Power(a, b) self.assertEqual(ws.ex(x), 'pow(c.a, 12.0f)') self.assertEqual(wd.ex(x), 'pow(c.a, 12.0)') self.assertEqual(wn.ex(x), 'pow(c.a, 12.0f)') # Square x = myokit.Power(a, myokit.Number(2)) self.assertEqual(ws.ex(x), '(c.a * c.a)') self.assertEqual(wd.ex(x), '(c.a * c.a)') self.assertEqual(wn.ex(x), '(c.a * c.a)') # Square with brackets x = myokit.Power(myokit.Plus(a, b), myokit.Number(2)) self.assertEqual(ws.ex(x), '((c.a + 12.0f) * (c.a + 12.0f))') self.assertEqual(wd.ex(x), '((c.a + 12.0) * (c.a + 12.0))') self.assertEqual(wn.ex(x), '((c.a + 12.0f) * (c.a + 12.0f))') # Sqrt x = myokit.Sqrt(b) self.assertEqual(ws.ex(x), 'native_sqrt(12.0f)') self.assertEqual(wd.ex(x), 'native_sqrt(12.0)') self.assertEqual(wn.ex(x), 'sqrt(12.0f)') # Exp x = myokit.Exp(a) self.assertEqual(ws.ex(x), 'native_exp(c.a)') self.assertEqual(wd.ex(x), 'native_exp(c.a)') self.assertEqual(wn.ex(x), 'exp(c.a)') # Log(a) x = myokit.Log(b) self.assertEqual(ws.ex(x), 'native_log(12.0f)') self.assertEqual(wd.ex(x), 'native_log(12.0)') self.assertEqual(wn.ex(x), 'log(12.0f)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(ws.ex(x), '(native_log(c.a) / native_log(12.0f))') self.assertEqual(wd.ex(x), '(native_log(c.a) / native_log(12.0))') self.assertEqual(wn.ex(x), '(log(c.a) / log(12.0f))') # Log10 x = myokit.Log10(b) self.assertEqual(ws.ex(x), 'native_log10(12.0f)') self.assertEqual(wd.ex(x), 'native_log10(12.0)') self.assertEqual(wn.ex(x), 'log10(12.0f)') # Sin x = myokit.Sin(b) self.assertEqual(ws.ex(x), 'native_sin(12.0f)') self.assertEqual(wd.ex(x), 'native_sin(12.0)') self.assertEqual(wn.ex(x), 'sin(12.0f)') # Cos x = myokit.Cos(b) self.assertEqual(ws.ex(x), 'native_cos(12.0f)') self.assertEqual(wd.ex(x), 'native_cos(12.0)') self.assertEqual(wn.ex(x), 'cos(12.0f)') # Tan x = myokit.Tan(b) self.assertEqual(ws.ex(x), 'native_tan(12.0f)') self.assertEqual(wd.ex(x), 'native_tan(12.0)') self.assertEqual(wn.ex(x), 'tan(12.0f)') # ASin x = myokit.ASin(b) self.assertEqual(ws.ex(x), 'asin(12.0f)') self.assertEqual(wd.ex(x), 'asin(12.0)') self.assertEqual(wn.ex(x), 'asin(12.0f)') # ACos x = myokit.ACos(b) self.assertEqual(ws.ex(x), 'acos(12.0f)') self.assertEqual(wd.ex(x), 'acos(12.0)') self.assertEqual(wn.ex(x), 'acos(12.0f)') # ATan x = myokit.ATan(b) self.assertEqual(ws.ex(x), 'atan(12.0f)') self.assertEqual(wd.ex(x), 'atan(12.0)') self.assertEqual(wn.ex(x), 'atan(12.0f)') # Floor x = myokit.Floor(b) self.assertEqual(ws.ex(x), 'floor(12.0f)') self.assertEqual(wd.ex(x), 'floor(12.0)') self.assertEqual(wn.ex(x), 'floor(12.0f)') # Ceil x = myokit.Ceil(b) self.assertEqual(ws.ex(x), 'ceil(12.0f)') self.assertEqual(wd.ex(x), 'ceil(12.0)') self.assertEqual(wn.ex(x), 'ceil(12.0f)') # Abs x = myokit.Abs(b) self.assertEqual(ws.ex(x), 'fabs(12.0f)') self.assertEqual(wd.ex(x), 'fabs(12.0)') self.assertEqual(wn.ex(x), 'fabs(12.0f)')
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 test_all(self): w = myokit.formats.ewriter('easyml') model = myokit.Model() component = model.add_component('c') avar = component.add_variable('a') # Name a = myokit.Name(avar) self.assertEqual(w.ex(a), 'c.a') # Number with unit b = myokit.Number('12', 'pF') self.assertEqual(w.ex(b), '12.0') # Integer c = myokit.Number(1) self.assertEqual(w.ex(c), '1.0') # Integer # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(w.ex(x), '12.0') # Prefix minus x = myokit.PrefixMinus(b) self.assertEqual(w.ex(x), '(-12.0)') # Plus x = myokit.Plus(a, b) self.assertEqual(w.ex(x), 'c.a + 12.0') # Minus x = myokit.Minus(a, b) self.assertEqual(w.ex(x), 'c.a - 12.0') # Multiply x = myokit.Multiply(a, b) self.assertEqual(w.ex(x), 'c.a * 12.0') # Divide x = myokit.Divide(a, b) self.assertEqual(w.ex(x), 'c.a / 12.0') # Quotient x = myokit.Quotient(a, b) with WarningCollector() as c: self.assertEqual(w.ex(x), 'floor(c.a / 12.0)') # Remainder x = myokit.Remainder(a, b) with WarningCollector() as c: self.assertEqual(w.ex(x), 'c.a - 12.0 * (floor(c.a / 12.0))') # Power x = myokit.Power(a, b) self.assertEqual(w.ex(x), 'pow(c.a, 12.0)') # Sqrt x = myokit.Sqrt(b) self.assertEqual(w.ex(x), 'sqrt(12.0)') # Exp x = myokit.Exp(a) self.assertEqual(w.ex(x), 'exp(c.a)') # Log(a) x = myokit.Log(b) self.assertEqual(w.ex(x), 'log(12.0)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(w.ex(x), '(log(c.a) / log(12.0))') # Log10 x = myokit.Log10(b) self.assertEqual(w.ex(x), 'log10(12.0)') # Sin with WarningCollector() as c: x = myokit.Sin(b) self.assertEqual(w.ex(x), 'sin(12.0)') # Cos x = myokit.Cos(b) self.assertEqual(w.ex(x), 'cos(12.0)') # Tan x = myokit.Tan(b) self.assertEqual(w.ex(x), 'tan(12.0)') # ASin x = myokit.ASin(b) self.assertEqual(w.ex(x), 'asin(12.0)') # ACos x = myokit.ACos(b) self.assertEqual(w.ex(x), 'acos(12.0)') # ATan x = myokit.ATan(b) self.assertEqual(w.ex(x), 'atan(12.0)') with WarningCollector() as c: # Floor x = myokit.Floor(b) self.assertEqual(w.ex(x), 'floor(12.0)') # Ceil x = myokit.Ceil(b) self.assertEqual(w.ex(x), 'ceil(12.0)') # Abs x = myokit.Abs(b) self.assertEqual(w.ex(x), 'fabs(12.0)') # Equal x = myokit.Equal(a, b) self.assertEqual(w.ex(x), '(c.a == 12.0)') # NotEqual x = myokit.NotEqual(a, b) self.assertEqual(w.ex(x), '(c.a != 12.0)') # More x = myokit.More(a, b) self.assertEqual(w.ex(x), '(c.a > 12.0)') # Less x = myokit.Less(a, b) self.assertEqual(w.ex(x), '(c.a < 12.0)') # MoreEqual x = myokit.MoreEqual(a, b) self.assertEqual(w.ex(x), '(c.a >= 12.0)') # LessEqual x = myokit.LessEqual(a, b) self.assertEqual(w.ex(x), '(c.a <= 12.0)') # Not cond1 = myokit.parse_expression('5 > 3') cond2 = myokit.parse_expression('2 < 1') x = myokit.Not(cond1) self.assertEqual(w.ex(x), '!((5.0 > 3.0))') # And x = myokit.And(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) and (2.0 < 1.0))') # Or x = myokit.Or(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) or (2.0 < 1.0))') # If x = myokit.If(cond1, a, b) self.assertEqual(w.ex(x), '((5.0 > 3.0) ? c.a : 12.0)') # Piecewise c = myokit.Number(1) x = myokit.Piecewise(cond1, a, cond2, b, c) self.assertEqual(w.ex(x), '((5.0 > 3.0) ? c.a : ((2.0 < 1.0) ? 12.0 : 1.0))') # Test without a Myokit expression self.assertRaisesRegex(ValueError, 'Unknown expression type', w.ex, 7)