myokit 1.36.1__py3-none-any.whl → 1.37.0__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 (43) hide show
  1. myokit/__init__.py +6 -19
  2. myokit/_datablock.py +45 -55
  3. myokit/_datalog.py +2 -2
  4. myokit/_err.py +26 -3
  5. myokit/_expressions.py +241 -127
  6. myokit/_model_api.py +19 -13
  7. myokit/_myokit_version.py +1 -1
  8. myokit/_sim/jacobian.py +3 -3
  9. myokit/_sim/openclsim.py +5 -5
  10. myokit/_sim/rhs.py +1 -1
  11. myokit/formats/__init__.py +4 -9
  12. myokit/formats/ansic/_ewriter.py +4 -20
  13. myokit/formats/heka/_patchmaster.py +16 -10
  14. myokit/formats/opencl/_ewriter.py +3 -42
  15. myokit/formats/opencl/template/minilog.py +1 -1
  16. myokit/formats/sympy/_ereader.py +2 -1
  17. myokit/formats/wcp/_wcp.py +3 -3
  18. myokit/gui/datalog_viewer.py +12 -7
  19. myokit/lib/markov.py +2 -2
  20. myokit/lib/plots.py +4 -4
  21. myokit/tests/data/formats/wcp-file-empty.wcp +0 -0
  22. myokit/tests/test_datablock.py +10 -10
  23. myokit/tests/test_datalog.py +4 -1
  24. myokit/tests/test_expressions.py +532 -251
  25. myokit/tests/test_formats_ansic.py +6 -18
  26. myokit/tests/test_formats_cpp.py +0 -5
  27. myokit/tests/test_formats_cuda.py +7 -15
  28. myokit/tests/test_formats_easyml.py +4 -9
  29. myokit/tests/test_formats_latex.py +10 -11
  30. myokit/tests/test_formats_matlab.py +0 -8
  31. myokit/tests/test_formats_opencl.py +0 -29
  32. myokit/tests/test_formats_python.py +2 -19
  33. myokit/tests/test_formats_stan.py +0 -13
  34. myokit/tests/test_formats_sympy.py +3 -3
  35. myokit/tests/test_formats_wcp.py +15 -0
  36. myokit/tests/test_model.py +20 -20
  37. myokit/tests/test_parsing.py +19 -0
  38. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/METADATA +1 -1
  39. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/RECORD +43 -42
  40. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/LICENSE.txt +0 -0
  41. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/WHEEL +0 -0
  42. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/entry_points.txt +0 -0
  43. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/top_level.txt +0 -0
@@ -415,8 +415,8 @@ class ExpressionTest(unittest.TestCase):
415
415
 
416
416
  pe = myokit.parse_expression
417
417
  self.assertFalse(pe('1 + 2 + 3').is_conditional())
418
- self.assertTrue(pe('if(1, 0, 2)').is_conditional())
419
- self.assertTrue(pe('1 + if(1, 0, 2)').is_conditional())
418
+ self.assertTrue(pe('if(1 == 2, 0, 2)').is_conditional())
419
+ self.assertTrue(pe('1 + if(1 == 1, 0, 2)').is_conditional())
420
420
 
421
421
  def test_pickling_error(self):
422
422
  # Tests pickling of expressions raises an exception
@@ -508,12 +508,6 @@ class ExpressionTest(unittest.TestCase):
508
508
  p._operands = (myokit.Number(2), p)
509
509
  self.assertRaisesRegex(myokit.IntegrityError, 'yclical', p.validate)
510
510
 
511
- # Wrong type operands
512
- # Again, need to hack this in so creation doesn't fault!
513
- p._operands = (myokit.Number(1), 2)
514
- self.assertRaisesRegex(
515
- myokit.IntegrityError, 'must be other Expression', p.validate)
516
-
517
511
  def test_walk(self):
518
512
  # Test :meth:`Expression.walk().
519
513
 
@@ -1103,8 +1097,11 @@ class DerivativeTest(unittest.TestCase):
1103
1097
 
1104
1098
  # Derivative of something other than a name: never allowed
1105
1099
  self.assertRaisesRegex(
1106
- myokit.IntegrityError, 'on variables', myokit.Derivative,
1107
- myokit.Number(1))
1100
+ myokit.TypeError, 'requires a myokit.Name',
1101
+ myokit.Derivative, myokit.Number(1))
1102
+ self.assertRaisesRegex( # Not even another LhsExpression
1103
+ myokit.TypeError, 'requires a myokit.Name',
1104
+ myokit.Derivative, myokit.Derivative(myokit.Name(x)))
1108
1105
 
1109
1106
  def test_bracket(self):
1110
1107
  # Test Derivative.bracket()
@@ -1323,22 +1320,22 @@ class PartialDerivativeTest(unittest.TestCase):
1323
1320
 
1324
1321
  # Others are not allowed
1325
1322
  self.assertRaisesRegex(
1326
- myokit.IntegrityError, 'first argument to a partial',
1323
+ myokit.TypeError, 'first argument to a partial',
1327
1324
  myokit.PartialDerivative, i, n)
1328
1325
  self.assertRaisesRegex(
1329
- myokit.IntegrityError, 'first argument to a partial',
1326
+ myokit.TypeError, 'first argument to a partial',
1330
1327
  myokit.PartialDerivative, myokit.Number(3), n)
1331
1328
  self.assertRaisesRegex(
1332
- myokit.IntegrityError, 'first argument to a partial',
1329
+ myokit.TypeError, 'first argument to a partial',
1333
1330
  myokit.PartialDerivative, myokit.PrefixPlus(n), n)
1334
1331
  self.assertRaisesRegex(
1335
- myokit.IntegrityError, 'second argument to a partial',
1332
+ myokit.TypeError, 'second argument to a partial',
1336
1333
  myokit.PartialDerivative, n, d)
1337
1334
  self.assertRaisesRegex(
1338
- myokit.IntegrityError, 'second argument to a partial',
1335
+ myokit.TypeError, 'second argument to a partial',
1339
1336
  myokit.PartialDerivative, n, myokit.Number(3))
1340
1337
  self.assertRaisesRegex(
1341
- myokit.IntegrityError, 'second argument to a partial',
1338
+ myokit.TypeError, 'second argument to a partial',
1342
1339
  myokit.PartialDerivative, n, myokit.PrefixPlus(n))
1343
1340
 
1344
1341
  def test_bracket(self):
@@ -1441,13 +1438,16 @@ class InitialValueTest(unittest.TestCase):
1441
1438
 
1442
1439
  # Value must be a name
1443
1440
  self.assertRaisesRegex(
1444
- myokit.IntegrityError, 'first argument to an initial',
1441
+ myokit.TypeError, 'argument to an initial value must be a variab',
1445
1442
  myokit.InitialValue, d)
1446
1443
  self.assertRaisesRegex(
1447
- myokit.IntegrityError, 'first argument to an initial',
1444
+ myokit.TypeError, 'argument to an initial value must be a variab',
1445
+ myokit.InitialValue, i)
1446
+ self.assertRaisesRegex(
1447
+ myokit.TypeError, 'argument to an initial value must be a variab',
1448
1448
  myokit.InitialValue, myokit.Number(3),)
1449
1449
  self.assertRaisesRegex(
1450
- myokit.IntegrityError, 'first argument to an initial',
1450
+ myokit.TypeError, 'argument to an initial value must be a variab',
1451
1451
  myokit.InitialValue, myokit.PrefixPlus(n))
1452
1452
 
1453
1453
  def test_bracket(self):
@@ -1545,6 +1545,16 @@ class InitialValueTest(unittest.TestCase):
1545
1545
  class PrefixPlusTest(unittest.TestCase):
1546
1546
  """Tests myokit.PrefixPlus."""
1547
1547
 
1548
+ def test_bracket(self):
1549
+ # Test PrefixPlus.bracket().
1550
+ i = myokit.Number(1)
1551
+ x = myokit.PrefixPlus(i)
1552
+ self.assertFalse(x.bracket(i))
1553
+ i = myokit.Plus(myokit.Number(1), myokit.Number(2))
1554
+ x = myokit.PrefixPlus(i)
1555
+ self.assertTrue(x.bracket(i))
1556
+ self.assertRaises(ValueError, x.bracket, myokit.Number(1))
1557
+
1548
1558
  def test_clone(self):
1549
1559
  # Test PrefixPlus.clone().
