def _add_dose_compartment(model, drug_amount, time_unit):
    """
    Adds a dose compartment to the model with a linear absorption rate to
    the connected compartment.
    """
    # Add a dose compartment to the model
    dose_comp = model.add_component_allow_renaming('dose')

    # Create a state variable for the drug amount in the dose compartment
    dose_drug_amount = dose_comp.add_variable('drug_amount')
    dose_drug_amount.set_rhs(0)
    dose_drug_amount.set_unit(drug_amount.unit())
    dose_drug_amount.promote()

    # Create an absorption rate variable
    absorption_rate = dose_comp.add_variable('absorption_rate')
    absorption_rate.set_rhs(1)
    absorption_rate.set_unit(1 / time_unit)

    # Add outflow expression to dose compartment
    dose_drug_amount.set_rhs(
        myokit.Multiply(myokit.PrefixMinus(myokit.Name(absorption_rate)),
                        myokit.Name(dose_drug_amount)))

    # Add inflow expression to connected compartment
    rhs = drug_amount.rhs()
    drug_amount.set_rhs(
        myokit.Plus(
            rhs,
            myokit.Multiply(myokit.Name(absorption_rate),
                            myokit.Name(dose_drug_amount))))

    return dose_drug_amount
def TT_HF_Lu(cell_type = None):
    m = myokit.load_model('tentusscher-2006.mmt')

    # INa: -40%
    ina = m.get('ina.INa')
    ina.set_rhs(myokit.Multiply(myokit.Number(0.6), ina.rhs()))

    # Ito: -36% (not -64% like other papers misquote)
    ito = m.get('ito.ITo')
    ito.set_rhs(myokit.Multiply(myokit.Number(0.64), ito.rhs()))

    # IK1: -20%
    ik1 = m.get('ik1.IK1')
    ik1.set_rhs(myokit.Multiply(myokit.Number(0.8), ik1.rhs()))

    # INaK: -42%
    inak = m.get('inak.INaK')
    inak.set_rhs(myokit.Multiply(myokit.Number(0.58), inak.rhs()))

    # ICab: +53%
    icab = m.get('icab.ICaB')
    icab.set_rhs(myokit.Multiply(myokit.Number(1.53), icab.rhs()))

    # INCX: +65%
    incx = m.get('inaca.INaCa')
    incx.set_rhs(myokit.Multiply(myokit.Number(1.65), incx.rhs()))


    # SR calcium pump current : -45%
    iup = m.get('calcium.i_up')
    iup.set_rhs(myokit.Multiply(myokit.Number(0.55), iup.rhs()))
    ## Can I call this serca?

    # ILeak: 0%
    ileak = m.get('calcium.i_leak')
    ileak.set_rhs(myokit.Multiply(myokit.Number(1.0), ileak.rhs()))


    #Jrel ??: -23%
    jrel = m.get('calcium.i_rel') # Not sure this is the right parameter
    jrel.set_rhs(myokit.Multiply(myokit.Number(0.77), jrel.rhs()))


    #Iks: -50%
    iks = m.get('iks.IKs')
    iks.set_rhs(myokit.Multiply(myokit.Number(0.5), iks.rhs()))

    return(m)
Exemple #3
0
 def _ex_remainder(self, e, t):
     # CellML 1.0 subset doesn't contain remainder
     # Note that this _must_ use the same round-to-neg-inf convention as
     # myokit.Quotient! Implementation below is consistent with Python
     # convention:
     return self.ex(myokit.Minus(
         e[0], myokit.Multiply(e[1], myokit.Quotient(e[0], e[1]))), t)
    def test_arithmetic(self):
        # Test basic arithmetic

        a = myokit.Name(self.avar)
        b = myokit.Number('12', 'pF')
        ca = '<ci>a</ci>'
        cb = ('<cn cellml:units="picofarad">12.0</cn>')

        # Prefix plus
        x = myokit.PrefixPlus(b)
        self.assertWrite(x, '<apply><plus/>' + cb + '</apply>')
        # Prefix minus
        x = myokit.PrefixMinus(b)
        self.assertWrite(x, '<apply><minus/>' + cb + '</apply>')

        # Plus
        x = myokit.Plus(a, b)
        self.assertWrite(x, '<apply><plus/>' + ca + cb + '</apply>')
        # Minus
        x = myokit.Minus(a, b)
        self.assertWrite(x, '<apply><minus/>' + ca + cb + '</apply>')
        # Multiply
        x = myokit.Multiply(a, b)
        self.assertWrite(x, '<apply><times/>' + ca + cb + '</apply>')
        # Divide
        x = myokit.Divide(a, b)
        self.assertWrite(x, '<apply><divide/>' + ca + cb + '</apply>')
Exemple #5
0
 def _ex_remainder(self, e):
     # Note that this _must_ use the same round-to-neg-inf convention as
     # myokit.Quotient.
     # Assuming it follows C and so we need a custom implementation.
     return self.ex(
         myokit.Minus(e[0],
                      myokit.Multiply(e[1], myokit.Quotient(e[0], e[1]))))
