myokit 1.35.4__py3-none-any.whl → 1.36.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- myokit/__init__.py +5 -3
- myokit/__main__.py +9 -159
- myokit/_config.py +2 -2
- myokit/_expressions.py +6 -6
- myokit/_model_api.py +11 -7
- myokit/_myokit_version.py +1 -1
- myokit/_protocol.py +4 -0
- myokit/_sim/__init__.py +1 -0
- myokit/_sim/cvodessim.c +321 -177
- myokit/_sim/cvodessim.py +107 -43
- myokit/_sim/mcl.h +54 -0
- myokit/formats/__init__.py +63 -12
- myokit/formats/ansic/__init__.py +2 -1
- myokit/formats/ansic/_ewriter.py +159 -40
- myokit/formats/cpp/_ewriter.py +12 -1
- myokit/formats/cuda/_ewriter.py +15 -51
- myokit/formats/easyml/_ewriter.py +26 -54
- myokit/formats/heka/_patchmaster.py +15 -3
- myokit/formats/latex/_ewriter.py +103 -88
- myokit/formats/latex/_exporter.py +1 -1
- myokit/formats/mathml/_ewriter.py +2 -2
- myokit/formats/matlab/_ewriter.py +50 -28
- myokit/formats/opencl/_ewriter.py +61 -78
- myokit/formats/python/_ewriter.py +81 -50
- myokit/formats/stan/_ewriter.py +29 -37
- myokit/gui/source.py +1 -1
- myokit/lib/hh.py +3 -0
- myokit/lib/markov.py +6 -0
- myokit/tests/__init__.py +70 -0
- myokit/tests/data/decker.model +59 -59
- myokit/tests/test_formats.py +115 -7
- myokit/tests/test_formats_ansic.py +344 -0
- myokit/tests/test_formats_axon.py +17 -0
- myokit/tests/test_formats_cpp.py +97 -0
- myokit/tests/test_formats_cuda.py +226 -0
- myokit/tests/test_formats_easyml.py +169 -152
- myokit/tests/{test_formats_exporters.py → test_formats_exporters_run.py} +1 -69
- myokit/tests/test_formats_html.py +1 -3
- myokit/tests/test_formats_latex.py +211 -0
- myokit/tests/test_formats_mathml_content.py +13 -0
- myokit/tests/test_formats_mathml_presentation.py +54 -42
- myokit/tests/test_formats_matlab.py +218 -0
- myokit/tests/test_formats_opencl.py +206 -380
- myokit/tests/test_formats_python.py +557 -0
- myokit/tests/test_formats_stan.py +175 -0
- myokit/tests/test_formats_sympy.py +9 -2
- myokit/tests/test_lib_hh.py +36 -0
- myokit/tests/test_lib_plots.py +0 -16
- myokit/tests/test_model.py +21 -1
- myokit/tests/test_simulation_cvodes.py +137 -56
- myokit/tools.py +3 -2
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/LICENSE.txt +1 -1
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/METADATA +19 -8
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/RECORD +57 -52
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/WHEEL +1 -1
- myokit/tests/test_formats_expression_writers.py +0 -1281
- myokit/tests/test_formats_importers.py +0 -53
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/entry_points.txt +0 -0
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# Tests writing of Stan equations.
|
|
4
|
+
#
|
|
5
|
+
# This file is part of Myokit.
|
|
6
|
+
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
7
|
+
#
|
|
8
|
+
import unittest
|
|
9
|
+
|
|
10
|
+
import myokit
|
|
11
|
+
import myokit.formats.latex
|
|
12
|
+
|
|
13
|
+
from myokit import (
|
|
14
|
+
Number, PrefixPlus, PrefixMinus, Plus, Minus,
|
|
15
|
+
Multiply, Divide, Quotient, Remainder, Power, Sqrt,
|
|
16
|
+
Exp, Log, Log10, Sin, Cos, Tan, ASin, ACos, ATan, Floor, Ceil, Abs,
|
|
17
|
+
Not, And, Or, Equal, NotEqual, More, Less, MoreEqual, LessEqual,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
import myokit.tests
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LatexExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
|
|
24
|
+
"""
|
|
25
|
+
Test conversion of expressions to Python.
|
|
26
|
+
This is used by pyfunc(), although that's usually done with the
|
|
27
|
+
NumPyExpressionWriter instead.
|
|
28
|
+
Numerical tests are provided.
|
|
29
|
+
"""
|
|
30
|
+
_name = 'latex'
|
|
31
|
+
_target = myokit.formats.latex.LatexExpressionWriter
|
|
32
|
+
_update_lhs_function = False
|
|
33
|
+
|
|
34
|
+
def test_number(self):
|
|
35
|
+
self.eq(Number(1), '1.0')
|
|
36
|
+
self.eq(Number(-1.3274924373284374), '-1.32749243732843736e+00')
|
|
37
|
+
self.eq(Number(+1.3274924373284374), '1.32749243732843736e+00')
|
|
38
|
+
self.eq(Number(-2), '-2.0')
|
|
39
|
+
self.eq(Number(13, 'mV'), r'13.0 \text{mV}')
|
|
40
|
+
self.eq(Number(2, 'A/F'), r'2.0 \text{A/F}')
|
|
41
|
+
self.eq(Number(7, myokit.units.dimensionless), '7.0')
|
|
42
|
+
|
|
43
|
+
def test_name(self):
|
|
44
|
+
self.eq(self.a, r'\text{a}')
|
|
45
|
+
m = myokit.Model()
|
|
46
|
+
c = m.add_component('c')
|
|
47
|
+
v = c.add_variable('under_score', rhs=3)
|
|
48
|
+
t = c.add_variable('time', rhs=0, binding='time')
|
|
49
|
+
m.validate()
|
|
50
|
+
self.eq(myokit.Name(v), r'\text{under\_score}')
|
|
51
|
+
|
|
52
|
+
w = self._target()
|
|
53
|
+
w.set_lhs_function(lambda v: v.var().qname().upper())
|
|
54
|
+
self.assertEqual(w.ex(self.a), 'COMP.A')
|
|
55
|
+
|
|
56
|
+
def test_derivative(self):
|
|
57
|
+
self.eq(myokit.Derivative(self.a), r'\frac{d\text{a}}{d\text{t}}')
|
|
58
|
+
|
|
59
|
+
w = self._target()
|
|
60
|
+
w.set_time_variable_name('timmy')
|
|
61
|
+
self.assertEqual(w.ex(myokit.Derivative(self.a)),
|
|
62
|
+
r'\frac{d\text{a}}{d\text{timmy}}')
|
|
63
|
+
w.set_time_variable_name('Bob')
|
|
64
|
+
self.assertEqual(w.ex(myokit.Derivative(self.a)),
|
|
65
|
+
r'\frac{d\text{a}}{d\text{Bob}}')
|
|
66
|
+
|
|
67
|
+
def test_partial_derivative(self):
|
|
68
|
+
self.eq(myokit.PartialDerivative(self.a, self.b),
|
|
69
|
+
r'\frac{\partial\text{a}}{\partial\text{b}}')
|
|
70
|
+
|
|
71
|
+
def test_initial_value(self):
|
|
72
|
+
self.eq(myokit.InitialValue(self.a), r'\text{a}(\text{t} = 0)')
|
|
73
|
+
|
|
74
|
+
def test_prefix_plus(self):
|
|
75
|
+
# Test with numbers
|
|
76
|
+
p = Number(11, 'mV')
|
|
77
|
+
self.eq(PrefixPlus(p), r'+11.0 \text{mV}')
|
|
78
|
+
p = Number(3)
|
|
79
|
+
self.eq(PrefixPlus(PrefixPlus(p)), '++3.0')
|
|
80
|
+
self.eq(PrefixPlus(Number('+1')), '+1.0')
|
|
81
|
+
|
|
82
|
+
# Test with operators of precedence SUM, PRODUCT, POWER
|
|
83
|
+
a, b, c = self.abc
|
|
84
|
+
self.eq(PrefixPlus(Plus(a, b)), r'+\left(\text{a}+\text{b}\right)')
|
|
85
|
+
self.eq(Divide(PrefixPlus(Plus(a, b)), c),
|
|
86
|
+
r'\frac{+\left(\text{a}+\text{b}\right)}{\text{c}}')
|
|
87
|
+
self.eq(Power(PrefixPlus(b), a),
|
|
88
|
+
r'\left(+\text{b}\right)^\text{a}')
|
|
89
|
+
|
|
90
|
+
def test_prefix_minus(self):
|
|
91
|
+
# Test with numbers
|
|
92
|
+
p = Number(3, 'uA')
|
|
93
|
+
self.eq(PrefixMinus(p), r'-3.0 \text{uA}')
|
|
94
|
+
p = Number(2)
|
|
95
|
+
self.eq(PrefixMinus(PrefixMinus(p)), '--2.0')
|
|
96
|
+
self.eq(PrefixMinus(Number('-1')), '--1.0')
|
|
97
|
+
|
|
98
|
+
# Test with operators of precedence SUM, PRODUCT, POWER
|
|
99
|
+
a, b, c = self.abc
|
|
100
|
+
self.eq(PrefixMinus(Plus(a, b)), r'-\left(\text{a}+\text{b}\right)')
|
|
101
|
+
self.eq(Divide(PrefixMinus(Plus(a, b)), c),
|
|
102
|
+
r'\frac{-\left(\text{a}+\text{b}\right)}{\text{c}}')
|
|
103
|
+
self.eq(Power(PrefixMinus(b), a),
|
|
104
|
+
r'\left(-\text{b}\right)^\text{a}')
|
|
105
|
+
|
|
106
|
+
def test_plus_minus(self):
|
|
107
|
+
a, b, c = self.abc
|
|
108
|
+
ta, tb, tc = r'\text{a}', r'\text{b}', r'\text{c}'
|
|
109
|
+
self.eq(Plus(a, b), f'{ta}+{tb}')
|
|
110
|
+
self.eq(Plus(Plus(a, b), c), f'{ta}+{tb}+{tc}')
|
|
111
|
+
self.eq(Plus(a, Plus(b, c)), rf'{ta}+\left({tb}+{tc}\right)')
|
|
112
|
+
|
|
113
|
+
self.eq(Minus(a, b), f'{ta}-{tb}')
|
|
114
|
+
self.eq(Minus(Minus(a, b), c), f'{ta}-{tb}-{tc}')
|
|
115
|
+
self.eq(Minus(a, Minus(b, c)), rf'{ta}-\left({tb}-{tc}\right)')
|
|
116
|
+
|
|
117
|
+
self.eq(Minus(a, b), f'{ta}-{tb}')
|
|
118
|
+
self.eq(Plus(Minus(a, b), c), f'{ta}-{tb}+{tc}')
|
|
119
|
+
self.eq(Minus(a, Plus(b, c)), rf'{ta}-\left({tb}+{tc}\right)')
|
|
120
|
+
self.eq(Minus(Plus(a, b), c), f'{ta}+{tb}-{tc}')
|
|
121
|
+
self.eq(Minus(a, Plus(b, c)), rf'{ta}-\left({tb}+{tc}\right)')
|
|
122
|
+
|
|
123
|
+
def test_multiply_divide(self):
|
|
124
|
+
a, b, c = self.abc
|
|
125
|
+
ta, tb, tc = r'\text{a}', r'\text{b}', r'\text{c}'
|
|
126
|
+
l, r = r'\left(', r'\right)'
|
|
127
|
+
self.eq(Multiply(a, b), rf'{ta}\cdot{tb}')
|
|
128
|
+
self.eq(Multiply(Multiply(a, b), c), rf'{ta}\cdot{tb}\cdot{tc}')
|
|
129
|
+
self.eq(Multiply(a, Multiply(b, c)), rf'{ta}\cdot{l}{tb}\cdot{tc}{r}')
|
|
130
|
+
self.eq(Divide(a, b), r'\frac{\text{a}}{\text{b}}')
|
|
131
|
+
self.eq(Divide(Divide(a, b), c),
|
|
132
|
+
r'\frac{\frac{\text{a}}{\text{b}}}{\text{c}}')
|
|
133
|
+
self.eq(Divide(a, Divide(b, c)),
|
|
134
|
+
r'\frac{\text{a}}{\frac{\text{b}}{\text{c}}}')
|
|
135
|
+
self.eq(Divide(Divide(a, b), c),
|
|
136
|
+
r'\frac{\frac{\text{a}}{\text{b}}}{\text{c}}')
|
|
137
|
+
self.eq(Divide(Multiply(a, b), c),
|
|
138
|
+
r'\frac{\text{a}\cdot\text{b}}{\text{c}}')
|
|
139
|
+
|
|
140
|
+
self.eq(Multiply(Minus(a, b), c), rf'{l}{ta}-{tb}{r}\cdot{tc}')
|
|
141
|
+
self.eq(Multiply(a, Plus(b, c)), rf'{ta}\cdot{l}{tb}+{tc}{r}')
|
|
142
|
+
self.eq(Plus(a, Multiply(b, c)), rf'{ta}+{tb}\cdot{tc}')
|
|
143
|
+
|
|
144
|
+
def test_remainder_quotient(self):
|
|
145
|
+
a, b = self.ab
|
|
146
|
+
self.eq(Remainder(a, b), r'\text{a}\bmod\text{b}')
|
|
147
|
+
self.eq(Quotient(a, b),
|
|
148
|
+
r'\left\lfloor\frac{\text{a}}{\text{b}}\right\rfloor')
|
|
149
|
+
|
|
150
|
+
def test_power(self):
|
|
151
|
+
a, b, c = self.abc
|
|
152
|
+
ta, tb, tc = r'\text{a}', r'\text{b}', r'\text{c}'
|
|
153
|
+
l, r = r'\left(', r'\right)'
|
|
154
|
+
p, q = '{', '}'
|
|
155
|
+
self.eq(Power(a, b), f'{ta}^{tb}')
|
|
156
|
+
self.eq(Power(Power(a, b), c), f'{l}{ta}^{tb}{r}^{tc}')
|
|
157
|
+
self.eq(Power(a, Power(b, c)), f'{ta}^{p}{tb}^{tc}{q}')
|
|
158
|
+
self.eq(Power(Plus(a, b), c), f'{l}{ta}+{tb}{r}^{tc}')
|
|
159
|
+
self.eq(Power(a, Minus(b, c)), f'{ta}^{p}{tb}-{tc}{q}')
|
|
160
|
+
|
|
161
|
+
def test_functions(self):
|
|
162
|
+
a, b = self.ab
|
|
163
|
+
|
|
164
|
+
self.eq(Sqrt(a), r'\sqrt{\text{a}}')
|
|
165
|
+
self.eq(Exp(a), r'\exp\left(\text{a}\right)')
|
|
166
|
+
self.eq(Log(a), r'\log\left(\text{a}\right)')
|
|
167
|
+
self.eq(Log(a, b), r'\log_{\text{b}}\left(\text{a}\right)')
|
|
168
|
+
self.eq(Log10(a), r'\log_{10}\left(\text{a}\right)')
|
|
169
|
+
self.eq(Sin(a), r'\sin\left(\text{a}\right)')
|
|
170
|
+
self.eq(Cos(a), r'\cos\left(\text{a}\right)')
|
|
171
|
+
self.eq(Tan(a), r'\tan\left(\text{a}\right)')
|
|
172
|
+
self.eq(ASin(a), r'\arcsin\left(\text{a}\right)')
|
|
173
|
+
self.eq(ACos(a), r'\arccos\left(\text{a}\right)')
|
|
174
|
+
self.eq(ATan(a), r'\arctan\left(\text{a}\right)')
|
|
175
|
+
self.eq(Floor(a), r'\left\lfloor{\text{a}}\right\rfloor')
|
|
176
|
+
self.eq(Ceil(a), r'\left\lceil{\text{a}}\right\rceil')
|
|
177
|
+
self.eq(Abs(a), r'\lvert{\text{a}}\rvert')
|
|
178
|
+
|
|
179
|
+
def test_conditions(self):
|
|
180
|
+
a, b, c, d = self.abcd
|
|
181
|
+
|
|
182
|
+
self.eq(And(a, b), r'\left(\text{a}\and\text{b}\right)')
|
|
183
|
+
self.eq(Or(d, c), r'\left(\text{d}\or\text{c}\right)')
|
|
184
|
+
self.eq(Not(c), r'\left(\not\text{c}\right)')
|
|
185
|
+
|
|
186
|
+
self.eq(Equal(a, b), r'\left(\text{a}=\text{b}\right)')
|
|
187
|
+
self.eq(NotEqual(a, b), r'\left(\text{a}\neq\text{b}\right)')
|
|
188
|
+
self.eq(More(b, a), r'\left(\text{b}>\text{a}\right)')
|
|
189
|
+
self.eq(Less(d, c), r'\left(\text{d}<\text{c}\right)')
|
|
190
|
+
self.eq(MoreEqual(c, a), r'\left(\text{c}\geq\text{a}\right)')
|
|
191
|
+
self.eq(LessEqual(b, d), r'\left(\text{b}\leq\text{d}\right)')
|
|
192
|
+
|
|
193
|
+
self.eq(And(Equal(a, b), NotEqual(c, d)),
|
|
194
|
+
r'\left(\left(\text{a}=\text{b}\right)\and'
|
|
195
|
+
r'\left(\text{c}\neq\text{d}\right)\right)')
|
|
196
|
+
self.eq(Not(Equal(d, d)),
|
|
197
|
+
r'\left(\not\left(\text{d}=\text{d}\right)\right)')
|
|
198
|
+
self.eq(Equal(Equal(Number(0), Number(0)), Number(0)),
|
|
199
|
+
r'\left(\left(0.0=0.0\right)=0.0\right)')
|
|
200
|
+
|
|
201
|
+
def test_conditionals(self):
|
|
202
|
+
|
|
203
|
+
a, b, c, d = self.abcd
|
|
204
|
+
self.eq(myokit.If(a, b, c),
|
|
205
|
+
r'\text{if}\left(\text{a},\text{b},\text{c}\right)')
|
|
206
|
+
self.eq(myokit.Piecewise(a, b, c, d, Number(1)), r'\text{piecewise}'
|
|
207
|
+
r'\left(\text{a},\text{b},\text{c},\text{d},1.0\right)')
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == '__main__':
|
|
211
|
+
unittest.main()
|
|
@@ -82,6 +82,10 @@ class ContentMathMLParserTest(unittest.TestCase):
|
|
|
82
82
|
e = myokit.PrefixPlus(myokit.Number(1))
|
|
83
83
|
x = '<apply><plus/><cn>1.0</cn></apply>'
|
|
84
84
|
self.assertEqual(self.p(x), e)
|
|
85
|
+
e = myokit.PrefixPlus(myokit.Plus(myokit.Number(3), myokit.Number(4)))
|
|
86
|
+
x = ('<apply><plus/><apply><plus /><cn>3.0</cn><cn>4.0</cn></apply>'
|
|
87
|
+
'</apply>')
|
|
88
|
+
self.assertEqual(self.p(x), e)
|
|
85
89
|
|
|
86
90
|
# Prefix minus
|
|
87
91
|
e = myokit.PrefixMinus(myokit.Number(1))
|
|
@@ -358,9 +362,18 @@ class ContentMathMLParserTest(unittest.TestCase):
|
|
|
358
362
|
# Power
|
|
359
363
|
a = myokit.Name('a')
|
|
360
364
|
b = myokit.Number(1)
|
|
365
|
+
c = myokit.Number(2)
|
|
361
366
|
e = myokit.Power(a, b)
|
|
362
367
|
x = '<apply><power/><ci>a</ci><cn>1.0</cn></apply>'
|
|
363
368
|
self.assertEqual(self.p(x), e)
|
|
369
|
+
e = myokit.Power(myokit.Power(a, b), c)
|
|
370
|
+
x = ('<apply><power/><apply><power/><ci>a</ci><cn>1.0</cn></apply>'
|
|
371
|
+
'<cn>2.0</cn></apply>')
|
|
372
|
+
self.assertEqual(self.p(x), e)
|
|
373
|
+
e = myokit.Power(a, myokit.Power(b, c))
|
|
374
|
+
x = ('<apply><power/><ci>a</ci><apply><power/><cn>1.0</cn><cn>2.0</cn>'
|
|
375
|
+
'</apply></apply>')
|
|
376
|
+
self.assertEqual(self.p(x), e)
|
|
364
377
|
|
|
365
378
|
#TODO: Degree etc.
|
|
366
379
|
|
|
@@ -22,6 +22,7 @@ class PresentationMathMLTest(unittest.TestCase):
|
|
|
22
22
|
model = myokit.Model()
|
|
23
23
|
component = model.add_component('c')
|
|
24
24
|
cls.avar = component.add_variable('a')
|
|
25
|
+
cls.bvar = component.add_variable('b')
|
|
25
26
|
|
|
26
27
|
cls.w = mathml.MathMLExpressionWriter()
|
|
27
28
|
cls.w.set_mode(presentation=True)
|
|
@@ -46,33 +47,40 @@ class PresentationMathMLTest(unittest.TestCase):
|
|
|
46
47
|
|
|
47
48
|
# Plus
|
|
48
49
|
x = myokit.Plus(a, b)
|
|
49
|
-
self.assertWrite(x, '<mrow>
|
|
50
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>+</mo>{cb}</mrow>')
|
|
50
51
|
|
|
51
52
|
# Minus
|
|
52
53
|
x = myokit.Minus(a, b)
|
|
53
|
-
self.assertWrite(x, '<mrow>
|
|
54
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>-</mo>{cb}</mrow>')
|
|
54
55
|
|
|
55
56
|
# Multiply
|
|
56
57
|
x = myokit.Multiply(a, b)
|
|
57
|
-
self.assertWrite(x, '<mrow>
|
|
58
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>*</mo>{cb}</mrow>')
|
|
58
59
|
|
|
59
60
|
# Divide
|
|
60
61
|
x = myokit.Divide(a, b)
|
|
61
|
-
self.assertWrite(x, '<mfrac>
|
|
62
|
+
self.assertWrite(x, f'<mfrac>{ca + cb}</mfrac>')
|
|
62
63
|
|
|
63
64
|
def test_arithmetic_unary(self):
|
|
64
65
|
# Tests writing prefix operators
|
|
65
66
|
|
|
67
|
+
a = myokit.Name(self.avar)
|
|
66
68
|
b = myokit.Number('12', 'pF')
|
|
69
|
+
ca = '<mi>c.a</mi>'
|
|
67
70
|
cb = '<mn>12.0</mn>'
|
|
68
71
|
|
|
69
72
|
# Prefix plus
|
|
70
73
|
x = myokit.PrefixPlus(b)
|
|
71
|
-
self.assertWrite(x, '<mrow><mo>+</mo>
|
|
74
|
+
self.assertWrite(x, f'<mrow><mo>+</mo>{cb}</mrow>')
|
|
75
|
+
x = myokit.Divide(myokit.PrefixPlus(myokit.Plus(a, b)), a)
|
|
76
|
+
self.assertWrite(
|
|
77
|
+
x,
|
|
78
|
+
f'<mfrac><mrow><mo>+</mo><mo>(</mo><mrow>{ca}<mo>+</mo>{cb}</mrow>'
|
|
79
|
+
f'<mo>)</mo></mrow>{ca}</mfrac>')
|
|
72
80
|
|
|
73
81
|
# Prefix minus
|
|
74
82
|
x = myokit.PrefixMinus(b)
|
|
75
|
-
self.assertWrite(x, '<mrow><mo>-</mo>
|
|
83
|
+
self.assertWrite(x, f'<mrow><mo>-</mo>{cb}</mrow>')
|
|
76
84
|
|
|
77
85
|
def test_conditionals(self):
|
|
78
86
|
# Tests if and piecewise writing
|
|
@@ -94,8 +102,8 @@ class PresentationMathMLTest(unittest.TestCase):
|
|
|
94
102
|
self.assertWrite(
|
|
95
103
|
x,
|
|
96
104
|
'<piecewise>'
|
|
97
|
-
'<piece>
|
|
98
|
-
'<otherwise>
|
|
105
|
+
f'<piece>{ca + c1}</piece>'
|
|
106
|
+
f'<otherwise>{cb}</otherwise>'
|
|
99
107
|
'</piecewise>'
|
|
100
108
|
)
|
|
101
109
|
# Piecewise
|
|
@@ -103,9 +111,9 @@ class PresentationMathMLTest(unittest.TestCase):
|
|
|
103
111
|
self.assertWrite(
|
|
104
112
|
x,
|
|
105
113
|
'<piecewise>'
|
|
106
|
-
'<piece>
|
|
107
|
-
'<piece>
|
|
108
|
-
'<otherwise>
|
|
114
|
+
f'<piece>{ca + c1}</piece>'
|
|
115
|
+
f'<piece>{cb + c2}</piece>'
|
|
116
|
+
f'<otherwise>{cc}</otherwise>'
|
|
109
117
|
'</piecewise>'
|
|
110
118
|
)
|
|
111
119
|
|
|
@@ -119,63 +127,69 @@ class PresentationMathMLTest(unittest.TestCase):
|
|
|
119
127
|
# Tests writing basic functions
|
|
120
128
|
|
|
121
129
|
a = myokit.Name(self.avar)
|
|
122
|
-
b = myokit.
|
|
130
|
+
b = myokit.Name(self.bvar)
|
|
131
|
+
c = myokit.Number('12', 'pF')
|
|
123
132
|
ca = '<mi>c.a</mi>'
|
|
124
|
-
cb = '<
|
|
133
|
+
cb = '<mi>c.b</mi>'
|
|
134
|
+
cc = '<mn>12.0</mn>'
|
|
125
135
|
|
|
126
136
|
# Power
|
|
127
137
|
x = myokit.Power(a, b)
|
|
128
|
-
self.assertWrite(x, '<msup>
|
|
138
|
+
self.assertWrite(x, f'<msup>{ca}{cb}</msup>')
|
|
139
|
+
x = myokit.Power(myokit.Power(a, b), c)
|
|
140
|
+
self.assertWrite(x, f'<msup><msup>{ca}{cb}</msup>{cc}</msup>')
|
|
141
|
+
x = myokit.Power(a, myokit.Power(b, c))
|
|
142
|
+
self.assertWrite(x, f'<msup>{ca}<msup>{cb}{cc}</msup></msup>')
|
|
129
143
|
|
|
130
144
|
# Sqrt
|
|
131
145
|
x = myokit.Sqrt(b)
|
|
132
146
|
self.assertWrite(
|
|
133
|
-
x, '<mrow><mi>root</mi><mfenced>
|
|
147
|
+
x, f'<mrow><mi>root</mi><mfenced>{cb}</mfenced></mrow>')
|
|
134
148
|
|
|
135
149
|
# Exp
|
|
136
150
|
x = myokit.Exp(a)
|
|
137
|
-
self.assertWrite(x, '<msup><mi>e</mi>
|
|
151
|
+
self.assertWrite(x, f'<msup><mi>e</mi>{ca}</msup>')
|
|
138
152
|
|
|
139
153
|
# Log(a)
|
|
140
154
|
x = myokit.Log(b)
|
|
141
155
|
self.assertWrite(
|
|
142
|
-
x, '<mrow><mi>ln</mi><mfenced>
|
|
156
|
+
x, f'<mrow><mi>ln</mi><mfenced>{cb}</mfenced></mrow>')
|
|
143
157
|
|
|
144
158
|
# Log(a, b)
|
|
145
159
|
x = myokit.Log(a, b)
|
|
146
160
|
self.assertWrite(
|
|
147
161
|
x,
|
|
148
|
-
'<mrow><msub><mi>log</mi>
|
|
149
|
-
'<mfenced>
|
|
162
|
+
f'<mrow><msub><mi>log</mi>{cb}</msub>'
|
|
163
|
+
f'<mfenced>{ca}</mfenced></mrow>'
|
|
150
164
|
)
|
|
151
165
|
|
|
152
166
|
# Log10
|
|
153
167
|
x = myokit.Log10(b)
|
|
154
168
|
self.assertWrite(
|
|
155
|
-
x, '<mrow><mi>log</mi><mfenced>
|
|
169
|
+
x, f'<mrow><mi>log</mi><mfenced>{cb}</mfenced></mrow>')
|
|
156
170
|
|
|
157
171
|
# Floor
|
|
158
172
|
x = myokit.Floor(b)
|
|
159
173
|
self.assertWrite(
|
|
160
|
-
x, '<mrow><mi>floor</mi><mfenced>
|
|
174
|
+
x, f'<mrow><mi>floor</mi><mfenced>{cb}</mfenced></mrow>')
|
|
161
175
|
|
|
162
176
|
# Ceil
|
|
163
177
|
x = myokit.Ceil(b)
|
|
164
178
|
self.assertWrite(
|
|
165
|
-
x, '<mrow><mi>ceiling</mi><mfenced>
|
|
179
|
+
x, f'<mrow><mi>ceiling</mi><mfenced>{cb}</mfenced></mrow>')
|
|
166
180
|
|
|
167
181
|
# Abs
|
|
168
182
|
x = myokit.Abs(b)
|
|
169
183
|
self.assertWrite(
|
|
170
|
-
x, '<mrow><mi>abs</mi><mfenced>
|
|
184
|
+
x, f'<mrow><mi>abs</mi><mfenced>{cb}</mfenced></mrow>')
|
|
171
185
|
|
|
172
186
|
# Quotient
|
|
173
187
|
x = myokit.Quotient(a, b)
|
|
174
|
-
self.assertWrite(x, '<mrow>
|
|
188
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>//</mo>{cb}</mrow>')
|
|
175
189
|
|
|
176
190
|
# Remainder
|
|
177
191
|
x = myokit.Remainder(a, b)
|
|
178
|
-
self.assertWrite(x, '<mrow>
|
|
192
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>%</mo>{cb}</mrow>')
|
|
179
193
|
|
|
180
194
|
def test_inequalities(self):
|
|
181
195
|
# Test writing (in)equalities
|
|
@@ -187,31 +201,29 @@ class PresentationMathMLTest(unittest.TestCase):
|
|
|
187
201
|
|
|
188
202
|
# Equal
|
|
189
203
|
x = myokit.Equal(a, b)
|
|
190
|
-
self.assertWrite(x, '<mrow>
|
|
204
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>==</mo>{cb}</mrow>')
|
|
191
205
|
|
|
192
206
|
# NotEqual
|
|
193
207
|
x = myokit.NotEqual(a, b)
|
|
194
|
-
self.assertWrite(x, '<mrow>
|
|
208
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>!=</mo>{cb}</mrow>')
|
|
195
209
|
|
|
196
210
|
# More
|
|
197
211
|
x = myokit.More(a, b)
|
|
198
|
-
self.assertWrite(x, '<mrow>
|
|
212
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>></mo>{cb}</mrow>')
|
|
199
213
|
|
|
200
214
|
# Less
|
|
201
215
|
x = myokit.Less(a, b)
|
|
202
|
-
self.assertWrite(x, '<mrow>
|
|
216
|
+
self.assertWrite(x, f'<mrow>{ca}<mo><</mo>{cb}</mrow>')
|
|
203
217
|
|
|
204
218
|
# MoreEqual
|
|
205
219
|
# Named version ≥ is not output, shows decimal code instead
|
|
206
220
|
x = myokit.MoreEqual(a, b)
|
|
207
|
-
self.assertWrite(
|
|
208
|
-
x, '<mrow>' + ca + '<mo>≥</mo>' + cb + '</mrow>')
|
|
221
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>≥</mo>{cb}</mrow>')
|
|
209
222
|
|
|
210
223
|
# LessEqual
|
|
211
224
|
# Named version ≤ is not output, shows decimal code instead
|
|
212
225
|
x = myokit.LessEqual(a, b)
|
|
213
|
-
self.assertWrite(
|
|
214
|
-
x, '<mrow>' + ca + '<mo>≤</mo>' + cb + '</mrow>')
|
|
226
|
+
self.assertWrite(x, f'<mrow>{ca}<mo>≤</mo>{cb}</mrow>')
|
|
215
227
|
|
|
216
228
|
def test_logic_operators(self):
|
|
217
229
|
# Tests writing logic operators
|
|
@@ -224,15 +236,15 @@ class PresentationMathMLTest(unittest.TestCase):
|
|
|
224
236
|
# Not
|
|
225
237
|
x = myokit.Not(cond1)
|
|
226
238
|
self.assertWrite(
|
|
227
|
-
x, '<mrow><mo>
|
|
239
|
+
x, f'<mrow><mo>not</mo><mo>(</mo>{c1}<mo>)</mo></mrow>')
|
|
228
240
|
|
|
229
241
|
# And
|
|
230
242
|
x = myokit.And(cond1, cond2)
|
|
231
|
-
self.assertWrite(x, '<mrow>
|
|
243
|
+
self.assertWrite(x, f'<mrow>{c1}<mo>and</mo>{c2}</mrow>')
|
|
232
244
|
|
|
233
245
|
# Or
|
|
234
246
|
x = myokit.Or(cond1, cond2)
|
|
235
|
-
self.assertWrite(x, '<mrow>
|
|
247
|
+
self.assertWrite(x, f'<mrow>{c1}<mo>or</mo>{c2}</mrow>')
|
|
236
248
|
|
|
237
249
|
def test_name_and_numbers(self):
|
|
238
250
|
# Test name and number writing
|
|
@@ -259,32 +271,32 @@ class PresentationMathMLTest(unittest.TestCase):
|
|
|
259
271
|
# Sin
|
|
260
272
|
x = myokit.Sin(b)
|
|
261
273
|
self.assertWrite(
|
|
262
|
-
x, '<mrow><mi>sin</mi><mfenced>
|
|
274
|
+
x, f'<mrow><mi>sin</mi><mfenced>{cb}</mfenced></mrow>')
|
|
263
275
|
|
|
264
276
|
# Cos
|
|
265
277
|
x = myokit.Cos(b)
|
|
266
278
|
self.assertWrite(
|
|
267
|
-
x, '<mrow><mi>cos</mi><mfenced>
|
|
279
|
+
x, f'<mrow><mi>cos</mi><mfenced>{cb}</mfenced></mrow>')
|
|
268
280
|
|
|
269
281
|
# Tan
|
|
270
282
|
x = myokit.Tan(b)
|
|
271
283
|
self.assertWrite(
|
|
272
|
-
x, '<mrow><mi>tan</mi><mfenced>
|
|
284
|
+
x, f'<mrow><mi>tan</mi><mfenced>{cb}</mfenced></mrow>')
|
|
273
285
|
|
|
274
286
|
# ASin
|
|
275
287
|
x = myokit.ASin(b)
|
|
276
288
|
self.assertWrite(
|
|
277
|
-
x, '<mrow><mi>arcsin</mi><mfenced>
|
|
289
|
+
x, f'<mrow><mi>arcsin</mi><mfenced>{cb}</mfenced></mrow>')
|
|
278
290
|
|
|
279
291
|
# ACos
|
|
280
292
|
x = myokit.ACos(b)
|
|
281
293
|
self.assertWrite(
|
|
282
|
-
x, '<mrow><mi>arccos</mi><mfenced>
|
|
294
|
+
x, f'<mrow><mi>arccos</mi><mfenced>{cb}</mfenced></mrow>')
|
|
283
295
|
|
|
284
296
|
# ATan
|
|
285
297
|
x = myokit.ATan(b)
|
|
286
298
|
self.assertWrite(
|
|
287
|
-
x, '<mrow><mi>arctan</mi><mfenced>
|
|
299
|
+
x, f'<mrow><mi>arctan</mi><mfenced>{cb}</mfenced></mrow>')
|
|
288
300
|
|
|
289
301
|
def test_unknown_expression(self):
|
|
290
302
|
# Test without a Myokit expression
|