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,218 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# Tests the expression writer for Matlab.
|
|
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.matlab
|
|
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
|
+
If, Piecewise,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
import myokit.tests
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MatlabExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
|
|
25
|
+
_name = 'matlab'
|
|
26
|
+
_target = myokit.formats.matlab.MatlabExpressionWriter
|
|
27
|
+
|
|
28
|
+
def test_number(self):
|
|
29
|
+
self.eq(Number(1), '1.0')
|
|
30
|
+
self.eq(Number(-2), '-2.0')
|
|
31
|
+
self.eq(Number(13, 'mV'), '13.0')
|
|
32
|
+
|
|
33
|
+
def test_name(self):
|
|
34
|
+
self.eq(self.a, 'a')
|
|
35
|
+
w = self._target()
|
|
36
|
+
w.set_lhs_function(lambda v: v.var().qname().upper())
|
|
37
|
+
self.assertEqual(w.ex(self.a), 'COMP.A')
|
|
38
|
+
|
|
39
|
+
def test_derivative(self):
|
|
40
|
+
self.eq(myokit.Derivative(self.a), 'dot(a)')
|
|
41
|
+
|
|
42
|
+
def test_partial_derivative(self):
|
|
43
|
+
e = myokit.PartialDerivative(self.a, self.b)
|
|
44
|
+
self.assertRaisesRegex(NotImplementedError, 'Partial', self.w.ex, e)
|
|
45
|
+
|
|
46
|
+
def test_initial_value(self):
|
|
47
|
+
e = myokit.InitialValue(self.a)
|
|
48
|
+
self.assertRaisesRegex(NotImplementedError, 'Initial', self.w.ex, e)
|
|
49
|
+
|
|
50
|
+
def test_prefix_plus(self):
|
|
51
|
+
# Inherited from Python writer
|
|
52
|
+
# Test with numbers
|
|
53
|
+
p = Number(11, 'kV')
|
|
54
|
+
self.eq(PrefixPlus(p), '+11.0')
|
|
55
|
+
self.eq(PrefixPlus(PrefixPlus(p)), '++11.0')
|
|
56
|
+
self.eq(PrefixPlus(Number('+1')), '+1.0')
|
|
57
|
+
|
|
58
|
+
a, b, c = self.abc
|
|
59
|
+
self.eq(PrefixPlus(Plus(a, b)), '+(a + b)')
|
|
60
|
+
self.eq(Divide(PrefixPlus(Plus(a, b)), c), '+(a + b) / c')
|
|
61
|
+
self.eq(Power(PrefixPlus(a), b), '(+a)^b')
|
|
62
|
+
|
|
63
|
+
def test_prefix_minus(self):
|
|
64
|
+
# Inherited from Python writer
|
|
65
|
+
# Test with numbers
|
|
66
|
+
p = Number(11, 'kV')
|
|
67
|
+
self.eq(PrefixMinus(p), '-11.0')
|
|
68
|
+
self.eq(PrefixMinus(PrefixMinus(p)), '--11.0')
|
|
69
|
+
self.eq(PrefixMinus(Number(-1)), '--1.0')
|
|
70
|
+
|
|
71
|
+
a, b, c = self.abc
|
|
72
|
+
self.eq(PrefixMinus(Minus(a, b)), '-(a - b)')
|
|
73
|
+
self.eq(Multiply(PrefixMinus(Plus(b, a)), c), '-(b + a) * c')
|
|
74
|
+
self.eq(Power(PrefixMinus(a), b), '(-a)^b')
|
|
75
|
+
|
|
76
|
+
def test_plus_minus(self):
|
|
77
|
+
# Inherited from Python writer
|
|
78
|
+
a, b, c = self.abc
|
|
79
|
+
self.eq(Plus(a, b), 'a + b')
|
|
80
|
+
self.eq(Plus(Plus(a, b), c), 'a + b + c')
|
|
81
|
+
self.eq(Plus(a, Plus(b, c)), 'a + (b + c)')
|
|
82
|
+
|
|
83
|
+
self.eq(Minus(a, b), 'a - b')
|
|
84
|
+
self.eq(Minus(Minus(a, b), c), 'a - b - c')
|
|
85
|
+
self.eq(Minus(a, Minus(b, c)), 'a - (b - c)')
|
|
86
|
+
|
|
87
|
+
self.eq(Minus(a, b), 'a - b')
|
|
88
|
+
self.eq(Plus(Minus(a, b), c), 'a - b + c')
|
|
89
|
+
self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
|
|
90
|
+
self.eq(Minus(Plus(a, b), c), 'a + b - c')
|
|
91
|
+
self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
|
|
92
|
+
|
|
93
|
+
def test_multiply_divide(self):
|
|
94
|
+
# Inherited from Python writer
|
|
95
|
+
|
|
96
|
+
a, b, c = self.abc
|
|
97
|
+
self.eq(Multiply(a, b), 'a * b')
|
|
98
|
+
# Left-to-right, so (a * b) * c is the same as a * b * c...
|
|
99
|
+
self.eq(Multiply(Multiply(a, b), c), 'a * b * c')
|
|
100
|
+
# ...but order-of-operations-wise, a * (b * c) is different!
|
|
101
|
+
self.eq(Multiply(a, Multiply(b, c)), 'a * (b * c)')
|
|
102
|
+
# Note that a user typing a * b * c results in (a * b) * c
|
|
103
|
+
|
|
104
|
+
self.eq(Divide(a, b), 'a / b')
|
|
105
|
+
self.eq(Divide(Divide(a, b), c), 'a / b / c')
|
|
106
|
+
self.eq(Divide(a, Divide(b, c)), 'a / (b / c)')
|
|
107
|
+
|
|
108
|
+
self.eq(Divide(Multiply(a, b), c), 'a * b / c')
|
|
109
|
+
self.eq(Multiply(Divide(a, b), c), 'a / b * c')
|
|
110
|
+
self.eq(Divide(a, Multiply(b, c)), 'a / (b * c)')
|
|
111
|
+
self.eq(Multiply(a, Divide(b, c)), 'a * (b / c)')
|
|
112
|
+
|
|
113
|
+
self.eq(Multiply(Minus(a, b), c), '(a - b) * c')
|
|
114
|
+
self.eq(Multiply(a, Plus(b, c)), 'a * (b + c)')
|
|
115
|
+
self.eq(Minus(Multiply(a, b), c), 'a * b - c')
|
|
116
|
+
self.eq(Plus(a, Multiply(b, c)), 'a + b * c')
|
|
117
|
+
self.eq(Divide(Plus(a, b), c), '(a + b) / c')
|
|
118
|
+
self.eq(Divide(a, Minus(b, c)), 'a / (b - c)')
|
|
119
|
+
self.eq(Plus(Divide(a, b), c), 'a / b + c')
|
|
120
|
+
self.eq(Minus(a, Divide(b, c)), 'a - b / c')
|
|
121
|
+
self.eq(Divide(a, Divide(b, c)), 'a / (b / c)')
|
|
122
|
+
self.eq(Divide(Divide(a, b), c), 'a / b / c')
|
|
123
|
+
|
|
124
|
+
def test_quotient(self):
|
|
125
|
+
a, b, c = self.abc
|
|
126
|
+
self.eq(Quotient(a, b), 'floor(a / b)')
|
|
127
|
+
self.eq(Quotient(Plus(a, c), b), 'floor((a + c) / b)')
|
|
128
|
+
self.eq(Quotient(Divide(a, c), b), 'floor(a / c / b)')
|
|
129
|
+
self.eq(Quotient(a, Divide(b, c)), 'floor(a / (b / c))')
|
|
130
|
+
self.eq(Multiply(Quotient(a, b), c), 'floor(a / b) * c')
|
|
131
|
+
# Bracket() method expects a PRODUCT level operation, so will add
|
|
132
|
+
# unnecessary brackets here
|
|
133
|
+
self.eq(Multiply(c, Quotient(a, b)), 'c * (floor(a / b))')
|
|
134
|
+
|
|
135
|
+
def test_remainder(self):
|
|
136
|
+
a, b, c = self.abc
|
|
137
|
+
self.eq(Remainder(a, b), 'mod(a, b)')
|
|
138
|
+
self.eq(Remainder(Plus(a, c), b), 'mod(a + c, b)')
|
|
139
|
+
self.eq(Multiply(Remainder(a, b), c), 'mod(a, b) * c')
|
|
140
|
+
# Bracket() method expects a PRODUCT level operation, so will add
|
|
141
|
+
# unnecessary brackets here
|
|
142
|
+
self.eq(Divide(c, Remainder(b, a)), 'c / (mod(b, a))')
|
|
143
|
+
|
|
144
|
+
def test_power(self):
|
|
145
|
+
a, b, c = self.abc
|
|
146
|
+
self.eq(Power(a, b), 'a^b')
|
|
147
|
+
|
|
148
|
+
# Like Myokit, Matlab sees a^b^c as (a^b)^c
|
|
149
|
+
self.eq(Power(Power(a, b), c), 'a^b^c')
|
|
150
|
+
self.eq(Power(a, Power(b, c)), 'a^(b^c)')
|
|
151
|
+
|
|
152
|
+
self.eq(Power(Plus(a, b), c), '(a + b)^c')
|
|
153
|
+
self.eq(Power(a, Minus(b, c)), 'a^(b - c)')
|
|
154
|
+
self.eq(Power(Multiply(a, b), c), '(a * b)^c')
|
|
155
|
+
self.eq(Power(a, Divide(b, c)), 'a^(b / c)')
|
|
156
|
+
|
|
157
|
+
def test_log(self):
|
|
158
|
+
a, b = self.ab
|
|
159
|
+
self.eq(Log(a), 'log(a)')
|
|
160
|
+
self.eq(Log10(a), 'log10(a)')
|
|
161
|
+
self.eq(Log(a, b), '(log(a) / log(b))')
|
|
162
|
+
|
|
163
|
+
def test_functions(self):
|
|
164
|
+
a, b = self.ab
|
|
165
|
+
self.eq(Sqrt(a), 'sqrt(a)')
|
|
166
|
+
self.eq(Exp(a), 'exp(a)')
|
|
167
|
+
self.eq(Sin(a), 'sin(a)')
|
|
168
|
+
self.eq(Cos(a), 'cos(a)')
|
|
169
|
+
self.eq(Tan(a), 'tan(a)')
|
|
170
|
+
self.eq(ASin(a), 'asin(a)')
|
|
171
|
+
self.eq(ACos(a), 'acos(a)')
|
|
172
|
+
self.eq(ATan(a), 'atan(a)')
|
|
173
|
+
self.eq(Floor(a), 'floor(a)')
|
|
174
|
+
self.eq(Ceil(a), 'ceil(a)')
|
|
175
|
+
self.eq(Abs(a), 'abs(a)')
|
|
176
|
+
|
|
177
|
+
def test_conditions(self):
|
|
178
|
+
a, b, c, d = self.abcd
|
|
179
|
+
|
|
180
|
+
self.eq(And(a, b), '(a && b)')
|
|
181
|
+
self.eq(Or(d, c), '(d || c)')
|
|
182
|
+
self.eq(Not(c), '(!c)')
|
|
183
|
+
|
|
184
|
+
self.eq(Equal(a, b), '(a == b)')
|
|
185
|
+
self.eq(NotEqual(a, b), '(a != b)')
|
|
186
|
+
self.eq(More(b, a), '(b > a)')
|
|
187
|
+
self.eq(Less(d, c), '(d < c)')
|
|
188
|
+
self.eq(MoreEqual(c, a), '(c >= a)')
|
|
189
|
+
self.eq(LessEqual(b, d), '(b <= d)')
|
|
190
|
+
|
|
191
|
+
self.eq(And(Equal(a, b), NotEqual(c, d)), '((a == b) && (c != d))')
|
|
192
|
+
self.eq(Or(More(d, c), Less(b, a)), '((d > c) || (b < a))')
|
|
193
|
+
self.eq(Not(Equal(d, d)), '(!(d == d))')
|
|
194
|
+
self.eq(Not(Or(Number(1), Number(2))), '(!(1.0 || 2.0))')
|
|
195
|
+
|
|
196
|
+
self.eq(Equal(Equal(Number(0), Number(0)), Number(0)),
|
|
197
|
+
'((0.0 == 0.0) == 0.0)')
|
|
198
|
+
|
|
199
|
+
def test_conditionals(self):
|
|
200
|
+
a, b, c, d = self.abcd
|
|
201
|
+
self.eq(If(Equal(a, b), d, c), 'ifthenelse((a == b), d, c)')
|
|
202
|
+
self.eq(Piecewise(NotEqual(d, c), b, a), 'ifthenelse((d != c), b, a)')
|
|
203
|
+
self.eq(Piecewise(Equal(a, b), c, Equal(a, d), Number(3), Number(4)),
|
|
204
|
+
'ifthenelse((a == b), c, ifthenelse((a == d), 3.0, 4.0))')
|
|
205
|
+
|
|
206
|
+
def test_unset_condition_function(self):
|
|
207
|
+
# No ternary operator, so matlab must always have an ifthenelse
|
|
208
|
+
w = self._target()
|
|
209
|
+
self.assertRaisesRegex(
|
|
210
|
+
ValueError, 'needs a condition function',
|
|
211
|
+
w.set_condition_function, None)
|
|
212
|
+
self.assertRaisesRegex(
|
|
213
|
+
ValueError, 'needs a condition function',
|
|
214
|
+
w.set_condition_function, '')
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
if __name__ == '__main__':
|
|
218
|
+
unittest.main()
|