myokit 1.37.5__py3-none-any.whl → 1.39.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 +5 -0
  2. myokit/_datablock.py +6 -5
  3. myokit/_expressions.py +6 -1
  4. myokit/_model_api.py +44 -18
  5. myokit/_myokit_version.py +1 -1
  6. myokit/_parsing.py +8 -2
  7. myokit/_sim/cvodessim.py +26 -0
  8. myokit/formats/__init__.py +37 -0
  9. myokit/formats/ansic/_ewriter.py +1 -1
  10. myokit/formats/axon/_abf.py +43 -9
  11. myokit/formats/cellml/v1/__init__.py +5 -5
  12. myokit/formats/cellml/v1/_api.py +220 -122
  13. myokit/formats/cellml/v1/_parser.py +91 -87
  14. myokit/formats/cellml/v1/_writer.py +13 -6
  15. myokit/formats/cellml/v2/__init__.py +5 -8
  16. myokit/formats/cellml/v2/_api.py +182 -106
  17. myokit/formats/cellml/v2/_parser.py +68 -64
  18. myokit/formats/cellml/v2/_writer.py +7 -3
  19. myokit/formats/heka/_patchmaster.py +71 -14
  20. myokit/formats/mathml/_parser.py +106 -67
  21. myokit/gui/source.py +18 -12
  22. myokit/lib/hh.py +21 -37
  23. myokit/tests/test_cellml_v1_api.py +227 -33
  24. myokit/tests/test_cellml_v1_parser.py +48 -17
  25. myokit/tests/test_cellml_v1_writer.py +14 -4
  26. myokit/tests/test_cellml_v2_api.py +132 -114
  27. myokit/tests/test_cellml_v2_parser.py +31 -1
  28. myokit/tests/test_cellml_v2_writer.py +8 -1
  29. myokit/tests/test_datalog.py +17 -0
  30. myokit/tests/test_expressions.py +61 -0
  31. myokit/tests/test_formats.py +99 -0
  32. myokit/tests/test_formats_mathml_content.py +97 -37
  33. myokit/tests/test_formats_python.py +1 -1
  34. myokit/tests/test_model_building.py +2 -0
  35. myokit/tests/test_parsing.py +32 -0
  36. myokit/tests/test_simulation_cvodes.py +10 -4
  37. myokit/tests/test_variable.py +10 -7
  38. {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/METADATA +22 -7
  39. {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/RECORD +43 -43
  40. {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/WHEEL +1 -1
  41. {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/entry_points.txt +0 -0
  42. {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info/licenses}/LICENSE.txt +0 -0
  43. {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/top_level.txt +0 -0
@@ -357,27 +357,10 @@ class ContentMathMLParserTest(unittest.TestCase):
357
357
  )
358
358
 
359
359
  def test_functions(self):
360
- # Tests parsing basic functions
361
-
362
- # Power
363
- a = myokit.Name('a')
364
- b = myokit.Number(1)
365
- c = myokit.Number(2)
366
- e = myokit.Power(a, b)
367
- x = '<apply><power/><ci>a</ci><cn>1.0</cn></apply>'
368
- self.assertEqual(self.p(x), e)
369
- e = myokit.Power(myokit.Power(a, b), c)
370
- x = ('<apply><power/><apply><power/><ci>a</ci><cn>1.0</cn></apply>'
371
- '<cn>2.0</cn></apply>')
372
- self.assertEqual(self.p(x), e)
373
- e = myokit.Power(a, myokit.Power(b, c))
374
- x = ('<apply><power/><ci>a</ci><apply><power/><cn>1.0</cn><cn>2.0</cn>'
375
- '</apply></apply>')
376
- self.assertEqual(self.p(x), e)
377
-
378
- #TODO: Degree etc.
360
+ # Tests parsing simple functions
379
361
 
380
362
  # Exp
363
+ a = myokit.Name('a')
381
364
  e = myokit.Exp(a)
382
365
  x = '<apply><exp/><ci>a</ci></apply>'
383
366
  self.assertEqual(self.p(x), e)
@@ -393,10 +376,19 @@ class ContentMathMLParserTest(unittest.TestCase):
393
376
  mathml.MathMLError, r'Expecting 1 operand\(s\)', self.p, x)
394
377
 
395
378
  # Floor
379
+ b = myokit.Number(1)
396
380
  e = myokit.Floor(b)
397
381
  x = '<apply><floor/><cn>1.0</cn></apply>'
398
382
  self.assertEqual(self.p(x), e)
399
383
 
384
+ # Test number of arguments to unary operator
385
+ x = '<apply><floor/></apply>'
386
+ self.assertRaisesRegex(
387
+ mathml.MathMLError, r'Expecting 1 operand\(s\), got 0', self.p, x)
388
+ x = '<apply><floor/><cn>1.0</cn><cn>2.0</cn></apply>'
389
+ self.assertRaisesRegex(
390
+ mathml.MathMLError, r'Expecting 1 operand\(s\), got 2', self.p, x)
391
+
400
392
  # Ceil
401
393
  e = myokit.Ceil(b)
402
394
  x = '<apply><ceiling/><cn>1.0</cn></apply>'
@@ -466,6 +458,50 @@ class ContentMathMLParserTest(unittest.TestCase):
466
458
  self.assertRaisesRegex(
467
459
  mathml.MathMLError, 'Expecting a single', self.p, x)
468
460
 
461
+ def test_functions_minmax(self):
462
+ # Tests parsing min and max
463
+
464
+ a = myokit.Name('a')
465
+ b = myokit.Number(1)
466
+ x = '<apply><min/><ci>a</ci><cn>1.0</cn></apply>'
467
+ e = myokit.If(myokit.Less(a, b), a, b)
468
+ self.assertEqual(self.p(x), e)
469
+
470
+ c = myokit.Number(7)
471
+ x = '<apply><max/><cn>7.0</cn><ci>a</ci></apply>'
472
+ e = myokit.If(myokit.More(c, a), c, a)
473
+ self.assertEqual(self.p(x), e)
474
+
475
+ x = f'<apply><min/><ci>a</ci>{x}</apply>'
476
+ e = myokit.If(myokit.Less(a, e), a, e)
477
+ self.assertEqual(self.p(x), e)
478
+
479
+ # Unsupported number of operands
480
+ x = '<apply><min/><ci>a</ci></apply>'
481
+ self.assertRaisesRegex(mathml.MathMLError, 'Only binary', self.p, x)
482
+ x = '<apply><min/><ci>a</ci><ci>b</ci><ci>c</ci></apply>'
483
+ self.assertRaisesRegex(mathml.MathMLError, 'Only binary', self.p, x)
484
+
485
+ def test_functions_power(self):
486
+ # Tests parsing powers
487
+
488
+ a = myokit.Name('a')
489
+ b = myokit.Number(1)
490
+ c = myokit.Number(2)
491
+ e = myokit.Power(a, b)
492
+ x = '<apply><power/><ci>a</ci><cn>1.0</cn></apply>'
493
+ self.assertEqual(self.p(x), e)
494
+
495
+ e = myokit.Power(myokit.Power(a, b), c)
496
+ x = ('<apply><power/><apply><power/><ci>a</ci><cn>1.0</cn></apply>'
497
+ '<cn>2.0</cn></apply>')
498
+ self.assertEqual(self.p(x), e)
499
+
500
+ e = myokit.Power(a, myokit.Power(b, c))
501
+ x = ('<apply><power/><ci>a</ci><apply><power/><cn>1.0</cn><cn>2.0</cn>'
502
+ '</apply></apply>')
503
+ self.assertEqual(self.p(x), e)
504
+
469
505
  def test_functions_root(self):
470
506
  # Tests parsing roots
471
507
 
@@ -618,19 +654,28 @@ class ContentMathMLParserTest(unittest.TestCase):
618
654
  # Real
619
655
  x = self.p('<cn>4</cn>')
620
656
  self.assertEqual(x, myokit.Number(4))
621
- x = self.p('<cn> 4 \n </cn>')
657
+ x = self.p('<cn> 4.0 \n </cn>')
622
658
  self.assertEqual(x, myokit.Number(4))
623
659
  x = self.p('<cn type="real">4</cn>')
624
660
  self.assertEqual(x, myokit.Number(4))
625
661
  self.assertRaisesRegex(
626
662
  mathml.MathMLError, 'Unable to convert contents of <cn>',
627
663
  self.p, '<cn>barry</cn>')
664
+ self.assertRaisesRegex(
665
+ mathml.MathMLError, 'Unable to convert contents of <cn>',
666
+ self.p, '<cn>nan</cn>')
667
+ self.assertRaisesRegex(
668
+ mathml.MathMLError, 'Unable to convert contents of <cn>',
669
+ self.p, '<cn>-Infinity</cn>')
670
+ self.assertRaisesRegex(
671
+ mathml.MathMLError, 'Unable to convert contents of <cn>',
672
+ self.p, '<cn>\n+inf</cn>')
628
673
 
629
674
  # Real with base
630
- x = self.p('<cn type="real" base="10">4</cn>')
631
- self.assertEqual(x, myokit.Number(4))
632
- x = self.p('<cn type="real" base=" 10 ">4</cn>')
633
- self.assertEqual(x, myokit.Number(4))
675
+ self.assertEqual(
676
+ myokit.Number(4), self.p('<cn type="real" base="10">4</cn>'))
677
+ self.assertEqual(
678
+ myokit.Number(-3), self.p('<cn type="real" base=" 10 ">-3.0</cn>'))
634
679
  self.assertRaisesRegex(
635
680
  mathml.MathMLError, 'bases other than 10',
636
681
  self.p, '<cn type="real" base="9">4</cn>')
@@ -653,43 +698,58 @@ class ContentMathMLParserTest(unittest.TestCase):
653
698
  x = self.p('<cn type="integer" base=" 2 ">100</cn>')
654
699
  self.assertEqual(x, myokit.Number(4))
655
700
  self.assertRaisesRegex(
656
- mathml.MathMLError, 'Unable to parse base',
701
+ mathml.MathMLError, 'Invalid base specified',
657
702
  self.p, '<cn type="integer" base="barry">7</cn>')
658
703
 
659
704
  # Double
660
- x = self.p('<cn type="double">4</cn>')
661
- self.assertEqual(x, myokit.Number(4))
662
- x = self.p('<cn type="double">\t4\n</cn>')
663
- self.assertEqual(x, myokit.Number(4))
705
+ self.assertEqual(
706
+ myokit.Number(-3), self.p('<cn type="double">-3</cn>'))
707
+ self.assertEqual(
708
+ myokit.Number(4), self.p('<cn type="double">\t4\n</cn>'))
664
709
  self.assertRaisesRegex(
665
710
  mathml.MathMLError, 'Unable to convert contents of <cn>',
666
711
  self.p, '<cn type="double">larry</cn>')
712
+ self.assertRaisesRegex(
713
+ mathml.MathMLError, 'Unable to convert contents of <cn>',
714
+ self.p, '<cn type="double">nan</cn>')
667
715
 
668
716
  # E-notation
669
717
  x = self.p('<cn type="e-notation">40<sep/>-1</cn>')
670
718
  self.assertEqual(x, myokit.Number(4))
671
719
  x = self.p('<cn type="e-notation">\n40<sep/>-1</cn>')
672
720
  self.assertEqual(x, myokit.Number(4))
673
- x = self.p('<cn type="e-notation">40<sep/>\t-1</cn>')
674
- self.assertEqual(x, myokit.Number(4))
721
+ x = self.p('<cn type="e-notation"> -40.3<sep/>\t-12</cn>')
722
+ self.assertEqual(x, myokit.Number(-40.3e-12))
675
723
  self.assertRaisesRegex(
676
724
  mathml.MathMLError, 'e-notation should have the format',
677
725
  self.p, '<cn type="e-notation">12</cn>')
678
726
  self.assertRaisesRegex(
679
- mathml.MathMLError, 'missing part before the separator',
727
+ mathml.MathMLError, 'before the separator should be a basic real',
680
728
  self.p, '<cn type="e-notation"> <sep/>2</cn>')
681
729
  self.assertRaisesRegex(
682
730
  mathml.MathMLError, 'missing part before the separator',
683
731
  self.p, '<cn type="e-notation"><sep/>2</cn>')
684
732
  self.assertRaisesRegex(
685
- mathml.MathMLError, 'missing part after the separator',
733
+ mathml.MathMLError, 'after the separator should be an integer',
686
734
  self.p, '<cn type="e-notation">2<sep/> </cn>')
687
735
  self.assertRaisesRegex(
688
736
  mathml.MathMLError, 'missing part after the separator',
689
737
  self.p, '<cn type="e-notation">2<sep/></cn>')
690
738
  self.assertRaisesRegex(
691
- mathml.MathMLError, 'Unable to parse number in e-notation',
739
+ mathml.MathMLError, 'before the separator should be a basic real',
740
+ self.p, '<cn type="e-notation">1e3<sep/>1</cn>')
741
+ self.assertRaisesRegex(
742
+ mathml.MathMLError, 'after the separator should be an integer',
743
+ self.p, '<cn type="e-notation">1<sep/>1e3</cn>')
744
+ self.assertRaisesRegex(
745
+ mathml.MathMLError, 'before the separator should be a basic real',
692
746
  self.p, '<cn type="e-notation">larry<sep/>2</cn>')
747
+ self.assertRaisesRegex(
748
+ mathml.MathMLError, 'should be a basic real number',
749
+ self.p, '<cn type="e-notation">nan<sep/>2</cn>')
750
+ self.assertRaisesRegex(
751
+ mathml.MathMLError, 'after the separator should be an integer',
752
+ self.p, '<cn type="e-notation">1<sep/>inf</cn>')
693
753
 
694
754
  # Rational
695
755
  x = self.p('<cn type="rational">16<sep/>4</cn>')
@@ -702,7 +762,7 @@ class ContentMathMLParserTest(unittest.TestCase):
702
762
  mathml.MathMLError, 'Rational number should have the format',
703
763
  self.p, '<cn type="rational">12</cn>')
704
764
  self.assertRaisesRegex(
705
- mathml.MathMLError, 'missing part before the separator',
765
+ mathml.MathMLError, 'before the separator should be an integer',
706
766
  self.p, '<cn type="rational"> <sep/>2</cn>')
707
767
  self.assertRaisesRegex(
708
768
  mathml.MathMLError, 'missing part before the separator',
@@ -711,10 +771,10 @@ class ContentMathMLParserTest(unittest.TestCase):
711
771
  mathml.MathMLError, 'missing part after the separator',
712
772
  self.p, '<cn type="rational">1<sep/></cn>')
713
773
  self.assertRaisesRegex(
714
- mathml.MathMLError, 'missing part after the separator',
774
+ mathml.MathMLError, 'after the separator should be an integer',
715
775
  self.p, '<cn type="rational">1<sep/> </cn>')
716
776
  self.assertRaisesRegex(
717
- mathml.MathMLError, 'Unable to parse rational number',
777
+ mathml.MathMLError, 'before the separator should be an integer',
718
778
  self.p, '<cn type="rational">larry<sep/>2</cn>')
719
779
 
720
780
  # Unknown type
@@ -302,7 +302,7 @@ class NumPyExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
302
302
  if arguments:
303
303
  arguments = [numpy.array(arg) for arg in arguments]
304
304
  values = function(*arguments)
305
- self.assertEqual(list(values), list(expected))
305
+ self.assertLess(numpy.max(numpy.abs(values - expected)), 1e-12)
306
306
  else:
307
307
  value = function()
308
308
  self.assertEqual(value, expected)
@@ -400,6 +400,7 @@ class ModelBuildTest(unittest.TestCase):
400
400
  # Keywords
401
401
  self.assertRaises(myokit.InvalidNameError, m.add_component, 'and')
402
402
  self.assertRaises(myokit.InvalidNameError, m.add_component, 'bind')
403
+ self.assertRaises(myokit.InvalidNameError, m.add_component, 'infinity')
403
404
  # Test adding variable to component
404
405
  c1 = m.add_component('c1')
405
406
  # Duplicate
@@ -414,6 +415,7 @@ class ModelBuildTest(unittest.TestCase):
414
415
  # Keywords
415
416
  self.assertRaises(myokit.InvalidNameError, c0.add_variable, 'or')
416
417
  self.assertRaises(myokit.InvalidNameError, c0.add_variable, 'label')
418
+ self.assertRaises(myokit.InvalidNameError, c0.add_variable, 'nan')
417
419
 
418
420
  # Test adding variable to variable
419
421
  v1 = c0.add_variable('c0')
@@ -6,6 +6,7 @@
6
6
  # This file is part of Myokit.
7
7
  # See http://myokit.org for copyright, sharing, and licensing details.
8
8
  #
9
+ import math
9
10
  import os
10
11
  import unittest
11
12
 
@@ -36,6 +37,12 @@ class TokenizerTest(unittest.TestCase):
36
37
  s = Tokenizer('.30')
37
38
  self.assertEqual(next(s), (p.FLOAT, '.30', 1, 0))
38
39
 
40
+ # Infinity and nan count as numbers
41
+ s = Tokenizer('infinity')
42
+ self.assertEqual(next(s), (p.INF, 'infinity', 1, 0))
43
+ s = Tokenizer('nan')
44
+ self.assertEqual(next(s), (p.NAN, 'nan', 1, 0))
45
+
39
46
  # Finished? Then should get a StopIteration
40
47
  self.assertEqual(s.peek()[0], p.EOL)
41
48
  self.assertEqual(next(s)[0], p.EOL)
@@ -716,6 +723,31 @@ class PhasedParseTest(unittest.TestCase):
716
723
  self.assertRaisesRegex(
717
724
  myokit.ParseError, 'not be a condition', p, code)
718
725
 
726
+ def test_parse_nan_inf(self):
727
+ # Nan and inf are recognised as numbers, and can have units
728
+ from myokit._parsing import parse_model as p
729
+
730
+ s = ('[[model]]',
731
+ '[x]',
732
+ 't = 0 bind time',
733
+ 'a = infinity + nan [mV]',
734
+ 'b = -infinity')
735
+ e = p(s).get('x.a').rhs()
736
+ self.assertIsInstance(e, myokit.Plus)
737
+ self.assertIsInstance(e[0], myokit.Number)
738
+ self.assertGreater(e[0].value(), 0)
739
+ self.assertTrue(math.isinf(e[0].value()))
740
+ self.assertEqual(e[0].unit(), None)
741
+ self.assertTrue(math.isnan(e[1].value()))
742
+ self.assertEqual(e[1].unit(), myokit.units.mV)
743
+ e = p(s).get('x.b').rhs()
744
+ self.assertIsInstance(e, myokit.PrefixMinus)
745
+ self.assertLess(e.eval(), 0)
746
+ self.assertTrue(math.isinf(e.eval()))
747
+ self.assertIsInstance(e[0], myokit.Number)
748
+ self.assertGreater(e[0].value(), 0)
749
+ self.assertTrue(math.isinf(e[0].value()))
750
+
719
751
  def test_parse_unit(self):
720
752
  # Test :meth:`parse_unit` and :meth:`parse_unit_string`.
721
753
  from myokit._parsing import parse_unit_string as p
@@ -522,16 +522,17 @@ class SimulationTest(unittest.TestCase):
522
522
  ValueError, 'no `apd_variable` specified',
523
523
  self.sim.run, 1, apd_threshold=12)
524
524
 
525
- def test_crash_state_and_inputs(self):
526
- # Tests Simulation.crash_state
525
+ def test_crash_state_inputs_log(self):
526
+ # Tests Simulation.crash_state, crash_inputs, and crash_log
527
527
 
528
528
  m = self.model.clone()
529
529
  istim = m.get('membrane.i_stim')
530
530
  istim.set_rhs('engine.pace / stim_amplitude')
531
- '''
531
+
532
532
  s = myokit.Simulation(m, self.protocol)
533
533
  self.assertIsNone(s.crash_state())
534
534
  self.assertIsNone(s.crash_inputs())
535
+ self.assertIsNone(s.crash_log())
535
536
  s.run(1)
536
537
  self.assertIsNone(s.crash_state())
537
538
  s.set_constant('membrane.i_stim.stim_amplitude', 0)
@@ -545,7 +546,8 @@ class SimulationTest(unittest.TestCase):
545
546
  self.assertEqual(s.crash_inputs()['pace'], 0)
546
547
  self.assertGreaterEqual(s.crash_inputs()['realtime'], 0)
547
548
  self.assertGreater(s.crash_inputs()['evaluations'], 0)
548
- '''
549
+ self.assertIsInstance(s.crash_log(), myokit.DataLog)
550
+ self.assertGreater(s.crash_log().length(), 0)
549
551
 
550
552
  # Test crash at later time
551
553
  istim.set_rhs('if(engine.time == 5, 1 / (5 - engine.time), 0)')
@@ -561,6 +563,8 @@ class SimulationTest(unittest.TestCase):
561
563
  self.assertEqual(s.crash_inputs()['time'], 5)
562
564
  self.assertEqual(s.crash_inputs()['pace'], 1)
563
565
  self.assertGreater(s.crash_inputs()['realtime'], 0)
566
+ self.assertIsInstance(s.crash_log(), myokit.DataLog)
567
+ self.assertGreater(s.crash_log().length(), 0)
564
568
 
565
569
  # Above should both crash via cvode flag set. Next should be halted by
566
570
  # C code for too many zero steps
@@ -572,6 +576,8 @@ class SimulationTest(unittest.TestCase):
572
576
  self.assertAlmostEqual(s.crash_inputs()['time'], 5)
573
577
  self.assertEqual(s.crash_inputs()['pace'], 1)
574
578
  self.assertGreater(s.crash_inputs()['realtime'], 0)
579
+ self.assertIsInstance(s.crash_log(), myokit.DataLog)
580
+ self.assertGreater(s.crash_log().length(), 0)
575
581
 
576
582
  # Test deprecated alias of crash_state
577
583
  x = s.crash_state()
@@ -768,14 +768,18 @@ class VariableTest(unittest.TestCase):
768
768
  v.set_initial_value(myokit.Number(12, myokit.units.g))
769
769
  self.assertEqual(v.initial_value(), myokit.Number(12, myokit.units.g))
770
770
 
771
- # Test setting expressions
772
- v.set_initial_value('1 + 11')
773
- #self.assertEqual(v.initial_value(),
774
- # myokit.Plus(myokit.Number(1), myokit.Number(11)))
771
+ # Test setting expressions and strings
772
+ e = myokit.Plus(myokit.Number(1), myokit.Number(11))
773
+ v.set_initial_value(e)
774
+ self.assertEqual(v.initial_value(), e)
775
775
  v.set_initial_value('1 + c.w')
776
776
  self.assertEqual(v.initial_value(),
777
777
  myokit.Plus(myokit.Number(1), myokit.Name(w)))
778
778
 
779
+ # Test setting using variables
780
+ v.set_initial_value(w)
781
+ self.assertEqual(v.initial_value(), myokit.Name(w))
782
+
779
783
  # Strings must use global context
780
784
  self.assertRaisesRegex(myokit.ParseError, 'No component specified for',
781
785
  v.set_initial_value, '1 / w')
@@ -802,9 +806,8 @@ class VariableTest(unittest.TestCase):
802
806
  x = v.add_variable('x')
803
807
  x.set_rhs(1)
804
808
  v.set_initial_value(x.lhs())
805
- self.assertRaises(
806
- myokit.IllegalReferenceInInitialValueError, m.validate)
807
- v.set_initial_value(x)
809
+ self.assertRaisesRegex(
810
+ myokit.IllegalReferenceInInitialValueError, 'is nest', m.validate)
808
811
 
809
812
  # InitialValue and PartialDerivative can not be used
810
813
  v.set_initial_value(myokit.PartialDerivative(v.lhs(), x.lhs()))
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: myokit
3
- Version: 1.37.5
3
+ Version: 1.39.0
4
4
  Summary: A modeling and simulation tool for cardiac cellular electrophysiology
5
5
  Home-page: http://myokit.org
6
6
  Author: Michael Clerx
@@ -28,22 +28,35 @@ Requires-Dist: lxml
28
28
  Requires-Dist: matplotlib>=2.2
29
29
  Requires-Dist: numpy
30
30
  Requires-Dist: setuptools
31
+ Provides-Extra: docs
32
+ Requires-Dist: sphinx>=1.7.4; extra == "docs"
31
33
  Provides-Extra: dev
32
34
  Requires-Dist: coverage; extra == "dev"
33
35
  Requires-Dist: flake8>=3; extra == "dev"
34
- Provides-Extra: docs
35
- Requires-Dist: sphinx>=1.7.4; extra == "docs"
36
- Provides-Extra: gui
37
- Requires-Dist: pyqt6; extra == "gui"
38
- Requires-Dist: sip; extra == "gui"
39
36
  Provides-Extra: optional
40
37
  Requires-Dist: scipy; extra == "optional"
41
38
  Requires-Dist: sympy; extra == "optional"
39
+ Provides-Extra: gui
40
+ Requires-Dist: pyqt6; extra == "gui"
41
+ Requires-Dist: sip; extra == "gui"
42
42
  Provides-Extra: pyqt
43
43
  Requires-Dist: pyqt6; extra == "pyqt"
44
44
  Requires-Dist: sip; extra == "pyqt"
45
45
  Provides-Extra: pyside
46
46
  Requires-Dist: pyside6; extra == "pyside"
47
+ Dynamic: author
48
+ Dynamic: author-email
49
+ Dynamic: classifier
50
+ Dynamic: description
51
+ Dynamic: description-content-type
52
+ Dynamic: home-page
53
+ Dynamic: license
54
+ Dynamic: license-file
55
+ Dynamic: project-url
56
+ Dynamic: provides-extra
57
+ Dynamic: requires-dist
58
+ Dynamic: requires-python
59
+ Dynamic: summary
47
60
 
48
61
  [![Ubuntu unit tests](https://github.com/myokit/myokit/actions/workflows/unit-tests-ubuntu.yml/badge.svg)](https://github.com/myokit/myokit/actions/workflows/unit-tests-ubuntu.yml)
49
62
  [![MacOS unit tests](https://github.com/myokit/myokit/actions/workflows/unit-tests-macos.yml/badge.svg)](https://github.com/myokit/myokit/actions/workflows/unit-tests-macos.yml)
@@ -51,6 +64,8 @@ Requires-Dist: pyside6; extra == "pyside"
51
64
  [![Windows Miniconda test](https://github.com/myokit/myokit/actions/workflows/unit-tests-windows-miniconda.yml/badge.svg)](https://github.com/myokit/myokit/actions/workflows/unit-tests-windows-miniconda.yml)
52
65
  [![codecov](https://codecov.io/gh/myokit/myokit/branch/main/graph/badge.svg)](https://codecov.io/gh/myokit/myokit)
53
66
  [![Documentation Status](https://readthedocs.org/projects/myokit/badge/?version=latest)](https://myokit.readthedocs.io/?badge=latest)
67
+ [![pypi](https://img.shields.io/pypi/v/myokit)](https://pypi.org/project/myokit/)
68
+ [![anaconda](https://anaconda.org/conda-forge/myokit/badges/version.svg)](https://anaconda.org/conda-forge/myokit)
54
69
 
55
70
  ![Myokit](https://myokit.org/static/img/logo.png)
56
71