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)
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>')
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]))))
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)
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
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
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)
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)
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)
def test_all(self): w = myokit.formats.latex.LatexExpressionWriter() model = myokit.Model() component = model.add_component('c') avar = component.add_variable('a') # Model needs to be validated --> sets unames avar.set_rhs(12) avar.set_binding('time') model.validate() # Name a = myokit.Name(avar) self.assertEqual(w.ex(a), '\\text{a}') # Number with unit b = myokit.Number('12', 'pF') self.assertEqual(w.ex(b), '12.0') # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(w.ex(x), '12.0') # Prefix minus x = myokit.PrefixMinus(b) self.assertEqual(w.ex(x), '\\left(-12.0\\right)') # Prefix minus with bracket x = myokit.PrefixMinus(myokit.Plus(a, b)) self.assertEqual(w.ex(x), '\\left(-\\left(\\text{a}+12.0\\right)\\right)') # Plus x = myokit.Plus(a, b) self.assertEqual(w.ex(x), '\\text{a}+12.0') # Minus x = myokit.Minus(a, b) self.assertEqual(w.ex(x), '\\text{a}-12.0') # Multiply x = myokit.Multiply(a, b) self.assertEqual(w.ex(x), '\\text{a}*12.0') # Divide x = myokit.Divide(a, b) self.assertEqual(w.ex(x), '\\frac{\\text{a}}{12.0}') # Quotient # Not supported in latex! x = myokit.Quotient(a, b) self.assertEqual( w.ex(x), '\\left\\lfloor\\frac{\\text{a}}{12.0}\\right\\rfloor') # Remainder x = myokit.Remainder(a, b) self.assertEqual(w.ex(x), '\\bmod\\left(\\text{a},12.0\\right)') # Power x = myokit.Power(a, b) self.assertEqual(w.ex(x), '\\text{a}^{12.0}') # Power with brackets x = myokit.Power(myokit.Plus(a, b), b) self.assertEqual(w.ex(x), '\\left(\\text{a}+12.0\\right)^{12.0}') # Sqrt x = myokit.Sqrt(b) self.assertEqual(w.ex(x), '\\sqrt{12.0}') # Exp x = myokit.Exp(a) self.assertEqual(w.ex(x), '\\exp\\left(\\text{a}\\right)') # Log(a) x = myokit.Log(b) self.assertEqual(w.ex(x), '\\log\\left(12.0\\right)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(w.ex(x), '\\log_{12.0}\\left(\\text{a}\\right)') # Log10 x = myokit.Log10(b) self.assertEqual(w.ex(x), '\\log_{10.0}\\left(12.0\\right)') # Sin x = myokit.Sin(b) self.assertEqual(w.ex(x), '\\sin\\left(12.0\\right)') # Cos x = myokit.Cos(b) self.assertEqual(w.ex(x), '\\cos\\left(12.0\\right)') # Tan x = myokit.Tan(b) self.assertEqual(w.ex(x), '\\tan\\left(12.0\\right)') # ASin x = myokit.ASin(b) self.assertEqual(w.ex(x), '\\arcsin\\left(12.0\\right)') # ACos x = myokit.ACos(b) self.assertEqual(w.ex(x), '\\arccos\\left(12.0\\right)') # ATan x = myokit.ATan(b) self.assertEqual(w.ex(x), '\\arctan\\left(12.0\\right)') # Floor x = myokit.Floor(b) self.assertEqual(w.ex(x), '\\left\\lfloor{12.0}\\right\\rfloor') # Ceil x = myokit.Ceil(b) self.assertEqual(w.ex(x), '\\left\\lceil{12.0}\\right\\rceil') # Abs x = myokit.Abs(b) self.assertEqual(w.ex(x), '\\lvert{12.0}\\rvert') # Equal x = myokit.Equal(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}=12.0\\right)') # NotEqual x = myokit.NotEqual(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}\\neq12.0\\right)') # More x = myokit.More(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}>12.0\\right)') # Less x = myokit.Less(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}<12.0\\right)') # MoreEqual x = myokit.MoreEqual(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}\\geq12.0\\right)') # LessEqual x = myokit.LessEqual(a, b) self.assertEqual(w.ex(x), '\\left(\\text{a}\\leq12.0\\right)') # Not cond1 = myokit.parse_expression('5 > 3') cond2 = myokit.parse_expression('2 < 1') x = myokit.Not(cond1) self.assertEqual(w.ex(x), '\\not\\left(\\left(5.0>3.0\\right)\\right)') # And x = myokit.And(cond1, cond2) self.assertEqual( w.ex(x), '\\left(\\left(5.0>3.0\\right)\\and' '\\left(2.0<1.0\\right)\\right)') # Or x = myokit.Or(cond1, cond2) self.assertEqual( w.ex(x), '\\left(\\left(5.0>3.0\\right)\\or' '\\left(2.0<1.0\\right)\\right)') # If x = myokit.If(cond1, a, b) self.assertEqual( w.ex(x), 'if\\left(\\left(5.0>3.0\\right),\\text{a},12.0\\right)') # Piecewise c = myokit.Number(1) x = myokit.Piecewise(cond1, a, cond2, b, c) self.assertEqual( w.ex(x), 'piecewise\\left(\\left(5.0>3.0\\right),\\text{a},' '\\left(2.0<1.0\\right),12.0,1.0\\right)') # Test fetching using ewriter method w = myokit.formats.ewriter('latex') self.assertIsInstance(w, myokit.formats.latex.LatexExpressionWriter) # Test without a Myokit expression self.assertRaisesRegex(ValueError, 'Unknown expression type', w.ex, 7)
def _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)
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
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)))))
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))
def _ex_remainder(self, e): return self.ex( myokit.Minus(e[0], myokit.Multiply(e[1], myokit.Quotient(e[0], e[1]))))
def test_all(self): w = myokit.formats.python.PythonExpressionWriter() model = myokit.Model() component = model.add_component('c') avar = component.add_variable('a') # Name a = myokit.Name(avar) self.assertEqual(w.ex(a), 'c.a') # Number with unit b = myokit.Number('12', 'pF') self.assertEqual(w.ex(b), '12.0') # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(w.ex(x), '12.0') # Prefix minus x = myokit.PrefixMinus(b) self.assertEqual(w.ex(x), '(-12.0)') # Plus x = myokit.Plus(a, b) self.assertEqual(w.ex(x), 'c.a + 12.0') # Minus x = myokit.Minus(a, b) self.assertEqual(w.ex(x), 'c.a - 12.0') # Multiply x = myokit.Multiply(a, b) self.assertEqual(w.ex(x), 'c.a * 12.0') # Divide x = myokit.Divide(a, b) self.assertEqual(w.ex(x), 'c.a / 12.0') # Quotient x = myokit.Quotient(a, b) self.assertEqual(w.ex(x), 'c.a // 12.0') # Remainder x = myokit.Remainder(a, b) self.assertEqual(w.ex(x), 'c.a % 12.0') # Power x = myokit.Power(a, b) self.assertEqual(w.ex(x), 'c.a ** 12.0') # Sqrt x = myokit.Sqrt(b) self.assertEqual(w.ex(x), 'math.sqrt(12.0)') # Exp x = myokit.Exp(a) self.assertEqual(w.ex(x), 'math.exp(c.a)') # Log(a) x = myokit.Log(b) self.assertEqual(w.ex(x), 'math.log(12.0)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(w.ex(x), 'math.log(c.a, 12.0)') # Log10 x = myokit.Log10(b) self.assertEqual(w.ex(x), 'math.log10(12.0)') # Sin x = myokit.Sin(b) self.assertEqual(w.ex(x), 'math.sin(12.0)') # Cos x = myokit.Cos(b) self.assertEqual(w.ex(x), 'math.cos(12.0)') # Tan x = myokit.Tan(b) self.assertEqual(w.ex(x), 'math.tan(12.0)') # ASin x = myokit.ASin(b) self.assertEqual(w.ex(x), 'math.asin(12.0)') # ACos x = myokit.ACos(b) self.assertEqual(w.ex(x), 'math.acos(12.0)') # ATan x = myokit.ATan(b) self.assertEqual(w.ex(x), 'math.atan(12.0)') # Floor x = myokit.Floor(b) self.assertEqual(w.ex(x), 'math.floor(12.0)') # Ceil x = myokit.Ceil(b) self.assertEqual(w.ex(x), 'math.ceil(12.0)') # Abs x = myokit.Abs(b) self.assertEqual(w.ex(x), 'abs(12.0)') # Equal x = myokit.Equal(a, b) self.assertEqual(w.ex(x), '(c.a == 12.0)') # NotEqual x = myokit.NotEqual(a, b) self.assertEqual(w.ex(x), '(c.a != 12.0)') # More x = myokit.More(a, b) self.assertEqual(w.ex(x), '(c.a > 12.0)') # Less x = myokit.Less(a, b) self.assertEqual(w.ex(x), '(c.a < 12.0)') # MoreEqual x = myokit.MoreEqual(a, b) self.assertEqual(w.ex(x), '(c.a >= 12.0)') # LessEqual x = myokit.LessEqual(a, b) self.assertEqual(w.ex(x), '(c.a <= 12.0)') # Not cond1 = myokit.parse_expression('5 > 3') cond2 = myokit.parse_expression('2 < 1') x = myokit.Not(cond1) self.assertEqual(w.ex(x), 'not ((5.0 > 3.0))') # And x = myokit.And(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) and (2.0 < 1.0))') # Or x = myokit.Or(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) or (2.0 < 1.0))') # If x = myokit.If(cond1, a, b) self.assertEqual(w.ex(x), '(c.a if (5.0 > 3.0) else 12.0)') # Piecewise c = myokit.Number(1) x = myokit.Piecewise(cond1, a, cond2, b, c) self.assertEqual( w.ex(x), '(c.a if (5.0 > 3.0) else (12.0 if (2.0 < 1.0) else 1.0))') # Test fetching using ewriter method w = myokit.formats.ewriter('python') self.assertIsInstance(w, myokit.formats.python.PythonExpressionWriter) # Test lhs method w.set_lhs_function(lambda x: 'sheep') self.assertEqual(w.ex(a), 'sheep') # Test without a Myokit expression self.assertRaisesRegex(ValueError, 'Unknown expression type', w.ex, 7)
def test_all(self): w = myokit.formats.matlab.MatlabExpressionWriter() model = myokit.Model() component = model.add_component('c') avar = component.add_variable('a') # Name a = myokit.Name(avar) self.assertEqual(w.ex(a), 'c.a') # Number with unit b = myokit.Number('12', 'pF') self.assertEqual(w.ex(b), '12.0') # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(w.ex(x), '12.0') # Prefix minus x = myokit.PrefixMinus(b) self.assertEqual(w.ex(x), '(-12.0)') # Plus x = myokit.Plus(a, b) self.assertEqual(w.ex(x), 'c.a + 12.0') # Minus x = myokit.Minus(a, b) self.assertEqual(w.ex(x), 'c.a - 12.0') # Multiply x = myokit.Multiply(a, b) self.assertEqual(w.ex(x), 'c.a * 12.0') # Divide x = myokit.Divide(a, b) self.assertEqual(w.ex(x), 'c.a / 12.0') # Quotient x = myokit.Quotient(a, b) self.assertEqual(w.ex(x), 'floor(c.a / 12.0)') # Remainder x = myokit.Remainder(a, b) self.assertEqual(w.ex(x), 'mod(c.a, 12.0)') # Power x = myokit.Power(a, b) self.assertEqual(w.ex(x), 'c.a ^ 12.0') # Sqrt x = myokit.Sqrt(b) self.assertEqual(w.ex(x), 'sqrt(12.0)') # Exp x = myokit.Exp(a) self.assertEqual(w.ex(x), 'exp(c.a)') # Log(a) x = myokit.Log(b) self.assertEqual(w.ex(x), 'log(12.0)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(w.ex(x), '(log(c.a) / log(12.0))') # Log10 x = myokit.Log10(b) self.assertEqual(w.ex(x), 'log10(12.0)') # Sin x = myokit.Sin(b) self.assertEqual(w.ex(x), 'sin(12.0)') # Cos x = myokit.Cos(b) self.assertEqual(w.ex(x), 'cos(12.0)') # Tan x = myokit.Tan(b) self.assertEqual(w.ex(x), 'tan(12.0)') # ASin x = myokit.ASin(b) self.assertEqual(w.ex(x), 'asin(12.0)') # ACos x = myokit.ACos(b) self.assertEqual(w.ex(x), 'acos(12.0)') # ATan x = myokit.ATan(b) self.assertEqual(w.ex(x), 'atan(12.0)') # Floor x = myokit.Floor(b) self.assertEqual(w.ex(x), 'floor(12.0)') # Ceil x = myokit.Ceil(b) self.assertEqual(w.ex(x), 'ceil(12.0)') # Abs x = myokit.Abs(b) self.assertEqual(w.ex(x), 'abs(12.0)') # Equal x = myokit.Equal(a, b) self.assertEqual(w.ex(x), '(c.a == 12.0)') # NotEqual x = myokit.NotEqual(a, b) self.assertEqual(w.ex(x), '(c.a != 12.0)') # More x = myokit.More(a, b) self.assertEqual(w.ex(x), '(c.a > 12.0)') # Less x = myokit.Less(a, b) self.assertEqual(w.ex(x), '(c.a < 12.0)') # MoreEqual x = myokit.MoreEqual(a, b) self.assertEqual(w.ex(x), '(c.a >= 12.0)') # LessEqual x = myokit.LessEqual(a, b) self.assertEqual(w.ex(x), '(c.a <= 12.0)') # Not cond1 = myokit.parse_expression('5 > 3') cond2 = myokit.parse_expression('2 < 1') x = myokit.Not(cond1) self.assertEqual(w.ex(x), '!((5.0 > 3.0))') # And x = myokit.And(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) && (2.0 < 1.0))') # Or x = myokit.Or(cond1, cond2) self.assertEqual(w.ex(x), '((5.0 > 3.0) || (2.0 < 1.0))') # If (custom function) x = myokit.If(cond1, a, b) self.assertEqual(w.ex(x), 'ifthenelse((5.0 > 3.0), c.a, 12.0)') # Piecewise c = myokit.Number(1) x = myokit.Piecewise(cond1, a, cond2, b, c) self.assertEqual( w.ex(x), 'ifthenelse((5.0 > 3.0), c.a, ifthenelse((2.0 < 1.0), 12.0, 1.0))') # Test fetching using ewriter method w = myokit.formats.ewriter('matlab') self.assertIsInstance(w, myokit.formats.matlab.MatlabExpressionWriter) # Test without a Myokit expression self.assertRaisesRegex(ValueError, 'Unknown expression type', w.ex, 7)
def test_all(self): # Single and double precision ws = myokit.formats.cuda.CudaExpressionWriter() wd = myokit.formats.cuda.CudaExpressionWriter(myokit.DOUBLE_PRECISION) model = myokit.Model() component = model.add_component('c') avar = component.add_variable('a') # Name a = myokit.Name(avar) self.assertEqual(ws.ex(a), 'c.a') self.assertEqual(wd.ex(a), 'c.a') # Number with unit b = myokit.Number('12', 'pF') self.assertEqual(ws.ex(b), '12.0f') self.assertEqual(wd.ex(b), '12.0') # Prefix plus x = myokit.PrefixPlus(b) self.assertEqual(ws.ex(x), '12.0f') self.assertEqual(wd.ex(x), '12.0') # Prefix minus x = myokit.PrefixMinus(b) self.assertEqual(ws.ex(x), '(-12.0f)') self.assertEqual(wd.ex(x), '(-12.0)') # Plus x = myokit.Plus(a, b) self.assertEqual(ws.ex(x), 'c.a + 12.0f') self.assertEqual(wd.ex(x), 'c.a + 12.0') # Minus x = myokit.Minus(a, b) self.assertEqual(ws.ex(x), 'c.a - 12.0f') self.assertEqual(wd.ex(x), 'c.a - 12.0') # Multiply x = myokit.Multiply(a, b) self.assertEqual(ws.ex(x), 'c.a * 12.0f') self.assertEqual(wd.ex(x), 'c.a * 12.0') # Divide x = myokit.Divide(a, b) self.assertEqual(ws.ex(x), 'c.a / 12.0f') self.assertEqual(wd.ex(x), 'c.a / 12.0') # Quotient x = myokit.Quotient(a, b) self.assertEqual(ws.ex(x), 'floorf(c.a / 12.0f)') self.assertEqual(wd.ex(x), 'floor(c.a / 12.0)') # Remainder x = myokit.Remainder(a, b) self.assertEqual(ws.ex(x), 'c.a - 12.0f * (floorf(c.a / 12.0f))') self.assertEqual(wd.ex(x), 'c.a - 12.0 * (floor(c.a / 12.0))') # Power x = myokit.Power(a, b) self.assertEqual(ws.ex(x), 'powf(c.a, 12.0f)') self.assertEqual(wd.ex(x), 'pow(c.a, 12.0)') # Square x = myokit.Power(a, myokit.Number(2)) self.assertEqual(ws.ex(x), '(c.a * c.a)') self.assertEqual(wd.ex(x), '(c.a * c.a)') # Square with brackets x = myokit.Power(myokit.Plus(a, b), myokit.Number(2)) self.assertEqual(ws.ex(x), '((c.a + 12.0f) * (c.a + 12.0f))') self.assertEqual(wd.ex(x), '((c.a + 12.0) * (c.a + 12.0))') # Sqrt x = myokit.Sqrt(b) self.assertEqual(ws.ex(x), 'sqrtf(12.0f)') self.assertEqual(wd.ex(x), 'sqrt(12.0)') # Exp x = myokit.Exp(a) self.assertEqual(ws.ex(x), 'expf(c.a)') self.assertEqual(wd.ex(x), 'exp(c.a)') # Log(a) x = myokit.Log(b) self.assertEqual(ws.ex(x), 'logf(12.0f)') self.assertEqual(wd.ex(x), 'log(12.0)') # Log(a, b) x = myokit.Log(a, b) self.assertEqual(ws.ex(x), '(logf(c.a) / logf(12.0f))') self.assertEqual(wd.ex(x), '(log(c.a) / log(12.0))') # Log10 x = myokit.Log10(b) self.assertEqual(ws.ex(x), 'log10f(12.0f)') self.assertEqual(wd.ex(x), 'log10(12.0)') # Sin x = myokit.Sin(b) self.assertEqual(ws.ex(x), 'sinf(12.0f)') self.assertEqual(wd.ex(x), 'sin(12.0)') # Cos x = myokit.Cos(b) self.assertEqual(ws.ex(x), 'cosf(12.0f)') self.assertEqual(wd.ex(x), 'cos(12.0)') # Tan x = myokit.Tan(b) self.assertEqual(ws.ex(x), 'tanf(12.0f)') self.assertEqual(wd.ex(x), 'tan(12.0)') # ASin x = myokit.ASin(b) self.assertEqual(ws.ex(x), 'asinf(12.0f)') self.assertEqual(wd.ex(x), 'asin(12.0)') # ACos x = myokit.ACos(b) self.assertEqual(ws.ex(x), 'acosf(12.0f)') self.assertEqual(wd.ex(x), 'acos(12.0)') # ATan x = myokit.ATan(b) self.assertEqual(ws.ex(x), 'atanf(12.0f)') self.assertEqual(wd.ex(x), 'atan(12.0)') # Floor x = myokit.Floor(b) self.assertEqual(ws.ex(x), 'floorf(12.0f)') self.assertEqual(wd.ex(x), 'floor(12.0)') # Ceil x = myokit.Ceil(b) self.assertEqual(ws.ex(x), 'ceilf(12.0f)') self.assertEqual(wd.ex(x), 'ceil(12.0)') # Abs x = myokit.Abs(b) self.assertEqual(ws.ex(x), 'fabsf(12.0f)') self.assertEqual(wd.ex(x), 'fabs(12.0)') # Equal x = myokit.Equal(a, b) self.assertEqual(ws.ex(x), '(c.a == 12.0f)') self.assertEqual(wd.ex(x), '(c.a == 12.0)') # NotEqual x = myokit.NotEqual(a, b) self.assertEqual(ws.ex(x), '(c.a != 12.0f)') self.assertEqual(wd.ex(x), '(c.a != 12.0)') # More x = myokit.More(a, b) self.assertEqual(ws.ex(x), '(c.a > 12.0f)') self.assertEqual(wd.ex(x), '(c.a > 12.0)') # Less x = myokit.Less(a, b) self.assertEqual(ws.ex(x), '(c.a < 12.0f)') self.assertEqual(wd.ex(x), '(c.a < 12.0)') # MoreEqual x = myokit.MoreEqual(a, b) self.assertEqual(ws.ex(x), '(c.a >= 12.0f)') self.assertEqual(wd.ex(x), '(c.a >= 12.0)') # LessEqual x = myokit.LessEqual(a, b) self.assertEqual(ws.ex(x), '(c.a <= 12.0f)') self.assertEqual(wd.ex(x), '(c.a <= 12.0)') # Not cond1 = myokit.parse_expression('5 > 3') cond2 = myokit.parse_expression('2 < 1') x = myokit.Not(cond1) self.assertEqual(ws.ex(x), '!((5.0f > 3.0f))') self.assertEqual(wd.ex(x), '!((5.0 > 3.0))') # And x = myokit.And(cond1, cond2) self.assertEqual(ws.ex(x), '((5.0f > 3.0f) && (2.0f < 1.0f))') self.assertEqual(wd.ex(x), '((5.0 > 3.0) && (2.0 < 1.0))') # Or x = myokit.Or(cond1, cond2) self.assertEqual(ws.ex(x), '((5.0f > 3.0f) || (2.0f < 1.0f))') self.assertEqual(wd.ex(x), '((5.0 > 3.0) || (2.0 < 1.0))') # If x = myokit.If(cond1, a, b) self.assertEqual(ws.ex(x), '((5.0f > 3.0f) ? c.a : 12.0f)') self.assertEqual(wd.ex(x), '((5.0 > 3.0) ? c.a : 12.0)') # Piecewise c = myokit.Number(1) x = myokit.Piecewise(cond1, a, cond2, b, c) self.assertEqual( ws.ex(x), '((5.0f > 3.0f) ? c.a : ((2.0f < 1.0f) ? 12.0f : 1.0f))') self.assertEqual(wd.ex(x), '((5.0 > 3.0) ? c.a : ((2.0 < 1.0) ? 12.0 : 1.0))') # Test fetching using ewriter method w = myokit.formats.ewriter('cuda') self.assertIsInstance(w, myokit.formats.cuda.CudaExpressionWriter) # Test without a Myokit expression self.assertRaisesRegex(ValueError, 'Unknown expression type', w.ex, 7)
def test_model_creation(self): # Create a model m = myokit.Model('LotkaVolterra') # Add the first component X = m.add_component('X') self.assertEqual(X.qname(), 'X') self.assertEqual(X.parent(), m) self.assertIsInstance(X, myokit.Component) self.assertIn(X.qname(), m) self.assertEqual(len(m), 1) # Add variable a self.assertFalse(X.has_variable('a')) a = X.add_variable('a') self.assertTrue(X.has_variable('a')) self.assertEqual(a, a) self.assertIsInstance(a, myokit.Variable) self.assertEqual(len(X), 1) self.assertIn(a.name(), X) a.set_rhs(3) self.assertFalse(a.is_state()) self.assertFalse(a.is_intermediary()) self.assertTrue(a.is_constant()) self.assertEqual(a.lhs(), myokit.Name(a)) self.assertEqual(a.rhs(), myokit.Number(3)) self.assertEqual(a.rhs().eval(), 3) self.assertEqual(a.code(), 'a = 3\n') self.assertEqual(a.eq().code(), 'X.a = 3') self.assertEqual(a.lhs().code(), 'X.a') self.assertEqual(a.rhs().code(), '3') self.assertEqual( a.eq(), myokit.Equation(myokit.Name(a), myokit.Number(3))) # Check lhs a_name1 = myokit.Name(a) a_name2 = myokit.Name(a) self.assertEqual(a_name1, a_name1) self.assertEqual(a_name2, a_name2) self.assertEqual(a_name1, a_name2) self.assertEqual(a_name2, a_name1) # Add variable b with two temporary variables b = X.add_variable('b') self.assertIsInstance(b, myokit.Variable) self.assertEqual(len(X), 2) self.assertIn(b.name(), X) self.assertFalse(b.has_variable('b1')) b1 = b.add_variable('b1') self.assertTrue(b.has_variable('b1')) self.assertEqual(len(b), 1) self.assertIn(b1.name(), b) self.assertIsInstance(b1, myokit.Variable) b2 = b.add_variable('b2') self.assertEqual(len(b), 2) self.assertIn(b2.name(), b) self.assertIsInstance(b2, myokit.Variable) b1.set_rhs(1) b2.set_rhs( myokit.Minus( myokit.Minus(myokit.Name(a), myokit.Name(b1)), myokit.Number(1)) ) b.set_rhs(myokit.Plus(myokit.Name(b1), myokit.Name(b2))) self.assertEqual(b.rhs().eval(), 2) self.assertFalse(b.is_state()) self.assertFalse(b.is_intermediary()) self.assertTrue(b.is_constant()) self.assertEqual(b.lhs(), myokit.Name(b)) # Add state variable x x = X.add_variable('x') x.set_rhs(10) x.promote() self.assertNotEqual(x, X) self.assertIsInstance(x, myokit.Variable) self.assertEqual(len(X), 3) self.assertIn(x.name(), X) self.assertTrue(x.is_state()) self.assertFalse(x.is_intermediary()) self.assertFalse(x.is_constant()) self.assertEqual(x.lhs(), myokit.Derivative(myokit.Name(x))) self.assertEqual(x.indice(), 0) # Test demoting, promoting x.demote() self.assertFalse(x.is_state()) self.assertFalse(x.is_intermediary()) self.assertTrue(x.is_constant()) self.assertEqual(x.lhs(), myokit.Name(x)) x.promote() self.assertTrue(x.is_state()) self.assertFalse(x.is_intermediary()) self.assertFalse(x.is_constant()) self.assertEqual(x.lhs(), myokit.Derivative(myokit.Name(x))) x.demote() x.promote() x.demote() x.promote() self.assertTrue(x.is_state()) self.assertFalse(x.is_intermediary()) self.assertFalse(x.is_constant()) self.assertEqual(x.lhs(), myokit.Derivative(myokit.Name(x))) # Add second component, variables Y = m.add_component('Y') self.assertNotEqual(X, Y) self.assertEqual(len(m), 2) c = Y.add_variable('c') c.set_rhs(myokit.Minus(myokit.Name(a), myokit.Number(1))) d = Y.add_variable('d') d.set_rhs(2) y = Y.add_variable('y') y.promote() # Set rhs for x and y x.set_rhs(myokit.Minus( myokit.Multiply(myokit.Name(a), myokit.Name(x)), myokit.Multiply( myokit.Multiply(myokit.Name(b), myokit.Name(x)), myokit.Name(y) ) )) x.set_state_value(10) self.assertEqual(x.rhs().code(), 'X.a * X.x - X.b * X.x * Y.y') y.set_rhs(myokit.Plus( myokit.Multiply( myokit.PrefixMinus(myokit.Name(c)), myokit.Name(y) ), myokit.Multiply( myokit.Multiply(myokit.Name(d), myokit.Name(x)), myokit.Name(y) ) )) y.set_state_value(5) self.assertEqual(y.rhs().code(), '-Y.c * Y.y + Y.d * X.x * Y.y') # Add ano component, variables Z = m.add_component('Z') self.assertNotEqual(X, Z) self.assertNotEqual(Y, Z) self.assertEqual(len(m), 3) t = Z.add_variable('total') self.assertEqual(t.name(), 'total') self.assertEqual(t.qname(), 'Z.total') self.assertEqual(t.qname(X), 'Z.total') self.assertEqual(t.qname(Z), 'total') t.set_rhs(myokit.Plus(myokit.Name(x), myokit.Name(y))) self.assertFalse(t.is_state()) self.assertFalse(t.is_constant()) self.assertTrue(t.is_intermediary()) self.assertEqual(t.rhs().code(), 'X.x + Y.y') self.assertEqual(t.rhs().code(X), 'x + Y.y') self.assertEqual(t.rhs().code(Y), 'X.x + y') self.assertEqual(t.rhs().code(Z), 'X.x + Y.y') # Add engine component E = m.add_component('engine') self.assertNotEqual(X, E) self.assertNotEqual(Y, E) self.assertNotEqual(Z, E) self.assertEqual(len(m), 4) time = E.add_variable('time') time.set_rhs(0) self.assertIsNone(time.binding()) time.set_binding('time') self.assertIsNotNone(time.binding()) # Check state state = [i for i in m.states()] self.assertEqual(len(state), 2) self.assertIn(x, state) self.assertIn(y, state) # Test variable iterators def has(*v): for var in v: self.assertIn(var, vrs) self.assertEqual(len(vrs), len(v)) vrs = [i for i in m.variables()] has(a, b, c, d, x, y, t, time) vrs = [i for i in m.variables(deep=True)] has(a, b, c, d, x, y, t, b1, b2, time) vrs = [i for i in m.variables(const=True)] has(a, b, c, d) vrs = [i for i in m.variables(const=True, deep=True)] has(a, b, c, d, b1, b2) vrs = [i for i in m.variables(const=False)] has(x, y, t, time) vrs = [i for i in m.variables(const=False, deep=True)] has(x, y, t, time) vrs = [i for i in m.variables(state=True)] has(x, y) vrs = [i for i in m.variables(state=True, deep=True)] has(x, y) vrs = [i for i in m.variables(state=False)] has(a, b, c, d, t, time) vrs = [i for i in m.variables(state=False, deep=True)] has(a, b, c, d, t, b1, b2, time) vrs = [i for i in m.variables(inter=True)] has(t) vrs = [i for i in m.variables(inter=True, deep=True)] has(t) vrs = [i for i in m.variables(inter=False)] has(a, b, c, d, x, y, time) vrs = [i for i in m.variables(inter=False, deep=True)] has(a, b, c, d, x, y, b1, b2, time) vrs = list(m.variables(const=True, state=True)) has() vrs = list(m.variables(const=True, state=False)) has(a, b, c, d) # Test sorted variable iteration names = [v.name() for v in m.variables(deep=True, sort=True)] self.assertEqual(names, [ 'a', 'b', 'b1', 'b2', 'x', 'c', 'd', 'y', 'total', 'time']) # Test equation iteration # Deeper testing is done when testing the ``variables`` method. eq = [eq for eq in X.equations(deep=False)] self.assertEqual(len(eq), 3) self.assertEqual(len(eq), X.count_equations(deep=False)) eq = [eq for eq in X.equations(deep=True)] self.assertEqual(len(eq), 5) self.assertEqual(len(eq), X.count_equations(deep=True)) eq = [eq for eq in Y.equations(deep=False)] self.assertEqual(len(eq), 3) self.assertEqual(len(eq), Y.count_equations(deep=False)) eq = [eq for eq in Y.equations(deep=True)] self.assertEqual(len(eq), 3) self.assertEqual(len(eq), Y.count_equations(deep=True)) eq = [eq for eq in Z.equations(deep=False)] self.assertEqual(len(eq), 1) self.assertEqual(len(eq), Z.count_equations(deep=False)) eq = [eq for eq in Z.equations(deep=True)] self.assertEqual(len(eq), 1) self.assertEqual(len(eq), Z.count_equations(deep=True)) eq = [eq for eq in E.equations(deep=False)] self.assertEqual(len(eq), 1) eq = [eq for eq in E.equations(deep=True)] self.assertEqual(len(eq), 1) eq = [eq for eq in m.equations(deep=False)] self.assertEqual(len(eq), 8) eq = [eq for eq in m.equations(deep=True)] self.assertEqual(len(eq), 10) # Test dependency mapping def has(var, *dps): lst = vrs[m.get(var).lhs() if isinstance(var, basestring) else var] self.assertEqual(len(lst), len(dps)) for d in dps: d = m.get(d).lhs() if isinstance(d, basestring) else d self.assertIn(d, lst) vrs = m.map_shallow_dependencies(omit_states=False) self.assertEqual(len(vrs), 12) has('X.a') has('X.b', 'X.b.b1', 'X.b.b2') has('X.b.b1') has('X.b.b2', 'X.a', 'X.b.b1') has('X.x', 'X.a', 'X.b', myokit.Name(x), myokit.Name(y)) has(myokit.Name(x)) has('Y.c', 'X.a') has('Y.d') has('Y.y', 'Y.c', 'Y.d', myokit.Name(x), myokit.Name(y)) has(myokit.Name(y)) has('Z.total', myokit.Name(x), myokit.Name(y)) vrs = m.map_shallow_dependencies() self.assertEqual(len(vrs), 10) has('X.a') has('X.b', 'X.b.b1', 'X.b.b2') has('X.b.b1') has('X.b.b2', 'X.a', 'X.b.b1') has('X.x', 'X.a', 'X.b') has('Y.c', 'X.a') has('Y.d') has('Y.y', 'Y.c', 'Y.d') has('Z.total') vrs = m.map_shallow_dependencies(collapse=True) self.assertEqual(len(vrs), 8) has('X.a') has('X.b', 'X.a') has('X.x', 'X.a', 'X.b') has('Y.c', 'X.a') has('Y.d') has('Y.y', 'Y.c', 'Y.d') has('Z.total') # Validate m.validate() # Get solvable order order = m.solvable_order() self.assertEqual(len(order), 5) self.assertIn('*remaining*', order) self.assertIn('X', order) self.assertIn('Y', order) self.assertIn('Z', order) # Check that X comes before Y pos = dict([(name, k) for k, name in enumerate(order)]) self.assertLess(pos['X'], pos['Y']) self.assertEqual(pos['*remaining*'], 4) # Check component equation lists eqs = order['*remaining*'] self.assertEqual(len(eqs), 0) eqs = order['Z'] self.assertEqual(len(eqs), 1) self.assertEqual(eqs[0].code(), 'Z.total = X.x + Y.y') eqs = order['Y'] self.assertEqual(len(eqs), 3) self.assertEqual( eqs[2].code(), 'dot(Y.y) = -Y.c * Y.y + Y.d * X.x * Y.y') eqs = order['X'] self.assertEqual(len(eqs), 5) self.assertEqual(eqs[0].code(), 'X.a = 3') self.assertEqual(eqs[1].code(), 'b1 = 1') self.assertEqual(eqs[2].code(), 'b2 = X.a - b1 - 1') self.assertEqual(eqs[3].code(), 'X.b = b1 + b2') # Test model export and cloning code1 = m.code() code2 = m.clone().code() self.assertEqual(code1, code2)
def test_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)
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