Exemple #6
0
def simulate_aps(scales, model_file, beats=2, cl=1000, prepace=100,
        stimulate=True):
    """
        Generate APs using the given scalings.
    """
    # Load model
    model = myokit.load_model(model_file)

    # Apply scalings
    for var in scales:
        scale = scales[var]
        v = model.get(var)
        v.set_rhs(myokit.Multiply(myokit.Number(scale), v.rhs()))

    # Simulate with modified model
    sim = myokit.Simulation(model)

    # Add stimulus
    if stimulate:
        protocol = myokit.pacing.blocktrain(period=cl, duration=1,
                offset=50)
        sim.set_protocol(protocol)
    
    # Pre-pace for some beats
    sim.pre(prepace * cl)

    # Log some beats and return
    log = ['engine.time', 'membrane.V']
    log = ['environment.time', 'membrane.V']
    log = ['membrane.V']
    return sim.run(beats * cl, log=log, log_interval=DT).npview()
    def create_myokit_model(self):
        if self.pk_model is None:
            pk_model = myokit.Model()
        else:
            pk_model = self.create_myokit_dosed_pk_model()
        if self.pd_model is None:
            pd_model = myokit.Model()
        else:
            pd_model = self.pd_model.create_myokit_model()
        have_both_models = (self.pk_model is not None
                            and self.pd_model is not None)
        have_no_models = (self.pk_model is None and self.pd_model is None)

        # use pk model as the base and import the pd model
        pkpd_model = pk_model

        # default model is one with just time
        if have_no_models:
            pkpd_model = myokit.parse_model('''
            [[model]]
            [myokit]
            time = 0 [s] bind time
                in [s]
        ''')

        # remove time binding if
        if have_both_models:
            time_var = pd_model.get('myokit.time')
            time_var.set_binding(None)

        pd_components = list(pd_model.components())
        pd_names = [c.name().replace('myokit', 'PD') for c in pd_components]

        if pd_components:
            pkpd_model.import_component(
                pd_components,
                new_name=pd_names,
            )

        # remove imported time var
        if have_both_models:
            imported_pd_component = pkpd_model.get('PD')
            imported_time = imported_pd_component.get('time')
            imported_pd_component.remove_variable(imported_time)

        # do mappings
        for mapping in self.mappings.all():
            pd_var = pkpd_model.get(
                mapping.pd_variable.qname.replace('myokit', 'PD'))
            pk_var = pkpd_model.get(mapping.pk_variable.qname)

            unit_conversion_multiplier = myokit.Unit.conversion_factor(
                pk_var.unit(), pd_var.unit())
            pd_var.set_rhs(
                myokit.Multiply(myokit.Number(unit_conversion_multiplier),
                                myokit.Name(pk_var)))

        pkpd_model.validate()
        return pkpd_model
def GPB_HF_Moreno(cell_type = None):
    m = myokit.load_model('grandi-2010_modified.mmt')

    # Original grandi model does not contain late channel. Changes from Tenor et al.
    # Late Na+ current. +900%
    inal = m.get('inal.GNal') # GNal multiplied by both junc and sl
    inal.set_rhs(myokit.Multiply(myokit.Number(10.0), inal.rhs()))

    # Transient outward K+ current -36%
    ito = m.get('ito.ito')
    ito.set_rhs(myokit.Multiply(myokit.Number(0.64), ito.rhs()))

    # Inward rectifier K+ current. -25% in HF
    ik1 = m.get('ik1.I_k1')
    ik1.set_rhs(myokit.Multiply(myokit.Number(0.75), ik1.rhs()))

    # Na+/ K+ pump current- Between -42% and -10% in literature. Taken mean:-26%
    inak = m.get('inak.I_nak')
    inak.set_rhs(myokit.Multiply(myokit.Number(0.9), inak.rhs()))

    # Background Na+ current +1600% in moreno (2013) from rabbit HF data
    inab = m.get('inab.GNaB') # Then slow and junc = 0, as both multiplied by GNaB
    inab.set_rhs(myokit.Multiply(myokit.Number(1.0), inab.rhs()))

    # Sarco/ endoplasmic reticulum Ca2+ pump current: -36% in HF human patients
    serca = m.get('caflux.J_serca')
    serca.set_rhs(myokit.Multiply(myokit.Number(0.64), serca.rhs()))

    # SR Ca2+ leak. +350% from rabbit HF data
    leak = m.get('caflux.J_SRleak')
    leak.set_rhs(myokit.Multiply(myokit.Number(4.5), leak.rhs()))

    return(m)
Exemple #9
0
def create_dimless_tumour_growth_model():
    r"""
    Returns a tumour growth myokit model.

    .. math::
        \frac{\text{d}v}{\text{d}\tau} = \frac{a_1 v}
        {v + a_0},

    where the tumour volume :math:`v` and time :math:`\tau` are dimensionless
    and measured in characteristic scales :math:`V^c_T` and :math:`t^c`.
    The model parameters :math:`a_0` and :math:`a_1` are also dimensionless.
    """
    # Instantiate model
    model = Model()

    # Add central compartment
    central_comp = model.add_compartment('central')

    # Add tumour growth variables to central compartment
    volume_t = central_comp.add_variable('volume_t')
    a_0 = central_comp.add_variable('a_0')
    a_1 = central_comp.add_variable('a_1')

    # Bind time
    time = central_comp.add_variable('time')
    time.set_binding('time')

    # Set intial values (some default values) and units
    time.set_rhs(0)

    volume_t.set_rhs(0)
    a_0.set_rhs(1)  # Avoid ZeroDivisionError
    a_1.set_rhs(0)

    # Set units
    time.set_unit('dimensionless')

    volume_t.set_unit('dimensionless')
    a_0.set_unit('dimensionless')
    a_1.set_unit('dimensionless')

    # Set rhs of tumor volume
    # dot(volume_t) =
    #  (a_1 * volume_t) /
    #  (volume_t + a_0)
    volume_t.promote()
    volume_t.set_rhs(
        myokit.Divide(myokit.Multiply(myokit.Name(a_1), myokit.Name(volume_t)),
                      myokit.Plus(myokit.Name(volume_t), myokit.Name(a_0))))

    # Validate model
    model.validate()

    # Check units
    model.check_units()

    return model
