mathjson-solver 2.1.1__tar.gz → 2.2.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.
Files changed (34) hide show
  1. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/CHANGELOG.md +8 -0
  2. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/PKG-INFO +31 -3
  3. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/README.md +30 -2
  4. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/pyproject.toml +1 -1
  5. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/src/mathjson_solver/__init__.py +1 -0
  6. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/src/mathjson_solver/__main__.py +30 -1
  7. mathjson_solver-2.2.0/tests/test_legacy_compat.py +73 -0
  8. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/.gitignore +0 -0
  9. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/CODE_OF_CONDUCT.md +0 -0
  10. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/CONTRIBUTING.md +0 -0
  11. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/LICENSE +0 -0
  12. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/SECURITY.md +0 -0
  13. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/docs/README.md +0 -0
  14. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/docs/integration-test-cases.md +0 -0
  15. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_TrapezoidalIntegratel.py +0 -0
  16. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_atindex.py +0 -0
  17. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_cortexjs_compat.py +0 -0
  18. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_cortexjs_tier3.py +0 -0
  19. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_cumulative.py +0 -0
  20. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_datetime.py +0 -0
  21. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_equal.py +0 -0
  22. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_exceptions.py +0 -0
  23. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_extract_variables.py +0 -0
  24. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_findinterval.py +0 -0
  25. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_gail_model.py +0 -0
  26. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_interpolate.py +0 -0
  27. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_range.py +0 -0
  28. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_reduce_simple.py +0 -0
  29. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_scalars.py +0 -0
  30. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_sets.py +0 -0
  31. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_simple_cases.py +0 -0
  32. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_slice.py +0 -0
  33. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_state_tuples_reduce.py +0 -0
  34. {mathjson_solver-2.1.1 → mathjson_solver-2.2.0}/tests/test_with_pytest.py +0 -0
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [2.2.0] - 2026-09-08
9
+
10
+ ### Added
11
+
12
+ - **Migration helper for 1.x users:** `translate_v1_mathjson(expr)` rewrites the one breaking change from 2.0.0 (`["Log", x]`, previously natural log) to its 2.x equivalent (`["Ln", x]`), leaving everything else untouched. `create_solver(parameters, legacy_v1=True)` applies this automatically, so existing 1.x expressions keep evaluating to the same results on 2.x without hand-editing, while still gaining access to functions added in 2.x.
13
+
14
+ [2.2.0]: https://github.com/LongenesisLtd/mathjson-solver/compare/v2.1.1...v2.2.0
15
+
8
16
  ## [2.1.1] - 2026-08-19
9
17
 
10
18
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mathjson-solver
3
- Version: 2.1.1
3
+ Version: 2.2.0
4
4
  Summary: Utilities for MathJSON evaluation
5
5
  Project-URL: Homepage, https://github.com/LongenesisLtd/mathjson-solver
6
6
  Project-URL: Bug Tracker, https://github.com/LongenesisLtd/mathjson-solver/issues
