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,344 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# Tests writing of AnsiC expressions, as used by the Simulation.
|
|
4
|
+
# C++ tests are lumped in as well.
|
|
5
|
+
#
|
|
6
|
+
# This file is part of Myokit.
|
|
7
|
+
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
8
|
+
#
|
|
9
|
+
import math
|
|
10
|
+
import unittest
|
|
11
|
+
|
|
12
|
+
import myokit
|
|
13
|
+
import myokit.formats.ansic
|
|
14
|
+
import myokit.formats.cpp
|
|
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
|
+
)
|
|
23
|
+
|
|
24
|
+
import myokit.tests
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AnsiCExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
|
|
28
|
+
"""
|
|
29
|
+
Test conversion to Ansi C, as used by the Simulation.
|
|
30
|
+
Numerical tests are provided by composing and evaluating a single RHS.
|
|
31
|
+
"""
|
|
32
|
+
_name = 'ansic'
|
|
33
|
+
_target = myokit.formats.ansic.AnsiCExpressionWriter
|
|
34
|
+
|
|
35
|
+
def test_number(self):
|
|
36
|
+
self.eq(Number(1), '1.0')
|
|
37
|
+
self.eq(Number(-2), '-2.0')
|
|
38
|
+
self.eq(Number(13, 'mV'), '13.0')
|
|
39
|
+
|
|
40
|
+
def test_name(self):
|
|
41
|
+
self.eq(self.a, 'a')
|
|
42
|
+
w = self._target()
|
|
43
|
+
w.set_lhs_function(lambda v: v.var().qname().upper())
|
|
44
|
+
self.assertEqual(w.ex(self.a), 'COMP.A')
|
|
45
|
+
|
|
46
|
+
def test_derivative(self):
|
|
47
|
+
self.eq(myokit.Derivative(self.a), 'dot(a)')
|
|
48
|
+
|
|
49
|
+
def test_partial_derivative(self):
|
|
50
|
+
self.eq(myokit.PartialDerivative(self.a, self.b), 'partial(a, b)')
|
|
51
|
+
|
|
52
|
+
def test_initial_value(self):
|
|
53
|
+
self.eq(myokit.InitialValue(self.a), 'initial(a)')
|
|
54
|
+
|
|
55
|
+
def test_prefix_plus(self):
|
|
56
|
+
# Test with numbers
|
|
57
|
+
p = Number(11, 'kV')
|
|
58
|
+
self.eq(PrefixPlus(p), '+11.0')
|
|
59
|
+
self.eq(PrefixPlus(PrefixPlus(p)), '+(+11.0)')
|
|
60
|
+
self.eq(PrefixPlus(PrefixPlus(PrefixPlus(p))), '+(+(+11.0))')
|
|
61
|
+
self.eq(PrefixPlus(Number('+1')), '+1.0')
|
|
62
|
+
|
|
63
|
+
# Test with operators of precedence SUM, PRODUCT
|
|
64
|
+
a, b, c = self.abc
|
|
65
|
+
self.eq(PrefixPlus(Plus(a, b)), '+(a + b)')
|
|
66
|
+
self.eq(Divide(PrefixPlus(Plus(a, b)), c), '+(a + b) / c')
|
|
67
|
+
self.eq(PrefixPlus(Divide(b, a)), '+(b / a)')
|
|
68
|
+
|
|
69
|
+
def test_prefix_minus(self):
|
|
70
|
+
# Test with numbers
|
|
71
|
+
p = Number(11, 'kV')
|
|
72
|
+
self.eq(PrefixMinus(p), '-11.0')
|
|
73
|
+
self.eq(PrefixMinus(PrefixMinus(p)), '-(-11.0)')
|
|
74
|
+
self.eq(PrefixMinus(Number(-1)), '-(-1.0)')
|
|
75
|
+
self.eq(PrefixMinus(PrefixMinus(Number(-2))), '-(-(-2.0))')
|
|
76
|
+
|
|
77
|
+
# Test with operators of precedence SUM, PRODUCT
|
|
78
|
+
a, b, c = self.abc
|
|
79
|
+
self.eq(PrefixMinus(Minus(a, b)), '-(a - b)')
|
|
80
|
+
self.eq(Multiply(PrefixMinus(Plus(b, a)), c), '-(b + a) * c')
|
|
81
|
+
self.eq(PrefixMinus(Divide(b, a)), '-(b / a)')
|
|
82
|
+
|
|
83
|
+
def test_plus_minus(self):
|
|
84
|
+
a, b, c = self.abc
|
|
85
|
+
self.eq(Plus(a, b), 'a + b')
|
|
86
|
+
self.eq(Plus(Plus(a, b), c), 'a + b + c')
|
|
87
|
+
self.eq(Plus(a, Plus(b, c)), 'a + (b + c)')
|
|
88
|
+
|
|
89
|
+
self.eq(Minus(a, b), 'a - b')
|
|
90
|
+
self.eq(Minus(Minus(a, b), c), 'a - b - c')
|
|
91
|
+
self.eq(Minus(a, Minus(b, c)), 'a - (b - c)')
|
|
92
|
+
|
|
93
|
+
self.eq(Minus(a, b), 'a - b')
|
|
94
|
+
self.eq(Plus(Minus(a, b), c), 'a - b + c')
|
|
95
|
+
self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
|
|
96
|
+
self.eq(Minus(Plus(a, b), c), 'a + b - c')
|
|
97
|
+
self.eq(Minus(a, Plus(b, c)), 'a - (b + c)')
|
|
98
|
+
|
|
99
|
+
def test_multiply_divide(self):
|
|
100
|
+
a, b, c = self.abc
|
|
101
|
+
self.eq(Multiply(a, b), 'a * b')
|
|
102
|
+
self.eq(Multiply(Multiply(a, b), c), 'a * b * c')
|
|
103
|
+
self.eq(Multiply(a, Multiply(b, c)), 'a * (b * c)')
|
|
104
|
+
|
|
105
|
+
self.eq(Divide(a, b), 'a / b')
|
|
106
|
+
self.eq(Divide(Divide(a, b), c), 'a / b / c')
|
|
107
|
+
self.eq(Divide(a, Divide(b, c)), 'a / (b / c)')
|
|
108
|
+
|
|
109
|
+
self.eq(Divide(Multiply(a, b), c), 'a * b / c')
|
|
110
|
+
self.eq(Multiply(Divide(a, b), c), 'a / b * c')
|
|
111
|
+
self.eq(Divide(a, Multiply(b, c)), 'a / (b * c)')
|
|
112
|
+
self.eq(Multiply(a, Divide(b, c)), 'a * (b / c)')
|
|
113
|
+
|
|
114
|
+
self.eq(Multiply(Minus(a, b), c), '(a - b) * c')
|
|
115
|
+
self.eq(Multiply(a, Plus(b, c)), 'a * (b + c)')
|
|
116
|
+
self.eq(Minus(Multiply(a, b), c), 'a * b - c')
|
|
117
|
+
self.eq(Plus(a, Multiply(b, c)), 'a + b * c')
|
|
118
|
+
self.eq(Divide(Plus(a, b), c), '(a + b) / c')
|
|
119
|
+
self.eq(Divide(a, Minus(b, c)), 'a / (b - c)')
|
|
120
|
+
self.eq(Plus(Divide(a, b), c), 'a / b + c')
|
|
121
|
+
self.eq(Minus(a, Divide(b, c)), 'a - b / c')
|
|
122
|
+
self.eq(Divide(a, Divide(b, c)), 'a / (b / c)')
|
|
123
|
+
self.eq(Divide(Divide(a, b), c), 'a / b / c')
|
|
124
|
+
|
|
125
|
+
def test_quotient(self):
|
|
126
|
+
a, b, c = self.abc
|
|
127
|
+
self.eq(Quotient(a, b), 'floor(a / b)')
|
|
128
|
+
self.eq(Quotient(Plus(a, c), b), 'floor((a + c) / b)')
|
|
129
|
+
self.eq(Quotient(Divide(a, c), b), 'floor(a / c / b)')
|
|
130
|
+
self.eq(Quotient(a, Divide(b, c)), 'floor(a / (b / c))')
|
|
131
|
+
self.eq(Multiply(Quotient(a, b), c), 'floor(a / b) * c')
|
|
132
|
+
# Bracket() method expects a PRODUCT level operation, so will add
|
|
133
|
+
# unnecessary brackets here
|
|
134
|
+
self.eq(Multiply(c, Quotient(a, b)), 'c * (floor(a / b))')
|
|
135
|
+
|
|
136
|
+
def test_remainder(self):
|
|
137
|
+
a, b, c = self.abc
|
|
138
|
+
self.eq(Remainder(a, b), '(a - b * floor(a / b))')
|
|
139
|
+
self.eq(Remainder(Plus(a, c), b), '(a + c - b * floor((a + c) / b))')
|
|
140
|
+
self.eq(Multiply(Remainder(a, b), c), '(a - b * floor(a / b)) * c')
|
|
141
|
+
# Bracket() method expects a PRODUCT level operation, so will add
|
|
142
|
+
# unnecessary brackets here
|
|
143
|
+
self.eq(Divide(c, Remainder(b, a)), 'c / ((b - a * floor(b / a)))')
|
|
144
|
+
|
|
145
|
+
def test_power(self):
|
|
146
|
+
a, b, c = self.abc
|
|
147
|
+
self.eq(Power(a, b), 'pow(a, b)')
|
|
148
|
+
self.eq(Power(Power(a, b), c), 'pow(pow(a, b), c)')
|
|
149
|
+
self.eq(Power(a, Power(b, c)), 'pow(a, pow(b, c))')
|
|
150
|
+
|
|
151
|
+
self.eq(Power(Plus(a, b), c), 'pow(a + b, c)')
|
|
152
|
+
self.eq(Power(a, Minus(b, c)), 'pow(a, b - c)')
|
|
153
|
+
self.eq(Power(Multiply(a, b), c), 'pow(a * b, c)')
|
|
154
|
+
self.eq(Power(a, Divide(b, c)), 'pow(a, b / c)')
|
|
155
|
+
|
|
156
|
+
def test_log(self):
|
|
157
|
+
a, b = self.ab
|
|
158
|
+
self.eq(Log(a), 'log(a)')
|
|
159
|
+
self.eq(Log10(a), 'log10(a)')
|
|
160
|
+
self.eq(Log(a, b), '(log(a) / log(b))')
|
|
161
|
+
|
|
162
|
+
def test_functions(self):
|
|
163
|
+
a, b = self.ab
|
|
164
|
+
|
|
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), 'fabs(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(And(a, b)), '(!(a && b))')
|
|
183
|
+
self.eq(Not(c), '(!(c))')
|
|
184
|
+
|
|
185
|
+
self.eq(Equal(a, b), '(a == b)')
|
|
186
|
+
self.eq(NotEqual(a, b), '(a != b)')
|
|
187
|
+
self.eq(More(b, a), '(b > a)')
|
|
188
|
+
self.eq(Less(d, c), '(d < c)')
|
|
189
|
+
self.eq(MoreEqual(c, a), '(c >= a)')
|
|
190
|
+
self.eq(LessEqual(b, d), '(b <= d)')
|
|
191
|
+
|
|
192
|
+
self.eq(And(Equal(a, b), NotEqual(c, d)), '((a == b) && (c != d))')
|
|
193
|
+
self.eq(Or(More(d, c), Less(b, a)), '((d > c) || (b < a))')
|
|
194
|
+
self.eq(Not(Or(Number(1), Number(2))), '(!(1.0 || 2.0))')
|
|
195
|
+
self.eq(Not(Less(Number(1), Number(2))), '(!(1.0 < 2.0))')
|
|
196
|
+
self.eq(Not(Plus(Number(1), Number(2))), '(!(1.0 + 2.0))')
|
|
197
|
+
|
|
198
|
+
self.eq(Equal(Equal(Number(0), Number(0)), Number(0)),
|
|
199
|
+
'((0.0 == 0.0) == 0.0)')
|
|
200
|
+
|
|
201
|
+
def test_conditionals(self):
|
|
202
|
+
|
|
203
|
+
a, b, c, d = self.abcd
|
|
204
|
+
self.eq(If(Equal(a, b), d, c), '((a == b) ? d : c)')
|
|
205
|
+
self.eq(Piecewise(NotEqual(d, c), b, a), '((d != c) ? b : a)')
|
|
206
|
+
self.eq(Piecewise(Equal(a, b), c, Equal(a, d), Number(3), Number(4)),
|
|
207
|
+
'((a == b) ? c : ((a == d) ? 3.0 : 4.0))')
|
|
208
|
+
|
|
209
|
+
# Extra parentheses if condition is not a condition
|
|
210
|
+
self.eq(If(a, d, c), '((a) ? d : c)')
|
|
211
|
+
self.eq(Piecewise(a, b, c, d, Number(4)),
|
|
212
|
+
'((a) ? b : ((c) ? d : 4.0))')
|
|
213
|
+
|
|
214
|
+
# Using if-then-else function
|
|
215
|
+
w = self._target()
|
|
216
|
+
w.set_lhs_function(lambda v: v.var().name())
|
|
217
|
+
w.set_condition_function('ite')
|
|
218
|
+
|
|
219
|
+
self.assertEqual(w.ex(If(More(b, a), c, d)), 'ite((b > a), c, d)')
|
|
220
|
+
self.assertEqual(w.ex(Piecewise(Less(d, c), b, a)),
|
|
221
|
+
'ite((d < c), b, a)')
|
|
222
|
+
self.assertEqual(w.ex(
|
|
223
|
+
Piecewise(Equal(a, b), c, Equal(a, d), Number(3), Number(4))),
|
|
224
|
+
'ite((a == b), c, ite((a == d), 3.0, 4.0))')
|
|
225
|
+
|
|
226
|
+
def test_in_c(self):
|
|
227
|
+
""" Compile and test the values evaluated in C. """
|
|
228
|
+
|
|
229
|
+
class CTester():
|
|
230
|
+
def __init__(self, parent):
|
|
231
|
+
self._parent = parent
|
|
232
|
+
self._m = myokit.Model()
|
|
233
|
+
self._c = self._m.add_component('c')
|
|
234
|
+
t = self._c.add_variable('time', rhs=0, binding='time')
|
|
235
|
+
self._expected = []
|
|
236
|
+
|
|
237
|
+
def add(self, expression, expected):
|
|
238
|
+
v = self._c.add_variable(
|
|
239
|
+
f'v{len(self._expected)}', rhs=expression, initial_value=0)
|
|
240
|
+
self._expected.append(float(expected))
|
|
241
|
+
|
|
242
|
+
def run(self):
|
|
243
|
+
s = myokit.Simulation(self._m)
|
|
244
|
+
x = s.evaluate_derivatives()
|
|
245
|
+
self._parent.assertEqual(len(x), len(self._expected))
|
|
246
|
+
for a, b in zip(x, self._expected):
|
|
247
|
+
self._parent.assertEqual(a, b)
|
|
248
|
+
|
|
249
|
+
c = CTester(self,)
|
|
250
|
+
|
|
251
|
+
c.add(Number(12, 'pF'), 12)
|
|
252
|
+
|
|
253
|
+
c.add(PrefixPlus(Number(3)), 3)
|
|
254
|
+
c.add(PrefixPlus(PrefixPlus(PrefixPlus(Number(4)))), 4)
|
|
255
|
+
c.add(PrefixMinus(Number(6)), -6)
|
|
256
|
+
c.add(PrefixMinus(PrefixMinus(Number(2))), 2)
|
|
257
|
+
c.add(PrefixMinus(PrefixMinus(PrefixMinus(Number(5)))), -5)
|
|
258
|
+
c.add(PrefixMinus(PrefixMinus(PrefixMinus(Number(-5)))), 5)
|
|
259
|
+
|
|
260
|
+
c.add(Plus(Number(4), Number(2)), 6)
|
|
261
|
+
c.add(Minus(Number(5), Number(1.5)), 3.5)
|
|
262
|
+
|
|
263
|
+
c.add(Multiply(Number(7), Number(9)), 63)
|
|
264
|
+
c.add(Divide(Number(5), Number(2)), 2.5)
|
|
265
|
+
c.add(Divide(Divide(Number(12), Number(2)), Number(2)), 3)
|
|
266
|
+
c.add(Divide(Number(1), Divide(Number(2), Number(3))), 1.5)
|
|
267
|
+
|
|
268
|
+
c.add(Remainder(Number(10), Number(4)), 2)
|
|
269
|
+
c.add(Remainder(Number(10), Number(6)), 4)
|
|
270
|
+
c.add(Remainder(Number(5), Number(3)), 2)
|
|
271
|
+
c.add(Remainder(Number(-5), Number(3)), 1)
|
|
272
|
+
c.add(Remainder(Number(5), Number(-3)), -1)
|
|
273
|
+
c.add(Remainder(Number(-5), Number(-3)), -2)
|
|
274
|
+
|
|
275
|
+
c.add(Quotient(Number(10), Number(4)), 2)
|
|
276
|
+
c.add(Quotient(Number(10), Number(6)), 1)
|
|
277
|
+
c.add(Quotient(Number(5), Number(3)), 1)
|
|
278
|
+
c.add(Quotient(Number(-5), Number(3)), -2)
|
|
279
|
+
c.add(Quotient(Number(5), Number(-3)), -2)
|
|
280
|
+
c.add(Quotient(Number(-5), Number(-3)), 1)
|
|
281
|
+
|
|
282
|
+
c.add(Power(Number(4), Power(Number(2), Number(3))), 65536)
|
|
283
|
+
c.add(Power(Power(Number(2), Number(3)), Number(4)), 4096)
|
|
284
|
+
|
|
285
|
+
c.add(Log(Number(3)), math.log(3))
|
|
286
|
+
c.add(Log10(Number(1000)), 3)
|
|
287
|
+
c.add(Log10(Number(0.01)), -2)
|
|
288
|
+
c.add(Log(Number(27), Number(3)), 3)
|
|
289
|
+
c.add(Divide(Number(12), Log(Number(256), Number(4))), 3)
|
|
290
|
+
c.add(Divide(Log(Number(256), Number(4)), Number(4)), 1)
|
|
291
|
+
|
|
292
|
+
c.add(Sqrt(Number(9)), 3)
|
|
293
|
+
c.add(Exp(Number(3)), math.exp(3))
|
|
294
|
+
c.add(Sin(Number(1)), math.sin(1))
|
|
295
|
+
c.add(Cos(Number(1)), math.cos(1))
|
|
296
|
+
c.add(Tan(Number(4)), math.tan(4))
|
|
297
|
+
c.add(ASin(Number(0.4)), math.asin(0.4))
|
|
298
|
+
c.add(ACos(Number(0.4)), math.acos(0.4))
|
|
299
|
+
c.add(ATan(Number(0.4)), math.atan(0.4))
|
|
300
|
+
|
|
301
|
+
c.add(Floor(Number(3.9)), 3)
|
|
302
|
+
c.add(Floor(Number(-3.9)), -4)
|
|
303
|
+
c.add(Ceil(Number(4.01)), 5)
|
|
304
|
+
c.add(Ceil(Number(-4.01)), -4)
|
|
305
|
+
c.add(Abs(PrefixMinus(Number(12))), 12)
|
|
306
|
+
c.add(Abs(Number(-13)), 13)
|
|
307
|
+
|
|
308
|
+
true = Equal(Number(2), Number(2))
|
|
309
|
+
false = Equal(Number(3), Number(-1))
|
|
310
|
+
a, b = Number(10), Number(20)
|
|
311
|
+
c.add(If(true, a, b), 10)
|
|
312
|
+
c.add(If(false, a, b), 20)
|
|
313
|
+
|
|
314
|
+
c.add(If(More(Number(5), Number(3)), a, b), 10)
|
|
315
|
+
c.add(If(More(Number(3), Number(3)), a, b), 20)
|
|
316
|
+
c.add(If(MoreEqual(Number(3), Number(3)), a, b), 10)
|
|
317
|
+
c.add(If(Less(Number(3), Number(5)), a, b), 10)
|
|
318
|
+
c.add(If(More(Number(3), Number(3)), a, b), 20)
|
|
319
|
+
c.add(If(LessEqual(Number(3), Number(3)), a, b), 10)
|
|
320
|
+
|
|
321
|
+
c.add(If(And(false, false), a, b), 20)
|
|
322
|
+
c.add(If(And(false, true), a, b), 20)
|
|
323
|
+
c.add(If(And(true, false), a, b), 20)
|
|
324
|
+
c.add(If(And(true, true), a, b), 10)
|
|
325
|
+
c.add(If(Or(false, false), a, b), 20)
|
|
326
|
+
c.add(If(Or(false, true), a, b), 10)
|
|
327
|
+
c.add(If(Or(true, false), a, b), 10)
|
|
328
|
+
c.add(If(Or(true, true), a, b), 10)
|
|
329
|
+
c.add(If(Not(true), a, b), 20)
|
|
330
|
+
c.add(If(Not(false), a, b), 10)
|
|
331
|
+
|
|
332
|
+
c.add(If(Equal(Equal(Number(0), Number(0)), Number(0)), a, b), 20)
|
|
333
|
+
c.add(If(Equal(Equal(Number(0), Number(0)), Number(1)), a, b), 10)
|
|
334
|
+
|
|
335
|
+
c.add(Piecewise(true, Number(10), false, Number(20), Number(30)), 10)
|
|
336
|
+
c.add(Piecewise(true, Number(10), true, Number(20), Number(30)), 10)
|
|
337
|
+
c.add(Piecewise(false, Number(10), true, Number(20), Number(30)), 20)
|
|
338
|
+
c.add(Piecewise(false, Number(10), false, Number(20), Number(30)), 30)
|
|
339
|
+
|
|
340
|
+
c.run()
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
if __name__ == '__main__':
|
|
344
|
+
unittest.main()
|
|
@@ -11,6 +11,7 @@ import unittest
|
|
|
11
11
|
import numpy as np
|
|
12
12
|
|
|
13
13
|
import myokit
|
|
14
|
+
import myokit.formats
|
|
14
15
|
import myokit.formats.axon as axon
|
|
15
16
|
|
|
16
17
|
from myokit.tests import TemporaryDirectory, DIR_FORMATS, WarningCollector
|
|
@@ -853,5 +854,21 @@ class AtfTest(unittest.TestCase):
|
|
|
853
854
|
self.assertEqual(atf.version(), '1.0')
|
|
854
855
|
|
|
855
856
|
|
|
857
|
+
class AbfImporterInterfaceTest(unittest.TestCase):
|
|
858
|
+
""" Tests ABF importer interface. """
|
|
859
|
+
|
|
860
|
+
def test_capability_reporting(self):
|
|
861
|
+
# Test if the right capabilities are reported.
|
|
862
|
+
i = myokit.formats.importer('abf')
|
|
863
|
+
self.assertFalse(i.supports_component())
|
|
864
|
+
self.assertFalse(i.supports_model())
|
|
865
|
+
self.assertTrue(i.supports_protocol())
|
|
866
|
+
|
|
867
|
+
def test_protocol(self):
|
|
868
|
+
i = myokit.formats.importer('abf')
|
|
869
|
+
self.assertTrue(i.supports_protocol())
|
|
870
|
+
i.protocol(os.path.join(DIR_FORMATS, 'abf-v1.abf'))
|
|
871
|
+
|
|
872
|
+
|
|
856
873
|
if __name__ == '__main__':
|
|
857
874
|
unittest.main()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# Tests the expression writer for C++.
|
|
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.cpp
|
|
12
|
+
|
|
13
|
+
import myokit.tests
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CppExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
|
|
17
|
+
"""
|
|
18
|
+
Test the C++ expression writer.
|
|
19
|
+
This inherits from Ansi C, only dropping support for initial values and
|
|
20
|
+
partial derivatives.
|
|
21
|
+
"""
|
|
22
|
+
_name = 'cpp'
|
|
23
|
+
_target = myokit.formats.cpp.CppExpressionWriter
|
|
24
|
+
|
|
25
|
+
def test_basics(self):
|
|
26
|
+
# Test a few arbitrary expressions, just to check the inheritcance has
|
|
27
|
+
# worked.
|
|
28
|
+
a, b, c, d = self.abcd
|
|
29
|
+
self.eq(myokit.Number(1), '1.0')
|
|
30
|
+
self.eq(myokit.Number(13, 'mV'), '13.0')
|
|
31
|
+
self.eq(myokit.Divide(myokit.PrefixPlus(myokit.Plus(a, b)), c),
|
|
32
|
+
'+(a + b) / c')
|
|
33
|
+
self.eq(myokit.Multiply(myokit.PrefixMinus(myokit.Plus(b, a)), c),
|
|
34
|
+
'-(b + a) * c')
|
|
35
|
+
self.eq(myokit.Multiply(myokit.Divide(a, b), c), 'a / b * c')
|
|
36
|
+
self.eq(myokit.Divide(a, myokit.Multiply(b, c)), 'a / (b * c)')
|
|
37
|
+
self.eq(myokit.Quotient(myokit.Divide(a, c), b), 'floor(a / c / b)')
|
|
38
|
+
self.eq(myokit.ASin(a), 'asin(a)')
|
|
39
|
+
self.eq(myokit.Power(myokit.PrefixMinus(a), b), 'pow(-a, b)')
|
|
40
|
+
self.eq(myokit.Power(a, myokit.Minus(b, c)), 'pow(a, b - c)')
|
|
41
|
+
self.eq(myokit.Power(myokit.Multiply(a, b), c), 'pow(a * b, c)')
|
|
42
|
+
self.eq(myokit.Log(a, b), '(log(a) / log(b))')
|
|
43
|
+
self.eq(myokit.Sin(a), 'sin(a)')
|
|
44
|
+
self.eq(myokit.And(myokit.Equal(a, b), myokit.NotEqual(c, d)),
|
|
45
|
+
'((a == b) && (c != d))')
|
|
46
|
+
self.eq(myokit.Not(myokit.Less(myokit.Number(1), myokit.Number(2))),
|
|
47
|
+
'(!(1.0 < 2.0))')
|
|
48
|
+
self.eq(myokit.If(myokit.Equal(a, b), d, c), '((a == b) ? d : c)')
|
|
49
|
+
|
|
50
|
+
def test_derivative(self):
|
|
51
|
+
self.eq(myokit.Derivative(self.a), 'dot(a)')
|
|
52
|
+
|
|
53
|
+
def test_partial_derivative(self):
|
|
54
|
+
self.assertRaisesRegex(
|
|
55
|
+
NotImplementedError, 'Partial',
|
|
56
|
+
self.w.ex, myokit.PartialDerivative(self.a, self.b))
|
|
57
|
+
|
|
58
|
+
def test_initial_value(self):
|
|
59
|
+
self.assertRaisesRegex(
|
|
60
|
+
NotImplementedError, 'Initial',
|
|
61
|
+
self.w.ex, myokit.InitialValue(self.a))
|
|
62
|
+
|
|
63
|
+
def test_conditionals(self):
|
|
64
|
+
# Inherited from AnsiCExpressionWriter
|
|
65
|
+
|
|
66
|
+
a, b, c, d = self.abcd
|
|
67
|
+
self.eq(myokit.If(myokit.Equal(a, b), d, c), '((a == b) ? d : c)')
|
|
68
|
+
self.eq(myokit.Piecewise(myokit.NotEqual(d, c), b, a),
|
|
69
|
+
'((d != c) ? b : a)')
|
|
70
|
+
self.eq(myokit.Piecewise(myokit.Equal(a, b), c,
|
|
71
|
+
myokit.Equal(a, d), myokit.Number(3),
|
|
72
|
+
myokit.Number(4)),
|
|
73
|
+
'((a == b) ? c : ((a == d) ? 3.0 : 4.0))')
|
|
74
|
+
|
|
75
|
+
# Extra parentheses if condition is not a condition
|
|
76
|
+
self.eq(myokit.If(a, d, c), '((a) ? d : c)')
|
|
77
|
+
self.eq(myokit.Piecewise(a, b, c, d, myokit.Number(4)),
|
|
78
|
+
'((a) ? b : ((c) ? d : 4.0))')
|
|
79
|
+
|
|
80
|
+
# Using if-then-else function
|
|
81
|
+
w = self._target()
|
|
82
|
+
w.set_lhs_function(lambda v: v.var().name())
|
|
83
|
+
w.set_condition_function('ite')
|
|
84
|
+
|
|
85
|
+
self.assertEqual(w.ex(myokit.If(myokit.More(b, a), c, d)),
|
|
86
|
+
'ite((b > a), c, d)')
|
|
87
|
+
self.assertEqual(w.ex(myokit.Piecewise(myokit.Less(d, c), b, a)),
|
|
88
|
+
'ite((d < c), b, a)')
|
|
89
|
+
self.assertEqual(
|
|
90
|
+
w.ex(myokit.Piecewise(myokit.Equal(a, b), c,
|
|
91
|
+
myokit.Equal(a, d), myokit.Number(3),
|
|
92
|
+
myokit.Number(4))),
|
|
93
|
+
'ite((a == b), c, ite((a == d), 3.0, 4.0))')
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == '__main__':
|
|
97
|
+
unittest.main()
|