Exemple #10
0
    def __init__(self, model, ivar, vvar=None):
        # Clone model
        self._model = model.clone()
        # Get stimulus current variable
        self._ivar = self._model.get(ivar)
        # Get membrane potential variable
        if vvar is None:
            self._vvar = model.label('membrane_potential')
            if self._vvar is None:
                raise ValueError(
                    'This method requires the membrane potential'
                    ' variable to be passed in as `vvar` or indicated in the'
                    ' model using the label `membrane_potential`.')
        else:
            if isinstance(vvar, myokit.Variable):
                vvar = vvar.qname()
            self._vvar = model.get(vvar)
        # Get time variable
        self._tvar = self._model.time()
        del (model, vvar, ivar)
        # Unbind any existing pace variable
        var = self._model.binding('pace')
        if var is not None:
            var.set_binding(None)
        # Create new pacing variable
        c = self._ivar.parent(myokit.Component)

        def add_variable(c, name):
            try:
                return c.add_variable(name)
            except myokit.DuplicateName:
                i = 2
                n = name + str(i)
                while True:
                    try:
                        return c.add_variable(n)
                    except myokit.DuplicateName:
                        i += 1
                        n = name + str(i)

        self._pvar = add_variable(c, 'pace')
        self._pvar.set_binding('pace')
        self._pvar.set_rhs(0)
        # Create new amplitude variable
        self._avar = add_variable(c, 'amplitude')
        self._avar.set_rhs(0)
        # Set rhs of current variable
        if self._ivar.is_state():
            self._ivar.demote()
        self._ivar.set_rhs(myokit.Multiply(self._pvar.lhs(), self._avar.lhs()))
        # Set default parameters
        self.set_currents()
        self.set_precision()
        self.set_threshold()
        self.set_times()
        # No data yet!
        self._data = None
Exemple #11
0
    def test_simulation_error_2(self):
        # Test for simulation error detection: failure occurred too often.

        # Cvode error (test failure occurred too many times)
        m = self.model.clone()
        v = m.get('membrane.V')
        v.set_rhs(myokit.Multiply(v.rhs(), myokit.Number(1e18)))
        s = myokit.LegacySimulation(m, self.protocol)
        with WarningCollector():
            self.assertRaisesRegex(
                myokit.SimulationError, 'numerical error', s.run, 5000)
Exemple #12
0
    def test_simulation_error_2(self):
        # Test for simulation error detection: failure occurred too often.

        # Cvode error (test failure occurred too many times)
        m = self.model.clone()
        v = m.get('membrane.V')
        v.set_rhs(myokit.Multiply(v.rhs(), myokit.Number(1e18)))
        s = myokit.Simulation(m, self.protocol)
        with self.assertRaises(myokit.SimulationError) as e:
            s.run(5000)
        self.assertIn('CV_ERR_FAILURE', str(e.exception))
    def test_arithmetic_binary(self):
        # Tests parsing prefix operators

        # Plus
        a = myokit.Name('a')
        b = myokit.Number(1.0)
        e = myokit.Plus(a, b)
        x = '<apply><plus/><ci>a</ci><cn>1.0</cn></apply>'
        self.assertEqual(self.p(x), e)

        # Minus
        e = myokit.Minus(a, b)
        x = '<apply><minus/><ci>a</ci><cn>1.0</cn></apply>'
        self.assertEqual(self.p(x), e)

        # Multiply
        e = myokit.Multiply(a, b)
        x = '<apply><times/><ci>a</ci><cn>1.0</cn></apply>'
        self.assertEqual(self.p(x), e)

        # Divide
        e = myokit.Divide(a, b)
        x = '<apply><divide/><ci>a</ci><cn>1.0</cn></apply>'
        self.assertEqual(self.p(x), e)

        # No operands
        self.assertRaisesRegex(
            mathml.MathMLError, 'at least one operand', self.p,
            '<apply><times/></apply>')

        # Only one operand
        self.assertRaisesRegex(
            mathml.MathMLError, 'at least two operands', self.p,
            '<apply><times/><cn>1.0</cn></apply>')

        # Several operands
        e = myokit.Multiply(
            myokit.Multiply(myokit.Multiply(a, b), myokit.Number(2)),
            myokit.Number(3))
        x = '<apply><times/><ci>a</ci><cn>1</cn><cn>2</cn><cn>3</cn></apply>'
        self.assertEqual(self.p(x), e)