@@ -18,7 +18,7 @@ Description-Content-Type: text/markdown
18
18
  [![PyPI Downloads](https://static.pepy.tech/badge/mathjson-solver/month)](https://pepy.tech/projects/mathjson-solver)
19
19
  [![Python 3.7+](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/)
20
20
 
21
- > **Heads up:** Version 2 introduces a few breaking changes (see [CHANGELOG.md](CHANGELOG.md)) as part of steering back towards greater compatibility with [CortexJS MathJSON](https://cortexjs.io/compute-engine/). If you need to stay on the old behavior, bugfix releases for the 1.x line continue on the [`1.x` branch](https://github.com/LongenesisLtd/mathjson-solver/tree/1.x).
21
+ > **Heads up:** Version 2 introduces a breaking change (see [CHANGELOG.md](CHANGELOG.md)) as part of steering back towards greater compatibility with [CortexJS MathJSON](https://cortexjs.io/compute-engine/): `Log` is now log base 10 instead of natural log. Stuck on pre-2.0.0 expressions but want the functions added in 2.x? Pass `legacy_v1=True` to `create_solver()` (see [Migrating from 1.x](#migrating-from-1x)) instead of hand-migrating every expression. Bugfix releases for the 1.x line also continue on the [`1.x` branch](https://github.com/LongenesisLtd/mathjson-solver/tree/1.x).
22
22
 
23
23
  A reliable Python library for numerically evaluating mathematical expressions in MathJSON format. Perfect for applications that need to safely execute user-provided formulas, calculate dynamic equations, or process mathematical data.
24
24
 
@@ -27,12 +27,14 @@ A reliable Python library for numerically evaluating mathematical expressions in
27
27
  ## Table of Contents
28
28
  - [Installation](#installation)
29
29
  - [Quick Start](#quick-start)
30
+ - [Migrating from 1.x](#migrating-from-1x)
30
31
  - [Supported Operations](#supported-operations)
31
32
  - [Error Handling](#error-handling)
32
33
  - [Use Cases](#use-cases)
33
34
  - [Testing](#testing)
34
35
  - [Community](#community)
35
36
  - [Contributing](#contributing)
37
+ - [Related Projects](#related-projects)
36
38
 
37
39
  ## Installation
38
40
 
@@ -82,6 +84,27 @@ solver(["Map", ["Array", 1, 2, 3, 4], ["Function", ["Multiply", "_", 2]]]) # [2
82
84
  solver(["Reduce", ["Array", 1, 2, 3, 4], ["Add"]]) # 10
83
85
  ```
84
86
 
87
+ ## Migrating from 1.x
88
+
89
+ Version 2.0.0's only breaking change is `Log`: pre-2.0.0 it was always natural log, `["Log", x]` == `math.log(x)`. From 2.0.0 on it matches [CortexJS](https://cortexjs.io/compute-engine/) — `["Log", x]` is log base 10, `["Log", x, b]` is log base `b` — and natural log moved to `Ln`.
90
+
91
+ If you have existing expressions built for 1.x and don't want to hand-edit every `Log` node just to pick up functions added in 2.x (`Product`, the CortexJS forms of `If`/`Map`/`Filter`/`Reduce`, the new aliases, etc.), pass `legacy_v1=True` when creating the solver. It rewrites every `["Log", x]` to `["Ln", x]` before evaluating, so old expressions keep producing the same results without modification:
92
+
93
+ ```python
94
+ solver = create_solver(parameters, legacy_v1=True)
95
+ solver(["Log", 8]) # 2.0794... (natural log, matching pre-2.0.0 behavior)
96
+ ```
97
+
98
+ You can also run the rewrite yourself and inspect or store the translated expression:
99
+
100
+ ```python
101
+ from mathjson_solver import translate_v1_mathjson
102
+
103
+ translate_v1_mathjson(["Add", ["Log", 8], 1]) # ["Add", ["Ln", 8], 1]
104
+ ```
105
+
106
+ `legacy_v1=True` only affects `Log`. Every other 1.x expression already evaluates identically on 2.x without any translation.
107
+
85
108
  ## Supported Operations
86
109
 
87
110
  The library supports a comprehensive set of mathematical operations:
@@ -161,6 +184,9 @@ We welcome contributions! Please feel free to:
161
184
  - Report bugs or request features via [GitHub Issues](https://github.com/LongenesisLtd/mathjson-solver/issues)
162
185
  - Submit pull requests with improvements
163
186
 
187
+ ## Related Projects
188
+
189
+ We also created [`londec`](https://pypi.org/project/londec/) — evaluate tree-structured conditions against an ordered history of events. It uses mathjson-solver internally.
164
190
 
165
191
  ## License
166
192
 
@@ -168,7 +194,9 @@ We welcome contributions! Please feel free to:
168
194
 
169
195
  ## References
170
196
 
171
- This implementation was inspired by the [CortexJS Compute Engine](https://cortexjs.io/compute-engine/), though designed as an independent implementation focused on our specific use cases.
197
+ This library implements the [MathJSON](https://cortexjs.io/mathjson/) format as defined by the [CortexJS Compute Engine](https://cortexjs.io/compute-engine/). Since 2.0.0, mathjson-solver has been steering towards greater compatibility with CortexJS's calling conventions and function set — while remaining an independent Python implementation, not a port or dependency of CortexJS.
198
+
199
+ **Scope:** mathjson-solver targets compatibility with CortexJS's array-form calling conventions and standard function names — not the full [Compute Engine](https://cortexjs.io/compute-engine/), which is a symbolic CAS.
172
200
 
173
201
  ---
174
202
 
@@ -4,7 +4,7 @@
4
4
  [![PyPI Downloads](https://static.pepy.tech/badge/mathjson-solver/month)](https://pepy.tech/projects/mathjson-solver)
5
5
  [![Python 3.7+](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/)
6
6
 
7
- > **Heads up:** Version 2 introduces a few breaking changes (see [CHANGELOG.md](CHANGELOG.md)) as part of steering back towards greater compatibility with [CortexJS MathJSON](https://cortexjs.io/compute-engine/). If you need to stay on the old behavior, bugfix releases for the 1.x line continue on the [`1.x` branch](https://github.com/LongenesisLtd/mathjson-solver/tree/1.x).
7
+ > **Heads up:** Version 2 introduces a breaking change (see [CHANGELOG.md](CHANGELOG.md)) as part of steering back towards greater compatibility with [CortexJS MathJSON](https://cortexjs.io/compute-engine/): `Log` is now log base 10 instead of natural log. Stuck on pre-2.0.0 expressions but want the functions added in 2.x? Pass `legacy_v1=True` to `create_solver()` (see [Migrating from 1.x](#migrating-from-1x)) instead of hand-migrating every expression. Bugfix releases for the 1.x line also continue on the [`1.x` branch](https://github.com/LongenesisLtd/mathjson-solver/tree/1.x).
8
8
 
9
9
  A reliable Python library for numerically evaluating mathematical expressions in MathJSON format. Perfect for applications that need to safely execute user-provided formulas, calculate dynamic equations, or process mathematical data.
10
10
 
@@ -13,12 +13,14 @@ A reliable Python library for numerically evaluating mathematical expressions in
13
13
  ## Table of Contents
14
14
  - [Installation](#installation)
15
15
  - [Quick Start](#quick-start)
16
+ - [Migrating from 1.x](#migrating-from-1x)
16
17
  - [Supported Operations](#supported-operations)
17
18
  - [Error Handling](#error-handling)
18
19
  - [Use Cases](#use-cases)
19
20
  - [Testing](#testing)
20
21
  - [Community](#community)
21
22
  - [Contributing](#contributing)
23
+ - [Related Projects](#related-projects)
22
24
 
23
25
  ## Installation
24
26
 
@@ -68,6 +70,27 @@ solver(["Map", ["Array", 1, 2, 3, 4], ["Function", ["Multiply", "_", 2]]]) # [2
68
70
  solver(["Reduce", ["Array", 1, 2, 3, 4], ["Add"]]) # 10
69
71
  ```
70
72
 
73
+ ## Migrating from 1.x
74
+
75
+ Version 2.0.0's only breaking change is `Log`: pre-2.0.0 it was always natural log, `["Log", x]` == `math.log(x)`. From 2.0.0 on it matches [CortexJS](https://cortexjs.io/compute-engine/) — `["Log", x]` is log base 10, `["Log", x, b]` is log base `b` — and natural log moved to `Ln`.
76
+
77
+ If you have existing expressions built for 1.x and don't want to hand-edit every `Log` node just to pick up functions added in 2.x (`Product`, the CortexJS forms of `If`/`Map`/`Filter`/`Reduce`, the new aliases, etc.), pass `legacy_v1=True` when creating the solver. It rewrites every `["Log", x]` to `["Ln", x]` before evaluating, so old expressions keep producing the same results without modification:
78
+
79
+ ```python
80
+ solver = create_solver(parameters, legacy_v1=True)
81
+ solver(["Log", 8]) # 2.0794... (natural log, matching pre-2.0.0 behavior)
82
+ ```
83
+
84
+ You can also run the rewrite yourself and inspect or store the translated expression:
85
+
86
+ ```python
87
+ from mathjson_solver import translate_v1_mathjson
88
+
89
+ translate_v1_mathjson(["Add", ["Log", 8], 1]) # ["Add", ["Ln", 8], 1]
90
+ ```
91
+
92
+ `legacy_v1=True` only affects `Log`. Every other 1.x expression already evaluates identically on 2.x without any translation.
93
+
71
94
  ## Supported Operations
72
95
 
73
96
  The library supports a comprehensive set of mathematical operations:
@@ -147,6 +170,9 @@ We welcome contributions! Please feel free to:
147
170
  - Report bugs or request features via [GitHub Issues](https://github.com/LongenesisLtd/mathjson-solver/issues)
148
171
  - Submit pull requests with improvements
149
172
 
173
+ ## Related Projects
174
+
175
+ We also created [`londec`](https://pypi.org/project/londec/) — evaluate tree-structured conditions against an ordered history of events. It uses mathjson-solver internally.
150
176
 
151
177
  ## License
152
178
 
@@ -154,7 +180,9 @@ We welcome contributions! Please feel free to:
154
180
 
155
181
  ## References
156
182
 
157
- This implementation was inspired by the [CortexJS Compute Engine](https://cortexjs.io/compute-engine/), though designed as an independent implementation focused on our specific use cases.
183
+ This library implements the [MathJSON](https://cortexjs.io/mathjson/) format as defined by the [CortexJS Compute Engine](https://cortexjs.io/compute-engine/). Since 2.0.0, mathjson-solver has been steering towards greater compatibility with CortexJS's calling conventions and function set — while remaining an independent Python implementation, not a port or dependency of CortexJS.
184
+
185
+ **Scope:** mathjson-solver targets compatibility with CortexJS's array-form calling conventions and standard function names — not the full [Compute Engine](https://cortexjs.io/compute-engine/), which is a symbolic CAS.
158
186
 
159
187
  ---
160
188
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mathjson-solver"
7
- version = "2.1.1"
7
+ version = "2.2.0"
8
8
  authors = [{name = "Martins Mednis", email = "mrt@mednis.info"}]
9
9
  description = "Utilities for MathJSON evaluation"
10
10
  readme = "README.md"
@@ -1,2 +1,3 @@
1
1
  from .__main__ import create_mathjson_solver as create_solver
2
2
  from .__main__ import MathJSONException, extract_variables
3
+ from .__main__ import translate_v1_mathjson
@@ -294,7 +294,29 @@ def comparison_safe_converter_for_pairs(
294
294
  return v1, v2
295
295
 
296
296
 
297
- def create_mathjson_solver(solver_parameters):
297
+ def translate_v1_mathjson(expr):
298
+ """
299
+ Rewrite a MathJSON expression written for mathjson-solver < 2.0.0 so it
300
+ evaluates to the same result on >= 2.0.0.
301
+
302
+ The only breaking change introduced in 2.0.0 is "Log": before 2.0.0 it
303
+ always meant natural log (`["Log", x]` == `math.log(x)`); from 2.0.0 on
304
+ it matches CortexJS (`["Log", x]` is log base 10, `["Log", x, b]` is log
305
+ base `b`), and natural log moved to "Ln". Since the pre-2.0.0 "Log" only
306
+ ever took one argument, every legacy `["Log", x]` node has exactly one
307
+ correct translation: `["Ln", x]`. This function walks the expression
308
+ tree and applies that rewrite, leaving everything else untouched, so
309
+ existing expressions don't need to be hand-migrated to keep working
310
+ while still gaining access to functions added in 2.x.
311
+ """
312
+ if isinstance(expr, list):
313
+ if len(expr) == 2 and expr[0] == "Log":
314
+ return ["Ln", translate_v1_mathjson(expr[1])]
315
+ return [translate_v1_mathjson(item) for item in expr]
316
+ return expr
317
+
318
+
319
+ def create_mathjson_solver(solver_parameters, legacy_v1=False):
298
320
  def f(s, *args):
299
321
  if args:
300
322
  c = deepcopy(args[0])
@@ -1510,6 +1532,13 @@ def create_mathjson_solver(solver_parameters):
1510
1532
  # raise KeyError(f"Parameter '{s}' is not defined")
1511
1533
  return s
1512
1534
 
1535
+ if legacy_v1:
1536
+
1537
+ def legacy_v1_f(s, *args):
1538
+ return f(translate_v1_mathjson(s), *args)
1539
+
1540
+ return legacy_v1_f
1541
+
1513
1542
  return f
1514
1543
 
1515
1544
 
@@ -0,0 +1,73 @@
1
+ import sys
2
+ import os
3
+ import math
4
+ import pytest
5
+
6
+ sys.path.append(os.path.join(os.path.dirname(__file__), "../src/"))
7
+
8
+ from mathjson_solver import create_solver, translate_v1_mathjson
9
+
10
+
11
+ @pytest.mark.parametrize(
12
+ "expression, expected_translation",
13
+ [
14
+ # The only rewrite: legacy 1-arg ["Log", x] -> ["Ln", x].
15
+ (["Log", 8], ["Ln", 8]),
16
+ (["Log", "x"], ["Ln", "x"]),
17
+ # Nested occurrences are rewritten too.
18
+ (["Add", ["Log", 8], 1], ["Add", ["Ln", 8], 1]),
19
+ (["Log", ["Log", 8]], ["Ln", ["Ln", 8]]),
20
+ # The 2.x 2-arg form is left alone (it never existed pre-2.0.0, so a
21
+ # legacy expression can never contain it).
22
+ (["Log", 8, 2], ["Log", 8, 2]),
23
+ # Everything else passes through unchanged.
24
+ (["Add", 1, 2], ["Add", 1, 2]),
25
+ (5, 5),
26
+ ("x", "x"),
27
+ ([], []),
28
+ ],
29
+ )
30
+ def test_translate_v1_mathjson(expression, expected_translation):
31
+ assert translate_v1_mathjson(expression) == expected_translation
32
+
33
+
34
+ def test_translate_v1_mathjson_does_not_mutate_input():
35
+ expression = ["Add", ["Log", 8], 1]
36
+ original = ["Add", ["Log", 8], 1]
37
+ translate_v1_mathjson(expression)
38
+ assert expression == original
39
+
40
+
41
+ @pytest.mark.parametrize(
42
+ "parameters, expression, expected_result",
43
+ [
44
+ ({}, ["Log", 8], math.log(8)),
45
+ ({}, ["Add", ["Log", 8], 1], math.log(8) + 1),
46
+ # New (2.x-only) functions remain usable alongside legacy expressions.
47
+ ({}, ["Product", ["Array", 2, 3, 4]], 24),
48
+ ],
49
+ )
50
+ def test_create_solver_legacy_v1_mode(parameters, expression, expected_result):
51
+ solver = create_solver(parameters, legacy_v1=True)
52
+ assert solver(expression) == pytest.approx(expected_result)
53
+
54
+
55
+ def test_create_solver_legacy_v1_mode_matches_pre_2_0_0_log_semantics():
56
+ # Pre-2.0.0, ["Log", x] was always natural log - the exact behavior
57
+ # legacy_v1=True must reproduce for existing expressions.
58
+ solver = create_solver({}, legacy_v1=True)
59
+ assert solver(["Log", 8]) == pytest.approx(math.log(8))
60
+
61
+
62
+ def test_create_solver_non_legacy_v1_mode_uses_2_x_log_semantics():
63
+ # Without legacy_v1=True, current (2.x) semantics apply: base 10.
64
+ solver = create_solver({})
65
+ assert solver(["Log", 1000]) == pytest.approx(3)
66
+
67
+
68
+ def test_create_solver_legacy_v1_mode_with_parameter_reference():
69
+ # The translation happens on the whole expression tree up front, so a
70
+ # ["Log", x] whose argument is a solver parameter (not a literal) is
71
+ # rewritten and resolved correctly too.
72
+ solver = create_solver({"x": 8}, legacy_v1=True)
73
+ assert solver(["Log", "x"]) == pytest.approx(math.log(8))
File without changes