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.
Files changed (59) hide show
  1. myokit/__init__.py +5 -3
  2. myokit/__main__.py +9 -159
  3. myokit/_config.py +2 -2
  4. myokit/_expressions.py +6 -6
  5. myokit/_model_api.py +11 -7
  6. myokit/_myokit_version.py +1 -1
  7. myokit/_protocol.py +4 -0
  8. myokit/_sim/__init__.py +1 -0
  9. myokit/_sim/cvodessim.c +321 -177
  10. myokit/_sim/cvodessim.py +107 -43
  11. myokit/_sim/mcl.h +54 -0
  12. myokit/formats/__init__.py +63 -12
  13. myokit/formats/ansic/__init__.py +2 -1
  14. myokit/formats/ansic/_ewriter.py +159 -40
  15. myokit/formats/cpp/_ewriter.py +12 -1
  16. myokit/formats/cuda/_ewriter.py +15 -51
  17. myokit/formats/easyml/_ewriter.py +26 -54
  18. myokit/formats/heka/_patchmaster.py +15 -3
  19. myokit/formats/latex/_ewriter.py +103 -88
  20. myokit/formats/latex/_exporter.py +1 -1
  21. myokit/formats/mathml/_ewriter.py +2 -2
  22. myokit/formats/matlab/_ewriter.py +50 -28
  23. myokit/formats/opencl/_ewriter.py +61 -78
  24. myokit/formats/python/_ewriter.py +81 -50
  25. myokit/formats/stan/_ewriter.py +29 -37
  26. myokit/gui/source.py +1 -1
  27. myokit/lib/hh.py +3 -0
  28. myokit/lib/markov.py +6 -0
  29. myokit/tests/__init__.py +70 -0
  30. myokit/tests/data/decker.model +59 -59
  31. myokit/tests/test_formats.py +115 -7
  32. myokit/tests/test_formats_ansic.py +344 -0
  33. myokit/tests/test_formats_axon.py +17 -0
  34. myokit/tests/test_formats_cpp.py +97 -0
  35. myokit/tests/test_formats_cuda.py +226 -0
  36. myokit/tests/test_formats_easyml.py +169 -152
  37. myokit/tests/{test_formats_exporters.py → test_formats_exporters_run.py} +1 -69
  38. myokit/tests/test_formats_html.py +1 -3
  39. myokit/tests/test_formats_latex.py +211 -0
  40. myokit/tests/test_formats_mathml_content.py +13 -0
  41. myokit/tests/test_formats_mathml_presentation.py +54 -42
  42. myokit/tests/test_formats_matlab.py +218 -0
  43. myokit/tests/test_formats_opencl.py +206 -380
  44. myokit/tests/test_formats_python.py +557 -0
  45. myokit/tests/test_formats_stan.py +175 -0
  46. myokit/tests/test_formats_sympy.py +9 -2
  47. myokit/tests/test_lib_hh.py +36 -0
  48. myokit/tests/test_lib_plots.py +0 -16
  49. myokit/tests/test_model.py +21 -1
  50. myokit/tests/test_simulation_cvodes.py +137 -56
  51. myokit/tools.py +3 -2
  52. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/LICENSE.txt +1 -1
  53. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/METADATA +19 -8
  54. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/RECORD +57 -52
  55. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/WHEEL +1 -1
  56. myokit/tests/test_formats_expression_writers.py +0 -1281
  57. myokit/tests/test_formats_importers.py +0 -53
  58. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/entry_points.txt +0 -0
  59. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,557 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ # Tests writing of Python and NumPy equations.
