Exemple #1
0
    def test_validate(self):
        # Test some edge cases for validation.

        # Test scope rule:
        # Variables are allowed access all children of their ancestors
        m = myokit.Model()
        c = m.add_component('c')
        p = c.add_variable('p')
        p11 = p.add_variable('p11')
        p12 = p.add_variable('p12')
        p121 = p12.add_variable('p121')
        p121.set_rhs('p + p11 + p12')
        p121.validate()

        # But not children of children of ancestors
        p112 = p11.add_variable('p112')
        p121.set_rhs(p112.lhs())
        self.assertRaises(myokit.IllegalReferenceError, p121.validate)

        # RHS cannot be a partial, init, or condition
        m = myokit.Model()
        c = m.add_component('c')
        p = c.add_variable('p')
        p.set_rhs(3)
        q = c.add_variable('q')
        q.set_rhs(2)
        p.validate()
        p.set_rhs(myokit.PartialDerivative(p.lhs(), q.lhs()))
        self.assertRaisesRegex(
            myokit.IntegrityError, 'Partial derivatives', p.validate)
        p.promote(1)
        p.set_rhs(myokit.InitialValue(myokit.Name(p)))
        self.assertRaisesRegex(
            myokit.IntegrityError, 'Initial value', p.validate)
        p.set_rhs('q == 3')
        self.assertRaisesRegex(
            myokit.IntegrityError, 'can not be a condition', p.validate)
Exemple #2
0
    def test_sensitivities(self):
        # Test instantiation of cmodel with sensitivities

        # Bad type
        sens = 'Bad type'
        with self.assertRaisesRegex(ValueError, 'The argument `sensitivities'):
            myokit.CModel(self.model, sens)

        # Empty deps or indeps
        sens = ([], ['some parameter'])
        m = myokit.CModel(self.model, sens)
        self.assertFalse(m.has_sensitivities)
        sens = (['some state'], [])
        m = myokit.CModel(self.model, sens)
        self.assertFalse(m.has_sensitivities)

        # Provide sensitivies as Variables
        s1 = self.model.get('ik1.gK1')
        s2 = self.model.get('ikp.IKp')
        p1 = self.model.get('cell.K_o')
        p2 = self.model.get('ikp.gKp')
        sens = ([s1, s2], [p1, p2])
        m = myokit.CModel(self.model, sens)
        self.assertTrue(m.has_sensitivities)

        # Provide sensitivities as Names
        sens = ([myokit.Name(s1),
                 myokit.Name(s2)], [myokit.Name(p1),
                                    myokit.Name(p2)])
        m = myokit.CModel(self.model, sens)
        self.assertTrue(m.has_sensitivities)

        # Sensitivity of derivative
        s3 = self.model.get('ik.x')
        sens = ([myokit.Derivative(myokit.Name(s3)),
                 myokit.Name(s2)], [myokit.Name(p1),
                                    myokit.Name(p2)])
        m = myokit.CModel(self.model, sens)
        self.assertTrue(m.has_sensitivities)
        s3 = 'dot(ik.x)'
        sens = ([s3, myokit.Name(s2)], [myokit.Name(p1), myokit.Name(p2)])
        m = myokit.CModel(self.model, sens)
        self.assertTrue(m.has_sensitivities)

        # Sensitivity of derivative of non-state
        sens = ([myokit.Derivative(myokit.Name(s1)),
                 myokit.Name(s2)], [myokit.Name(p1),
                                    myokit.Name(p2)])
        with self.assertRaisesRegex(ValueError, 'Sensitivity of '):
            myokit.CModel(self.model, sens)

        # Sensitivity of bound variable
        sens = (['engine.time',
                 myokit.Name(s2)], [myokit.Name(p1),
                                    myokit.Name(p2)])
        with self.assertRaisesRegex(ValueError, 'Sensitivities cannot'):
            myokit.CModel(self.model, sens)

        # Sensitivity w.r.t. Initial value
        s3 = self.model.get('ik.x')
        sens = ([s3, myokit.Name(s2)],
                [myokit.Name(p1),
                 myokit.InitialValue(myokit.Name(s3))])
        m = myokit.CModel(self.model, sens)
        self.assertTrue(m.has_sensitivities)

        # Sensitivity w.r.t. initial value of non-state
        sens = ([myokit.Name(s1), myokit.Name(s2)],
                [myokit.Name(p1),
                 myokit.InitialValue(myokit.Name(p2))])
        with self.assertRaisesRegex(ValueError, 'Sensitivity with respect to'):
            myokit.CModel(self.model, sens)

        # Sensitivity w.r.t. non-literal
        sens = ([myokit.Name(s1), myokit.Name(s2)], [myokit.Name(p1), 'ik.E'])
        with self.assertRaisesRegex(ValueError, 'Sensitivity with respect to'):
            myokit.CModel(self.model, sens)