1550
1560
  x = myokit.PrefixPlus(myokit.Number(3))
@@ -1567,15 +1577,13 @@ class PrefixPlusTest(unittest.TestCase):
1567
1577
  self.assertNotEqual(x, y)
1568
1578
  self.assertEqual(y, myokit.PrefixPlus(j))
1569
1579
 
1570
- def test_bracket(self):
1571
- # Test PrefixPlus.bracket().
1572
- i = myokit.Number(1)
1573
- x = myokit.PrefixPlus(i)
1574
- self.assertFalse(x.bracket(i))
1575
- i = myokit.Plus(myokit.Number(1), myokit.Number(2))
1576
- x = myokit.PrefixPlus(i)
1577
- self.assertTrue(x.bracket(i))
1578
- self.assertRaises(ValueError, x.bracket, myokit.Number(1))
1580
+ def test_creation(self):
1581
+ # Operand must be a numerical expression
1582
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
1583
+ myokit.PrefixPlus, 3)
1584
+ self.assertRaisesRegex(
1585
+ myokit.TypeError, 'expected a numerical', myokit.PrefixPlus,
1586
+ myokit.Equal(myokit.Number(1), myokit.Number(2)))
1579
1587
 
1580
1588
  def test_diff(self):
1581
1589
  # Tests PrefixPlus.diff()
@@ -1648,6 +1656,17 @@ class PrefixPlusTest(unittest.TestCase):
1648
1656
  class PrefixMinusTest(unittest.TestCase):
1649
1657
  """Tests myokit.PrefixMinus."""
1650
1658
 
1659
+ def test_bracket(self):
1660
+ # Test PrefixMinus.bracket().
1661
+
1662
+ i = myokit.Number(1)
1663
+ x = myokit.PrefixMinus(i)
1664
+ self.assertFalse(x.bracket(i))
1665
+ i = myokit.Plus(myokit.Number(1), myokit.Number(2))
1666
+ x = myokit.PrefixMinus(i)
1667
+ self.assertTrue(x.bracket(i))
1668
+ self.assertRaises(ValueError, x.bracket, myokit.Number(1))
1669
+
1651
1670
  def test_clone(self):
1652
1671
  # Test PrefixMinus.clone().
1653
1672
  x = myokit.PrefixMinus(myokit.Number(3))
@@ -1670,16 +1689,13 @@ class PrefixMinusTest(unittest.TestCase):
1670
1689
  self.assertNotEqual(x, y)
1671
1690
  self.assertEqual(y, myokit.PrefixMinus(j))
1672
1691
 
1673
- def test_bracket(self):
1674
- # Test PrefixMinus.bracket().
1675
-
1676
- i = myokit.Number(1)
1677
- x = myokit.PrefixMinus(i)
1678
- self.assertFalse(x.bracket(i))
1679
- i = myokit.Plus(myokit.Number(1), myokit.Number(2))
1680
- x = myokit.PrefixMinus(i)
1681
- self.assertTrue(x.bracket(i))
1682
- self.assertRaises(ValueError, x.bracket, myokit.Number(1))
1692
+ def test_creation(self):
1693
+ # Operand must be an expression
1694
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
1695
+ myokit.PrefixMinus, 'test')
1696
+ self.assertRaisesRegex(
1697
+ myokit.TypeError, 'expected a numerical', myokit.PrefixMinus,
1698
+ myokit.Equal(myokit.Number(1), myokit.Number(2)))
1683
1699
 
1684
1700
  def test_diff(self):
1685
1701
  # Tests PrefixMinus.diff()
@@ -1752,6 +1768,15 @@ class PrefixMinusTest(unittest.TestCase):
1752
1768
  class PlusTest(unittest.TestCase):
1753
1769
  """Tests myokit.Plus."""
1754
1770
 
1771
+ def test_bracket(self):
1772
+ # Test Plus.bracket().
1773
+ i = myokit.Number(1)
1774
+ j = myokit.parse_expression('1 + 2')
1775
+ x = myokit.Plus(i, j)
1776
+ self.assertFalse(x.bracket(i))
1777
+ self.assertTrue(x.bracket(j))
1778
+ self.assertRaises(ValueError, x.bracket, myokit.Number(3))
1779
+
1755
1780
  def test_clone(self):
1756
1781
  # Test Plus.clone().
1757
1782
  i = myokit.Number(3)
@@ -1777,14 +1802,17 @@ class PlusTest(unittest.TestCase):
1777
1802
  self.assertNotEqual(x, y)
1778
1803
  self.assertEqual(y, myokit.Plus(i, i))
1779
1804
 
1780
- def test_bracket(self):
1781
- # Test Plus.bracket().
1782
- i = myokit.Number(1)
1783
- j = myokit.parse_expression('1 + 2')
1784
- x = myokit.Plus(i, j)
1785
- self.assertFalse(x.bracket(i))
1786
- self.assertTrue(x.bracket(j))
1787
- self.assertRaises(ValueError, x.bracket, myokit.Number(3))
1805
+ def test_creation(self):
1806
+ # Operands must be numerical expressions
1807
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
1808
+ myokit.Plus, 'toast', 4)
1809
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
1810
+ self.assertRaisesRegex(
1811
+ myokit.TypeError, 'first operand: expected a numerical',
1812
+ myokit.Plus, a, myokit.Number(1))
1813
+ self.assertRaisesRegex(
1814
+ myokit.TypeError, 'second operand: expected a numerical',
1815
+ myokit.Plus, myokit.Number(1), a)
1788
1816
 
1789
1817
  def test_diff(self):
1790
1818
  # Tests Plus.diff()
@@ -1903,9 +1931,20 @@ class PlusTest(unittest.TestCase):
1903
1931
  class MinusTest(unittest.TestCase):
1904
1932
  """Tests myokit.Minus."""
1905
1933
 
1934
+ def test_creation(self):
1935
+ # Operands must be numerical expressions
1936
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
1937
+ myokit.Minus, 7, None)
1938
+ a = myokit.NotEqual(myokit.Number(3), myokit.Number(2))
1939
+ self.assertRaisesRegex(
1940
+ myokit.TypeError, 'first operand: expected a numerical',
1941
+ myokit.Minus, a, myokit.Number(3))
1942
+ self.assertRaisesRegex(
1943
+ myokit.TypeError, 'second operand: expected a numerical',
1944
+ myokit.Minus, myokit.Number(3), a)
1945
+
1906
1946
  def test_diff(self):
1907
1947
  # Tests Minus.diff()
1908
-
1909
1948
  m = pd_model.clone()
1910
1949
  V = m.get('membrane.V')
1911
1950
  g = m.get('ina.g')
@@ -2002,9 +2041,20 @@ class MinusTest(unittest.TestCase):
2002
2041
  class MultiplyTest(unittest.TestCase):
2003
2042
  """Tests myokit.Multiply."""
2004
2043
 
2044
+ def test_creation(self):
2045
+ # Operands must be expressions
2046
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
2047
+ myokit.Multiply, 15, 3)
2048
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
2049
+ self.assertRaisesRegex(
2050
+ myokit.TypeError, 'first operand: expected a numerical',
2051
+ myokit.Multiply, a, myokit.Number(1))
2052
+ self.assertRaisesRegex(
2053
+ myokit.TypeError, 'second operand: expected a numerical',
2054
+ myokit.Multiply, myokit.Number(1), a)
2055
+
2005
2056
  def test_diff(self):
2006
2057
  # Tests Multiply.diff()
2007
-
2008
2058
  m = pd_model.clone()
2009
2059
  V = m.get('membrane.V')
2010
2060
  g = m.get('ina.g')
@@ -2091,9 +2141,20 @@ class MultiplyTest(unittest.TestCase):
2091
2141
  class DivideTest(unittest.TestCase):
2092
2142
  """Tests myokit.Divide."""
2093
2143
 
2144
+ def test_creation(self):
2145
+ # Operands must be expressions
2146
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
2147
+ myokit.Divide, 15, 3)
2148
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
2149
+ self.assertRaisesRegex(
2150
+ myokit.TypeError, 'first operand: expected a numerical',
2151
+ myokit.Divide, a, myokit.Number(1))
2152
+ self.assertRaisesRegex(
2153
+ myokit.TypeError, 'second operand: expected a numerical',
2154
+ myokit.Divide, myokit.Number(1), a)
2155
+
2094
2156
  def test_diff(self):