Exemple #14
0
    def test_unused_and_cycles(self):
        # Test unused variable and cycle detection.

        m = myokit.Model('LotkaVolterra')
        c0 = m.add_component('c0')
        t = c0.add_variable('time')
        t.set_rhs(myokit.Number(0))
        t.set_binding('time')
        c1 = m.add_component('c1')
        m.add_component('c2')
        c1_a = c1.add_variable('a')
        c1_b = c1.add_variable('b')
        c1_a.promote(1.0)
        c1_a.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Number(0.5)))
        c1_b.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Number(1.0)))
        # b is unused, test if found
        m.validate()
        w = m.warnings()
        self.assertEqual(len(w), 1)
        self.assertEqual(type(w[0]), myokit.UnusedVariableError)
        # b is used by c, c is unused, test if found
        c1_c = c1.add_variable('c')
        c1_c.set_rhs(myokit.Name(c1_b))
        m.validate()
        w = m.warnings()
        self.assertEqual(len(w), 2)
        self.assertEqual(type(w[0]), myokit.UnusedVariableError)
        self.assertEqual(type(w[1]), myokit.UnusedVariableError)
        # Test 1:1 cycle
        c1_b.set_rhs(myokit.Name(c1_b))
        self.assertRaises(myokit.CyclicalDependencyError, m.validate)
        # Test longer cycles
        c1_b.set_rhs(myokit.Multiply(myokit.Number(10), myokit.Name(c1_c)))
        self.assertRaises(myokit.CyclicalDependencyError, m.validate)
        # Reset
        c1_b.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Number(1.0)))
        m.validate()
        # Test cycle involving state variable
        c1_a.set_rhs(myokit.Name(c1_b))
        m.validate()
        c1_b.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Name(c1_b)))
        self.assertRaises(myokit.CyclicalDependencyError, m.validate)
        c1_b.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Name(c1_c)))
        c1_c.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Number(3)))
        m.validate()
        w = m.warnings()
        self.assertEqual(len(w), 0)
        c1_c.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Name(c1_b)))
        self.assertRaises(myokit.CyclicalDependencyError, m.validate)
    def test_arithmetic_binary(self):
        # Tests writing basic arithmetic operators

        a = myokit.Name(self.avar)
        b = myokit.Number('12', 'pF')
        ca = '<mi>c.a</mi>'
        cb = '<mn>12.0</mn>'

        # Plus
        x = myokit.Plus(a, b)
        self.assertWrite(x, '<mrow>' + ca + '<mo>+</mo>' + cb + '</mrow>')

        # Minus
        x = myokit.Minus(a, b)
        self.assertWrite(x, '<mrow>' + ca + '<mo>-</mo>' + cb + '</mrow>')

        # Multiply
        x = myokit.Multiply(a, b)
        self.assertWrite(x, '<mrow>' + ca + '<mo>*</mo>' + cb + '</mrow>')

        # Divide
        x = myokit.Divide(a, b)
        self.assertWrite(x, '<mfrac>' + ca + cb + '</mfrac>')
    def test_default_state_sensitivites(self):
        # Test :meth:`Simulation.default_state_sensitivies`

        # Create bolus infusion model with linear clearance
        model = myokit.Model()
        comp = model.add_component('myokit')

        amount = comp.add_variable('amount')
        time = comp.add_variable('time')
        dose_rate = comp.add_variable('dose_rate')
        elimination_rate = comp.add_variable('elimination_rate')

        time.set_binding('time')
        dose_rate.set_binding('pace')

        amount.promote(10)
        amount.set_rhs(
            myokit.Minus(
                myokit.Name(dose_rate),
                myokit.Multiply(myokit.Name(elimination_rate),
                                myokit.Name(amount))))
        elimination_rate.set_rhs(myokit.Number(1))
        time.set_rhs(myokit.Number(0))
        dose_rate.set_rhs(myokit.Number(0))

        # Check no sensitivities set
        sim = myokit.Simulation(model)
        self.assertIsNone(sim.default_state_sensitivities())

        # Check for set sensitvities
        sensitivities = (['myokit.amount'],
                         ['init(myokit.amount)', 'myokit.elimination_rate'])
        sim = myokit.Simulation(model, sensitivities=sensitivities)
        s = sim.default_state_sensitivities()
        self.assertEqual(len(s), 2)
        self.assertEqual(s[0][0], 1)
        self.assertEqual(s[1][0], 0)
    def test_arithmetic_binary(self):
        # Tests writing basic arithmetic operators

        # Plus
        a = myokit.Name('a')
        b = myokit.Number(1)
        e = myokit.Plus(a, b)
        x = '<apply><plus/><ci>a</ci><cn>1.0</cn></apply>'
        self.assertWrite(e, x)

        # Minus
        e = myokit.Minus(a, b)
        x = '<apply><minus/><ci>a</ci><cn>1.0</cn></apply>'
        self.assertWrite(e, x)

        # Multiply
        e = myokit.Multiply(a, b)
        x = '<apply><times/><ci>a</ci><cn>1.0</cn></apply>'
        self.assertWrite(e, x)

        # Divide
        e = myokit.Divide(a, b)
        x = '<apply><divide/><ci>a</ci><cn>1.0</cn></apply>'
        self.assertWrite(e, x)
Exemple #18
0
    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)