4
+ # These are used in pyfunc() and related, so numerical tests are possible and
5
+ # desirable.
6
+ #
7
+ # This file is part of Myokit.
8
+ # See http://myokit.org for copyright, sharing, and licensing details.
9
+ #
10
+ import math
11
+ import unittest
12
+
13
+ import numpy
14
+
15
+ import myokit
16
+ import myokit.formats.python
17
+
18
+ from myokit import (
19
+ Number, PrefixPlus, PrefixMinus, Plus, Minus,
20
+ Multiply, Divide, Quotient, Remainder, Power, Sqrt,
21
+ Exp, Log, Log10, Sin, Cos, Tan, ASin, ACos, ATan, Floor, Ceil, Abs,
22
+ Not, And, Or, Equal, NotEqual, More, Less, MoreEqual, LessEqual,
23
+ If, Piecewise,
24
+ )
25
+
26
+ import myokit.tests
27
+
28
+
29
+ class PythonExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
30
+ """
31
+ Test conversion of expressions to Python.
32
+ This is used by pyfunc(), although that's usually done with the
33
+ NumPyExpressionWriter instead.
34
+ Numerical tests are provided.
35
+ """
36
+ _name = 'python'
37
+ _target = myokit.formats.python.PythonExpressionWriter
38
+
39
+ def py(self, expression, expected):
40
+ """ Test by converting to Python and executing. """
41
+ function = expression.pyfunc(use_numpy=False)
42
+ value = function()
43
+ self.assertEqual(value, expected)
44
+
45
+ def test_number(self):
46
+ self.eq(Number(1), '1.0')
47
+ self.eq(Number(-1.3274924373284374), '-1.32749243732843736e+00')
48
+ self.eq(Number(+1.3274924373284374), '1.32749243732843736e+00')
49
+ self.eq(Number(-2), '-2.0')
50
+ self.eq(Number(13, 'mV'), '13.0')
51
+ self.py(Number(14, 'mV'), 14)
52
+
53
+ def test_name(self):
54
+ self.eq(self.a, 'a')
55
+ w = self._target()
56
+ w.set_lhs_function(lambda v: v.var().qname().upper())
57
+ self.assertEqual(w.ex(self.a), 'COMP.A')
58
+
59
+ def test_derivative(self):
60
+ self.eq(myokit.Derivative(self.a), 'dot(a)')
61
+
62
+ def test_partial_derivative(self):
63
+ e = myokit.PartialDerivative(self.a, self.b)
64
+ self.assertRaisesRegex(NotImplementedError, 'Partial', self.w.ex, e)
65
+
66
+ def test_initial_value(self):
67
+ e = myokit.InitialValue(self.a)
68
+ self.assertRaisesRegex(NotImplementedError, 'Initial', self.w.ex, e)
69
+
70
+ def test_prefix_plus(self):
71
+ # Test with numbers
72
+ p = Number(11, 'kV')
73
+ self.eq(PrefixPlus(p), '+11.0')
74
+ self.eq(PrefixPlus(PrefixPlus(p)), '++11.0')
75
+ self.eq(PrefixPlus(Number('+1')), '+1.0')
76
+ self.py(PrefixPlus(Number(3)), 3)
77
+
78
+ # Test with operators of precedence SUM, PRODUCT, POWER
79
+ a, b, c = self.abc
80
+ self.eq(PrefixPlus(Plus(a, b)), '+(a + b)')
81
+ self.eq(Divide(PrefixPlus(Plus(a, b)), c), '+(a + b) / c')
82
+ self.eq(PrefixPlus(Remainder(b, a)), '+(b % a)')
83
+ self.eq(Power(PrefixPlus(a), b), '(+a)**b')
84
+ self.eq(Power(PrefixPlus(Power(b, a)), c), '(+b**a)**c')
85
+ self.eq(Power(a, PrefixPlus(Power(b, c))), 'a**(+b**c)')
86
+
87
+ def test_prefix_minus(self):
88
+ # Test with numbers
89
+ p = Number(11, 'kV')
90
+ self.eq(PrefixMinus(p), '-11.0')
91
+ self.eq(PrefixMinus(PrefixMinus(p)), '--11.0')
92
+ self.eq(PrefixMinus(Number(-1)), '--1.0')
93
+ self.py(PrefixMinus(Number(3)), -3)
94
+
95
+ # Test with operators of precedence SUM, PRODUCT, POWER
96
+ a, b, c = self.abc
97
+ self.eq(PrefixMinus(Minus(a, b)), '-(a - b)')
98
+ self.eq(Multiply(PrefixMinus(Plus(b, a)), c), '-(b + a) * c')
99
+ self.eq(PrefixMinus(Quotient(b, a)), '-(b // a)')
100
+ self.eq(Power(PrefixMinus(a), b), '(-a)**b')
101
+ self.eq(Power(PrefixMinus(Power(c, b)), a), '(-c**b)**a')
102
+ self.eq(Power(a, PrefixMinus(Power(b, c))), 'a**(-b**c)')
103
+
104
+ def test_plus_minus(self):
105
+ a, b, c = self.abc
106
+ self.eq(Plus(a, b), 'a + b')
107
+ self.eq(Plus(Plus(a, b), c), 'a + b + c')
108
+ self.eq(Plus(a, Plus(b, c)), 'a + (b + c)')
109
+
110
+ self.eq(Minus(a, b), 'a - b')
111
+ self.eq(Minus(Minus(a, b), c), 'a - b - c')
112
+ self.eq(Minus(a, Minus(b, c)), 'a - (b - c)')
113
+
114
+ self.eq(Minus(a, b), 'a - b')
115
+ self.eq(Plus(Minus(a, b), c), 'a - b + c')
116
+ self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
117
+ self.eq(Minus(Plus(a, b), c), 'a + b - c')
118
+ self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
119
+
120
+ self.py(Plus(Number(4), Number(2)), 6)
121
+ self.py(Minus(Number(5), Number(1.5)), 3.5)
122
+
123
+ def test_multiply_divide(self):
124
+ a, b, c = self.abc
125
+ self.eq(Multiply(a, b), 'a * b')
126
+ # Left-to-right, so (a * b) * c is the same as a * b * c...
127
+ self.eq(Multiply(Multiply(a, b), c), 'a * b * c')
128
+ # ...but order-of-operations-wise, a * (b * c) is different!
129
+ self.eq(Multiply(a, Multiply(b, c)), 'a * (b * c)')
130
+ # Note that a user typing a * b * c results in (a * b) * c
131
+
132
+ self.eq(Divide(a, b), 'a / b')
133
+ self.eq(Divide(Divide(a, b), c), 'a / b / c')
134
+ self.eq(Divide(a, Divide(b, c)), 'a / (b / c)')
135
+
136
+ self.eq(Divide(Multiply(a, b), c), 'a * b / c')
137
+ self.eq(Multiply(Divide(a, b), c), 'a / b * c')
138
+ self.eq(Divide(a, Multiply(b, c)), 'a / (b * c)')
139
+ self.eq(Multiply(a, Divide(b, c)), 'a * (b / c)')
140
+
141
+ self.eq(Multiply(Minus(a, b), c), '(a - b) * c')
142
+ self.eq(Multiply(a, Plus(b, c)), 'a * (b + c)')
143
+ self.eq(Minus(Multiply(a, b), c), 'a * b - c')
144
+ self.eq(Plus(a, Multiply(b, c)), 'a + b * c')
145
+ self.eq(Divide(Plus(a, b), c), '(a + b) / c')
146
+ self.eq(Divide(a, Minus(b, c)), 'a / (b - c)')
147
+ self.eq(Plus(Divide(a, b), c), 'a / b + c')
148
+ self.eq(Minus(a, Divide(b, c)), 'a - b / c')
149
+
150
+ self.py(Multiply(Number(7), Number(9)), 63)
151
+ self.py(Divide(Number(5), Number(2)), 2.5)
152
+ self.py(Multiply(Number(7), Plus(Number(1), Number(9))), 70)
153
+ self.py(Divide(Minus(Number(19), Number(5)), Number(2)), 7)
154
+
155
+ def test_quotient_remainder(self):
156
+ a, b, c = self.abc
157
+
158
+ self.eq(Remainder(Multiply(a, b), c), 'a * b % c')
159
+ self.eq(Multiply(Quotient(a, b), c), 'a // b * c')
160
+ self.eq(Quotient(a, Multiply(b, c)), 'a // (b * c)')
161
+ self.eq(Multiply(a, Remainder(b, c)), 'a * (b % c)')
162
+
163
+ self.eq(Divide(a, Quotient(b, c)), 'a / (b // c)')
164
+ self.eq(Divide(Quotient(b, c), a), 'b // c / a')
165
+ self.eq(Divide(a, Remainder(b, c)), 'a / (b % c)')
166
+ self.eq(Divide(Remainder(b, c), a), 'b % c / a')
167
+
168
+ self.py(Quotient(Number(10), Number(4)), 2) # 2*4 + 2
169
+ self.py(Remainder(Number(10), Number(4)), 2)
170
+ self.py(Quotient(Number(10), Number(6)), 1) # 6 + 4
171
+ self.py(Remainder(Number(10), Number(6)), 4)
172
+ self.py(Quotient(Number(5), Number(3)), 1) # 1*3 + 2
173
+ self.py(Remainder(Number(5), Number(3)), 2)
174
+ self.py(Quotient(Number(-5), Number(3)), -2) # -2*3 + 1
175
+ self.py(Remainder(Number(-5), Number(3)), 1)
176
+ self.py(Quotient(Number(5), Number(-3)), -2) # -2*-3 - 1
177
+ self.py(Remainder(Number(5), Number(-3)), -1)
178
+ self.py(Quotient(Number(-5), Number(-3)), 1) # 1*-3 - 2
179
+ self.py(Remainder(Number(-5), Number(-3)), -2)
180
+ # The quotient has sign sign(a)*sign(b)
181
+
182
+ def test_power(self):
183
+ a, b, c = self.abc
184
+ self.eq(Power(a, b), 'a**b')
185
+
186
+ # In Python, a**b**c = a**(b**c), whereas Myokit uses a^b^c = (a^b)^c
187
+ self.eq(Power(Power(a, b), c), '(a**b)**c')
188
+ self.eq(Power(a, Power(b, c)), 'a**b**c')
189
+
190
+ self.eq(Power(Plus(a, b), c), '(a + b)**c')
191
+ self.eq(Power(a, Minus(b, c)), 'a**(b - c)')
192
+ self.eq(Power(Multiply(a, b), c), '(a * b)**c')
193
+ self.eq(Power(a, Divide(b, c)), 'a**(b / c)')
194
+
195
+ self.py(Power(Number(4), Power(Number(2), Number(3))), 65536)
196
+ self.py(Power(Power(Number(2), Number(3)), Number(4)), 4096)
197
+
198
+ def test_functions(self):
199
+ a, b = self.ab
200
+
201
+ self.eq(Sqrt(a), 'math.sqrt(a)')
202
+ self.eq(Exp(a), 'math.exp(a)')
203
+ self.eq(Log(a), 'math.log(a)')
204
+ self.eq(Log(a, b), 'math.log(a, b)')
205
+ self.eq(Log10(a), 'math.log10(a)')
206
+ self.eq(Sin(a), 'math.sin(a)')
207
+ self.eq(Cos(a), 'math.cos(a)')
208
+ self.eq(Tan(a), 'math.tan(a)')
209
+ self.eq(ASin(a), 'math.asin(a)')
210
+ self.eq(ACos(a), 'math.acos(a)')
211
+ self.eq(ATan(a), 'math.atan(a)')
212
+ self.eq(Floor(a), 'math.floor(a)')
213
+ self.eq(Ceil(a), 'math.ceil(a)')
214
+ self.eq(Abs(a), 'abs(a)')
215
+
216
+ self.py(Sqrt(Number(9)), 3)
217
+ self.py(Exp(Number(3)), math.exp(3))
218
+ self.py(Log(Number(3)), math.log(3))
219
+ self.py(Log(Number(8), Number(2)), 3)
220
+ self.py(Log10(Number(100)), 2)
221
+ self.py(Sin(Number(1)), math.sin(1))
222
+ self.py(Cos(Number(1)), math.cos(1))
223
+ self.py(Tan(Number(4)), math.tan(4))
224
+ self.py(ASin(Number(0.4)), math.asin(0.4))
225
+ self.py(ACos(Number(0.4)), math.acos(0.4))
226
+ self.py(ATan(Number(0.4)), math.atan(0.4))
227
+ self.py(Floor(Number(3.9)), 3)
228
+ self.py(Ceil(Number(4.01)), 5)
229
+ self.py(Abs(PrefixMinus(Number(12))), 12)
230
+ self.py(Abs(Number(-13)), 13)
231
+
232
+ def test_conditions(self):
233
+ a, b, c, d = self.abcd
234
+
235
+ self.eq(And(a, b), '(a and b)')
236
+ self.eq(Or(d, c), '(d or c)')
237
+ self.eq(Not(c), '(not c)')
238
+
239
+ self.eq(Equal(a, b), '(a == b)')
240
+ self.eq(NotEqual(a, b), '(a != b)')
241
+ self.eq(More(b, a), '(b > a)')
242
+ self.eq(Less(d, c), '(d < c)')
243
+ self.eq(MoreEqual(c, a), '(c >= a)')
244
+ self.eq(LessEqual(b, d), '(b <= d)')
245
+
246
+ self.eq(And(Equal(a, b), NotEqual(c, d)), '((a == b) and (c != d))')
247
+ self.eq(Or(More(d, c), Less(b, a)), '((d > c) or (b < a))')
248
+ self.eq(Not(Equal(d, d)), '(not (d == d))')
249
+ self.eq(Not(Or(Number(1), Number(2))), '(not (1.0 or 2.0))')
250
+
251
+ true = Equal(Number(3), Number(3))
252
+ self.py(true, True)
253
+ false = NotEqual(Number(3), Number(3))
254
+ self.py(false, False)
255
+ self.py(More(Number(5), Number(3)), True)
256
+ self.py(More(Number(3), Number(3)), False)
257
+ self.py(MoreEqual(Number(3), Number(3)), True)
258
+ self.py(Less(Number(3), Number(5)), True)
259
+ self.py(More(Number(3), Number(3)), False)
260
+ self.py(LessEqual(Number(3), Number(3)), True)
261
+
262
+ self.py(And(true, false), False)
263
+ self.py(And(true, true), True)
264
+ self.py(Or(true, false), True)
265
+ self.py(Or(true, true), True)
266
+ self.py(Or(false, false), False)
267
+ self.py(Not(true), False)
268
+ self.py(Not(false), True)
269
+
270
+ self.eq(Equal(Equal(Number(0), Number(0)), Number(0)),
271
+ '((0.0 == 0.0) == 0.0)')
272
+ self.py(Equal(Equal(Number(0), Number(0)), Number(0)), False)
273
+ self.py(Equal(Equal(Number(0), Number(0)), Number(1)), True)
274
+
275
+ def test_conditionals(self):
276
+
277
+ a, b, c, d = self.abcd
278
+ self.eq(If(Equal(a, b), d, c), '(d if (a == b) else c)')
279
+ self.py(If(Equal(Number(1), Number(1)), Number(2), Number(3)), 2)
280
+ self.py(If(Equal(Number(10), Number(1)), Number(2), Number(3)), 3)
281
+
282
+ self.eq(Piecewise(NotEqual(d, c), b, a), '(b if (d != c) else a)')
283
+ self.eq(Piecewise(Equal(a, b), c, Equal(a, d), Number(3), Number(4)),
284
+ '(c if (a == b) else (3.0 if (a == d) else 4.0))')
285
+
286
+ self.py(Piecewise(Equal(Number(7), Number(7)), Number(2),
287
+ Equal(Number(7), Number(8)), Number(3),
288
+ Number(4)), 2)
289
+ self.py(Piecewise(Equal(Number(7), Number(7)), Number(2),
290
+ Equal(Number(7), Number(7)), Number(3),
291
+ Number(4)), 2)
292
+ self.py(Piecewise(NotEqual(Number(7), Number(7)), Number(2),
293
+ Equal(Number(7), Number(7)), Number(3),
294
+ Number(4)), 3)
295
+ self.py(Piecewise(NotEqual(Number(7), Number(7)), Number(2),
296
+ NotEqual(Number(8), Number(8)), Number(3),
297
+ Number(4)), 4)
298
+
299
+
300
+ class NumPyExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
301
+ """
302
+ Test conversion of expressions to NumPy-compatible Python.
303
+ This is used e.g. to graph expressions.
304
+ Numerical tests are provided.
305
+ """
306
+ _name = 'numpy'
307
+ _target = myokit.formats.python.NumPyExpressionWriter
308
+
309
+ def py(self, expression, expected, *arguments):
310
+ """ Test by executing in NumPy, possibly with vector arguments. """
311
+ function = expression.pyfunc(use_numpy=True)
312
+ if arguments:
313
+ arguments = [numpy.array(arg) for arg in arguments]
314
+ values = function(*arguments)
315
+ self.assertEqual(list(values), list(expected))
316
+ else:
317
+ value = function()
318
+ self.assertEqual(value, expected)
319
+
320
+ def test_number(self):
321
+ self.eq(Number(2), '2.0')
322
+ self.eq(Number(-1), '-1.0')
323
+ self.eq(Number(14, 'kV'), '14.0')
324
+ self.py(Number(-7, 'mA'), -7)
325
+
326
+ def test_name(self):
327
+ self.eq(self.b, 'b')
328
+ w = self._target()
329
+ w.set_lhs_function(lambda v: v.var().qname().upper())
330
+ self.assertEqual(w.ex(self.d), 'COMP.D')
331
+
332
+ def test_derivative(self):
333
+ self.eq(myokit.Derivative(self.d), 'dot(d)')
334
+
335
+ def test_partial_derivative(self):
336
+ e = myokit.InitialValue(self.a)
337
+ self.assertRaisesRegex(NotImplementedError, 'Initial', self.w.ex, e)
338
+
339
+ def test_initial_value(self):
340
+ e = myokit.PartialDerivative(self.a, self.b)
341
+ self.assertRaisesRegex(NotImplementedError, 'Partial', self.w.ex, e)
342
+
343
+ def test_prefix_plus(self):
344
+ # Test with numbers
345
+ p = Number(11, 'kV')
346
+ self.eq(PrefixPlus(p), '+11.0')
347
+ self.eq(PrefixPlus(PrefixPlus(p)), '++11.0')
348
+ self.eq(PrefixPlus(Number('+1')), '+1.0')
349
+ self.py(PrefixPlus(Number(3)), 3)
350
+ self.py(PrefixPlus(self.a), [3, 4], [3, 4])
351
+
352
+ # Test with operators of precedence SUM, PRODUCT, POWER
353
+ a, b, c = self.abc
354
+ self.eq(PrefixPlus(Plus(a, b)), '+(a + b)')
355
+ self.eq(Divide(PrefixPlus(Plus(a, b)), c), '+(a + b) / c')
356
+ self.eq(PrefixPlus(Remainder(b, a)), '+(b % a)')
357
+ self.eq(Power(PrefixPlus(Power(b, b)), c), '(+b**b)**c')
358
+ self.eq(Power(a, PrefixPlus(Power(b, c))), 'a**(+b**c)')
359
+
360
+ def test_prefix_minus(self):
361
+ # Test with numbers
362
+ p = Number(11, 'kV')
363
+ self.eq(PrefixMinus(p), '-11.0')
364
+ self.eq(PrefixMinus(PrefixMinus(p)), '--11.0')
365
+ self.eq(PrefixMinus(Number(-1)), '--1.0')
366
+ self.py(PrefixMinus(Number(3)), -3)
367
+ self.py(PrefixMinus(self.a), [3, -4], [-3, 4])
368
+
369
+ # Test with operators of precedence SUM, PRODUCT, POWER
370
+ a, b, c = self.abc
371
+ self.eq(PrefixMinus(Minus(a, b)), '-(a - b)')
372
+ self.eq(Multiply(PrefixMinus(Plus(b, a)), c), '-(b + a) * c')
373
+ self.eq(PrefixMinus(Quotient(b, a)), '-(b // a)')
374
+ self.eq(Power(PrefixMinus(Power(c, b)), a), '(-c**b)**a')
375
+ self.eq(Power(a, PrefixMinus(Power(b, c))), 'a**(-b**c)')
376
+
377
+ def test_plus_minus(self):
378
+ a, b, c = self.abc
379
+ self.eq(Plus(a, b), 'a + b')
380
+ self.eq(Plus(Plus(a, b), c), 'a + b + c')
381
+ self.eq(Plus(a, Plus(b, c)), 'a + (b + c)')
382
+
383
+ self.eq(Minus(a, b), 'a - b')
384
+ self.eq(Minus(Minus(a, b), c), 'a - b - c')
385
+ self.eq(Minus(a, Minus(b, c)), 'a - (b - c)')
386
+
387
+ self.eq(Minus(a, b), 'a - b')
388
+ self.eq(Plus(Minus(a, b), c), 'a - b + c')
389
+ self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
390
+ self.eq(Minus(Plus(a, b), c), 'a + b - c')
391
+ self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
392
+
393
+ self.py(Plus(Number(4), Number(2)), 6)
394
+ self.py(Minus(Number(5), Number(1.5)), 3.5)
395
+ self.py(Plus(a, Number(2)), [5, 1], [3, -1])
396
+ self.py(Minus(Number(5), b), [2, -5], [3, 10])
397
+ self.py(Plus(a, b), [6, 5, 4], [1, 2, 3], [5, 3, 1])
398
+ self.py(Minus(a, b), [1, 2, 3], [8, 2, 5], [7, 0, 2])
399
+
400
+ def test_multiply_divide(self):
401
+ a, b, c = self.abc
402
+ self.eq(Multiply(a, b), 'a * b')
403
+ # Left-to-right, so (a * b) * c is the same as a * b * c...
404
+ self.eq(Multiply(Multiply(a, b), c), 'a * b * c')
405
+ # ...but order-of-operations-wise, a * (b * c) is different!
406
+ self.eq(Multiply(a, Multiply(b, c)), 'a * (b * c)')
407
+ # Note that a user typing a * b * c results in (a * b) * c
408
+
409
+ self.eq(Divide(a, b), 'a / b')
410
+ self.eq(Divide(Divide(a, b), c), 'a / b / c')
411
+ self.eq(Divide(a, Divide(b, c)), 'a / (b / c)')
412
+
413
+ self.eq(Divide(Multiply(a, b), c), 'a * b / c')
414
+ self.eq(Multiply(Divide(a, b), c), 'a / b * c')
415
+ self.eq(Divide(a, Multiply(b, c)), 'a / (b * c)')
416
+ self.eq(Multiply(a, Divide(b, c)), 'a * (b / c)')
417
+
418
+ self.eq(Remainder(Multiply(a, b), c), 'a * b % c')
419
+ self.eq(Multiply(Quotient(a, b), c), 'a // b * c')
420
+ self.eq(Quotient(a, Multiply(b, c)), 'a // (b * c)')
421
+ self.eq(Multiply(a, Remainder(b, c)), 'a * (b % c)')
422
+
423
+ self.eq(Divide(a, Quotient(b, c)), 'a / (b // c)')
424
+ self.eq(Divide(Quotient(b, c), a), 'b // c / a')
425
+ self.eq(Divide(a, Remainder(b, c)), 'a / (b % c)')
426
+ self.eq(Divide(Remainder(b, c), a), 'b % c / a')
427
+
428
+ self.eq(Multiply(Minus(a, b), c), '(a - b) * c')
429
+ self.eq(Multiply(a, Plus(b, c)), 'a * (b + c)')
430
+ self.eq(Minus(Multiply(a, b), c), 'a * b - c')
431
+ self.eq(Plus(a, Multiply(b, c)), 'a + b * c')
432
+ self.eq(Divide(Plus(a, b), c), '(a + b) / c')
433
+ self.eq(Divide(a, Minus(b, c)), 'a / (b - c)')
434
+ self.eq(Plus(Divide(a, b), c), 'a / b + c')
435
+ self.eq(Minus(a, Divide(b, c)), 'a - b / c')
436
+ self.eq(Divide(a, Divide(b, c)), 'a / (b / c)')
437
+ self.eq(Divide(Divide(a, b), c), 'a / b / c')
438
+
439
+ self.py(Multiply(Number(7), Number(9)), 63)
440
+ self.py(Divide(Number(5), Number(2)), 2.5)
441
+ self.py(Multiply(a, Number(2)), [6, -2], [3, -1])
442
+ self.py(Divide(a, b), [5, -3, 10], 60, [12, -20, 6])
443
+
444
+ def test_power(self):
445
+ a, b, c = self.abc
446
+ self.eq(Power(a, b), 'a**b')
447
+
448
+ # In Python, a**b**c = a**(b**c), whereas Myokit uses a^b^c = (a^b)^c
449
+ self.eq(Power(Power(a, b), c), '(a**b)**c')
450
+ self.eq(Power(a, Power(b, c)), 'a**b**c')
451
+
452
+ self.eq(Power(Plus(a, b), c), '(a + b)**c')
453
+ self.eq(Power(a, Minus(b, c)), 'a**(b - c)')
454
+ self.eq(Power(Multiply(a, b), c), '(a * b)**c')
455
+ self.eq(Power(a, Divide(b, c)), 'a**(b / c)')
456
+
457
+ self.py(Power(Number(4), Power(Number(2), Number(3))), 65536)
458
+ self.py(Power(Power(Number(2), Number(3)), Number(4)), 4096)
459
+ self.py(Power(a, Power(b, c)),
460
+ [65536, 1, 4], [4, 1, 2], [2, 1, 4], [3, 1, 0.5])
461
+ self.py(Power(Power(a, b), c),
462
+ [4096, 0.01], [2, 10.0], [3, 2], [4, -1])
463
+
464
+ def test_functions(self):
465
+ a, b, c = self.abc
466
+
467
+ self.eq(Sqrt(a), 'numpy.sqrt(a)')
468
+ self.eq(Exp(a), 'numpy.exp(a)')
469
+ self.eq(Log(a), 'numpy.log(a)')
470
+ # Log replaced by divide always adds brackets, because parent element
471
+ # won't have added any (never needed around a log(a, b) function).
472
+ self.eq(Log(a, b), '(numpy.log(a) / numpy.log(b))')
473
+ self.eq(Divide(a, Log(b, c)), 'a / (numpy.log(b) / numpy.log(c))')
474
+ self.eq(Log10(a), 'numpy.log10(a)')
475
+ self.eq(Sin(a), 'numpy.sin(a)')
476
+ self.eq(Cos(a), 'numpy.cos(a)')
477
+ self.eq(Tan(a), 'numpy.tan(a)')
478
+ self.eq(ASin(a), 'numpy.arcsin(a)')
479
+ self.eq(ACos(a), 'numpy.arccos(a)')
480
+ self.eq(ATan(a), 'numpy.arctan(a)')
481
+ self.eq(Floor(a), 'numpy.floor(a)')
482
+ self.eq(Ceil(a), 'numpy.ceil(a)')
483
+ self.eq(Abs(a), 'numpy.abs(a)')
484
+
485
+ self.py(Sqrt(a), [3, 4], [9, 16])
486
+ self.py(Exp(a), numpy.exp([2, 3]), [2, 3])
487
+ self.py(Log(a), numpy.log([3, 5]), [3, 5])
488
+ self.py(Log(a, b), [3, 4], [27, 16], [3, 2])
489
+ self.py(Log10(a), [2, 3], [100, 1000])
490
+ self.py(Sin(a), numpy.sin([1, 2]), [1, 2])
491
+ self.py(Cos(a), numpy.cos([3, 4]), [3, 4])
492
+ self.py(Tan(a), numpy.tan([5, 6]), [5, 6])
493
+ self.py(ASin(a), numpy.arcsin([0.1, 0.2]), [0.1, 0.2])
494
+ self.py(ACos(a), numpy.arccos([0.3, 0.4, 0.5]), [0.3, 0.4, 0.5])
495
+ self.py(ATan(a), numpy.arctan([0.1]), [0.1])
496
+ self.py(Floor(a), [3, 3], [3.1, 3.9])
497
+ self.py(Ceil(a), [2, 3], [1.1, 3])
498
+ self.py(Abs(a), [13, 13], [13, -13])
499
+ self.py(Abs(PrefixMinus(a)), [1, 2], [1, -2])
500
+
501
+ def test_conditions(self):
502
+ a, b, c, d = self.abcd
503
+
504
+ self.eq(And(a, b), 'numpy.logical_and(a, b)')
505
+ self.eq(Or(d, c), 'numpy.logical_or(d, c)')
506
+ self.eq(Not(c), 'numpy.logical_not(c)')
507
+
508
+ self.eq(Equal(a, b), '(a == b)')
509
+ self.eq(NotEqual(a, b), '(a != b)')
510
+ self.eq(More(b, a), '(b > a)')
511
+ self.eq(Less(d, c), '(d < c)')
512
+ self.eq(MoreEqual(c, a), '(c >= a)')
513
+ self.eq(LessEqual(b, d), '(b <= d)')
514
+
515
+ self.eq(And(Equal(a, b), NotEqual(c, d)),
516
+ 'numpy.logical_and((a == b), (c != d))')
517
+ self.eq(Or(More(d, c), Less(b, a)),
518
+ 'numpy.logical_or((d > c), (b < a))')
519
+ self.eq(Not(Equal(d, d)), 'numpy.logical_not((d == d))')
520
+ self.eq(Not(Or(Number(1), Number(2))),
521
+ 'numpy.logical_not(numpy.logical_or(1.0, 2.0))')
522
+
523
+ true = Equal(Number(3), Number(3))
524
+ self.py(true, True)
525
+ false = NotEqual(Number(3), Number(3))
526
+ self.py(false, False)
527
+ self.py(More(Number(5), Number(3)), True)
528
+ self.py(More(Number(3), Number(3)), False)
529
+ self.py(MoreEqual(Number(3), Number(3)), True)
530
+ self.py(Less(Number(3), Number(5)), True)
531
+ self.py(More(Number(3), Number(3)), False)
532
+ self.py(LessEqual(Number(3), Number(3)), True)
533
+ self.py(And(true, false), False)
534
+ self.py(And(true, true), True)
535
+ self.py(Or(a, b), [True, True, True, False],
536
+ [True, True, False, False], [True, False, True, False])
537
+ self.py(Not(a), [True, False], [False, True])
538
+
539
+ def test_conditionals(self):
540
+ a, b, c, d = self.abcd
541
+ e, f, g = self.efg
542
+
543
+ self.eq(If(Equal(a, b), d, c), 'numpy.select([(a == b)], [d], c)')
544
+ self.eq(Piecewise(NotEqual(d, c), b, a),
545
+ 'numpy.select([(d != c)], [b], a)')
546
+ self.eq(Piecewise(Equal(a, b), c, Equal(a, d), Number(3), Number(4)),
547
+ 'numpy.select([(a == b), (a == d)], [c, 3.0], 4.0)')
548
+
549
+ self.py(If(Equal(Number(10), Number(1)), Number(2), Number(3)), 3)
550
+ self.py(If(Equal(a, b), c, d), [3, 2], [2, 2], [2, 3], [3, 4], [1, 2])
551
+ self.py(Piecewise(Equal(a, b), c, Equal(d, e), f, g),
552
+ [1, 2, 30], [1, 1, 1], [1, 2, 3], [1, 1, 1], [1, 4, 5],
553
+ [1, 4, 6], [2, 2, 2], [3, 3, 30])
554
+
555
+
556
+ if __name__ == '__main__':
557
+ unittest.main()