2095
2157
  # Tests Divide.diff()
2096
-
2097
2158
  m = pd_model.clone()
2098
2159
  V = m.get('membrane.V')
2099
2160
  g = m.get('ina.g')
@@ -2180,6 +2241,18 @@ class DivideTest(unittest.TestCase):
2180
2241
  class QuotientTest(unittest.TestCase):
2181
2242
  """Tests myokit.Quotient."""
2182
2243
 
2244
+ def test_creation(self):
2245
+ # Operands must be expressions
2246
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
2247
+ myokit.Quotient, 15, 3)
2248
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
2249
+ self.assertRaisesRegex(
2250
+ myokit.TypeError, 'first operand: expected a numerical',
2251
+ myokit.Quotient, a, myokit.Number(1))
2252
+ self.assertRaisesRegex(
2253
+ myokit.TypeError, 'second operand: expected a numerical',
2254
+ myokit.Quotient, myokit.Number(1), a)
2255
+
2183
2256
  def test_diff(self):
2184
2257
  # Tests Quotient.diff()
2185
2258
 
@@ -2256,6 +2329,18 @@ class QuotientTest(unittest.TestCase):
2256
2329
  class RemainderTest(unittest.TestCase):
2257
2330
  """Tests myokit.Remainder."""
2258
2331
 
2332
+ def test_creation(self):
2333
+ # Operands must be expressions
2334
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
2335
+ myokit.Remainder, myokit.Number(3), 3)
2336
+ a = myokit.MoreEqual(myokit.Number(1.1), myokit.Number(2.1))
2337
+ self.assertRaisesRegex(
2338
+ myokit.TypeError, 'first operand: expected a numerical',
2339
+ myokit.Remainder, a, myokit.Number(1.3))
2340
+ self.assertRaisesRegex(
2341
+ myokit.TypeError, 'second operand: expected a numerical',
2342
+ myokit.Remainder, myokit.Number(-1.2), a)
2343
+
2259
2344
  def test_diff(self):
2260
2345
  # Tests Remainder.diff()
2261
2346
 
@@ -2380,6 +2465,18 @@ class PowerTest(unittest.TestCase):
2380
2465
  self.assertNotEqual(x, y)
2381
2466
  self.assertEqual(y, myokit.Power(i, i))
2382
2467
 
2468
+ def test_creation(self):
2469
+ # Operands must be expressions
2470
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
2471
+ myokit.Power, myokit.Number(3), 3)
2472
+ a = myokit.Less(myokit.Number(1), myokit.Number(2))
2473
+ self.assertRaisesRegex(
2474
+ myokit.TypeError, 'first operand: expected a numerical',
2475
+ myokit.Power, a, myokit.Number(2))
2476
+ self.assertRaisesRegex(
2477
+ myokit.TypeError, 'second operand: expected a numerical',
2478
+ myokit.Power, myokit.Number(3.2), a)
2479
+
2383
2480
  def test_diff(self):
2384
2481
  # Tests Power.diff()
2385
2482
 
@@ -2506,13 +2603,6 @@ class SqrtTest(unittest.TestCase):
2506
2603
  self.assertFalse(x.bracket(j))
2507
2604
  self.assertRaises(ValueError, x.bracket, myokit.Number(3))
2508
2605
 
2509
- def test_creation(self):
2510
- # Test Sqrt creation.
2511
- myokit.Sqrt(myokit.Number(1))
2512
- self.assertRaisesRegex(
2513
- myokit.IntegrityError, 'wrong number', myokit.Sqrt,
2514
- myokit.Number(1), myokit.Number(2))
2515
-
2516
2606
  def test_clone(self):
2517
2607
  # Test Sqrt.clone().
2518
2608
  i = myokit.Number(3)
@@ -2534,6 +2624,21 @@ class SqrtTest(unittest.TestCase):
2534
2624
  self.assertNotEqual(x, y)
2535
2625
  self.assertEqual(y, z)
2536
2626
 
2627
+ def test_creation(self):
2628
+ myokit.Sqrt(myokit.Number(1))
2629
+
2630
+ # Wrong number of operands
2631
+ self.assertRaisesRegex(
2632
+ myokit.IntegrityError, 'wrong number', myokit.Sqrt,
2633
+ myokit.Number(1), myokit.Number(2))
2634
+
2635
+ # Operand must be a numerical expression
2636
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
2637
+ myokit.Sqrt, False)
2638
+ self.assertRaisesRegex(
2639
+ myokit.TypeError, r'sqrt\(\) expects a numerical operand',
2640
+ myokit.Sqrt, myokit.Equal(myokit.Number(1), myokit.Number(2)))
2641
+
2537
2642
  def test_diff(self):
2538
2643
  # Tests Sqrt.diff()
2539
2644
 
@@ -2635,10 +2740,19 @@ class ExpTest(unittest.TestCase):
2635
2740
  def test_creation(self):
2636
2741
  # Test Exp creation.
2637
2742
  myokit.Exp(myokit.Number(1))
2743
+
2744
+ # Wrong number of operands
2638
2745
  self.assertRaisesRegex(
2639
2746
  myokit.IntegrityError, 'wrong number', myokit.Exp,
2640
2747
  myokit.Number(1), myokit.Number(2))
2641
2748
 
2749
+ # Operand must be a numerical expression
2750
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
2751
+ myokit.Exp, 1.2)
2752
+ self.assertRaisesRegex(
2753
+ myokit.TypeError, r'Function exp\(\) expects a numerical operand',
2754
+ myokit.Exp, myokit.Equal(myokit.Number(1), myokit.Number(2)))
2755
+
2642
2756
  def test_diff(self):
2643
2757
  # Tests Exp.diff()
2644
2758
 
@@ -2785,10 +2899,28 @@ class LogTest(unittest.TestCase):
2785
2899
  # Test Log creation.
2786
2900
  myokit.Log(myokit.Number(1))
2787
2901
  myokit.Log(myokit.Number(1), myokit.Number(2))
2902
+
2903
+ # Wrong number of operatnds
2788
2904
  self.assertRaisesRegex(
2789
2905
  myokit.IntegrityError, 'wrong number', myokit.Log,
2790
2906
  myokit.Number(1), myokit.Number(2), myokit.Number(3))
2791
2907
 
2908
+ # Operands must be numerical expressions
2909
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
2910
+ myokit.Log, 1.2)
2911
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
2912
+ myokit.Log, myokit.Number(3), 1.2)
2913
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
2914
+ self.assertRaisesRegex(
2915
+ myokit.TypeError, r'first operand: function log\(\) expects numer',
2916
+ myokit.Log, a)
2917
+ self.assertRaisesRegex(
2918
+ myokit.TypeError, r'first operand: function log\(\) expects numer',
2919
+ myokit.Log, a, myokit.Number(1))
2920
+ self.assertRaisesRegex(
2921
+ myokit.TypeError, r'second operand: function log\(\) expects nume',
2922
+ myokit.Log, myokit.Number(1), a)
2923
+
2792
2924
  def test_diff(self):
2793
2925
  # Tests Log.diff()
2794
2926
 
@@ -2918,6 +3050,14 @@ class LogTest(unittest.TestCase):
2918
3050
  class Log10Test(unittest.TestCase):
2919
3051
  """Tests myokit.Log10."""
2920
3052
 
3053
+ def test_creation(self):
3054
+ # Operand must be a numerical expression
3055
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3056
+ myokit.Log10, 1.2)
3057
+ self.assertRaisesRegex(
3058
+ myokit.TypeError, r'log10\(\) expects a numerical operand',
3059
+ myokit.Log10, myokit.Equal(myokit.Number(1), myokit.Number(2)))
3060
+
2921
3061
  def test_diff(self):
2922
3062
  # Tests Log10.diff()
2923
3063
 
@@ -2948,6 +3088,14 @@ class Log10Test(unittest.TestCase):
2948
3088
  class SinTest(unittest.TestCase):
2949
3089
  """Tests myokit.Sin."""
2950
3090
 
3091
+ def test_creation(self):
3092
+ # Operand must be a numerical expression
3093
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3094
+ myokit.Sin, 2.1)
3095
+ self.assertRaisesRegex(
3096
+ myokit.TypeError, r'sin\(\) expects a numerical operand',
3097
+ myokit.Sin, myokit.Equal(myokit.Number(1), myokit.Number(2)))
3098
+
2951
3099
  def test_diff(self):