Exemple #19
0
    def _parse_apply(self, apply_element):
        """
        Parses an ``<apply>`` element.
        """
        # Apply must have kids
        if len(apply_element) == 0:
            raise MathMLError('Apply must contain at least one child element.',
                              apply_element)

        # Get first child
        iterator = iter(apply_element)
        element = self._next(iterator)

        # Decide what to do based on first child
        _, name = split(element.tag)

        # Handle derivative
        if name == 'diff':
            return self._parse_derivative(element, iterator)

        # Algebra (unary/binary/n-ary operators)
        elif name == 'plus':
            return self._parse_nary(element, iterator, myokit.Plus,
                                    myokit.PrefixPlus)
        elif name == 'minus':
            return self._parse_nary(element, iterator, myokit.Minus,
                                    myokit.PrefixMinus)
        elif name == 'times':
            return self._parse_nary(element, iterator, myokit.Multiply)
        elif name == 'divide':
            return self._parse_nary(element, iterator, myokit.Divide)

        # Basic functions
        elif name == 'exp':
            return myokit.Exp(*self._eat(element, iterator))
        elif name == 'ln':
            return myokit.Log(*self._eat(element, iterator))
        elif name == 'log':
            return self._parse_log(element, iterator)
        elif name == 'root':
            return self._parse_root(element, iterator)
        elif name == 'power':
            return myokit.Power(*self._eat(element, iterator, 2))
        elif name == 'floor':
            return myokit.Floor(*self._eat(element, iterator))
        elif name == 'ceiling':
            return myokit.Ceil(*self._eat(element, iterator))
        elif name == 'abs':
            return myokit.Abs(*self._eat(element, iterator))
        elif name == 'quotient':
            return myokit.Quotient(*self._eat(element, iterator, 2))
        elif name == 'rem':
            return myokit.Remainder(*self._eat(element, iterator, 2))

        # Logic
        elif name == 'and':
            return self._parse_nary(element, iterator, myokit.And)
        elif name == 'or':
            return self._parse_nary(element, iterator, myokit.Or)
        elif name == 'xor':
            # Becomes ``(x or y) and not(x and y)``
            x, y = self._eat(element, iterator, 2)
            return myokit.And(myokit.Or(x, y), myokit.Not(myokit.And(x, y)))

        elif name == 'not':
            return myokit.Not(*self._eat(element, iterator))
        elif name == 'eq' or name == 'equivalent':
            return myokit.Equal(*self._eat(element, iterator, 2))
        elif name == 'neq':
            return myokit.NotEqual(*self._eat(element, iterator, 2))
        elif name == 'gt':
            return myokit.More(*self._eat(element, iterator, 2))
        elif name == 'lt':
            return myokit.Less(*self._eat(element, iterator, 2))
        elif name == 'geq':
            return myokit.MoreEqual(*self._eat(element, iterator, 2))
        elif name == 'leq':
            return myokit.LessEqual(*self._eat(element, iterator, 2))

        # Trigonometry
        elif name == 'sin':
            return myokit.Sin(*self._eat(element, iterator))
        elif name == 'cos':
            return myokit.Cos(*self._eat(element, iterator))
        elif name == 'tan':
            return myokit.Tan(*self._eat(element, iterator))
        elif name == 'arcsin':
            return myokit.ASin(*self._eat(element, iterator))
        elif name == 'arccos':
            return myokit.ACos(*self._eat(element, iterator))
        elif name == 'arctan':
            return myokit.ATan(*self._eat(element, iterator))

        # Redundant trigonometry (CellML includes this)
        elif name == 'csc':
            # Cosecant: csc(x) = 1 / sin(x)
            return myokit.Divide(self._const(1),
                                 myokit.Sin(*self._eat(element, iterator)))
        elif name == 'sec':
            # Secant: sec(x) = 1 / cos(x)
            return myokit.Divide(self._const(1),
                                 myokit.Cos(*self._eat(element, iterator)))
        elif name == 'cot':
            # Contangent: cot(x) = 1 / tan(x)
            return myokit.Divide(self._const(1),
                                 myokit.Tan(*self._eat(element, iterator)))
        elif name == 'arccsc':
            # ArcCosecant: acsc(x) = asin(1/x)
            return myokit.ASin(
                myokit.Divide(self._const(1), *self._eat(element, iterator)))
        elif name == 'arcsec':
            # ArcSecant: asec(x) = acos(1/x)
            return myokit.ACos(
                myokit.Divide(self._const(1), *self._eat(element, iterator)))
        elif name == 'arccot':
            # ArcCotangent: acot(x) = atan(1/x)
            return myokit.ATan(
                myokit.Divide(self._const(1), *self._eat(element, iterator)))

        # Hyperbolic trig
        elif name == 'sinh':
            # Hyperbolic sine: sinh(x) = 0.5 * (e^x - e^-x)
            x = self._eat(element, iterator)[0]
            return myokit.Multiply(
                self._const(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 = self._eat(element, iterator)[0]
            return myokit.Multiply(
                self._const(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 = self._eat(element, iterator)[0]
            e2x = myokit.Exp(myokit.Multiply(self._const(2), x))
            return myokit.Divide(myokit.Minus(e2x, self._const(1)),
                                 myokit.Plus(e2x, self._const(1)))
        elif name == 'arcsinh':
            # Inverse hyperbolic sine: asinh(x) = log(x + sqrt(x*x + 1))
            x = self._eat(element, iterator)[0]
            return myokit.Log(
                myokit.Plus(
                    x,
                    myokit.Sqrt(
                        myokit.Plus(myokit.Multiply(x, x), self._const(1)))))
        elif name == 'arccosh':
            # Inverse hyperbolic cosine:
            #   acosh(x) = log(x + sqrt(x*x - 1))
            x = self._eat(element, iterator)[0]
            return myokit.Log(
                myokit.Plus(
                    x,
                    myokit.Sqrt(
                        myokit.Minus(myokit.Multiply(x, x), self._const(1)))))
        elif name == 'arctanh':
            # Inverse hyperbolic tangent:
            #   atanh(x) = 0.5 * log((1 + x) / (1 - x))
            x = self._eat(element, iterator)[0]
            return myokit.Multiply(
                self._const(0.5),
                myokit.Log(
                    myokit.Divide(myokit.Plus(self._const(1), x),
                                  myokit.Minus(self._const(1), x))))

        # Hyperbolic redundant trig
        elif name == 'csch':
            # Hyperbolic cosecant: csch(x) = 2 / (exp(x) - exp(-x))
            x = self._eat(element, iterator)[0]
            return myokit.Divide(
                self._const(2),
                myokit.Minus(myokit.Exp(x), myokit.Exp(myokit.PrefixMinus(x))))
        elif name == 'sech':
            # Hyperbolic secant: sech(x) = 2 / (exp(x) + exp(-x))
            x = self._eat(element, iterator)[0]
            return myokit.Divide(
                self._const(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 = self._eat(element, iterator)[0]
            e2x = myokit.Exp(myokit.Multiply(self._const(2), x))
            return myokit.Divide(myokit.Plus(e2x, self._const(1)),
                                 myokit.Minus(e2x, self._const(1)))
        elif name == 'arccsch':
            # Inverse hyperbolic cosecant:
            #   arccsch(x) = log(1 / x + sqrt(1 / x^2 + 1))
            x = self._eat(element, iterator)[0]
            return myokit.Log(
                myokit.Plus(
                    myokit.Divide(self._const(1), x),
                    myokit.Sqrt(
                        myokit.Plus(
                            myokit.Divide(self._const(1),
                                          myokit.Multiply(x, x)),
                            self._const(1)))))
        elif name == 'arcsech':
            # Inverse hyperbolic secant:
            #   arcsech(x) = log(1 / x + sqrt(1 / x^2 - 1))
            x = self._eat(element, iterator)[0]
            return myokit.Log(
                myokit.Plus(
                    myokit.Divide(self._const(1), x),
                    myokit.Sqrt(
                        myokit.Minus(
                            myokit.Divide(self._const(1),
                                          myokit.Multiply(x, x)),
                            self._const(1)))))
        elif name == 'arccoth':
            # Inverse hyperbolic cotangent:
            #   arccoth(x) = 0.5 * log((x + 1) / (x - 1))
            x = self._eat(element, iterator)[0]
            return myokit.Multiply(
                self._const(0.5),
                myokit.Log(
                    myokit.Divide(myokit.Plus(x, self._const(1)),
                                  myokit.Minus(x, self._const(1)))))

        # Last option: A single atomic inside an apply
        # Do this one last to stop e.g. <apply><times /></apply> returning the
        # error 'Unsupported element' (which is what parse_atomic would call).
        elif len(apply_element) == 1:
            return self._parse_atomic(element)

        # Unexpected element
        else:
            raise MathMLError(
                'Unsupported element in apply: ' + str(element.tag) + '.',
                element)
Exemple #20
0
def convert_hh_states_to_inf_tau_form(model, v=None):
    """
    Scans a :class:`myokit.Model` for Hodgkin-Huxley style states written in
    "alpha-beta form", and converts them to "inf-tau form".

    For any state ``x`` written in the form
    ``dot(x) = alpha * (1 - x) - beta * x`` this method will calculate the
    steady state and time constant, and add variables for both. Next, the state
    RHS will be replaced by an expression of the form
    ``dot(x) = (x_inf - x) / tau_x``, where ``x_inf`` and ``tau_x`` are the
    new variables.

    See also: :meth:`get_alpha_and_beta()`.

    Arguments:

    ``model``
        A :class:`myokit.Model` object to convert.
    ``v``
        An optional :class:`myokit.Variable`` representing the membrane
        potential. If not given, the method will search for a variable labelled
        ``membrane_potential``. An error is raised if no membrane potential
        variable can be found.

    Returns an updated copy of the given model.
    """
    # Clone the model before editing
    if not isinstance(model, myokit.Model):
        raise ValueError('Given `model` must be a myokit.Model.')
    model = model.clone()

    # Check membrane potential variable is known.
    if v is None:
        v = model.label('membrane_potential')
        if v is None:
            raise ValueError(
                'Membrane potential must be given as `v` or by setting the'
                ' label `membrane_potential` in the model.')
    else:
        # Ensure v is a variable, and from the cloned model
        if isinstance(v, myokit.Variable):
            v = v.qname()
        v = model.get(v)

    # Loop over states
    # - If they're in alpha-beta form, add new (nested) variables inf and tau
    for x in model.states():
        res = get_alpha_and_beta(x, v)
        if res is not None:
            # Create variabless for inf and tau
            a = myokit.Name(res[0])
            b = myokit.Name(res[1])
            tau = x.add_variable_allow_renaming('tau')
            tau.set_rhs(myokit.Divide(myokit.Number(1), myokit.Plus(a, b)))
            inf = x.add_variable_allow_renaming('inf')
            inf.set_rhs(myokit.Multiply(a, myokit.Name(tau)))

            # Update RHS expression for state
            x.set_rhs(
                myokit.Divide(myokit.Minus(myokit.Name(inf), myokit.Name(x)),
                              myokit.Name(tau)))

    return model
Exemple #21
0
def get_rl_expression(x, dt, v=None):
    """
    For states ``x`` with RHS expressions written in the "inf-tau form" (see
    :meth:`has_inf_tau_form`) this returns a Rush-Larsen (RL) update expression
    of the form ``x_inf + (x - x_inf) * exp(-(dt / tau_x))``.

    If the state does not have the "inf-tau form", ``None`` is returned.

    Arguments:

    ``x``
        A state variable for which to return the RL update expression.
    ``dt``
        A :class:`myokit.Name` expression to use for the time step in the RL
        expression.
    ``v``
        An optional variable representing the membrane potential.

    Returns a :class:`myokit.Expression` if succesful, or ``None`` if not.

    Example:

        # Load a Myokit model
        model = myokit.load_model('example')
        v = model.get('membrane.V')

        # Get a copy where all HH-state variables are written in inf-tau form
        model = myokit.lib.hh.convert_hh_states_to_inf_tau_form(model, v)

        # Create an expression for the time step
        dt = myokit.Name('dt')

        # Show an RL-update for the variable ina.h
        h = model.get('ina.h')
        e = myokit.lib.hh.get_rl_expression(h, dt, v)
        print('h[t + dt] = ' + str(e))

    See:

    [1] Rush, Larsen (1978) A Practical Algorithm for Solving Dynamic Membrane
    Equations. IEEE Transactions on Biomedical Engineering,
    https://doi.org/10.1109/TBME.1978.326270

    [2] Marsh, Ziaratgahi, Spiteri (2012) The secrets to the success of the
    Rush-Larsen method and its generalization. IEEE Transactions on Biomedical
    Engineering, https://doi.org/10.1109/TBME.2012.2205575

    """
    # Test dt is an expression
    if not isinstance(dt, myokit.Expression):
        raise ValueError('Argument `dt` must be a myokit.Expression.')

    # Get x_inf and tau_x, if possible
    res = get_inf_and_tau(x, v)
    if res is None:
        return None

    # Create expression for RL update
    # x_inf + (x - x_inf) * exp(-(dt / tau_x))
    x = myokit.Name(x)
    x_inf = myokit.Name(res[0])
    tau_x = myokit.Name(res[1])
    return myokit.Plus(
        x_inf,
        myokit.Multiply(
            myokit.Minus(x, x_inf),
            myokit.Exp(myokit.PrefixMinus(myokit.Divide(dt, tau_x)))))
Exemple #22
0
 def _ex_multiply(self, e):
     from sympy.core.numbers import NegativeOne
     a, b = e.as_two_terms()
     if type(a) == NegativeOne:
         return myokit.PrefixMinus(self.ex(b))
     return myokit.Multiply(self.ex(a), self.ex(b))
Exemple #23
0
 def _ex_remainder(self, e):
     return self.ex(
         myokit.Minus(e[0],
                      myokit.Multiply(e[1], myokit.Quotient(e[0], e[1]))))
Exemple #24
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')
        # 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)
Exemple #25
0
    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)
Exemple #26
0
    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)
Exemple #27
0
    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)
Exemple #28
0
    def test_basic(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')

        # Name
        self.assertEqual(ws.ex(a), 'c.a')
        self.assertEqual(wd.ex(a), 'c.a')
        self.assertEqual(wn.ex(a), 'c.a')

        # Number with unit
        self.assertEqual(ws.ex(b), '12.0f')
        self.assertEqual(wd.ex(b), '12.0')
        self.assertEqual(wn.ex(b), '12.0f')

        # Prefix plus
        x = myokit.PrefixPlus(b)
        self.assertEqual(ws.ex(x), '12.0f')
        self.assertEqual(wd.ex(x), '12.0')
        self.assertEqual(wn.ex(x), '12.0f')
        # Prefix minus
        x = myokit.PrefixMinus(b)
        self.assertEqual(ws.ex(x), '(-12.0f)')
        self.assertEqual(wd.ex(x), '(-12.0)')
        self.assertEqual(wn.ex(x), '(-12.0f)')

        # Plus
        x = myokit.Plus(a, b)
        self.assertEqual(ws.ex(x), 'c.a + 12.0f')
        self.assertEqual(wd.ex(x), 'c.a + 12.0')
        self.assertEqual(wn.ex(x), 'c.a + 12.0f')
        # Minus
        x = myokit.Minus(a, b)
        self.assertEqual(ws.ex(x), 'c.a - 12.0f')
        self.assertEqual(wd.ex(x), 'c.a - 12.0')
        self.assertEqual(wn.ex(x), 'c.a - 12.0f')
        # Multiply
        x = myokit.Multiply(a, b)
        self.assertEqual(ws.ex(x), 'c.a * 12.0f')
        self.assertEqual(wd.ex(x), 'c.a * 12.0')
        self.assertEqual(wn.ex(x), 'c.a * 12.0f')
        # Divide
        x = myokit.Divide(a, b)
        self.assertEqual(ws.ex(x), 'c.a / 12.0f')
        self.assertEqual(wd.ex(x), 'c.a / 12.0')
        self.assertEqual(wn.ex(x), 'c.a / 12.0f')

        # Quotient
        x = myokit.Quotient(a, b)
        self.assertEqual(ws.ex(x), 'floor(c.a / 12.0f)')
        self.assertEqual(wd.ex(x), 'floor(c.a / 12.0)')
        self.assertEqual(wn.ex(x), 'floor(c.a / 12.0f)')
        # Remainder
        x = myokit.Remainder(a, b)
        self.assertEqual(ws.ex(x), 'c.a - 12.0f * (floor(c.a / 12.0f))')
        self.assertEqual(wd.ex(x), 'c.a - 12.0 * (floor(c.a / 12.0))')
        self.assertEqual(wn.ex(x), 'c.a - 12.0f * (floor(c.a / 12.0f))')
    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)
Exemple #30
0
    def model(self, E=-88, g=1, component='ikr', current='IKr'):
        m = myokit.Model()

        # Add time variable
        ce = m.add_component('engine')
        t = ce.add_variable('time')
        t.set_rhs(0)
        t.set_binding('time')

        p = ce.add_variable('pace')
        p.set_rhs(0)
        p.set_binding('pace')

        # Add membrane potential variable
        cm = m.add_component('membrane')
        v = cm.add_variable('V')
        v.set_rhs(-80)#-80
        v.set_label('membrane_potential')

        # Add current component
        cc = m.add_component(component)
        cc.add_alias('V', v)

        # Add parameters
        i = 0
        pvars = []
        for rate in self.rates:
            i += 1
            p = cc.add_variable('p' + str(i))
            p.set_rhs(rate.alpha)
            pvars.append(p)
            i += 1
            p = cc.add_variable('p' + str(i))
            p.set_rhs(rate.beta)
            pvars.append(p)
        i += 1
        p = cc.add_variable('p' + str(i))
        p.set_rhs(myokit.Number(g))
        pvars.append(p)

        # Add rates, store in map from names to variables
        rvars = {}
        for i, rate in enumerate(self.rates):
            var = cc.add_variable(rate.name)
            rvars[rate.name] = var

            # Generate term a * exp(+-b * V)
            alpha = myokit.Name(pvars[2 * i])
            beta = myokit.Name(pvars[1 + 2 * i])
            if not rate.positive:
                beta = myokit.PrefixMinus(beta)
            var.set_rhs(myokit.Multiply(
                alpha, myokit.Exp(myokit.Multiply(beta, myokit.Name(v)))))

        # Add reversal potential variable
        e = cc.add_variable('E')
        e.set_rhs(myokit.Number(E))

        # Add maximum conductance variable
        g = cc.add_variable('g')
        g.set_rhs(myokit.Name(p))

        # Create states, store in map from names to variables
        svars = {}
        for i, state in enumerate(self.states):
            var = cc.add_variable(state.name)
            if i==0: # Add initial condition of all probability in first state.
                var.promote(1)
            else:
                var.promote(0)
            svars[state.name] = var

        # Set equations for states
        for state in self.states:

            incoming = []
            outgoing = []
            for edge in self.edges:
                # Gather info
                if state == edge.state_from:
                    # Edge from this state to other state
                    state2 = edge.state_to
                    rate_in = edge.backward_rate
                    rate_out = edge.forward_rate
                    mult_in = edge.backward_multiplier
                    mult_out = edge.forward_multiplier
                elif state == edge.state_to:
                    # Edge from other state to this state
                    state2 = edge.state_from
                    rate_in = edge.forward_rate
                    rate_out = edge.backward_rate
                    mult_in = edge.forward_multiplier
                    mult_out = edge.backward_multiplier
                else:
                    continue

                # Add incoming term
                term = myokit.Name(svars[state2.name])
                term = myokit.Multiply(
                    myokit.Name(rvars[rate_in.name]), term)
                if mult_in != 1:
                    term = myokit.Multiply(myokit.Number(mult_in), term)
                incoming.append(term)

                # Add outgoing term
                term = myokit.Name(rvars[rate_out.name])
                if mult_out != 1:
                    term = myokit.Multiply(myokit.Number(mult_out), term)
                outgoing.append(term)

            # Start with outgoing terms (grouped)
            outgoing = iter(outgoing)
            rhs = next(outgoing)
            for term in outgoing:
                rhs = myokit.Plus(rhs, term)
            rhs = myokit.PrefixMinus(rhs)
            rhs = myokit.Multiply(rhs, myokit.Name(svars[state.name]))

            # Add incoming terms (one by one)
            incoming = iter(incoming)
            for term in incoming:
                rhs = myokit.Plus(rhs, term)

            # Set rhs
            svars[state.name].set_rhs(rhs)

        # Add current variable
        var = cc.add_variable(current)
        rhs = myokit.Name(g)
        for state in self.states:
            if state.conducting:
                rhs = myokit.Multiply(rhs, myokit.Name(svars[state.name]))
        rhs = myokit.Multiply(
            rhs, myokit.Minus(myokit.Name(v), myokit.Name(e)))
        var.set_rhs(rhs)

        print(m.code())
        return m