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,226 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# Tests the expression writer for CUDA.
|
|
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.cuda
|
|
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 CudaExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
|
|
25
|
+
"""
|
|
26
|
+
Test conversion to Ansi C, as used by the Simulation.
|
|
27
|
+
Numerical tests are provided by composing and evaluating a single RHS.
|
|
28
|
+
"""
|
|
29
|
+
_name = 'cuda'
|
|
30
|
+
_target = myokit.formats.cuda.CudaExpressionWriter
|
|
31
|
+
|
|
32
|
+
def test_number(self):
|
|
33
|
+
self.eq(Number(1), '1.0f')
|
|
34
|
+
self.eq(Number(-2), '-2.0f')
|
|
35
|
+
self.eq(Number(13, 'mV'), '13.0f')
|
|
36
|
+
|
|
37
|
+
def test_number_double(self):
|
|
38
|
+
w = self._target(precision=myokit.DOUBLE_PRECISION)
|
|
39
|
+
self.assertEqual(w.ex(Number(1)), '1.0')
|
|
40
|
+
self.assertEqual(w.ex(Number(-2)), '-2.0')
|
|
41
|
+
self.assertEqual(w.ex(Number(13, 'mV')), '13.0')
|
|
42
|
+
|
|
43
|
+
def test_name(self):
|
|
44
|
+
self.eq(self.a, 'a')
|
|
45
|
+
w = self._target()
|
|
46
|
+
w.set_lhs_function(lambda v: v.var().qname().upper())
|
|
47
|
+
self.assertEqual(w.ex(self.a), 'COMP.A')
|
|
48
|
+
|
|
49
|
+
def test_derivative(self):
|
|
50
|
+
self.eq(myokit.Derivative(self.a), 'dot(a)')
|
|
51
|
+
|
|
52
|
+
def test_partial_derivative(self):
|
|
53
|
+
self.assertRaisesRegex(
|
|
54
|
+
NotImplementedError, 'Partial',
|
|
55
|
+
self.w.ex, myokit.PartialDerivative(self.a, self.b))
|
|
56
|
+
|
|
57
|
+
def test_initial_value(self):
|
|
58
|
+
self.assertRaisesRegex(
|
|
59
|
+
NotImplementedError, 'Initial',
|
|
60
|
+
self.w.ex, myokit.InitialValue(self.a))
|
|
61
|
+
|
|
62
|
+
def test_prefix_plus_minus(self):
|
|
63
|
+
# Inherited from c-based
|
|
64
|
+
|
|
65
|
+
p = Number(11, 'kV')
|
|
66
|
+
self.eq(PrefixPlus(p), '+11.0f')
|
|
67
|
+
self.eq(PrefixPlus(PrefixPlus(p)), '+(+11.0f)')
|
|
68
|
+
self.eq(PrefixPlus(PrefixPlus(PrefixPlus(p))), '+(+(+11.0f))')
|
|
69
|
+
self.eq(PrefixPlus(Number('+1')), '+1.0f')
|
|
70
|
+
self.eq(PrefixMinus(p), '-11.0f')
|
|
71
|
+
self.eq(PrefixMinus(PrefixMinus(p)), '-(-11.0f)')
|
|
72
|
+
self.eq(PrefixMinus(Number(-1)), '-(-1.0f)')
|
|
73
|
+
self.eq(PrefixMinus(PrefixMinus(Number(-2))), '-(-(-2.0f))')
|
|
74
|
+
|
|
75
|
+
# Test with operators of precedence SUM, PRODUCT
|
|
76
|
+
a, b, c = self.abc
|
|
77
|
+
self.eq(PrefixPlus(Plus(a, b)), '+(a + b)')
|
|
78
|
+
self.eq(Divide(PrefixPlus(Plus(a, b)), c), '+(a + b) / c')
|
|
79
|
+
self.eq(PrefixPlus(Divide(b, a)), '+(b / a)')
|
|
80
|
+
self.eq(PrefixMinus(Minus(a, b)), '-(a - b)')
|
|
81
|
+
self.eq(Multiply(PrefixMinus(Plus(b, a)), c), '-(b + a) * c')
|
|
82
|
+
self.eq(PrefixMinus(Divide(b, a)), '-(b / a)')
|
|
83
|
+
|
|
84
|
+
def test_prefix_plus_minus_double(self):
|
|
85
|
+
# Inherited from c-based
|
|
86
|
+
|
|
87
|
+
w = self._target(precision=myokit.DOUBLE_PRECISION)
|
|
88
|
+
p = Number(3, 'mA')
|
|
89
|
+
self.assertEqual(w.ex(PrefixPlus(p)), '+3.0')
|
|
90
|
+
self.assertEqual(w.ex(PrefixPlus(PrefixPlus(p))), '+(+3.0)')
|
|
91
|
+
self.assertEqual(
|
|
92
|
+
w.ex(PrefixPlus(PrefixPlus(PrefixPlus(p)))), '+(+(+3.0))')
|
|
93
|
+
self.assertEqual(w.ex(PrefixPlus(Number('+1'))), '+1.0')
|
|
94
|
+
self.assertEqual(w.ex(PrefixMinus(p)), '-3.0')
|
|
95
|
+
self.assertEqual(w.ex(PrefixMinus(PrefixMinus(p))), '-(-3.0)')
|
|
96
|
+
self.assertEqual(w.ex(PrefixMinus(Number(-1))), '-(-1.0)')
|
|
97
|
+
self.assertEqual(
|
|
98
|
+
w.ex(PrefixMinus(PrefixMinus(Number(-2)))), '-(-(-2.0))')
|
|
99
|
+
|
|
100
|
+
def test_arithmetic(self):
|
|
101
|
+
# Inherited from c-based
|
|
102
|
+
|
|
103
|
+
a, b, c = self.abc
|
|
104
|
+
self.eq(Minus(Plus(a, b), c), 'a + b - c')
|
|
105
|
+
self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
|
|
106
|
+
self.eq(Multiply(Minus(a, b), c), '(a - b) * c')
|
|
107
|
+
self.eq(Multiply(a, Plus(b, c)), 'a * (b + c)')
|
|
108
|
+
self.eq(Minus(Multiply(a, b), c), 'a * b - c')
|
|
109
|
+
self.eq(Divide(a, Minus(b, c)), 'a / (b - c)')
|
|
110
|
+
self.eq(Plus(Divide(a, b), c), 'a / b + c')
|
|
111
|
+
self.eq(Divide(Divide(a, b), c), 'a / b / c')
|
|
112
|
+
|
|
113
|
+
def test_quotient_remainder(self):
|
|
114
|
+
# Inherited from c-based
|
|
115
|
+
|
|
116
|
+
a, b, c = self.abc
|
|
117
|
+
self.eq(Quotient(a, Divide(b, c)), 'floorf(a / (b / c))')
|
|
118
|
+
self.eq(Remainder(Plus(a, c), b), '(a + c - b * floorf((a + c) / b))')
|
|
119
|
+
self.eq(Divide(c, Remainder(b, a)), 'c / ((b - a * floorf(b / a)))')
|
|
120
|
+
|
|
121
|
+
def test_quotient_remainder_double(self):
|
|
122
|
+
w = self._target(precision=myokit.DOUBLE_PRECISION)
|
|
123
|
+
a, b, c = self.abc
|
|
124
|
+
self.assertEqual(w.ex(Quotient(a, b)), 'floor(comp.a / comp.b)')
|
|
125
|
+
|
|
126
|
+
def test_power(self):
|
|
127
|
+
a, b, c = self.abc
|
|
128
|
+
self.eq(Power(a, b), 'powf(a, b)')
|
|
129
|
+
self.eq(Power(Power(a, b), c), 'powf(powf(a, b), c)')
|
|
130
|
+
self.eq(Power(a, Power(b, c)), 'powf(a, powf(b, c))')
|
|
131
|
+
|
|
132
|
+
self.eq(Power(Plus(a, b), c), 'powf(a + b, c)')
|
|
133
|
+
self.eq(Power(a, Minus(b, c)), 'powf(a, b - c)')
|
|
134
|
+
self.eq(Power(Multiply(a, b), c), 'powf(a * b, c)')
|
|
135
|
+
self.eq(Power(a, Divide(b, c)), 'powf(a, b / c)')
|
|
136
|
+
|
|
137
|
+
def test_power_double(self):
|
|
138
|
+
w = self._target(precision=myokit.DOUBLE_PRECISION)
|
|
139
|
+
a, b, c = self.abc
|
|
140
|
+
self.assertEqual(w.ex(Power(a, b)), 'pow(comp.a, comp.b)')
|
|
141
|
+
self.assertEqual(w.ex(Power(Plus(a, b), c)),
|
|
142
|
+
'pow(comp.a + comp.b, comp.c)')
|
|
143
|
+
|
|
144
|
+
def test_log(self):
|
|
145
|
+
a, b = self.ab
|
|
146
|
+
self.eq(Log(a), 'logf(a)')
|
|
147
|
+
self.eq(Log10(a), 'log10f(a)')
|
|
148
|
+
self.eq(Log(a, b), '(logf(a) / logf(b))')
|
|
149
|
+
|
|
150
|
+
def test_log_double(self):
|
|
151
|
+
w = self._target(precision=myokit.DOUBLE_PRECISION)
|
|
152
|
+
a, b = self.ab
|
|
153
|
+
self.assertEqual(w.ex(Log(a)), 'log(comp.a)')
|
|
154
|
+
self.assertEqual(w.ex(Log10(b)), 'log10(comp.b)')
|
|
155
|
+
self.assertEqual(w.ex(Log(b, a)), '(log(comp.b) / log(comp.a))')
|
|
156
|
+
|
|
157
|
+
def test_functions(self):
|
|
158
|
+
a = self.a
|
|
159
|
+
self.eq(Sqrt(self.a), 'sqrtf(a)')
|
|
160
|
+
self.eq(Exp(self.a), 'expf(a)')
|
|
161
|
+
self.eq(Sin(self.a), 'sinf(a)')
|
|
162
|
+
self.eq(Cos(self.a), 'cosf(a)')
|
|
163
|
+
self.eq(Tan(self.a), 'tanf(a)')
|
|
164
|
+
self.eq(ASin(self.a), 'asinf(a)')
|
|
165
|
+
self.eq(ACos(self.a), 'acosf(a)')
|
|
166
|
+
self.eq(ATan(self.a), 'atanf(a)')
|
|
167
|
+
self.eq(Floor(self.a), 'floorf(a)')
|
|
168
|
+
self.eq(Ceil(self.a), 'ceilf(a)')
|
|
169
|
+
self.eq(Abs(self.a), 'fabsf(a)')
|
|
170
|
+
|
|
171
|
+
def test_functions_double(self):
|
|
172
|
+
w = self._target(precision=myokit.DOUBLE_PRECISION)
|
|
173
|
+
self.assertEqual(w.ex(Sqrt(self.a)), 'sqrt(comp.a)')
|
|
174
|
+
self.assertEqual(w.ex(Exp(self.a)), 'exp(comp.a)')
|
|
175
|
+
self.assertEqual(w.ex(Sin(self.a)), 'sin(comp.a)')
|
|
176
|
+
self.assertEqual(w.ex(Cos(self.a)), 'cos(comp.a)')
|
|
177
|
+
self.assertEqual(w.ex(Tan(self.a)), 'tan(comp.a)')
|
|
178
|
+
self.assertEqual(w.ex(ASin(self.a)), 'asin(comp.a)')
|
|
179
|
+
self.assertEqual(w.ex(ACos(self.a)), 'acos(comp.a)')
|
|
180
|
+
self.assertEqual(w.ex(ATan(self.a)), 'atan(comp.a)')
|
|
181
|
+
self.assertEqual(w.ex(Floor(self.a)), 'floor(comp.a)')
|
|
182
|
+
self.assertEqual(w.ex(Ceil(self.a)), 'ceil(comp.a)')
|
|
183
|
+
self.assertEqual(w.ex(Abs(self.a)), 'fabs(comp.a)')
|
|
184
|
+
|
|
185
|
+
def test_conditions(self):
|
|
186
|
+
# Inherited from c-based
|
|
187
|
+
|
|
188
|
+
a, b, c, d = self.abcd
|
|
189
|
+
self.eq(And(a, b), '(a && b)')
|
|
190
|
+
self.eq(Or(d, c), '(d || c)')
|
|
191
|
+
self.eq(Not(And(a, b)), '(!(a && b))')
|
|
192
|
+
self.eq(Not(c), '(!(c))')
|
|
193
|
+
|
|
194
|
+
self.eq(Equal(a, b), '(a == b)')
|
|
195
|
+
self.eq(NotEqual(a, b), '(a != b)')
|
|
196
|
+
self.eq(More(b, a), '(b > a)')
|
|
197
|
+
self.eq(Less(d, c), '(d < c)')
|
|
198
|
+
self.eq(MoreEqual(c, a), '(c >= a)')
|
|
199
|
+
self.eq(LessEqual(b, d), '(b <= d)')
|
|
200
|
+
|
|
201
|
+
self.eq(And(Equal(a, b), NotEqual(c, d)), '((a == b) && (c != d))')
|
|
202
|
+
self.eq(Or(More(d, c), Less(b, a)), '((d > c) || (b < a))')
|
|
203
|
+
self.eq(Not(Or(Number(1), Number(2))), '(!(1.0f || 2.0f))')
|
|
204
|
+
self.eq(Not(Less(Number(1), Number(2))), '(!(1.0f < 2.0f))')
|
|
205
|
+
self.eq(Not(Plus(Number(1), Number(2))), '(!(1.0f + 2.0f))')
|
|
206
|
+
|
|
207
|
+
self.eq(Equal(Equal(Number(0), Number(0)), Number(0)),
|
|
208
|
+
'((0.0f == 0.0f) == 0.0f)')
|
|
209
|
+
|
|
210
|
+
def test_conditionals(self):
|
|
211
|
+
# Inherited from c-based
|
|
212
|
+
|
|
213
|
+
a, b, c, d = self.abcd
|
|
214
|
+
self.eq(If(Equal(a, b), d, c), '((a == b) ? d : c)')
|
|
215
|
+
self.eq(Piecewise(NotEqual(d, c), b, a), '((d != c) ? b : a)')
|
|
216
|
+
self.eq(Piecewise(Equal(a, b), c, Equal(a, d), Number(3), Number(4)),
|
|
217
|
+
'((a == b) ? c : ((a == d) ? 3.0f : 4.0f))')
|
|
218
|
+
|
|
219
|
+
# If condition is not a condition
|
|
220
|
+
self.eq(If(a, d, c), '((a) ? d : c)')
|
|
221
|
+
self.eq(Piecewise(a, b, c, d, Number(4)),
|
|
222
|
+
'((a) ? b : ((c) ? d : 4.0f))')
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if __name__ == '__main__':
|
|
226
|
+
unittest.main()
|
|
@@ -11,6 +11,15 @@ import unittest
|
|
|
11
11
|
import myokit
|
|
12
12
|
import myokit.formats
|
|
13
13
|
import myokit.formats.easyml
|
|
14
|
+
import myokit.tests
|
|
15
|
+
|
|
16
|
+
from myokit import (
|
|
17
|
+
Number, PrefixPlus, PrefixMinus, Plus, Minus,
|
|
18
|
+
Multiply, Divide, Quotient, Remainder, Power, Sqrt,
|
|
19
|
+
Exp, Log, Log10, Sin, Cos, Tan, ASin, ACos, ATan, Floor, Ceil, Abs,
|
|
20
|
+
Not, And, Or, Equal, NotEqual, More, Less, MoreEqual, LessEqual,
|
|
21
|
+
If, Piecewise,
|
|
22
|
+
)
|
|
14
23
|
|
|
15
24
|
from myokit.tests import TemporaryDirectory, WarningCollector, DIR_DATA
|
|
16
25
|
|
|
@@ -247,164 +256,172 @@ class EasyMLExporterTest(unittest.TestCase):
|
|
|
247
256
|
self.assertTrue(e.supports_model())
|
|
248
257
|
|
|
249
258
|
|
|
250
|
-
class EasyMLExpressionWriterTest(
|
|
251
|
-
"""
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
self.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
self.assertEqual(w.ex(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
self.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
self.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
self.
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
self.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
self.
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
259
|
+
class EasyMLExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
|
|
260
|
+
""" Test conversion to EasyML syntax. """
|
|
261
|
+
_name = 'easyml'
|
|
262
|
+
_target = myokit.formats.easyml.EasyMLExpressionWriter
|
|
263
|
+
|
|
264
|
+
def test_number(self):
|
|
265
|
+
self.eq(Number(1), '1.0')
|
|
266
|
+
self.eq(Number(-2), '-2.0')
|
|
267
|
+
self.eq(Number(13, 'mV'), '13.0')
|
|
268
|
+
|
|
269
|
+
def test_name(self):
|
|
270
|
+
# Inherited from CBasedExpressionWriter
|
|
271
|
+
self.eq(self.a, 'a')
|
|
272
|
+
w = self._target()
|
|
273
|
+
w.set_lhs_function(lambda v: v.var().qname().upper())
|
|
274
|
+
self.assertEqual(w.ex(self.a), 'COMP.A')
|
|
275
|
+
|
|
276
|
+
def test_derivative(self):
|
|
277
|
+
# Inherited from CBasedExpressionWriter
|
|
278
|
+
self.eq(myokit.Derivative(self.a), 'dot(a)')
|
|
279
|
+
|
|
280
|
+
def test_partial_derivative(self):
|
|
281
|
+
e = myokit.PartialDerivative(self.a, self.b)
|
|
282
|
+
self.assertRaisesRegex(NotImplementedError, 'Partial', self.w.ex, e)
|
|
283
|
+
|
|
284
|
+
def test_initial_value(self):
|
|
285
|
+
e = myokit.InitialValue(self.a)
|
|
286
|
+
self.assertRaisesRegex(NotImplementedError, 'Initial', self.w.ex, e)
|
|
287
|
+
|
|
288
|
+
def test_prefix_plus_minus(self):
|
|
289
|
+
# Inherited from CBasedExpressionWriter
|
|
290
|
+
p = Number(11, 'kV')
|
|
291
|
+
a, b, c = self.abc
|
|
292
|
+
self.eq(PrefixPlus(p), '+11.0')
|
|
293
|
+
self.eq(PrefixPlus(PrefixPlus(PrefixPlus(p))), '+(+(+11.0))')
|
|
294
|
+
self.eq(Divide(PrefixPlus(Plus(a, b)), c), '+(a + b) / c')
|
|
295
|
+
self.eq(PrefixMinus(p), '-11.0')
|
|
296
|
+
self.eq(PrefixMinus(PrefixMinus(p)), '-(-11.0)')
|
|
297
|
+
self.eq(PrefixMinus(Number(-1)), '-(-1.0)')
|
|
298
|
+
self.eq(PrefixMinus(Minus(a, b)), '-(a - b)')
|
|
299
|
+
self.eq(Multiply(PrefixMinus(Plus(b, a)), c), '-(b + a) * c')
|
|
300
|
+
self.eq(PrefixMinus(Divide(b, a)), '-(b / a)')
|
|
301
|
+
|
|
302
|
+
def test_plus_minus(self):
|
|
303
|
+
a, b, c = self.abc
|
|
304
|
+
self.eq(Plus(a, b), 'a + b')
|
|
305
|
+
self.eq(Plus(Plus(a, b), c), 'a + b + c')
|
|
306
|
+
self.eq(Plus(a, Plus(b, c)), 'a + (b + c)')
|
|
307
|
+
|
|
308
|
+
self.eq(Minus(a, b), 'a - b')
|
|
309
|
+
self.eq(Minus(Minus(a, b), c), 'a - b - c')
|
|
310
|
+
self.eq(Minus(a, Minus(b, c)), 'a - (b - c)')
|
|
311
|
+
|
|
312
|
+
self.eq(Minus(a, b), 'a - b')
|
|
313
|
+
self.eq(Plus(Minus(a, b), c), 'a - b + c')
|
|
314
|
+
self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
|
|
315
|
+
self.eq(Minus(Plus(a, b), c), 'a + b - c')
|
|
316
|
+
self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
|
|
317
|
+
|
|
318
|
+
# Substitution of expm for 1 - exp() and exp() - 1
|
|
319
|
+
self.eq(Minus(Exp(Number(2)), Number(1)), 'expm1(2.0)')
|
|
320
|
+
self.eq(Minus(Number(1), Exp(Number(3))), '-expm1(3.0)')
|
|
321
|
+
|
|
322
|
+
def test_multiply_divide(self):
|
|
323
|
+
# Inherited from CBasedExpressionWriter
|
|
324
|
+
a, b, c = self.abc
|
|
325
|
+
self.eq(Multiply(a, b), 'a * b')
|
|
326
|
+
self.eq(Multiply(Multiply(a, b), c), 'a * b * c')
|
|
327
|
+
self.eq(Multiply(a, Multiply(b, c)), 'a * (b * c)')
|
|
328
|
+
self.eq(Divide(a, b), 'a / b')
|
|
329
|
+
self.eq(Divide(Divide(a, b), c), 'a / b / c')
|
|
330
|
+
self.eq(Divide(a, Divide(b, c)), 'a / (b / c)')
|
|
331
|
+
|
|
332
|
+
def test_quotient(self):
|
|
333
|
+
# Inherited from CBasedExpressionWriter
|
|
334
|
+
a, b, c = self.abc
|
|
335
|
+
with WarningCollector():
|
|
336
|
+
self.eq(Quotient(a, b), 'floor(a / b)')
|
|
337
|
+
self.eq(Quotient(Plus(a, c), b), 'floor((a + c) / b)')
|
|
338
|
+
self.eq(Quotient(Divide(a, c), b), 'floor(a / c / b)')
|
|
339
|
+
self.eq(Quotient(a, Divide(b, c)), 'floor(a / (b / c))')
|
|
340
|
+
self.eq(Multiply(Quotient(a, b), c), 'floor(a / b) * c')
|
|
341
|
+
self.eq(Multiply(c, Quotient(a, b)), 'c * (floor(a / b))')
|
|
342
|
+
|
|
343
|
+
def test_remainder(self):
|
|
344
|
+
# Inherited from CBasedExpressionWriter
|
|
345
|
+
a, b, c = self.abc
|
|
346
|
+
with WarningCollector():
|
|
347
|
+
self.eq(Remainder(a, b), '(a - b * floor(a / b))')
|
|
348
|
+
self.eq(Remainder(Plus(a, c), b),
|
|
349
|
+
'(a + c - b * floor((a + c) / b))')
|
|
350
|
+
self.eq(Multiply(Remainder(a, b), c), '(a - b * floor(a / b)) * c')
|
|
351
|
+
self.eq(Divide(c, Remainder(b, a)), 'c / ((b - a * floor(b / a)))')
|
|
352
|
+
|
|
353
|
+
def test_power(self):
|
|
354
|
+
# Inherited from CBasedExpressionWriter
|
|
355
|
+
a, b, c = self.abc
|
|
356
|
+
self.eq(Power(a, b), 'pow(a, b)')
|
|
357
|
+
self.eq(Power(Power(a, b), c), 'pow(pow(a, b), c)')
|
|
358
|
+
self.eq(Power(a, Power(b, c)), 'pow(a, pow(b, c))')
|
|
359
|
+
|
|
360
|
+
def test_log(self):
|
|
361
|
+
# Inherited from CBasedExpressionWriter
|
|
362
|
+
a, b = self.ab
|
|
363
|
+
self.eq(Log(a), 'log(a)')
|
|
364
|
+
self.eq(Log10(a), 'log10(a)')
|
|
365
|
+
self.eq(Log(a, b), '(log(a) / log(b))')
|
|
366
|
+
|
|
367
|
+
def test_functions(self):
|
|
368
|
+
a, b = self.ab
|
|
369
|
+
|
|
370
|
+
self.eq(Sqrt(a), 'sqrt(a)')
|
|
371
|
+
self.eq(Exp(a), 'exp(a)')
|
|
372
|
+
self.eq(Cos(a), 'cos(a)')
|
|
373
|
+
self.eq(ACos(a), 'acos(a)')
|
|
374
|
+
self.eq(Abs(a), 'fabs(a)')
|
|
375
|
+
|
|
299
376
|
with WarningCollector() as c:
|
|
300
|
-
self.
|
|
301
|
-
|
|
302
|
-
|
|
377
|
+
self.eq(Sin(a), 'sin(a)')
|
|
378
|
+
self.assertIn('Unsupported', c.text())
|
|
379
|
+
|
|
303
380
|
with WarningCollector() as c:
|
|
304
|
-
self.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
x = myokit.Power(a, b)
|
|
308
|
-
self.assertEqual(w.ex(x), 'pow(c.a, 12.0)')
|
|
309
|
-
# Sqrt
|
|
310
|
-
x = myokit.Sqrt(b)
|
|
311
|
-
self.assertEqual(w.ex(x), 'sqrt(12.0)')
|
|
312
|
-
# Exp
|
|
313
|
-
x = myokit.Exp(a)
|
|
314
|
-
self.assertEqual(w.ex(x), 'exp(c.a)')
|
|
315
|
-
# Log(a)
|
|
316
|
-
x = myokit.Log(b)
|
|
317
|
-
self.assertEqual(w.ex(x), 'log(12.0)')
|
|
318
|
-
# Log(a, b)
|
|
319
|
-
x = myokit.Log(a, b)
|
|
320
|
-
self.assertEqual(w.ex(x), '(log(c.a) / log(12.0))')
|
|
321
|
-
# Log10
|
|
322
|
-
x = myokit.Log10(b)
|
|
323
|
-
self.assertEqual(w.ex(x), 'log10(12.0)')
|
|
324
|
-
|
|
325
|
-
# Sin
|
|
381
|
+
self.eq(Tan(a), 'tan(a)')
|
|
382
|
+
self.assertIn('Unsupported', c.text())
|
|
383
|
+
|
|
326
384
|
with WarningCollector() as c:
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
# Cos
|
|
330
|
-
x = myokit.Cos(b)
|
|
331
|
-
self.assertEqual(w.ex(x), 'cos(12.0)')
|
|
332
|
-
# Tan
|
|
333
|
-
x = myokit.Tan(b)
|
|
334
|
-
self.assertEqual(w.ex(x), 'tan(12.0)')
|
|
335
|
-
# ASin
|
|
336
|
-
x = myokit.ASin(b)
|
|
337
|
-
self.assertEqual(w.ex(x), 'asin(12.0)')
|
|
338
|
-
# ACos
|
|
339
|
-
x = myokit.ACos(b)
|
|
340
|
-
self.assertEqual(w.ex(x), 'acos(12.0)')
|
|
341
|
-
# ATan
|
|
342
|
-
x = myokit.ATan(b)
|
|
343
|
-
self.assertEqual(w.ex(x), 'atan(12.0)')
|
|
385
|
+
self.eq(ASin(a), 'asin(a)')
|
|
386
|
+
self.assertIn('Unsupported', c.text())
|
|
344
387
|
|
|
345
388
|
with WarningCollector() as c:
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
self.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
self.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
self.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
self.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
self.
|
|
380
|
-
|
|
381
|
-
x = myokit.And(cond1, cond2)
|
|
382
|
-
self.assertEqual(w.ex(x), '((5.0 > 3.0) and (2.0 < 1.0))')
|
|
383
|
-
# Or
|
|
384
|
-
x = myokit.Or(cond1, cond2)
|
|
385
|
-
self.assertEqual(w.ex(x), '((5.0 > 3.0) or (2.0 < 1.0))')
|
|
386
|
-
|
|
387
|
-
# If
|
|
388
|
-
x = myokit.If(cond1, a, b)
|
|
389
|
-
self.assertEqual(w.ex(x), '((5.0 > 3.0) ? c.a : 12.0)')
|
|
390
|
-
# Piecewise
|
|
391
|
-
c = myokit.Number(1)
|
|
392
|
-
x = myokit.Piecewise(cond1, a, cond2, b, c)
|
|
393
|
-
self.assertEqual(
|
|
394
|
-
w.ex(x),
|
|
395
|
-
'((5.0 > 3.0) ? c.a : ((2.0 < 1.0) ? 12.0 : 1.0))')
|
|
396
|
-
|
|
397
|
-
# Test without a Myokit expression
|
|
398
|
-
self.assertRaisesRegex(
|
|
399
|
-
ValueError, 'Unknown expression type', w.ex, 7)
|
|
400
|
-
|
|
401
|
-
def test_easyml_ewriter_fetching(self):
|
|
402
|
-
|
|
403
|
-
# Test fetching using ewriter method
|
|
404
|
-
w = myokit.formats.ewriter('easyml')
|
|
405
|
-
self.assertIsInstance(w, myokit.formats.easyml.EasyMLExpressionWriter)
|
|
389
|
+
self.eq(ATan(a), 'atan(a)')
|
|
390
|
+
self.assertIn('Unsupported', c.text())
|
|
391
|
+
|
|
392
|
+
with WarningCollector() as c:
|
|
393
|
+
self.eq(Floor(a), 'floor(a)')
|
|
394
|
+
self.assertIn('Unsupported', c.text())
|
|
395
|
+
|
|
396
|
+
with WarningCollector() as c:
|
|
397
|
+
self.eq(Ceil(a), 'ceil(a)')
|
|
398
|
+
self.assertIn('Unsupported', c.text())
|
|
399
|
+
|
|
400
|
+
def test_conditions(self):
|
|
401
|
+
# Inherited from CBasedExpressionWriter
|
|
402
|
+
a, b, c, d = self.abcd
|
|
403
|
+
self.eq(Not(And(a, b)), '(!(a && b))')
|
|
404
|
+
self.eq(Not(c), '(!(c))')
|
|
405
|
+
self.eq(And(Equal(a, b), NotEqual(c, d)), '((a == b) && (c != d))')
|
|
406
|
+
self.eq(Or(More(d, c), MoreEqual(b, a)), '((d > c) || (b >= a))')
|
|
407
|
+
self.eq(Or(Less(d, c), LessEqual(b, a)), '((d < c) || (b <= a))')
|
|
408
|
+
self.eq(Not(Or(Number(1), Number(2))), '(!(1.0 || 2.0))')
|
|
409
|
+
self.eq(Not(Less(Number(1), Number(2))), '(!(1.0 < 2.0))')
|
|
410
|
+
self.eq(Not(Plus(Number(1), Number(2))), '(!(1.0 + 2.0))')
|
|
411
|
+
self.eq(Equal(Equal(Number(0), Number(0)), Number(0)),
|
|
412
|
+
'((0.0 == 0.0) == 0.0)')
|
|
413
|
+
|
|
414
|
+
def test_conditionals(self):
|
|
415
|
+
# Inherited from CBasedExpressionWriter
|
|
416
|
+
a, b, c, d = self.abcd
|
|
417
|
+
self.eq(If(Equal(a, b), d, c), '((a == b) ? d : c)')
|
|
418
|
+
self.eq(Piecewise(NotEqual(d, c), b, a), '((d != c) ? b : a)')
|
|
419
|
+
self.eq(Piecewise(Equal(a, b), c, Equal(a, d), Number(3), Number(4)),
|
|
420
|
+
'((a == b) ? c : ((a == d) ? 3.0 : 4.0))')
|
|
421
|
+
self.eq(If(a, d, c), '((a) ? d : c)')
|
|
422
|
+
self.eq(Piecewise(a, b, c, d, Number(4)),
|
|
423
|
+
'((a) ? b : ((c) ? d : 4.0))')
|
|
406
424
|
|
|
407
425
|
|
|
408
426
|
if __name__ == '__main__':
|
|
409
427
|
unittest.main()
|
|
410
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
#
|
|
3
|
-
# Tests
|
|
3
|
+
# Tests that exporters run: doesn't check output
|
|
4
4
|
#
|
|
5
5
|
# This file is part of Myokit.
|
|
6
6
|
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
@@ -95,74 +95,6 @@ class ExportTest(unittest.TestCase):
|
|
|
95
95
|
raise Exception(
|
|
96
96
|
'No types of export supported by: ' + exporter)
|
|
97
97
|
|
|
98
|
-
def test_runnable_exporter_shared(self):
|
|
99
|
-
# Test shared functionality of the TemplatedRunnableExporters.
|
|
100
|
-
|
|
101
|
-
e = myokit.formats.exporter('ansic')
|
|
102
|
-
|
|
103
|
-
# Load model, protocol
|
|
104
|
-
m, p, x = myokit.load('example')
|
|
105
|
-
|
|
106
|
-
# Create empty output directory as subdirectory of DIR_OUT
|
|
107
|
-
with TemporaryDirectory() as d:
|
|
108
|
-
path = d.path()
|
|
109
|
-
|
|
110
|
-
# Simple export
|
|
111
|
-
dpath = os.path.join(path, 'runnable1')
|
|
112
|
-
ret = e.runnable(dpath, m)
|
|
113
|
-
self.assertIsNone(ret)
|
|
114
|
-
self.assertTrue(os.path.isdir(dpath))
|
|
115
|
-
self.assertTrue(len(os.listdir(dpath)) > 0)
|
|
116
|
-
|
|
117
|
-
# Write to complex path
|
|
118
|
-
dpath = os.path.join(path, 'runnable2', 'nest', 'test')
|
|
119
|
-
ret = e.runnable(dpath, m, p)
|
|
120
|
-
self.assertIsNone(ret)
|
|
121
|
-
self.assertTrue(os.path.isdir(dpath))
|
|
122
|
-
self.assertTrue(len(os.listdir(dpath)) > 0)
|
|
123
|
-
|
|
124
|
-
# Overwrite existing path
|
|
125
|
-
ret = e.runnable(dpath, m, p)
|
|
126
|
-
self.assertIsNone(ret)
|
|
127
|
-
self.assertTrue(os.path.isdir(dpath))
|
|
128
|
-
self.assertTrue(len(os.listdir(dpath)) > 0)
|
|
129
|
-
|
|
130
|
-
# Path pointing to file
|
|
131
|
-
dpath = os.path.join(path, 'file')
|
|
132
|
-
with open(dpath, 'w') as f:
|
|
133
|
-
f.write('contents\n')
|
|
134
|
-
self.assertRaisesRegex(
|
|
135
|
-
myokit.ExportError, 'file exists', e.runnable, dpath, m, p)
|
|
136
|
-
|
|
137
|
-
# Directory exists where we're trying to write a file
|
|
138
|
-
dpath = os.path.join(path, 'runnable3')
|
|
139
|
-
fname = os.path.join(dpath, 'sim.c')
|
|
140
|
-
os.makedirs(fname)
|
|
141
|
-
self.assertRaisesRegex(
|
|
142
|
-
myokit.ExportError, 'Directory exists',
|
|
143
|
-
e.runnable, dpath, m, p)
|
|
144
|
-
|
|
145
|
-
# Directory embedded in the output file path
|
|
146
|
-
def embedded():
|
|
147
|
-
return {'sim.c': 'nested/sim.c'}
|
|
148
|
-
|
|
149
|
-
# 1. Normal operation
|
|
150
|
-
e._dict = embedded
|
|
151
|
-
dpath = os.path.join(path, 'runnable4')
|
|
152
|
-
ret = e.runnable(dpath, m, p)
|
|
153
|
-
self.assertIsNone(ret)
|
|
154
|
-
self.assertTrue(os.path.isdir(dpath))
|
|
155
|
-
self.assertTrue(len(os.listdir(dpath)) > 0)
|
|
156
|
-
|
|
157
|
-
# 2. Try to create directory where file exists
|
|
158
|
-
def embedded():
|
|
159
|
-
return {'sim.c': 'nested/sim.c/som.c'}
|
|
160
|
-
|
|
161
|
-
e._dict = embedded
|
|
162
|
-
dpath = os.path.join(path, 'runnable4')
|
|
163
|
-
self.assertRaisesRegex(
|
|
164
|
-
myokit.ExportError, 'file or link', e.runnable, dpath, m, p)
|
|
165
|
-
|
|
166
98
|
def test_completeness(self):
|
|
167
99
|
# Test that all exporters have a test (so meta!).
|
|
168
100
|
|