Exemple #3
0
    def _parse_sensitivities(self, model, sensitivities):
        """
        Parses the ``sensitivities`` constructor argument and returns a tuple
        ``(has_sensitivities, dependents, independents)``, where
        ``has_sensitivities`` is a boolean and the other two entries are lists
        containing :class:`myokit.Expression` objects.

        Acceptable input for dependents (y in dy/dx):

        - Variable (state or intermediary)
        - Name or Derivative
        - "ina.INa" or "dot(membrane.V)"

        Acceptable input for independents (x in dy/dx):

        - Variable (literal)
        - Name or InitialValue
        - "ikr.gKr" or "init(membrane.V)"

        The resulting lists contain Expression objects.
        """
        if sensitivities is None:
            return False, [], []

        # Get lists
        try:
            deps, indeps = sensitivities
            deps = list(deps)
            indeps = list(indeps)
        except Exception:
            raise ValueError(
                'The argument `sensitivities` must be None, or a tuple'
                ' containing two lists.')
        if len(deps) == 0 or len(indeps) == 0:
            return False, [], []

        # Create output lists
        dependents = []
        independents = []

        # Check dependents, make sure all are Name or Derivative objects from
        # the cloned model.
        for x in deps:
            deriv = False
            if isinstance(x, myokit.Variable):
                var = model.get(x.qname())
            elif isinstance(x, myokit.Name):
                var = model.get(x.var().qname())
            elif isinstance(x, myokit.Derivative):
                deriv = True
                var = model.get(x.var().qname())
            else:
                x = str(x)
                if x[:4] == 'dot(' and x[-1:] == ')':
                    deriv = True
                    var = x = x[4:-1]
                var = model.get(x)
            lhs = myokit.Name(var)
            if deriv:
                lhs = myokit.Derivative(lhs)
                if not var.is_state():
                    raise ValueError('Sensitivity of ' + lhs.code() +
                                     ' requested, but ' + var.qname() +
                                     ' is not a state variable.')
            elif var.is_bound():
                raise ValueError('Sensitivities cannot be calculated for bound'
                                 ' variables (got ' + str(var.qname()) + ').')
            # Note: constants are fine, just not very useful! But may be
            # easy, e.g. when working with multiple models.
            dependents.append(lhs)

        # Check independents, make sure all are Name or InitialValue
        # objects from the cloned model.
        for x in indeps:
            init = False
            if isinstance(x, myokit.Variable):
                var = model.get(x.qname())
            elif isinstance(x, myokit.Name):
                var = model.get(x.var().qname())
            elif isinstance(x, myokit.InitialValue):
                init = True
                var = model.get(x.var().qname())
            else:
                x = str(x)
                if x[:5] == 'init(' and x[-1:] == ')':
                    init = True
                    x = x[5:-1]
                var = model.get(x)
            lhs = myokit.Name(var)
            if init:
                lhs = myokit.InitialValue(myokit.Name(var))
                if not var.is_state():
                    raise ValueError('Sensitivity with respect to ' +
                                     lhs.code() + ' requested, but ' +
                                     var.qname() + ' is not a'
                                     ' state variable.')
            elif not var.is_literal():
                raise ValueError(
                    'Sensitivity with respect to ' + var.qname() +
                    ' requested, but this is not a literal variable (it'
                    ' depends on other variables).')
            independents.append(lhs)

        return True, dependents, independents
    def test_sensitivities_with_derivatives(self):
        # Test various inputs to the `sensitivities` argument are handled
        # correctly, including sensitivites of derivatives and intermediary
        # variables depending on derivatives.

        # Note: passing the wrong values into the sensitivities constructor arg
        # is tested in the CModel tests.

        m = myokit.parse_model('''
            [[model]]
            e.x = 1.2
            e.y = 2.3

            [e]
            t = 0 bind time
            p = 1 / 100
            q = 2
            r = 3
            dot(x) = p * q
            dot(y) = 2 * p - y
            fx = x^2
            fy = r + p^2 + dot(y)
            ''')
        m.validate()

        x0 = m.get('e.x').state_value()
        y0 = m.get('e.y').state_value()
        p = m.get('e.p').eval()
        q = m.get('e.q').eval()
        r = m.get('e.r').eval()

        # Dependents is a list of y in dy/dx.
        # Must refer to state, derivative, or intermediary, given as:
        #  - Variable
        #  - Name
        #  - Derivative
        #  - "qname"
        #  - "dot(qname)"
        dependents = [
            'e.x',  # qname
            'e.y',  # qname
            'dot(e.x)',  # dot(qname)
            m.get('e.y').lhs(),  # Derivative
            m.get('e.fx'),  # Variable
            m.get('e.fy').lhs(),  # Name
        ]

        # Independents is a list of x in dy/dx
        # Must refer to literals or initial values, given as:
        #  - Variable
        #  - Name
        #  - InitialValue
        #  - "qname"
        #  - "init(qname)"
        independents = [
            'e.p',  # qname
            m.get('e.q'),  # qname
            m.get('e.r').lhs(),  # Name
            'init(e.x)',  # init(qname)
            myokit.InitialValue(myokit.Name(m.get('e.y'))),  # InitialValue
        ]

        # Run, get output
        s = myokit.Simulation(m, sensitivities=(dependents, independents))
        s.set_tolerance(1e-9, 1e-9)
        d, e = s.run(8)
        d, e = d.npview(), np.array(e)
        t = d.time()

        # Check solution
        self.assertTrue(np.allclose(d['e.x'], x0 + p * q * t))
        self.assertTrue(
            np.allclose(d['e.y'], 2 * p + (y0 - 2 * p) * np.exp(-t)))
        self.assertTrue(
            np.allclose(d['e.fx'],
                        (p * q * t)**2 + 2 * p * q * t * x0 + x0**2))
        self.assertTrue(
            np.allclose(d['e.fy'], r + p**2 + (2 * p - y0) * np.exp(-t)))
        self.assertTrue(np.allclose(d['dot(e.x)'], p * q))
        self.assertTrue(np.allclose(d['dot(e.y)'], (2 * p - y0) * np.exp(-t)))

        # Sensitivities of  x  to (p, q, r, x0, y0)
        self.assertTrue(np.allclose(e[:, 0, 0], q * t))
        self.assertTrue(np.allclose(e[:, 0, 1], p * t))
        self.assertTrue(np.allclose(e[:, 0, 2], 0))
        self.assertTrue(np.allclose(e[:, 0, 3], 1))
        self.assertTrue(np.allclose(e[:, 0, 4], 0))

        # Sensitivities of  y  to (p, q, r, x0, y0)
        self.assertTrue(np.allclose(e[:, 1, 0], 2 - 2 * np.exp(-t)))
        self.assertTrue(np.allclose(e[:, 1, 1], 0))
        self.assertTrue(np.allclose(e[:, 1, 2], 0))
        self.assertTrue(np.allclose(e[:, 1, 3], 0))
        self.assertTrue(np.allclose(e[:, 1, 4], np.exp(-t)))

        # Sensitivities of  dot(x)  to (p, q, r, x0, y0)
        self.assertTrue(np.allclose(e[:, 2, 0], q))
        self.assertTrue(np.allclose(e[:, 2, 1], p))
        self.assertTrue(np.allclose(e[:, 2, 2], 0))
        self.assertTrue(np.allclose(e[:, 2, 3], 0))
        self.assertTrue(np.allclose(e[:, 2, 4], 0))

        # Sensitivities of  dot(y)  to (p, q, r, x0, y0)
        self.assertTrue(np.allclose(e[:, 3, 0], 2 * np.exp(-t)))
        self.assertTrue(np.allclose(e[:, 3, 1], 0))
        self.assertTrue(np.allclose(e[:, 3, 2], 0))
        self.assertTrue(np.allclose(e[:, 3, 3], 0))
        self.assertTrue(np.allclose(e[:, 3, 4], -np.exp(-t)))

        # Sensitivities of  fx  to (p, q, r, x0, y0)
        self.assertTrue(
            np.allclose(e[:, 4, 0], 2 * p * (q * t)**2 + 2 * q * t * x0))
        self.assertTrue(
            np.allclose(e[:, 4, 1], 2 * q * (p * t)**2 + 2 * p * t * x0))
        self.assertTrue(np.allclose(e[:, 4, 2], 0))
        self.assertTrue(np.allclose(e[:, 4, 3], 2 * p * q * t + 2 * x0))
        self.assertTrue(np.allclose(e[:, 4, 4], 0))

        # Sensitivities of  fy  to (p, q, r, x0, y0)
        self.assertTrue(np.allclose(e[:, 5, 0], 2 * p + 2 * np.exp(-t)))
        self.assertTrue(np.allclose(e[:, 5, 1], 0))
        self.assertTrue(np.allclose(e[:, 5, 2], 1))
        self.assertTrue(np.allclose(e[:, 5, 3], 0))
        self.assertTrue(np.allclose(e[:, 5, 4], -np.exp(-t)))
Exemple #5
0
    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')
        # Derivative
        x = myokit.Derivative(a)
        self.assertEqual(w.ex(x), 'dot(c.a)')
        # Partial derivative
        x = myokit.PartialDerivative(a, a)
        self.assertEqual(w.ex(x), 'diff(c.a, c.a)')
        # Initial value
        x = myokit.InitialValue(a)
        self.assertEqual(w.ex(x), 'init(c.a)')

        # Number
        b = myokit.Number(3)
        self.assertEqual(w.ex(b), '3.0')
        # 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)