2952
3100
  # Tests Sin.diff()
2953
3101
 
@@ -2977,6 +3125,14 @@ class SinTest(unittest.TestCase):
2977
3125
  class CosTest(unittest.TestCase):
2978
3126
  """Tests myokit.Cos."""
2979
3127
 
3128
+ def test_creation(self):
3129
+ # Operand must be a numerical expression
3130
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3131
+ myokit.Cos, myokit.Cos)
3132
+ self.assertRaisesRegex(
3133
+ myokit.TypeError, r'cos\(\) expects a numerical operand',
3134
+ myokit.Cos, myokit.Equal(myokit.Number(1), myokit.Number(2)))
3135
+
2980
3136
  def test_diff(self):
2981
3137
  # Tests Cos.diff()
2982
3138
 
@@ -3005,6 +3161,14 @@ class CosTest(unittest.TestCase):
3005
3161
  class TanTest(unittest.TestCase):
3006
3162
  """ Tests myokit.Tan. """
3007
3163
 
3164
+ def test_creation(self):
3165
+ # Operand must be a numerical expression
3166
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3167
+ myokit.Tan, myokit.Model())
3168
+ self.assertRaisesRegex(
3169
+ myokit.TypeError, r'tan\(\) expects a numerical operand',
3170
+ myokit.Tan, myokit.Equal(myokit.Number(1), myokit.Number(2)))
3171
+
3008
3172
  def test_diff(self):
3009
3173
  # Tests Tan.diff()
3010
3174
 
@@ -3033,6 +3197,14 @@ class TanTest(unittest.TestCase):
3033
3197
  class ASinTest(unittest.TestCase):
3034
3198
  """ Tests myokit.ASin. """
3035
3199
 
3200
+ def test_creation(self):
3201
+ # Operand must be a numerical expression
3202
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3203
+ myokit.ASin, 4)
3204
+ self.assertRaisesRegex(
3205
+ myokit.TypeError, r'asin\(\) expects a numerical operand',
3206
+ myokit.ASin, myokit.Equal(myokit.Number(1), myokit.Number(2)))
3207
+
3036
3208
  def test_diff(self):
3037
3209
  # Tests ASin.diff()
3038
3210
 
@@ -3062,6 +3234,14 @@ class ASinTest(unittest.TestCase):
3062
3234
  class ACosTest(unittest.TestCase):
3063
3235
  """ Tests myokit.ACos. """
3064
3236
 
3237
+ def test_creation(self):
3238
+ # Operand must be a numerical expression
3239
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3240
+ myokit.ACos, 4)
3241
+ self.assertRaisesRegex(
3242
+ myokit.TypeError, r'acos\(\) expects a numerical operand',
3243
+ myokit.ACos, myokit.Equal(myokit.Number(1), myokit.Number(2)))
3244
+
3065
3245
  def test_diff(self):
3066
3246
  # Tests ACos.diff()
3067
3247
 
@@ -3092,6 +3272,14 @@ class ACosTest(unittest.TestCase):
3092
3272
  class ATanTest(unittest.TestCase):
3093
3273
  """ Tests myokit.ATan. """
3094
3274
 
3275
+ def test_creation(self):
3276
+ # Operand must be a numerical expression
3277
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3278
+ myokit.ATan, 4)
3279
+ self.assertRaisesRegex(
3280
+ myokit.TypeError, r'atan\(\) expects a numerical operand',
3281
+ myokit.ATan, myokit.Equal(myokit.Number(1), myokit.Number(2)))
3282
+
3095
3283
  def test_diff(self):
3096
3284
  # Tests ATan.diff()
3097
3285
 
@@ -3122,6 +3310,15 @@ class ATanTest(unittest.TestCase):
3122
3310
  class FloorTest(unittest.TestCase):
3123
3311
  """ Tests myokit.Floor. """
3124
3312
 
3313
+ def test_creation(self):
3314
+ # Operand must be a numerical expression
3315
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3316
+ myokit.Floor, 1.2)
3317
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3318
+ self.assertRaisesRegex(
3319
+ myokit.TypeError, r'floor\(\) expects a numerical operand',
3320
+ myokit.Floor, myokit.Equal(myokit.Number(1), myokit.Number(2)))
3321
+
3125
3322
  def test_diff(self):
3126
3323
  # Tests Floor.diff()
3127
3324
 
@@ -3173,6 +3370,14 @@ class FloorTest(unittest.TestCase):
3173
3370
  class CeilTest(unittest.TestCase):
3174
3371
  """ Tests myokit.Ceil. """
3175
3372
 
3373
+ def test_creation(self):
3374
+ # Operand must be a numerical expression
3375
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3376
+ myokit.Ceil, 3.4)
3377
+ self.assertRaisesRegex(
3378
+ myokit.TypeError, r'ceil\(\) expects a numerical operand',
3379
+ myokit.Ceil, myokit.LessEqual(myokit.Number(3), myokit.Number(2)))
3380
+
3176
3381
  def test_diff(self):
3177
3382
  # Tests Ceil.diff()
3178
3383
 
@@ -3224,6 +3429,14 @@ class CeilTest(unittest.TestCase):
3224
3429
  class AbsTest(unittest.TestCase):
3225
3430
  """ Tests myokit.Abs. """
3226
3431
 
3432
+ def test_creation(self):
3433
+ # Operand must be a numerical expression
3434
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3435
+ myokit.Abs, 3.4)
3436
+ self.assertRaisesRegex(
3437
+ myokit.TypeError, r'abs\(\) expects a numerical operand',
3438
+ myokit.Abs, myokit.Equal(myokit.Number(1), myokit.Number(2)))
3439
+
3227
3440
  def test_diff(self):
3228
3441
  # Tests Abs.diff()
3229
3442
 
@@ -3296,6 +3509,18 @@ class AbsTest(unittest.TestCase):
3296
3509
  class EqualTest(unittest.TestCase):
3297
3510
  """ Tests myokit.Equal. """
3298
3511
 
3512
+ def test_creation(self):
3513
+ # Operands must be numerical expressions
3514
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3515
+ myokit.Equal, 1, 1)
3516
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3517
+ self.assertRaisesRegex(
3518
+ myokit.TypeError, 'first operand: expected a numerical',
3519
+ myokit.Equal, a, myokit.Number(1))
3520
+ self.assertRaisesRegex(
3521
+ myokit.TypeError, 'second operand: expected a numerical',
3522
+ myokit.Equal, myokit.Number(1), a)
3523
+
3299
3524
  def test_diff(self):
3300
3525
  # Tests Equal.diff()
3301
3526
  x = myokit.Equal(myokit.Number(1), myokit.Number(1))
@@ -3314,58 +3539,55 @@ class EqualTest(unittest.TestCase):
3314
3539
  def test_eval_unit(self):
3315
3540
  # Test Equal.eval_unit().
3316
3541
 
3317
- # Mini model
3318
- m = myokit.Model()
3319
- c = m.add_component('c')
3320
- x = c.add_variable('x')
3321
- x.set_rhs('3')
3322
- y = c.add_variable('y')
3323
- y.set_rhs('3')
3324
- z = c.add_variable('z')
3325
- z.set_rhs('x == y')
3326
-
3327
- # Test in tolerant mode
3328
- self.assertEqual(z.rhs().eval_unit(), None)
3329
- x.set_unit(myokit.units.ampere)
3330
- self.assertEqual(z.rhs().eval_unit(), myokit.units.dimensionless)
3331
- y.set_unit(myokit.units.ampere)
3332
- self.assertEqual(z.rhs().eval_unit(), myokit.units.dimensionless)
3333
- y.set_unit(myokit.units.volt)
3334
- self.assertRaisesRegex(
3335
- myokit.IncompatibleUnitError, 'equal units', z.rhs().eval_unit)
3336
- x.set_unit(None)
3337
- self.assertEqual(z.rhs().eval_unit(), myokit.units.dimensionless)
3338
- y.set_unit(None)
3339
- self.assertEqual(z.rhs().eval_unit(), None)
3340
-
3341
- # Test in strict mode
3542
+ # None and None is always fine
3342
3543
  s = myokit.UNIT_STRICT
