mathjson-solver 1.13.0__tar.gz → 1.14.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: mathjson-solver
3
- Version: 1.13.0
3
+ Version: 1.14.0
4
4
  Summary: Utilities for MathJSON evaluation
5
5
  Home-page: https://github.com/LongenesisLtd/mathjson-solver
6
6
  Author: Martins Mednis
@@ -18,6 +18,7 @@ Dynamic: classifier
18
18
  Dynamic: description
19
19
  Dynamic: description-content-type
20
20
  Dynamic: home-page
21
+ Dynamic: license-file
21
22
  Dynamic: project-url
22
23
  Dynamic: requires-python
23
24
  Dynamic: summary
@@ -26,7 +27,7 @@ Dynamic: summary
26
27
 
27
28
  [![PyPI](https://img.shields.io/pypi/v/mathjson-solver.svg)](https://pypi.org/project/mathjson-solver/)
28
29
  [![PyPI Downloads](https://static.pepy.tech/badge/mathjson-solver/month)](https://pepy.tech/projects/mathjson-solver)
29
- ![Coverage](https://img.shields.io/badge/coverage-93-green)
30
+ ![Coverage](https://img.shields.io/badge/coverage-91-green)
30
31
 
31
32
  _MathJSON Solver_ is a Python module to numerically evaluate MathJSON expressions, like `["Add", 1, 2]`. It is developed by [Longenesis](https://longenesis.com/team) to enable numerical evaluation of user provided mathematical expressions in Longenesis digital health products. Its development was inspired by [CortexJS](https://cortexjs.io/compute-engine/) Compute Engine.
32
33
 
@@ -73,11 +74,3 @@ Make sure you have `pytest` installed. Then `cd` into project directory and run:
73
74
  ```bash
74
75
  pytest
75
76
  ```
76
-
77
- ## Contributing Code
78
-
79
- We welcome your contributions in the form of pull requests.
80
-
81
- 1. Fork the repo;
82
- 2. Make improvements;
83
- 3. Make a pull request to share your improvements with the community and to include it into official release.
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![PyPI](https://img.shields.io/pypi/v/mathjson-solver.svg)](https://pypi.org/project/mathjson-solver/)
4
4
  [![PyPI Downloads](https://static.pepy.tech/badge/mathjson-solver/month)](https://pepy.tech/projects/mathjson-solver)
5
- ![Coverage](https://img.shields.io/badge/coverage-93-green)
5
+ ![Coverage](https://img.shields.io/badge/coverage-91-green)
6
6
 
7
7
  _MathJSON Solver_ is a Python module to numerically evaluate MathJSON expressions, like `["Add", 1, 2]`. It is developed by [Longenesis](https://longenesis.com/team) to enable numerical evaluation of user provided mathematical expressions in Longenesis digital health products. Its development was inspired by [CortexJS](https://cortexjs.io/compute-engine/) Compute Engine.
8
8
 
@@ -49,11 +49,3 @@ Make sure you have `pytest` installed. Then `cd` into project directory and run:
49
49
  ```bash
50
50
  pytest
51
51
  ```
52
-
53
- ## Contributing Code
54
-
55
- We welcome your contributions in the form of pull requests.
56
-
57
- 1. Fork the repo;
58
- 2. Make improvements;
59
- 3. Make a pull request to share your improvements with the community and to include it into official release.
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = mathjson-solver
3
- version = 1.13.0
3
+ version = 1.14.0
4
4
  author = Martins Mednis
5
5
  author_email = mrt@mednis.info
6
6
  description = Utilities for MathJSON evaluation
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="mathjson-solver",
8
- version="1.13.0",
8
+ version="1.14.0",
9
9
  author="Martins Mednis",
10
10
  author_email="mrt@mednis.info",
11
11
  description="Utilities for MathJSON evaluation",
@@ -1,5 +1,5 @@
1
1
  import numbers
2
- from typing import Union
2
+ from typing import Union, Any
3
3
  from functools import reduce
4
4
  import math
5
5
  from copy import deepcopy
@@ -46,6 +46,8 @@ def is_numeric(x):
46
46
  float(x)
47
47
  except ValueError:
48
48
  return False
49
+ except TypeError:
50
+ return False
49
51
  else:
50
52
  return True
51
53
 
@@ -141,6 +143,16 @@ def comparison_safe_converter(x):
141
143
  return x
142
144
 
143
145
 
146
+ def comparison_safe_converter_for_pairs(
147
+ v1, v2
148
+ ) -> (Union[str, float], Union[str, float]):
149
+ if is_numeric(v1):
150
+ v1 = float(v1)
151
+ if is_numeric(v2):
152
+ v2 = float(v2)
153
+ return v1, v2
154
+
155
+
144
156
  def create_mathjson_solver(solver_parameters):
145
157
  def f(s, *args):
146
158
  if args:
@@ -160,10 +172,15 @@ def create_mathjson_solver(solver_parameters):
160
172
  tmp = 0
161
173
  for i, x in enumerate(s[1:]):
162
174
  res = f(x, c)
175
+ if is_numeric(res):
176
+ res = float(res)
163
177
  if i == 0:
164
178
  tmp = res
165
179
  else:
166
- tmp = tmp + res
180
+ try:
181
+ tmp = tmp + res
182
+ except TypeError:
183
+ pass
167
184
 
168
185
  # if isinstance(res, list):
169
186
  # l_res.append(sum([xx for xx in res[1:]]))
@@ -180,15 +197,25 @@ def create_mathjson_solver(solver_parameters):
180
197
  # tmp = tmp + x
181
198
  # return tmp
182
199
 
200
+ # def Sum(s):
201
+ # l_res = []
202
+ # for x in s[1:]:
203
+ # res = f(x, c)
204
+ # if isinstance(res, list):
205
+ # l_res.append(sum([xx for xx in res[1:]]))
206
+ # else:
207
+ # l_res.append(res)
208
+ # return sum(l_res)
209
+
183
210
  def Sum(s):
184
- l_res = []
211
+ l_res = ["Array"]
185
212
  for x in s[1:]:
186
213
  res = f(x, c)
187
214
  if isinstance(res, list):
188
- l_res.append(sum([xx for xx in res[1:]]))
215
+ l_res.append(Add(["Array"] + [xx for xx in res[1:]]))
189
216
  else:
190
217
  l_res.append(res)
191
- return sum(l_res)
218
+ return Add(l_res)
192
219
 
193
220
  def Max(s):
194
221
  if isinstance(s[1], str):
@@ -222,7 +249,6 @@ def create_mathjson_solver(solver_parameters):
222
249
 
223
250
  def Length(s):
224
251
  if isinstance(s[1], str):
225
-
226
252
  return len([x for x in f(s[1], c)][1:])
227
253
  else:
228
254
  return len([x for x in s[1][1:]])
@@ -352,6 +378,24 @@ def create_mathjson_solver(solver_parameters):
352
378
  return not f(s[1])
353
379
 
354
380
  def Map(s):
381
+ """
382
+ ["Map", list, function, more parameters]
383
+ The `function` must accept at least one parameter. That is for the current loop element.
384
+ The `more parameters` are for any additional parameters that function might have.
385
+ """
386
+ z = f(s[1], c)
387
+ if isinstance(z, list):
388
+ retlist = ["Array"]
389
+ for x in z[1:]:
390
+ try:
391
+ the_function_name = s[2][0]
392
+ ss = [the_function_name, x] + s[3:]
393
+ retlist.append(f(ss, c))
394
+ except MathJSONException:
395
+ retlist.append(x)
396
+ return retlist
397
+
398
+ def StrictMap(s):
355
399
  """
356
400
  ["Map", list, function, more parameters]
357
401
  The `function` must accept at least one parameter. That is for the current loop element.
@@ -366,6 +410,22 @@ def create_mathjson_solver(solver_parameters):
366
410
  retlist.append(f(ss, c))
367
411
  return retlist
368
412
 
413
+ def Filter(s):
414
+ """
415
+ ["Filter", list, function, more parameters]
416
+ The `function` must accept at least one parameter. That is for the current loop element.
417
+ The `more parameters` are for any additional parameters that function might have.
418
+ """
419
+ z = f(s[1], c)
420
+ if isinstance(z, list):
421
+ retlist = ["Array"]
422
+ for x in z[1:]:
423
+ the_function_name = s[2][0]
424
+ ss = [the_function_name, x] + s[3:]
425
+ if f(ss, c):
426
+ retlist.append(x)
427
+ return retlist
428
+
369
429
  def HasMatchingSublist(s):
370
430
  """
371
431
  ["HasMatchingSublist", list, required_match_count, position, contiguous, function, more parameters]
@@ -438,6 +498,34 @@ def create_mathjson_solver(solver_parameters):
438
498
  # Todo: This does not work
439
499
  return s[1] in c.keys()
440
500
 
501
+ def Greater(s):
502
+ v1, v2 = comparison_safe_converter_for_pairs(f(s[1], c), f(s[2], c))
503
+ try:
504
+ return v1 > v2
505
+ except TypeError:
506
+ return False
507
+
508
+ def GreaterEqual(s):
509
+ v1, v2 = comparison_safe_converter_for_pairs(f(s[1], c), f(s[2], c))
510
+ try:
511
+ return v1 >= v2
512
+ except TypeError:
513
+ return False
514
+
515
+ def Less(s):
516
+ v1, v2 = comparison_safe_converter_for_pairs(f(s[1], c), f(s[2], c))
517
+ try:
518
+ return v1 < v2
519
+ except TypeError:
520
+ return False
521
+
522
+ def LessEqual(s):
523
+ v1, v2 = comparison_safe_converter_for_pairs(f(s[1], c), f(s[2], c))
524
+ try:
525
+ return v1 <= v2
526
+ except TypeError:
527
+ return False
528
+
441
529
  constructs = {
442
530
  "Sum": Sum,
443
531
  "Add": Add,
@@ -465,11 +553,17 @@ def create_mathjson_solver(solver_parameters):
465
553
  "Equal": lambda s: comparison_safe_converter(f(s[1], c))
466
554
  == comparison_safe_converter(f(s[2], c)),
467
555
  "StrictEqual": lambda s: f(s[1], c) == f(s[2], c),
468
- "Greater": lambda s: f(s[1], c) > f(s[2], c),
469
- "GreaterEqual": lambda s: f(s[1], c) >= f(s[2], c),
470
- "Less": lambda s: f(s[1], c) < f(s[2], c),
471
- "LessEqual": lambda s: f(s[1], c) <= f(s[2], c),
472
- "NotEqual": lambda s: f(s[1], c) != f(s[2], c),
556
+ # "Greater": lambda s: f(s[1], c) > f(s[2], c),
557
+ "Greater": Greater,
558
+ # "GreaterEqual": lambda s: f(s[1], c) >= f(s[2], c),
559
+ "GreaterEqual": GreaterEqual,
560
+ # "Less": lambda s: f(s[1], c) < f(s[2], c),
561
+ "Less": Less,
562
+ # "LessEqual": lambda s: f(s[1], c) <= f(s[2], c),
563
+ "LessEqual": LessEqual,
564
+ # "NotEqual": lambda s: f(s[1], c) != f(s[2], c),
565
+ "NotEqual": lambda s: comparison_safe_converter(f(s[1], c))
566
+ != comparison_safe_converter(f(s[2], c)),
473
567
  "Abs": lambda s: abs(f(s[1], c)),
474
568
  "Round": lambda s: (
475
569
  round(f(s[1], c), f(s[2], c))
@@ -500,6 +594,8 @@ def create_mathjson_solver(solver_parameters):
500
594
  # "IsDefined": lambda s: s[1] in c,
501
595
  "IsDefined": IsDefined,
502
596
  "Map": Map,
597
+ "StrictMap": StrictMap,
598
+ "Filter": Filter,
503
599
  "HasMatchingSublist": HasMatchingSublist,
504
600
  "Strptime": Strptime,
505
601
  "Strftime": Strftime,
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: mathjson-solver
3
- Version: 1.13.0
3
+ Version: 1.14.0
4
4
  Summary: Utilities for MathJSON evaluation
5
5
  Home-page: https://github.com/LongenesisLtd/mathjson-solver
6
6
  Author: Martins Mednis
@@ -18,6 +18,7 @@ Dynamic: classifier
18
18
  Dynamic: description
19
19
  Dynamic: description-content-type
20
20
  Dynamic: home-page
21
+ Dynamic: license-file
21
22
  Dynamic: project-url
22
23
  Dynamic: requires-python
23
24
  Dynamic: summary
@@ -26,7 +27,7 @@ Dynamic: summary
26
27
 
27
28
  [![PyPI](https://img.shields.io/pypi/v/mathjson-solver.svg)](https://pypi.org/project/mathjson-solver/)
28
29
  [![PyPI Downloads](https://static.pepy.tech/badge/mathjson-solver/month)](https://pepy.tech/projects/mathjson-solver)
29
- ![Coverage](https://img.shields.io/badge/coverage-93-green)
30
+ ![Coverage](https://img.shields.io/badge/coverage-91-green)
30
31
 
31
32
  _MathJSON Solver_ is a Python module to numerically evaluate MathJSON expressions, like `["Add", 1, 2]`. It is developed by [Longenesis](https://longenesis.com/team) to enable numerical evaluation of user provided mathematical expressions in Longenesis digital health products. Its development was inspired by [CortexJS](https://cortexjs.io/compute-engine/) Compute Engine.
32
33
 
@@ -73,11 +74,3 @@ Make sure you have `pytest` installed. Then `cd` into project directory and run:
73
74
  ```bash
74
75
  pytest
75
76
  ```
76
-
77
- ## Contributing Code
78
-
79
- We welcome your contributions in the form of pull requests.
80
-
81
- 1. Fork the repo;
82
- 2. Make improvements;
83
- 3. Make a pull request to share your improvements with the community and to include it into official release.
@@ -13,6 +13,10 @@ from mathjson_solver import create_solver, MathJSONException, extract_variables
13
13
  [
14
14
  ({}, ["Add", 2, 4, 3], 9),
15
15
  ({}, ["Sum", 2, 4, 3], 9),
16
+ ({}, ["Sum", 2, 4, 3, None], 9),
17
+ ({}, ["Add", 2, 4, "3"], 9),
18
+ ({}, ["Sum", 2, 4, "3"], 9),
19
+ ({}, ["Sum", 2, 4, "3", "abc"], 9),
16
20
  ({}, ["Sum", ["Array", 1, 1], 4, 3], 9),
17
21
  ({}, ["Sum", ["Array", 2, 4, 3]], 9),
18
22
  ({"a": 2}, ["Sum", "a", 4, 3], 9),
@@ -33,6 +37,16 @@ from mathjson_solver import create_solver, MathJSONException, extract_variables
33
37
  ({}, ["Round", ["Log", 2.7183], 3], 1.0),
34
38
  ({}, ["Log2", 8], 3),
35
39
  ({}, ["Log10", 1000], 3),
40
+ ],
41
+ )
42
+ def test_solver_arithmetics(parameters, expression, expected_result):
43
+ solver = create_solver(parameters)
44
+ assert solver(expression) == expected_result
45
+
46
+
47
+ @pytest.mark.parametrize(
48
+ "parameters, expression, expected_result",
49
+ [
36
50
  ({}, ["Equal", 10, 10], True),
37
51
  ({}, ["Equal", 10, 12], False),
38
52
  ({}, ["Equal", "aaa", "aaa"], True),
@@ -41,7 +55,6 @@ from mathjson_solver import create_solver, MathJSONException, extract_variables
41
55
  ({}, ["Equal", None, "1"], False),
42
56
  ({}, ["Equal", 1, "1"], True),
43
57
  ({}, ["Equal", None, False], True),
44
-
45
58
  ({}, ["StrictEqual", 1, "1"], False),
46
59
  ({}, ["StrictEqual", 10, 10], True),
47
60
  ({}, ["StrictEqual", 10, 12], False),
@@ -50,12 +63,12 @@ from mathjson_solver import create_solver, MathJSONException, extract_variables
50
63
  ({}, ["StrictEqual", "aaa", 11], False),
51
64
  ({}, ["StrictEqual", None, "1"], False),
52
65
  ({}, ["Equal", None, False], True),
53
-
54
-
55
66
  ({}, ["Greater", 1, 2], False),
56
67
  ({}, ["Greater", 2, -2], True),
68
+ ({}, ["Greater", "2", -2], True),
57
69
  ({}, ["GreaterEqual", 1, 1], True),
58
70
  ({}, ["GreaterEqual", 2, 1], True),
71
+ ({}, ["GreaterEqual", 2, "1"], True),
59
72
  ({}, ["GreaterEqual", 0, 0], True),
60
73
  ({}, ["GreaterEqual", 1, 2], False),
61
74
  ({}, ["Less", 1, 1], False),
@@ -67,6 +80,16 @@ from mathjson_solver import create_solver, MathJSONException, extract_variables
67
80
  ({}, ["NotEqual", 1, 2], True),
68
81
  ({}, ["NotEqual", "aaa", "bbb"], True),
69
82
  ({}, ["NotEqual", "aaa", 0], True),
83
+ ],
84
+ )
85
+ def test_solver_comparison(parameters, expression, expected_result):
86
+ solver = create_solver(parameters)
87
+ assert solver(expression) == expected_result
88
+
89
+
90
+ @pytest.mark.parametrize(
91
+ "parameters, expression, expected_result",
92
+ [
70
93
  # ({"a": 12}, ["IsDefined", "a"], True),
71
94
  ({"a": 12}, ["IsDefined", "b"], False),
72
95
  ({}, ["Abs", -3.5], 3.5),
@@ -132,26 +155,20 @@ from mathjson_solver import create_solver, MathJSONException, extract_variables
132
155
  # ],
133
156
  # 13
134
157
  # ),
135
-
136
-
137
158
  # The next tests check the type-forgiving nature of Switch.
138
159
  ({"value": 2}, ["Switch", "value", 0, ["1", 11], ["2", 22]], 22),
139
160
  ({"value": "2"}, ["Switch", "value", 0, [1, 11], [2, 22]], 22),
140
-
141
161
  # The next tests verify that the StrictSwitch is not forgiving.
142
162
  ({"value": 2}, ["StrictSwitch", "value", 0, ["1", 11], ["2", 22]], 0),
143
163
  ({"value": "2"}, ["StrictSwitch", "value", 0, [1, 11], [2, 22]], 0),
144
-
145
164
  ({"color": "red"}, ["Switch", "color", 0, ["blue", 10], ["red", 30]], 30),
146
165
  ({"color": "green"}, ["Switch", "color", 0, ["blue", 10], ["red", 30]], 0),
147
166
  ({"color": "green"}, ["Switch", "undefined", 0, ["blue", 10], ["red", 30]], 0),
148
167
  ({}, ["If", [["Equal", 1, 0], 10], [["Equal", 2, 2], 20], 9000], 20),
149
168
  # # The following tests that If don't fail on incompatible data types
150
169
  # ({}, ["If", [["Equal", 1, "1"], 10], [["Equal", "1", "1"], 20], -1], 20),
151
-
152
170
  # ({"a": 1}, ["If", [["Equal", "a", "1"], 10], [["Equal", "a", 1], 20], -1], 20),
153
171
  # ({"a": 1}, ["If", [["Equal", "a", 1], 10], [["Equal", "a", "1"], 20], -1], 10),
154
-
155
172
  ({"a": 10, "b": 10}, ["If", [["Equal", "a", "b"], 10], 9000], 10),
156
173
  ({"a": 10, "b": 20}, ["If", [["Equal", "a", "b"], 10], 9000], 9000),
157
174
  ({"a": 10}, ["If", [["Equal", "a", "b"], 10], 9000], 9000),
@@ -196,6 +213,7 @@ from mathjson_solver import create_solver, MathJSONException, extract_variables
196
213
  ({"a": ["Array", 2, 8]}, ["Average", "a"], 5),
197
214
  ({"a": 10, "b": 20}, ["Average", ["Array", "a", "b"]], 15),
198
215
  ({}, ["Length", ["Array", 1, 2, 3, 5, 2, 9]], 6),
216
+ ({}, ["Length", ["Array", 1, 2, 3, None]], 4),
199
217
  ({"a": ["Array", 1, 2, 3, 5, 2, 9]}, ["Length", "a"], 6),
200
218
  ({}, ["Length", ["Array"]], 0),
201
219
  # Next test illustrates an invalid formula, where 'Length' receives an invalid array.
@@ -281,7 +299,15 @@ from mathjson_solver import create_solver, MathJSONException, extract_variables
281
299
  ({}, ["Not", 0], True),
282
300
  ({}, ["Not", ["In", 2, ["Array", 1, 2, 3]]], False),
283
301
  ({}, ["Array", 2, 4, 3], ["Array", 2, 4, 3]),
302
+ ({}, ["StrictMap", ["Array", 1, 2, 3], ["Square"]], ["Array", 1, 4, 9]),
303
+ ({}, ["Filter", ["Array", 1, 2, 3], ["LessEqual"], 2], ["Array", 1, 2]),
304
+ ({}, ["Filter", ["Array", 1, 2, 3, "None"], ["LessEqual"], 2], ["Array", 1, 2]),
284
305
  ({}, ["Map", ["Array", 1, 2, 3], ["Square"]], ["Array", 1, 4, 9]),
306
+ (
307
+ {},
308
+ ["Map", ["Array", 1, 2, 3, None, "a"], ["Square"]],
309
+ ["Array", 1, 4, 9, None, "a"],
310
+ ),
285
311
  ({}, ["Map", ["Array", 1, 2, 3], ["Power"], 2], ["Array", 1, 4, 9]),
286
312
  (
287
313
  {},