3343
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3344
- x.set_unit(myokit.units.ampere)
3345
- self.assertRaisesRegex(
3346
- myokit.IncompatibleUnitError, 'equal units', z.rhs().eval_unit, s)
3347
- y.set_unit(myokit.units.ampere)
3348
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3349
- y.set_unit(myokit.units.volt)
3350
- self.assertRaisesRegex(
3351
- myokit.IncompatibleUnitError, 'equal units', z.rhs().eval_unit)
3352
- x.set_unit(None)
3353
- self.assertRaisesRegex(
3354
- myokit.IncompatibleUnitError, 'equal units', z.rhs().eval_unit, s)
3355
- y.set_unit(None)
3356
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3544
+ e = myokit.Equal(myokit.Number(1), myokit.Number(2))
3545
+ d = myokit.units.dimensionless
3546
+ self.assertEqual(e.eval_unit(), d)
3547
+ self.assertEqual(e.eval_unit(s), d)
3548
+ # None and dimensionless is always fine
3549
+ e = myokit.Equal(myokit.Number(1), myokit.Number(2, d))
3550
+ self.assertEqual(e.eval_unit(), d)
3551
+ self.assertEqual(e.eval_unit(s), d)
3552
+ # Equal is always fine
3553
+ e = myokit.Equal(myokit.Number(1, d), myokit.Number(2, d))
3554
+ self.assertEqual(e.eval_unit(), d)
3555
+ self.assertEqual(e.eval_unit(s), d)
3556
+ a = myokit.units.ampere
3557
+ e = myokit.Equal(myokit.Number(1, a), myokit.Number(2, a))
3558
+ self.assertEqual(e.eval_unit(), d)
3559
+ self.assertEqual(e.eval_unit(s), d)
3560
+ # Unequal is never fine
3561
+ e = myokit.Equal(myokit.Number(1, d), myokit.Number(2, a))
3562
+ self.assertRaises(myokit.IncompatibleUnitError, e.eval_unit)
3563
+ self.assertRaises(myokit.IncompatibleUnitError, e.eval_unit, s)
3564
+ # One None one not-dimensionless is only fine in tolerant mode
3565
+ e = myokit.Equal(myokit.Number(1, a), myokit.Number(2))
3566
+ self.assertEqual(e.eval_unit(), d)
3567
+ self.assertRaises(myokit.IncompatibleUnitError, e.eval_unit, s)
3357
3568
 
3358
3569
  def test_tree_str(self):
3359
3570
  # Test Equal.tree_str().
3360
3571
  x = myokit.Equal(myokit.Number(1), myokit.Number(2))
3361
3572
  self.assertEqual(x.tree_str(), '==\n 1\n 2\n')
3362
- x = myokit.Plus(myokit.Number(3), x)
3363
- self.assertEqual(x.tree_str(), '+\n 3\n ==\n 1\n 2\n')
3573
+ self.assertEqual(myokit.Not(x).tree_str(), 'not\n ==\n 1\n 2\n')
3364
3574
 
3365
3575
 
3366
3576
  class NotEqualTest(unittest.TestCase):
3367
3577
  """ Tests myokit.NotEqual. """
3368
3578
 
3579
+ def test_creation(self):
3580
+ # Operands must be numerical expressions
3581
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3582
+ myokit.NotEqual, 1, 1)
3583
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3584
+ self.assertRaisesRegex(
3585
+ myokit.TypeError, 'first operand: expected a numerical',
3586
+ myokit.NotEqual, a, myokit.Number(1))
3587
+ self.assertRaisesRegex(
3588
+ myokit.TypeError, 'second operand: expected a numerical',
3589
+ myokit.NotEqual, myokit.Number(1), a)
3590
+
3369
3591
  def test_eval(self):
3370
3592
  # Test NotEqual.eval().
3371
3593
  x = myokit.NotEqual(myokit.Number(1), myokit.Number(1))
@@ -3377,12 +3599,25 @@ class NotEqualTest(unittest.TestCase):
3377
3599
  # Test NotEqual.tree_str().
3378
3600
  x = myokit.NotEqual(myokit.Number(1), myokit.Number(2))
3379
3601
  self.assertEqual(x.tree_str(), '!=\n 1\n 2\n')
3380
- x = myokit.Plus(myokit.Number(3), x)
3381
- self.assertEqual(x.tree_str(), '+\n 3\n !=\n 1\n 2\n')
3602
+ self.assertEqual(myokit.Not(x).tree_str(),
3603
+ 'not\n !=\n 1\n 2\n')
3382
3604
 
3383
3605
 
3384
3606
  class MoreTest(unittest.TestCase):
3385
3607
  """ Tests myokit.More. """
3608
+
3609
+ def test_creation(self):
3610
+ # Operands must be numerical expressions
3611
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3612
+ myokit.More, 1, 1)
3613
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3614
+ self.assertRaisesRegex(
3615
+ myokit.TypeError, 'first operand: expected a numerical',
3616
+ myokit.More, a, myokit.Number(1))
3617
+ self.assertRaisesRegex(
3618
+ myokit.TypeError, 'second operand: expected a numerical',
3619
+ myokit.More, myokit.Number(1), a)
3620
+
3386
3621
  def test_eval(self):
3387
3622
  # Test More.eval().
3388
3623
  x = myokit.More(myokit.Number(1), myokit.Number(1))
@@ -3394,12 +3629,24 @@ class MoreTest(unittest.TestCase):
3394
3629
  # Test More.tree_str().
3395
3630
  x = myokit.More(myokit.Number(1), myokit.Number(2))
3396
3631
  self.assertEqual(x.tree_str(), '>\n 1\n 2\n')
3397
- x = myokit.Plus(myokit.Number(3), x)
3398
- self.assertEqual(x.tree_str(), '+\n 3\n >\n 1\n 2\n')
3632
+ self.assertEqual(myokit.Not(x).tree_str(), 'not\n >\n 1\n 2\n')
3399
3633
 
3400
3634
 
3401
3635
  class LessTest(unittest.TestCase):
3402
3636
  """ Tests myokit.Less. """
3637
+
3638
+ def test_creation(self):
3639
+ # Operands must be expressions
3640
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3641
+ myokit.Less, 1, 1)
3642
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3643
+ self.assertRaisesRegex(
3644
+ myokit.TypeError, 'first operand: expected a numerical',
3645
+ myokit.Less, a, myokit.Number(1))
3646
+ self.assertRaisesRegex(
3647
+ myokit.TypeError, 'second operand: expected a numerical',
3648
+ myokit.Less, myokit.Number(1), a)
3649
+
3403
3650
  def test_eval(self):
3404
3651
  # Test Less.eval().
3405
3652
  x = myokit.Less(myokit.Number(1), myokit.Number(1))
@@ -3411,13 +3658,24 @@ class LessTest(unittest.TestCase):
3411
3658
  # Test Less.tree_str().
3412
3659
  x = myokit.Less(myokit.Number(1), myokit.Number(2))
3413
3660
  self.assertEqual(x.tree_str(), '<\n 1\n 2\n')
3414
- x = myokit.Plus(myokit.Number(3), x)
3415
- self.assertEqual(x.tree_str(), '+\n 3\n <\n 1\n 2\n')
3661
+ self.assertEqual(myokit.Not(x).tree_str(), 'not\n <\n 1\n 2\n')
3416
3662
 
3417
3663
 
3418
3664
  class MoreEqualTest(unittest.TestCase):
3419
3665
  """ Tests myokit.MoreEqual. """
3420
3666
 
3667
+ def test_creation(self):
3668
+ # Operands must be numerical expressions
3669
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3670
+ myokit.MoreEqual, 1, 1)
3671
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3672
+ self.assertRaisesRegex(
3673
+ myokit.TypeError, 'first operand: expected a numerical',
3674
+ myokit.MoreEqual, a, myokit.Number(1))
3675
+ self.assertRaisesRegex(
3676
+ myokit.TypeError, 'second operand: expected a numerical',
3677
+ myokit.MoreEqual, myokit.Number(1), a)
3678
+
3421
3679
  def test_eval(self):
3422
3680
  # Test MoreEqual.eval().
3423
3681
  x = myokit.MoreEqual(myokit.Number(1), myokit.Number(1))
@@ -3431,13 +3689,24 @@ class MoreEqualTest(unittest.TestCase):
3431
3689
  # Test MoreEqual.tree_str().
3432
3690
  x = myokit.MoreEqual(myokit.Number(1), myokit.Number(2))
3433
3691
  self.assertEqual(x.tree_str(), '>=\n 1\n 2\n')
3434
- x = myokit.Plus(myokit.Number(3), x)
3435
- self.assertEqual(x.tree_str(), '+\n 3\n >=\n 1\n 2\n')
3692
+ self.assertEqual(myokit.Not(x).tree_str(), 'not\n >=\n 1\n 2\n')
3436
3693
 
3437
3694
 
3438
3695
  class LessEqualTest(unittest.TestCase):
3439
3696
  """ Tests myokit.LessEqual. """
3440
3697
 
3698
+ def test_creation(self):
3699
+ # Operands must be numerical expressions
3700
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3701
+ myokit.LessEqual, 1, 1)
3702
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3703
+ self.assertRaisesRegex(
3704
+ myokit.TypeError, 'first operand: expected a numerical',
3705
+ myokit.LessEqual, a, myokit.Number(1))
3706
+ self.assertRaisesRegex(
3707
+ myokit.TypeError, 'second operand: expected a numerical',
3708
+ myokit.LessEqual, myokit.Number(1), a)
3709
+
3441
3710
  def test_eval(self):
3442
3711
  # Test LessEqual.eval().
3443
3712
  x = myokit.LessEqual(myokit.Number(1), myokit.Number(1))
@@ -3451,13 +3720,24 @@ class LessEqualTest(unittest.TestCase):
3451
3720
  # Test LessEqual.tree_str().
3452
3721
  x = myokit.LessEqual(myokit.Number(1), myokit.Number(2))
3453
3722
  self.assertEqual(x.tree_str(), '<=\n 1\n 2\n')
3454
- x = myokit.Plus(myokit.Number(3), x)
3455
- self.assertEqual(x.tree_str(), '+\n 3\n <=\n 1\n 2\n')
3723
+ self.assertEqual(myokit.Not(x).tree_str(), 'not\n <=\n 1\n 2\n')
3456
3724
 
3457
3725
 
3458
3726
  class AndTest(unittest.TestCase):
3459
3727
  """ Tests myokit.And. """
3460
3728
 
3729
+ def test_creation(self):
3730
+ # Operands must be conditional expressions
3731
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3732
+ myokit.And, 1, 1)
3733
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3734
+ self.assertRaisesRegex(
3735
+ myokit.TypeError, 'first operand: expected a condition',
3736
+ myokit.And, myokit.Number(1), a)
3737
+ self.assertRaisesRegex(
3738
+ myokit.TypeError, 'second operand: expected a condition',
3739
+ myokit.And, a, myokit.Number(1))
3740
+
3461
3741
  def test_diff(self):
3462
3742
  # Tests And.diff()
3463
3743
  x1 = myokit.Equal(myokit.Number(1), myokit.Number(1))
@@ -3471,146 +3751,121 @@ class AndTest(unittest.TestCase):
3471
3751
 
3472
3752
  def test_eval(self):
3473
3753
  # Test And.eval().
3474
- x = myokit.And(myokit.Number(1), myokit.Number(1))
3475
- self.assertTrue(x.eval())
3476
- x = myokit.And(myokit.Number(0), myokit.Number(2))
3477
- self.assertFalse(x.eval())
3754
+ a = myokit.Equal(myokit.Number(2), myokit.Number(2))
3755
+ b = myokit.Equal(myokit.Number(3), myokit.Number(1))
3756
+ self.assertTrue(myokit.And(a, a).eval())
3757
+ self.assertFalse(myokit.And(a, b).eval())
3758
+ self.assertFalse(myokit.And(b, b).eval())
3478
3759
 
3479
3760
  def test_eval_unit(self):
3480
3761
  # Test And.eval_unit().
3481
3762
 
3482
- # Mini model
3483
- m = myokit.Model()
3484
- c = m.add_component('c')
3485
- x = c.add_variable('x')
3486
- x.set_rhs('1')
3487
- y = c.add_variable('y')
3488
- y.set_rhs('1')
3489
- z = c.add_variable('z')
3490
- z.set_rhs('x and y')
3491
-
3492
- # Test in tolerant mode
3493
- self.assertEqual(z.rhs().eval_unit(), None)
3494
- x.set_unit(myokit.units.ampere)
3495
- self.assertRaisesRegex(
3496
- myokit.IncompatibleUnitError, 'dimensionless', z.rhs().eval_unit)
3497
- y.set_unit(myokit.units.ampere)
3498
- self.assertRaisesRegex(
3499
- myokit.IncompatibleUnitError, 'dimensionless', z.rhs().eval_unit)
3500
- x.set_unit(myokit.units.dimensionless)
3501
- self.assertRaisesRegex(
3502
- myokit.IncompatibleUnitError, 'dimensionless', z.rhs().eval_unit)
3503
- y.set_unit(myokit.units.dimensionless)
3504
- self.assertEqual(z.rhs().eval_unit(), myokit.units.dimensionless)
3505
- x.set_unit(None)
3506
- self.assertEqual(z.rhs().eval_unit(), myokit.units.dimensionless)
3507
- y.set_unit(None)
3508
- self.assertEqual(z.rhs().eval_unit(), None)
3509
-
3510
- # Test in strict mode
3763
+ # Test in tolerant mode: no own testing, but should test operands!
3764
+ e = myokit.parse_expression('1 == 1 and 2 == 2')
3765
+ self.assertEqual(e.eval_unit(), myokit.units.dimensionless)
3766
+ e = myokit.parse_expression('1 == 1 [1] and 2 [1] == 2')
3767
+ self.assertEqual(e.eval_unit(), myokit.units.dimensionless)
3768
+ e = myokit.parse_expression('1 [1] == 1 [1] and 2 [1] == 2 [1]')
3769
+ self.assertEqual(e.eval_unit(), myokit.units.dimensionless)
3770
+ e = myokit.parse_expression('1 [1] == 1 [1] and 2 [1] == 2 [mg]')
3771
+ self.assertRaises(myokit.IncompatibleUnitError, e.eval_unit)
3772
+
3773
+ # Test in strict mode: no own testing, but should test operands!
3511
3774
  s = myokit.UNIT_STRICT
3512
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3513
- x.set_unit(myokit.units.ampere)
3514
- self.assertRaisesRegex(
3515
- myokit.IncompatibleUnitError, 'dimensionles', z.rhs().eval_unit, s)
3516
- y.set_unit(myokit.units.ampere)
3517
- self.assertRaisesRegex(
3518
- myokit.IncompatibleUnitError, 'dimensionles', z.rhs().eval_unit, s)
3519
- x.set_unit(myokit.units.dimensionless)
3520
- self.assertRaisesRegex(
3521
- myokit.IncompatibleUnitError, 'dimensionles', z.rhs().eval_unit, s)
3522
- y.set_unit(myokit.units.dimensionless)
3523
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3524
- x.set_unit(None)
3525
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3526
- y.set_unit(None)
3527
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3775
+ e = myokit.parse_expression('1 == 1 and 2 == 2')
3776
+ self.assertEqual(e.eval_unit(s), myokit.units.dimensionless)
3777
+ e = myokit.parse_expression('1 == 1 [1] and 2 [1] == 2')
3778
+ self.assertEqual(e.eval_unit(s), myokit.units.dimensionless)
3779
+ e = myokit.parse_expression('1 [1] == 1 [1] and 2 [1] == 2 [1]')
3780
+ self.assertEqual(e.eval_unit(s), myokit.units.dimensionless)
3781
+ e = myokit.parse_expression('1 == 1 [V] and 1 == 1 [1]')
3782
+ self.assertRaises(myokit.IncompatibleUnitError, e.eval_unit, s)
3528
3783
 
3529
3784
  def test_tree_str(self):
3530
3785
  # Test And.tree_str().
3531
- x = myokit.And(myokit.Number(1), myokit.Number(2))
3532
- self.assertEqual(x.tree_str(), 'and\n 1\n 2\n')
3533
- x = myokit.Plus(myokit.Number(3), x)
3534
- self.assertEqual(x.tree_str(), '+\n 3\n and\n 1\n 2\n')
3786
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3787
+ b = myokit.Less(myokit.Number(4), myokit.Number(3))
3788
+ self.assertEqual(myokit.And(a, b).tree_str(),
3789
+ 'and\n ==\n 1\n 2\n <\n 4\n 3\n')
3790
+ self.assertEqual(
3791
+ myokit.Not(myokit.And(b, a)).tree_str(),
3792
+ 'not\n and\n <\n 4\n 3\n ==\n 1\n 2\n')
3535
3793
 
3536
3794
 
3537
3795
  class OrTest(unittest.TestCase):
3538
3796
  """ Tests myokit.Or. """
3539
3797
 
3798
+ def test_creation(self):
3799
+ # Operands must be expressions
3800
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3801
+ myokit.Or, 1, 1)
3802
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3803
+ self.assertRaisesRegex(
3804
+ myokit.TypeError, 'first operand: expected a condition',
3805
+ myokit.Or, myokit.Number(1), a)
3806
+ self.assertRaisesRegex(
3807
+ myokit.TypeError, 'second operand: expected a condition',
3808
+ myokit.Or, a, myokit.Number(1))
3809
+
3540
3810
  def test_eval(self):
3541
3811
  # Test Or.eval().
3542
- x = myokit.Or(myokit.Number(1), myokit.Number(1))
3543
- self.assertTrue(x.eval())
3544
- x = myokit.Or(myokit.Number(0), myokit.Number(2))
3545
- self.assertTrue(x.eval())
3546
- x = myokit.Or(myokit.Number(0), myokit.Number(0))
3547
- self.assertFalse(x.eval())
3812
+ a = myokit.Equal(myokit.Number(2), myokit.Number(2))
3813
+ b = myokit.Equal(myokit.Number(3), myokit.Number(1))
3814
+ self.assertTrue(myokit.Or(a, a).eval())
3815
+ self.assertTrue(myokit.Or(a, b).eval())
3816
+ self.assertFalse(myokit.Or(b, b).eval())
3548
3817
 
3549
3818
  def test_eval_unit(self):
3550
3819
  # Test Or.eval_unit().
3551
3820
 
3552
- # Mini model
3553
- m = myokit.Model()
3554
- c = m.add_component('c')
3555
- x = c.add_variable('x')
3556
- x.set_rhs('1')
3557
- y = c.add_variable('y')
3558
- y.set_rhs('1')
3559
- z = c.add_variable('z')
3560
- z.set_rhs('x or y')
3561
-
3562
- # Test in tolerant mode
3563
- self.assertEqual(z.rhs().eval_unit(), None)
3564
- x.set_unit(myokit.units.ampere)
3565
- self.assertRaisesRegex(
3566
- myokit.IncompatibleUnitError, 'dimensionless', z.rhs().eval_unit)
3567
- y.set_unit(myokit.units.ampere)
3568
- self.assertRaisesRegex(
3569
- myokit.IncompatibleUnitError, 'dimensionless', z.rhs().eval_unit)
3570
- x.set_unit(myokit.units.dimensionless)
3571
- self.assertRaisesRegex(
3572
- myokit.IncompatibleUnitError, 'dimensionless', z.rhs().eval_unit)
3573
- y.set_unit(myokit.units.dimensionless)
3574
- self.assertEqual(z.rhs().eval_unit(), myokit.units.dimensionless)
3575
- x.set_unit(None)
3576
- self.assertEqual(z.rhs().eval_unit(), myokit.units.dimensionless)
3577
- y.set_unit(None)
3578
- self.assertEqual(z.rhs().eval_unit(), None)
3579
-
3580
- # Test in strict mode
3821
+ # Test in tolerant mode: no own testing, but should test operands!
3822
+ e = myokit.parse_expression('1 == 1 or 2 == 2')
3823
+ self.assertEqual(e.eval_unit(), myokit.units.dimensionless)
3824
+ e = myokit.parse_expression('1 == 1 [1] or 2 [1] == 2')
3825
+ self.assertEqual(e.eval_unit(), myokit.units.dimensionless)
3826
+ e = myokit.parse_expression('1 [1] == 1 [1] or 2 [1] == 2 [1]')
3827
+ self.assertEqual(e.eval_unit(), myokit.units.dimensionless)
3828
+ e = myokit.parse_expression('1 [1] == 1 [1] or 2 [1] == 2 [mg]')
3829
+ self.assertRaises(myokit.IncompatibleUnitError, e.eval_unit)
3830
+
3831
+ # Test in strict mode: no own testing, but should test operands!
3581
3832
  s = myokit.UNIT_STRICT
3582
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3583
- x.set_unit(myokit.units.ampere)
3584
- self.assertRaisesRegex(
3585
- myokit.IncompatibleUnitError, 'dimensionles', z.rhs().eval_unit, s)
3586
- y.set_unit(myokit.units.ampere)
3587
- self.assertRaisesRegex(
3588
- myokit.IncompatibleUnitError, 'dimensionles', z.rhs().eval_unit, s)
3589
- x.set_unit(myokit.units.dimensionless)
3590
- self.assertRaisesRegex(
3591
- myokit.IncompatibleUnitError, 'dimensionles', z.rhs().eval_unit, s)
3592
- y.set_unit(myokit.units.dimensionless)
3593
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3594
- x.set_unit(None)
3595
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3596
- y.set_unit(None)
3597
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3833
+ e = myokit.parse_expression('1 == 1 or 2 == 2')
3834
+ self.assertEqual(e.eval_unit(s), myokit.units.dimensionless)
3835
+ e = myokit.parse_expression('1 == 1 [1] or 2 [1] == 2')
3836
+ self.assertEqual(e.eval_unit(s), myokit.units.dimensionless)
3837
+ e = myokit.parse_expression('1 [1] == 1 [1] or 2 [1] == 2 [1]')
3838
+ self.assertEqual(e.eval_unit(s), myokit.units.dimensionless)
3839
+ e = myokit.parse_expression('1 == 1 [A] or 2 [1] == 2 [1]')
3840
+ self.assertRaises(myokit.IncompatibleUnitError, e.eval_unit, s)
3598
3841
 
3599
3842
  def test_tree_str(self):
3600
3843
  # Test Or.tree_str().
3601
- x = myokit.Or(myokit.Number(1), myokit.Number(2))
3602
- self.assertEqual(x.tree_str(), 'or\n 1\n 2\n')
3603
- x = myokit.Plus(myokit.Number(3), x)
3604
- self.assertEqual(x.tree_str(), '+\n 3\n or\n 1\n 2\n')
3844
+ a = myokit.Equal(myokit.Number(1), myokit.Number(2))
3845
+ b = myokit.NotEqual(myokit.Number(3), myokit.Number(4))
3846
+ x = myokit.Or(a, b)
3847
+ self.assertEqual(
3848
+ x.tree_str(),
3849
+ 'or\n ==\n 1\n 2\n !=\n 3\n 4\n')
3850
+ x = myokit.Not(x)
3851
+ self.assertEqual(
3852
+ x.tree_str(),
3853
+ 'not\n or\n ==\n 1\n 2\n !=\n 3\n 4\n')
3605
3854
 
3606
3855
 
3607
3856
  class NotTest(unittest.TestCase):
3608
3857
  """ Tests myokit.Not. """
3609
3858
 
3859
+ def test_creation(self):
3860
+ # Operand must be an expression
3861
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3862
+ myokit.Not, 1)
3863
+ self.assertRaisesRegex(
3864
+ myokit.TypeError, 'expected a condition',
3865
+ myokit.Not, myokit.Number(1))
3866
+
3610
3867
  def test_code(self):
3611
3868
  # Test Not.code().
3612
- x = myokit.Not(myokit.Number(1))
3613
- self.assertEqual(x.code(), 'not 1')
3614
3869
  x = myokit.Not(myokit.Equal(myokit.Number(1), myokit.Number(1)))
3615
3870
  self.assertEqual(x.code(), 'not (1 == 1)')
3616
3871
 
@@ -3624,9 +3879,9 @@ class NotTest(unittest.TestCase):
3624
3879
 
3625
3880
  def test_eval(self):
3626
3881
  # Test Not.eval().
3627
- x = myokit.Not(myokit.Number(1))
3882
+ x = myokit.Not(myokit.Equal(myokit.Number(1), myokit.Number(1)))
3628
3883
  self.assertFalse(x.eval())
3629
- x = myokit.Not(myokit.Number(0))
3884
+ x = myokit.Not(myokit.Equal(myokit.Number(1), myokit.Number(0)))
3630
3885
  self.assertTrue(x.eval())
3631
3886
 
3632
3887
  def test_eval_unit(self):
@@ -3635,36 +3890,34 @@ class NotTest(unittest.TestCase):
3635
3890
  # Mini model
3636
3891
  m = myokit.Model()
3637
3892
  c = m.add_component('c')
3638
- x = c.add_variable('x')
3639
- x.set_rhs('1')
3640
- z = c.add_variable('z')
3641
- z.set_rhs('not x')
3893
+ x = c.add_variable('x', rhs=1)
3894
+ y = c.add_variable('y', rhs=2)
3895
+ e = myokit.parse_expression('not (x != y)', context=c)
3642
3896
 
3643
3897
  # Test in tolerant mode
3644
- self.assertEqual(z.rhs().eval_unit(), None)
3898
+ self.assertEqual(e.eval_unit(), myokit.units.dimensionless)
3645
3899
  x.set_unit(myokit.units.ampere)
3900
+ y.set_unit(myokit.units.dimensionless)
3646
3901
  self.assertRaisesRegex(
3647
- myokit.IncompatibleUnitError, 'dimensionless', z.rhs().eval_unit)
3902
+ myokit.IncompatibleUnitError, 'equal units on both', e.eval_unit)
3648
3903
  x.set_unit(myokit.units.dimensionless)
3649
- self.assertEqual(z.rhs().eval_unit(), myokit.units.dimensionless)
3904
+ self.assertEqual(e.eval_unit(), myokit.units.dimensionless)
3650
3905
  x.set_unit(None)
3651
- self.assertEqual(z.rhs().eval_unit(), None)
3906
+ self.assertEqual(e.eval_unit(), myokit.units.dimensionless)
3652
3907
 
3653
3908
  # Test in strict mode
3654
3909
  s = myokit.UNIT_STRICT
3655
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3910
+ self.assertEqual(e.eval_unit(s), myokit.units.dimensionless)
3656
3911
  x.set_unit(myokit.units.ampere)
3657
3912
  self.assertRaisesRegex(
3658
- myokit.IncompatibleUnitError, 'dimensionles', z.rhs().eval_unit, s)
3913
+ myokit.IncompatibleUnitError, 'equal units on bot', e.eval_unit, s)
3659
3914
  x.set_unit(myokit.units.dimensionless)
3660
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3915
+ self.assertEqual(e.eval_unit(s), myokit.units.dimensionless)
3661
3916
  x.set_unit(None)
3662
- self.assertEqual(z.rhs().eval_unit(s), myokit.units.dimensionless)
3917
+ self.assertEqual(e.eval_unit(s), myokit.units.dimensionless)
3663
3918
 
3664
3919
  def test_polish(self):
3665
3920
  # Test Not._polish().
3666
- x = myokit.Not(myokit.Number(1))
3667
- self.assertEqual(x._polish(), 'not 1')
3668
3921
  x = myokit.Not(myokit.Equal(myokit.Number(1), myokit.Number(1)))
3669
3922
  self.assertEqual(x._polish(), 'not == 1 1')
3670
3923
 
@@ -3686,6 +3939,16 @@ class IfTest(unittest.TestCase):
3686
3939
  # Test is_conditional()
3687
3940
  self.assertTrue(if_.is_conditional())
3688
3941
 
3942
+ # Operands must be expressions and have right types
3943
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
3944
+ myokit.If, 1, 1, 1)
3945
+ self.assertRaisesRegex(
3946
+ myokit.TypeError, 'first operand', myokit.If, then, then, then)
3947
+ self.assertRaisesRegex(
3948
+ myokit.TypeError, 'second operand', myokit.If, cond, cond, then)
3949
+ self.assertRaisesRegex(
3950
+ myokit.TypeError, 'third operand', myokit.If, cond, then, cond)
3951
+
3689
3952
  def test_diff(self):
3690
3953
  # Tests If.diff()
3691
3954
 
@@ -3731,14 +3994,12 @@ class IfTest(unittest.TestCase):
3731
3994
  # Mini model
3732
3995
  m = myokit.Model()
3733
3996
  c = m.add_component('c')
3734
- v1 = c.add_variable('v1')
3735
3997
  v2 = c.add_variable('v2')
3736
3998
  v3 = c.add_variable('v3')
3737
3999
  v4 = c.add_variable('v4')
3738
- v1.set_rhs('1 == 1')
3739
4000
  v2.set_rhs(2)
3740
4001
  v3.set_rhs(3)
3741
- v4.set_rhs('if(v1, v2, v3)')
4002
+ v4.set_rhs('if(1 == 1, v2, v3)')
3742
4003
  z = v4.rhs()
3743
4004
 
3744
4005
  # Test in tolerant mode
@@ -3851,6 +4112,30 @@ class PiecewiseTest(unittest.TestCase):
3851
4112
  self.assertRaisesRegex(
3852
4113
  myokit.IntegrityError, '3 or more', myokit.Piecewise, cond1)
3853
4114
 
4115
+ # Operands must be expressions
4116
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be other Express',
4117
+ myokit.Piecewise, 1, 1, 1)
4118
+
4119
+ # Operands must have right types
4120
+ self.assertRaisesRegex(
4121
+ myokit.TypeError, 'operand at index 0 must be a condition',
4122
+ myokit.Piecewise, then1, then2, then3)
4123
+ self.assertRaisesRegex(
4124
+ myokit.TypeError, 'operand at index 1 must be numerical',
4125
+ myokit.Piecewise, cond1, cond1, then1)
4126
+ self.assertRaisesRegex(
4127
+ myokit.TypeError, 'operand at index 2 must be numerical',
4128
+ myokit.Piecewise, cond1, then1, cond2)
4129
+ self.assertRaisesRegex(
4130
+ myokit.TypeError, 'operand at index 2 must be a condition',
4131
+ myokit.Piecewise, cond1, then1, then2, then3, then1)
4132
+ self.assertRaisesRegex(
4133
+ myokit.TypeError, 'operand at index 3 must be numerical',
4134
+ myokit.Piecewise, cond1, then1, cond2, cond3, then2)
4135
+ self.assertRaisesRegex(
4136
+ myokit.TypeError, 'operand at index 4 must be numerical',
4137
+ myokit.Piecewise, cond1, then1, cond2, then2, cond3)
4138
+
3854
4139
  def test_diff(self):
3855
4140
  # Tests Piecewise.diff()
3856
4141
 
@@ -3918,24 +4203,13 @@ class PiecewiseTest(unittest.TestCase):
3918
4203
  # Mini model
3919
4204
  m = myokit.Model()
3920
4205
  comp = m.add_component('comp')
3921
-
3922
- # Create conditions
3923
- c1 = comp.add_variable('c1')
3924
- c2 = comp.add_variable('c2')
3925
- c1.set_rhs('1 == 2')
3926
- c2.set_rhs('1 == 2')
3927
-
3928
- # Create values
3929
- t1 = comp.add_variable('t1')
3930
- t2 = comp.add_variable('t2')
3931
- t3 = comp.add_variable('t3')
3932
- t1.set_rhs(1)
3933
- t2.set_rhs(2)
3934
- t3.set_rhs(3)
4206
+ t1 = comp.add_variable('t1', rhs=1)
4207
+ t2 = comp.add_variable('t2', rhs=2)
4208
+ t3 = comp.add_variable('t3', rhs=3)
3935
4209
 
3936
4210
  # Create piecewise
3937
4211
  pw = comp.add_variable('pw')
3938
- pw.set_rhs('piecewise(c1, t1, c2, t2, t3)')
4212
+ pw.set_rhs('piecewise(1 == 2, t1, 1 == 2, t2, t3)')
3939
4213
  z = pw.rhs()
3940
4214
 
3941
4215
  # Test in tolerant mode
@@ -4007,6 +4281,13 @@ class EquationTest(unittest.TestCase):
4007
4281
  rhs = myokit.Number('3')
4008
4282
  myokit.Equation(lhs, rhs)
4009
4283
 
4284
+ # Operands must be expressions
4285
+ # Operands must be expressions
4286
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be myokit.Expres',
4287
+ myokit.Equation, 1.2, 3.4)
4288
+ self.assertRaisesRegex(myokit.IntegrityError, 'must be myokit.Express',
4289
+ myokit.Equation, myokit.Number(3), 1.2)
4290
+
4010
4291
  def test_eq(self):
4011
4292
  # Test equality checking.
4012
4293
  eq1 = myokit.Equation(myokit.Name('x'), myokit.Number('3'))