mathjson-solver 1.7.0__tar.gz → 1.9.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.
- {mathjson_solver-1.7.0/src/mathjson_solver.egg-info → mathjson_solver-1.9.0}/PKG-INFO +1 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/setup.cfg +1 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/setup.py +1 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/src/mathjson_solver/__init__.py +1 -1
- mathjson_solver-1.9.0/src/mathjson_solver/__main__.py +495 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0/src/mathjson_solver.egg-info}/PKG-INFO +1 -1
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/tests/test_with_pytest.py +121 -1
- mathjson_solver-1.7.0/src/mathjson_solver/__main__.py +0 -273
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/LICENSE +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/README.md +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/pyproject.toml +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/src/mathjson_solver.egg-info/SOURCES.txt +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/src/mathjson_solver.egg-info/dependency_links.txt +0 -0
- {mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/src/mathjson_solver.egg-info/top_level.txt +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
from .__main__ import create_mathjson_solver as create_solver
|
|
2
|
-
from .__main__ import MathJSONException
|
|
2
|
+
from .__main__ import MathJSONException, extract_variables
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import numbers
|
|
2
|
+
from typing import Union
|
|
3
|
+
from functools import reduce
|
|
4
|
+
import math
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
from statistics import median
|
|
7
|
+
import logging
|
|
8
|
+
import traceback
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MathJSONException(Exception):
|
|
12
|
+
"""Exception for MathJSON processing issues"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, e, expr, *args, **kwargs):
|
|
15
|
+
super().__init__(args)
|
|
16
|
+
self.e = e
|
|
17
|
+
self.expr = expr
|
|
18
|
+
self.construct = kwargs.get("mathjson_construct", "MathJSON")
|
|
19
|
+
|
|
20
|
+
def __str__(self):
|
|
21
|
+
if hasattr(self.e, "message"):
|
|
22
|
+
m = self.e.message
|
|
23
|
+
else:
|
|
24
|
+
m = str(self.e)
|
|
25
|
+
return f"Problem in {self.construct}. {self.expr}. {m}"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def requires_array(func):
|
|
29
|
+
def inner1(*args, **kwargs):
|
|
30
|
+
try:
|
|
31
|
+
if args[0][1][0] == "Array" and len(args[0][1]) > 0:
|
|
32
|
+
return func(*args, **kwargs)
|
|
33
|
+
else:
|
|
34
|
+
raise ValueError(f"'{func.__name__}' should receive a list")
|
|
35
|
+
except TypeError:
|
|
36
|
+
raise ValueError(f"'{func.__name__}' really should receive a list")
|
|
37
|
+
|
|
38
|
+
return inner1
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def is_numeric(x):
|
|
42
|
+
try:
|
|
43
|
+
float(x)
|
|
44
|
+
except ValueError:
|
|
45
|
+
return False
|
|
46
|
+
else:
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def has_matching_sublist(
|
|
51
|
+
*,
|
|
52
|
+
my_list: list,
|
|
53
|
+
required_match_count: int,
|
|
54
|
+
position: int,
|
|
55
|
+
contiguous: bool,
|
|
56
|
+
conditions: list[bool],
|
|
57
|
+
) -> bool:
|
|
58
|
+
if contiguous:
|
|
59
|
+
# Check for contiguous matches based on position
|
|
60
|
+
if position == 0:
|
|
61
|
+
# Check if the beginning of the list matches
|
|
62
|
+
count = sum(
|
|
63
|
+
1
|
|
64
|
+
for i in range(min(required_match_count, len(my_list)))
|
|
65
|
+
if conditions[i]
|
|
66
|
+
)
|
|
67
|
+
return count == required_match_count
|
|
68
|
+
elif position > 0:
|
|
69
|
+
# Skip the first `position` elements
|
|
70
|
+
count = sum(
|
|
71
|
+
1
|
|
72
|
+
for i in range(position, position + required_match_count)
|
|
73
|
+
if i < len(my_list) and conditions[i]
|
|
74
|
+
)
|
|
75
|
+
return count == required_match_count
|
|
76
|
+
elif position == -1:
|
|
77
|
+
# Check if the end of the list matches
|
|
78
|
+
count = sum(
|
|
79
|
+
1
|
|
80
|
+
for i in range(len(my_list) - required_match_count, len(my_list))
|
|
81
|
+
if conditions[i]
|
|
82
|
+
)
|
|
83
|
+
return count == required_match_count
|
|
84
|
+
elif position < -1:
|
|
85
|
+
# Skip the last `abs(position)` elements
|
|
86
|
+
count = sum(1 for i in range(len(my_list) + position) if conditions[i])
|
|
87
|
+
return count == required_match_count
|
|
88
|
+
else:
|
|
89
|
+
# Check for non-contiguous matches
|
|
90
|
+
count = sum(1 for i in range(len(my_list)) if conditions[i])
|
|
91
|
+
return count >= required_match_count
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def has_sublist2(
|
|
95
|
+
*,
|
|
96
|
+
my_list: list,
|
|
97
|
+
required_match_count: int,
|
|
98
|
+
position: int,
|
|
99
|
+
contiguous: bool,
|
|
100
|
+
condition: callable,
|
|
101
|
+
) -> bool:
|
|
102
|
+
if contiguous:
|
|
103
|
+
# Check for contiguous matches based on position
|
|
104
|
+
if position == 0:
|
|
105
|
+
# Check if the beginning of the list matches
|
|
106
|
+
count = sum(1 for x in my_list[:required_match_count] if condition(x))
|
|
107
|
+
return count == required_match_count
|
|
108
|
+
elif position > 0:
|
|
109
|
+
# Skip the first `position` elements
|
|
110
|
+
count = sum(
|
|
111
|
+
1
|
|
112
|
+
for x in my_list[position : position + required_match_count]
|
|
113
|
+
if condition(x)
|
|
114
|
+
)
|
|
115
|
+
return count == required_match_count
|
|
116
|
+
elif position == -1:
|
|
117
|
+
# Check if the end of the list matches
|
|
118
|
+
count = sum(1 for x in my_list[-required_match_count:] if condition(x))
|
|
119
|
+
return count == required_match_count
|
|
120
|
+
elif position < -1:
|
|
121
|
+
# Skip the last `abs(position)` elements
|
|
122
|
+
count = sum(1 for x in my_list[:position] if condition(x))
|
|
123
|
+
return count == required_match_count
|
|
124
|
+
else:
|
|
125
|
+
# Check for non-contiguous matches
|
|
126
|
+
count = sum(1 for x in my_list if condition(x))
|
|
127
|
+
return count >= required_match_count
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def create_mathjson_solver(solver_parameters):
|
|
131
|
+
def f(s, *args):
|
|
132
|
+
if args:
|
|
133
|
+
c = deepcopy(args[0])
|
|
134
|
+
else:
|
|
135
|
+
c = {}
|
|
136
|
+
# c = deepcopy(kwargs.get("c", {}))
|
|
137
|
+
if isinstance(s, numbers.Number):
|
|
138
|
+
return s
|
|
139
|
+
if isinstance(s, list):
|
|
140
|
+
|
|
141
|
+
def Arr(s):
|
|
142
|
+
return s
|
|
143
|
+
|
|
144
|
+
def Sum(s):
|
|
145
|
+
l_res = []
|
|
146
|
+
for x in s[1:]:
|
|
147
|
+
res = f(x, c)
|
|
148
|
+
if isinstance(res, list):
|
|
149
|
+
l_res.append(sum([xx for xx in res[1:]]))
|
|
150
|
+
else:
|
|
151
|
+
l_res.append(res)
|
|
152
|
+
return sum(l_res)
|
|
153
|
+
|
|
154
|
+
# @requires_array
|
|
155
|
+
def Max(s):
|
|
156
|
+
if isinstance(s[1], str):
|
|
157
|
+
return max([f(x, c) for x in f(s[1], c) if is_numeric(f(x, c))])
|
|
158
|
+
else:
|
|
159
|
+
return max([f(x, c) for x in s[1][1:] if is_numeric(f(x, c))])
|
|
160
|
+
|
|
161
|
+
# @requires_array
|
|
162
|
+
def Min(s):
|
|
163
|
+
if isinstance(s[1], str):
|
|
164
|
+
return min([f(x, c) for x in f(s[1], c) if is_numeric(f(x, c))])
|
|
165
|
+
else:
|
|
166
|
+
return min([f(x, c) for x in s[1][1:] if is_numeric(f(x, c))])
|
|
167
|
+
|
|
168
|
+
def Average(s):
|
|
169
|
+
if isinstance(s[1], str):
|
|
170
|
+
# A reference to "answer" has been passed
|
|
171
|
+
s_ = [float(f(x, c)) for x in f(s[1], c) if is_numeric(f(x, c))]
|
|
172
|
+
else:
|
|
173
|
+
s_ = [float(f(x, c)) for x in s[1][1:] if is_numeric(f(x, c))]
|
|
174
|
+
# print(f"{s_} {sum(s_)}/{len(s_)}")
|
|
175
|
+
try:
|
|
176
|
+
return sum(s_) / len(s_)
|
|
177
|
+
except ZeroDivisionError:
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
# @requires_array
|
|
181
|
+
def Median(s):
|
|
182
|
+
if isinstance(s[1], str):
|
|
183
|
+
return median([f(x, c) for x in f(s[1], c) if is_numeric(f(x, c))])
|
|
184
|
+
else:
|
|
185
|
+
return median([f(x, c) for x in s[1][1:] if is_numeric(f(x, c))])
|
|
186
|
+
|
|
187
|
+
# @requires_array
|
|
188
|
+
def Length(s):
|
|
189
|
+
if isinstance(s[1], str):
|
|
190
|
+
|
|
191
|
+
return len([x for x in f(s[1], c)][1:])
|
|
192
|
+
else:
|
|
193
|
+
return len([x for x in s[1][1:]])
|
|
194
|
+
|
|
195
|
+
# @requires_array
|
|
196
|
+
def Any(s):
|
|
197
|
+
return any([f(x, c) for x in s[1][1:]])
|
|
198
|
+
|
|
199
|
+
# @requires_array
|
|
200
|
+
def All(s):
|
|
201
|
+
return all([f(x, c) for x in s[1][1:]])
|
|
202
|
+
|
|
203
|
+
def Int(s):
|
|
204
|
+
try:
|
|
205
|
+
return int(f(s[1], c))
|
|
206
|
+
except ValueError:
|
|
207
|
+
return int(float(f(s[1], c)))
|
|
208
|
+
|
|
209
|
+
def Float(s):
|
|
210
|
+
return float(f(s[1], c))
|
|
211
|
+
|
|
212
|
+
def Constants(s):
|
|
213
|
+
for x in s[1:-1]:
|
|
214
|
+
c[x[0]] = f(x[1], c)
|
|
215
|
+
return f(s[-1], c)
|
|
216
|
+
|
|
217
|
+
def Switch(s):
|
|
218
|
+
expression = f(s[1], c)
|
|
219
|
+
for x in s[3:]:
|
|
220
|
+
if len(x) != 2:
|
|
221
|
+
raise ValueError(
|
|
222
|
+
"Case of 'Switch' should have exactly two parameters"
|
|
223
|
+
)
|
|
224
|
+
if expression == f(x[0], c):
|
|
225
|
+
return f(x[1], c)
|
|
226
|
+
else:
|
|
227
|
+
return f(s[2], c)
|
|
228
|
+
|
|
229
|
+
def If(s):
|
|
230
|
+
if len(s) < 3:
|
|
231
|
+
raise ValueError("Wrong parameters for 'If'")
|
|
232
|
+
for x in s[1:-1]:
|
|
233
|
+
if len(x) != 2:
|
|
234
|
+
raise ValueError("Wrong if or elif in 'If'")
|
|
235
|
+
try:
|
|
236
|
+
if f(x[0], c):
|
|
237
|
+
try:
|
|
238
|
+
return f(x[1], c)
|
|
239
|
+
except MathJSONException:
|
|
240
|
+
logging.error(
|
|
241
|
+
"MathJSONException: %s", traceback.format_exc()
|
|
242
|
+
)
|
|
243
|
+
continue
|
|
244
|
+
except MathJSONException:
|
|
245
|
+
logging.error("MathJSONException: %s", traceback.format_exc())
|
|
246
|
+
return f(s[-1], c) # return default value (else)
|
|
247
|
+
|
|
248
|
+
return f(s[-1], c)
|
|
249
|
+
|
|
250
|
+
def In(s):
|
|
251
|
+
if len(s) != 3:
|
|
252
|
+
raise ValueError("Wrong parameters for 'In'")
|
|
253
|
+
if isinstance(s[2], list) and s[2][0] == "Array":
|
|
254
|
+
return f(s[1], c) in [f(x, c) for x in s[2][1:]]
|
|
255
|
+
|
|
256
|
+
elif isinstance(s[2], str):
|
|
257
|
+
return f(s[1], c) in f(s[2], c)
|
|
258
|
+
else:
|
|
259
|
+
raise ValueError(
|
|
260
|
+
"Wrong parameters for 'In'. Parameter 2 must be a list."
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
def Not_in(s):
|
|
264
|
+
return not In(s)
|
|
265
|
+
|
|
266
|
+
def Contains_any_of(s):
|
|
267
|
+
if isinstance(s[1], list) and s[1][0] == "Array":
|
|
268
|
+
list1 = [f(x, c) for x in s[1][1:]]
|
|
269
|
+
elif isinstance(s[1], str):
|
|
270
|
+
list1 = f(s[1], c)
|
|
271
|
+
|
|
272
|
+
if isinstance(s[2], list) and s[2][0] == "Array":
|
|
273
|
+
list2 = [f(x, c) for x in s[2][1:]]
|
|
274
|
+
elif isinstance(s[2], str):
|
|
275
|
+
list2 = f(s[2], c)
|
|
276
|
+
|
|
277
|
+
if any(x in list1 for x in list2):
|
|
278
|
+
return True
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
def Contains_all_of(s):
|
|
282
|
+
if isinstance(s[1], list) and s[1][0] == "Array":
|
|
283
|
+
list1 = [f(x, c) for x in s[1][1:]]
|
|
284
|
+
elif isinstance(s[1], str):
|
|
285
|
+
list1 = f(s[1], c)
|
|
286
|
+
|
|
287
|
+
if isinstance(s[2], list) and s[2][0] == "Array":
|
|
288
|
+
list2 = [f(x, c) for x in s[2][1:]]
|
|
289
|
+
elif isinstance(s[2], str):
|
|
290
|
+
list2 = f(s[2], c)
|
|
291
|
+
|
|
292
|
+
if all(x in list1 for x in list2):
|
|
293
|
+
return True
|
|
294
|
+
return False
|
|
295
|
+
|
|
296
|
+
def Contains_none_of(s):
|
|
297
|
+
return not Contains_any_of(s)
|
|
298
|
+
|
|
299
|
+
def Str(s):
|
|
300
|
+
if len(s) < 2:
|
|
301
|
+
raise ValueError("Wrong parameters for 'Str'")
|
|
302
|
+
return f"{f(s[1])}"
|
|
303
|
+
|
|
304
|
+
def Not(s):
|
|
305
|
+
return not f(s[1])
|
|
306
|
+
|
|
307
|
+
def Map(s):
|
|
308
|
+
"""
|
|
309
|
+
["Map", list, function, more parameters]
|
|
310
|
+
The `function` must accept at least one parameter. That is for the current loop element.
|
|
311
|
+
The `more parameters` are for any additional parameters that function might have.
|
|
312
|
+
"""
|
|
313
|
+
z = f(s[1], c)
|
|
314
|
+
if isinstance(z, list):
|
|
315
|
+
retlist = ["Array"]
|
|
316
|
+
for x in z[1:]:
|
|
317
|
+
the_function_name = s[2][0]
|
|
318
|
+
ss = [the_function_name, x] + s[3:]
|
|
319
|
+
retlist.append(f(ss, c))
|
|
320
|
+
return retlist
|
|
321
|
+
|
|
322
|
+
def HasMatchingSublist(s):
|
|
323
|
+
"""
|
|
324
|
+
["HasMatchingSublist", list, required_match_count, position, contiguous, function, more parameters]
|
|
325
|
+
"""
|
|
326
|
+
the_list = f(s[1], c)[1:]
|
|
327
|
+
required_match_count = f(s[2], c)
|
|
328
|
+
position = f(s[3], c)
|
|
329
|
+
contiguous = f(s[4], c)
|
|
330
|
+
conditions = []
|
|
331
|
+
|
|
332
|
+
for i, x in enumerate(the_list):
|
|
333
|
+
the_function_name = s[5][0]
|
|
334
|
+
ss = [the_function_name, x] + s[6:]
|
|
335
|
+
conditions.append(f(ss, c))
|
|
336
|
+
pass
|
|
337
|
+
|
|
338
|
+
return has_matching_sublist(
|
|
339
|
+
my_list=the_list,
|
|
340
|
+
required_match_count=required_match_count,
|
|
341
|
+
position=position,
|
|
342
|
+
contiguous=contiguous,
|
|
343
|
+
conditions=conditions,
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
constructs = {
|
|
347
|
+
"Sum": Sum,
|
|
348
|
+
"Add": Sum,
|
|
349
|
+
"Subtract": lambda s: reduce(
|
|
350
|
+
lambda a, b: a - b, [f(x, c) for x in s[1:]]
|
|
351
|
+
),
|
|
352
|
+
"Constants": Constants,
|
|
353
|
+
"Switch": Switch,
|
|
354
|
+
"If": If,
|
|
355
|
+
"Multiply": lambda s: reduce(
|
|
356
|
+
lambda a, b: float(a) * float(b), [f(x, c) for x in s[1:]]
|
|
357
|
+
),
|
|
358
|
+
"Divide": lambda s: f(s[1], c) / f(s[2], c),
|
|
359
|
+
"Negate": lambda s: -f(s[1], c),
|
|
360
|
+
"Power": lambda s: pow(f(s[1], c), f(s[2], c)),
|
|
361
|
+
"Root": lambda s: pow(f(s[1], c), 1.0 / f(s[2], c)),
|
|
362
|
+
"Sqrt": lambda s: pow(f(s[1], c), 1.0 / 2),
|
|
363
|
+
"Square": lambda s: pow(f(s[1], c), 2),
|
|
364
|
+
"Exp": lambda s: math.exp(f(s[1], c)),
|
|
365
|
+
"Log": lambda s: math.log(f(s[1], c)),
|
|
366
|
+
"Log2": lambda s: math.log2(f(s[1], c)),
|
|
367
|
+
"Log10": lambda s: math.log10(f(s[1], c)),
|
|
368
|
+
"Equal": lambda s: f(s[1], c) == f(s[2], c),
|
|
369
|
+
"Greater": lambda s: f(s[1], c) > f(s[2], c),
|
|
370
|
+
"GreaterEqual": lambda s: f(s[1], c) >= f(s[2], c),
|
|
371
|
+
"Less": lambda s: f(s[1], c) < f(s[2], c),
|
|
372
|
+
"LessEqual": lambda s: f(s[1], c) <= f(s[2], c),
|
|
373
|
+
"NotEqual": lambda s: f(s[1], c) != f(s[2], c),
|
|
374
|
+
"Abs": lambda s: abs(f(s[1], c)),
|
|
375
|
+
"Round": lambda s: (
|
|
376
|
+
round(f(s[1], c), f(s[2], c))
|
|
377
|
+
if len(s) == 3
|
|
378
|
+
else int(round(f(s[1], c)))
|
|
379
|
+
),
|
|
380
|
+
"Max": Max,
|
|
381
|
+
"Min": Min,
|
|
382
|
+
"Average": Average,
|
|
383
|
+
"Median": Median,
|
|
384
|
+
"Length": Length,
|
|
385
|
+
"Any": Any,
|
|
386
|
+
"All": All,
|
|
387
|
+
"Array": Arr,
|
|
388
|
+
"In": In,
|
|
389
|
+
"Not_in": Not_in,
|
|
390
|
+
"Contains_any_of": Contains_any_of,
|
|
391
|
+
"Contains_all_of": Contains_all_of,
|
|
392
|
+
"Contains_none_of": Contains_none_of,
|
|
393
|
+
"NotIn": Not_in,
|
|
394
|
+
"ContainsAnyOf": Contains_any_of,
|
|
395
|
+
"ContainsAllOf": Contains_all_of,
|
|
396
|
+
"ContainsNoneOf": Contains_none_of,
|
|
397
|
+
"Int": Int,
|
|
398
|
+
"Float": Float,
|
|
399
|
+
"Str": Str,
|
|
400
|
+
"Not": Not,
|
|
401
|
+
"IsDefined": lambda s: s[1] in c,
|
|
402
|
+
"Map": Map,
|
|
403
|
+
"HasMatchingSublist": HasMatchingSublist,
|
|
404
|
+
}
|
|
405
|
+
if s[0] in constructs:
|
|
406
|
+
try:
|
|
407
|
+
return constructs[s[0]](s)
|
|
408
|
+
except Exception as e:
|
|
409
|
+
raise MathJSONException(e, s, mathjson_construct=s[0])
|
|
410
|
+
else:
|
|
411
|
+
# raise MathJSONException(
|
|
412
|
+
# NotImplementedError(f"'{s[0]}' is not supported"), s
|
|
413
|
+
# )
|
|
414
|
+
return s
|
|
415
|
+
elif s in solver_parameters:
|
|
416
|
+
return f(solver_parameters[s], c)
|
|
417
|
+
elif s in c:
|
|
418
|
+
return f(c[s], c)
|
|
419
|
+
else:
|
|
420
|
+
# raise KeyError(f"Parameter '{s}' is not defined")
|
|
421
|
+
return s
|
|
422
|
+
|
|
423
|
+
return f
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def extract_variables(s: Union[list, int, float, str], li: set, ignore_list: set):
|
|
427
|
+
constructs = [
|
|
428
|
+
"Add",
|
|
429
|
+
"Sum",
|
|
430
|
+
"Subtract",
|
|
431
|
+
"Constants",
|
|
432
|
+
"Switch",
|
|
433
|
+
"If",
|
|
434
|
+
"Multiply",
|
|
435
|
+
"Divide",
|
|
436
|
+
"Negate",
|
|
437
|
+
"Power",
|
|
438
|
+
"Root",
|
|
439
|
+
"Sqrt",
|
|
440
|
+
"Square",
|
|
441
|
+
"Exp",
|
|
442
|
+
"Log",
|
|
443
|
+
"Log2",
|
|
444
|
+
"Log10",
|
|
445
|
+
"Equal",
|
|
446
|
+
"Greater",
|
|
447
|
+
"GreaterEqual",
|
|
448
|
+
"Less",
|
|
449
|
+
"LessEqual",
|
|
450
|
+
"NotEqual",
|
|
451
|
+
"Abs",
|
|
452
|
+
"Round",
|
|
453
|
+
"Max",
|
|
454
|
+
"Min",
|
|
455
|
+
"Average",
|
|
456
|
+
"Median",
|
|
457
|
+
"Length",
|
|
458
|
+
"Any",
|
|
459
|
+
"All",
|
|
460
|
+
"Array",
|
|
461
|
+
"In",
|
|
462
|
+
"Not_in",
|
|
463
|
+
"Contains_any_of",
|
|
464
|
+
"Contains_all_of",
|
|
465
|
+
"Contains_none_of",
|
|
466
|
+
"NotIn",
|
|
467
|
+
"ContainsAnyOf",
|
|
468
|
+
"ContainsAllOf",
|
|
469
|
+
"ContainsNoneOf",
|
|
470
|
+
"Int",
|
|
471
|
+
"Float",
|
|
472
|
+
"Str",
|
|
473
|
+
"Not",
|
|
474
|
+
"IsDefined",
|
|
475
|
+
"Map",
|
|
476
|
+
"HasMatchingSublist",
|
|
477
|
+
]
|
|
478
|
+
if isinstance(s, str):
|
|
479
|
+
if s in ignore_list:
|
|
480
|
+
return li
|
|
481
|
+
if s not in constructs:
|
|
482
|
+
li.add(s)
|
|
483
|
+
return li
|
|
484
|
+
elif isinstance(s, list):
|
|
485
|
+
if s[0] == "Constants":
|
|
486
|
+
for x in s[1:-1]:
|
|
487
|
+
ignore_list.add(x[0])
|
|
488
|
+
li.update(extract_variables(x[1], li, ignore_list))
|
|
489
|
+
li.update(extract_variables(x[-1], li, ignore_list))
|
|
490
|
+
else:
|
|
491
|
+
for x in s[1:]:
|
|
492
|
+
li.update(extract_variables(x, li, ignore_list))
|
|
493
|
+
return li
|
|
494
|
+
else:
|
|
495
|
+
return li
|
|
@@ -5,7 +5,7 @@ import re
|
|
|
5
5
|
|
|
6
6
|
sys.path.append(os.path.join(os.path.dirname(__file__), "../src/"))
|
|
7
7
|
|
|
8
|
-
from mathjson_solver import create_solver, MathJSONException
|
|
8
|
+
from mathjson_solver import create_solver, MathJSONException, extract_variables
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
@pytest.mark.parametrize(
|
|
@@ -13,6 +13,10 @@ from mathjson_solver import create_solver, MathJSONException
|
|
|
13
13
|
[
|
|
14
14
|
({}, ["Add", 2, 4, 3], 9),
|
|
15
15
|
({}, ["Sum", 2, 4, 3], 9),
|
|
16
|
+
({}, ["Sum", ["Array", 1, 1], 4, 3], 9),
|
|
17
|
+
({}, ["Sum", ["Array", 2, 4, 3]], 9),
|
|
18
|
+
({"a": 2}, ["Sum", "a", 4, 3], 9),
|
|
19
|
+
({"a": ["Array", 1, 1]}, ["Sum", "a", 4, 3], 9),
|
|
16
20
|
({}, ["Subtract", 10, 5, 2], 3),
|
|
17
21
|
({}, ["Add", 5, 4, ["Negate", 3]], 6),
|
|
18
22
|
({}, ["Multiply", 2, 3, 4], 24),
|
|
@@ -52,7 +56,11 @@ from mathjson_solver import create_solver, MathJSONException
|
|
|
52
56
|
({}, ["Array", 1, 2, 3, 5, 2], ["Array", 1, 2, 3, 5, 2]),
|
|
53
57
|
({}, ["Max", ["Array", 1, 2, 3, 5, 2]], 5),
|
|
54
58
|
({}, ["Max", ["Array", 1, 2, ["Sum", 2, 4, 3], 5, 2]], 9),
|
|
59
|
+
({"a": ["Array", 1, 2, 3, 5, 2]}, ["Max", "a"], 5),
|
|
60
|
+
({}, ["Min", ["Array", 1, 2, 3, 5, 2]], 1),
|
|
61
|
+
({"a": ["Array", 2, 1, 3, 5, 2]}, ["Min", "a"], 1),
|
|
55
62
|
({}, ["Median", ["Array", 1, 2, 3, 5, 2]], 2),
|
|
63
|
+
({"a": ["Array", 1, 2, 3, 5, 2]}, ["Median", "a"], 2),
|
|
56
64
|
({}, ["Average", ["Array", 1, 2, 3, 5, 2]], 2.6),
|
|
57
65
|
(
|
|
58
66
|
{},
|
|
@@ -80,8 +88,10 @@ from mathjson_solver import create_solver, MathJSONException
|
|
|
80
88
|
5.0,
|
|
81
89
|
),
|
|
82
90
|
({}, ["Average", ["Array"]], None),
|
|
91
|
+
({"a": ["Array", 2, 8]}, ["Average", "a"], 5),
|
|
83
92
|
({"a": 10, "b": 20}, ["Average", ["Array", "a", "b"]], 15),
|
|
84
93
|
({}, ["Length", ["Array", 1, 2, 3, 5, 2, 9]], 6),
|
|
94
|
+
({"a": ["Array", 1, 2, 3, 5, 2, 9]}, ["Length", "a"], 6),
|
|
85
95
|
({}, ["Length", ["Array"]], 0),
|
|
86
96
|
({}, ["Int", "12"], 12),
|
|
87
97
|
({}, ["Int", "12.2"], 12),
|
|
@@ -162,6 +172,93 @@ from mathjson_solver import create_solver, MathJSONException
|
|
|
162
172
|
({}, ["Not", True], False),
|
|
163
173
|
({}, ["Not", 0], True),
|
|
164
174
|
({}, ["Not", ["In", 2, ["Array", 1, 2, 3]]], False),
|
|
175
|
+
({}, ["Array", 2, 4, 3], ["Array", 2, 4, 3]),
|
|
176
|
+
({}, ["Map", ["Array", 1, 2, 3], ["Square"]], ["Array", 1, 4, 9]),
|
|
177
|
+
({}, ["Map", ["Array", 1, 2, 3], ["Power"], 2], ["Array", 1, 4, 9]),
|
|
178
|
+
(
|
|
179
|
+
{},
|
|
180
|
+
["Map", ["Array", 1, 2, 3], ["GreaterEqual"], 2],
|
|
181
|
+
["Array", False, True, True],
|
|
182
|
+
),
|
|
183
|
+
# ["HasMatchingSublist", list, required_match_count, position, contiguous, function, more parameters]
|
|
184
|
+
(
|
|
185
|
+
{},
|
|
186
|
+
[
|
|
187
|
+
"HasMatchingSublist",
|
|
188
|
+
["Array", 1, 2, 3, 4, 5, 6],
|
|
189
|
+
3,
|
|
190
|
+
0,
|
|
191
|
+
True,
|
|
192
|
+
["GreaterEqual"],
|
|
193
|
+
1,
|
|
194
|
+
],
|
|
195
|
+
True, # first 3 elements are greater than 3
|
|
196
|
+
),
|
|
197
|
+
(
|
|
198
|
+
{},
|
|
199
|
+
[
|
|
200
|
+
"HasMatchingSublist",
|
|
201
|
+
["Array", 1, 2, 3, 4, 5, 6],
|
|
202
|
+
3,
|
|
203
|
+
0,
|
|
204
|
+
True,
|
|
205
|
+
["GreaterEqual"],
|
|
206
|
+
2,
|
|
207
|
+
],
|
|
208
|
+
False, # first 3 elements are greater than 3 - False
|
|
209
|
+
),
|
|
210
|
+
(
|
|
211
|
+
{},
|
|
212
|
+
[
|
|
213
|
+
"HasMatchingSublist",
|
|
214
|
+
["Array", 1, 2, 3, 4, 5, 6],
|
|
215
|
+
3,
|
|
216
|
+
0,
|
|
217
|
+
False, # anywhere
|
|
218
|
+
["GreaterEqual"],
|
|
219
|
+
4,
|
|
220
|
+
],
|
|
221
|
+
True,
|
|
222
|
+
),
|
|
223
|
+
(
|
|
224
|
+
{},
|
|
225
|
+
[
|
|
226
|
+
"HasMatchingSublist",
|
|
227
|
+
["Array", 1, 2, 3, 4, 5, 6],
|
|
228
|
+
4,
|
|
229
|
+
0,
|
|
230
|
+
False, # anywhere
|
|
231
|
+
["GreaterEqual"],
|
|
232
|
+
4,
|
|
233
|
+
],
|
|
234
|
+
False,
|
|
235
|
+
),
|
|
236
|
+
(
|
|
237
|
+
{},
|
|
238
|
+
[
|
|
239
|
+
"HasMatchingSublist",
|
|
240
|
+
["Array", 1, 2, 3, 4, 5, 6],
|
|
241
|
+
3,
|
|
242
|
+
-1,
|
|
243
|
+
True,
|
|
244
|
+
["GreaterEqual"],
|
|
245
|
+
4,
|
|
246
|
+
],
|
|
247
|
+
True,
|
|
248
|
+
),
|
|
249
|
+
(
|
|
250
|
+
{},
|
|
251
|
+
[
|
|
252
|
+
"HasMatchingSublist",
|
|
253
|
+
["Array", 1, 2, 3, 4, 5, 6],
|
|
254
|
+
5,
|
|
255
|
+
-1,
|
|
256
|
+
True,
|
|
257
|
+
["GreaterEqual"],
|
|
258
|
+
4,
|
|
259
|
+
],
|
|
260
|
+
False,
|
|
261
|
+
),
|
|
165
262
|
],
|
|
166
263
|
)
|
|
167
264
|
def test_solver_simple(parameters, expression, expected_result):
|
|
@@ -206,3 +303,26 @@ def test_handle_exception():
|
|
|
206
303
|
assert True
|
|
207
304
|
else:
|
|
208
305
|
assert False
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@pytest.mark.parametrize(
|
|
309
|
+
"parameters, expression, expected_result",
|
|
310
|
+
[
|
|
311
|
+
({}, ["Add", 2, 4, 3], set()),
|
|
312
|
+
({}, ["Sum", 2, "a", 3], set(["a"])),
|
|
313
|
+
({"a": 5}, ["Sum", 2, "a", 3], set()),
|
|
314
|
+
({"a": 5, "b": 6}, ["Sum", 2, "a", "c"], set(["c"])),
|
|
315
|
+
({}, ["Constants", ["c", 1], ["d", ["Add", 2, "a"]], "d"], set(["a"])),
|
|
316
|
+
# Now with "ugly" variables that mimic the ones used for deep referencing.
|
|
317
|
+
(
|
|
318
|
+
{},
|
|
319
|
+
["Add", "[slug1][0][question1]", "y", 4, "[slug1][-3:-1][question1]"],
|
|
320
|
+
set(["[slug1][0][question1]", "y", "[slug1][-3:-1][question1]"]),
|
|
321
|
+
),
|
|
322
|
+
],
|
|
323
|
+
)
|
|
324
|
+
def test_extract_variables(parameters, expression, expected_result):
|
|
325
|
+
assert (
|
|
326
|
+
extract_variables(expression, set(), set([x for x in parameters.keys()]))
|
|
327
|
+
== expected_result
|
|
328
|
+
)
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import numbers
|
|
2
|
-
from functools import reduce
|
|
3
|
-
import math
|
|
4
|
-
from copy import deepcopy
|
|
5
|
-
from statistics import median
|
|
6
|
-
import logging
|
|
7
|
-
import traceback
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class MathJSONException(Exception):
|
|
11
|
-
"""Exception for MathJSON processing issues"""
|
|
12
|
-
|
|
13
|
-
def __init__(self, e, expr, *args, **kwargs):
|
|
14
|
-
super().__init__(args)
|
|
15
|
-
self.e = e
|
|
16
|
-
self.expr = expr
|
|
17
|
-
self.construct = kwargs.get("mathjson_construct", "MathJSON")
|
|
18
|
-
|
|
19
|
-
def __str__(self):
|
|
20
|
-
if hasattr(self.e, "message"):
|
|
21
|
-
m = self.e.message
|
|
22
|
-
else:
|
|
23
|
-
m = str(self.e)
|
|
24
|
-
return f"Problem in {self.construct}. {self.expr}. {m}"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def requires_array(func):
|
|
28
|
-
def inner1(*args, **kwargs):
|
|
29
|
-
try:
|
|
30
|
-
if args[0][1][0] == "Array" and len(args[0][1]) > 0:
|
|
31
|
-
return func(*args, **kwargs)
|
|
32
|
-
else:
|
|
33
|
-
raise ValueError(f"'{func.__name__}' should receive a list")
|
|
34
|
-
except TypeError:
|
|
35
|
-
raise ValueError(f"'{func.__name__}' really should receive a list")
|
|
36
|
-
|
|
37
|
-
return inner1
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def is_numeric(x):
|
|
41
|
-
try:
|
|
42
|
-
float(x)
|
|
43
|
-
except ValueError:
|
|
44
|
-
return False
|
|
45
|
-
else:
|
|
46
|
-
return True
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def create_mathjson_solver(solver_parameters):
|
|
50
|
-
def f(s, *args):
|
|
51
|
-
if args:
|
|
52
|
-
c = deepcopy(args[0])
|
|
53
|
-
else:
|
|
54
|
-
c = {}
|
|
55
|
-
# c = deepcopy(kwargs.get("c", {}))
|
|
56
|
-
if isinstance(s, numbers.Number):
|
|
57
|
-
return s
|
|
58
|
-
if isinstance(s, list):
|
|
59
|
-
|
|
60
|
-
def Arr(s):
|
|
61
|
-
return s
|
|
62
|
-
|
|
63
|
-
@requires_array
|
|
64
|
-
def Max(s):
|
|
65
|
-
return max([f(x, c) for x in s[1][1:]])
|
|
66
|
-
|
|
67
|
-
@requires_array
|
|
68
|
-
def Min(s):
|
|
69
|
-
return min([f(x, c) for x in s[1][1:]])
|
|
70
|
-
|
|
71
|
-
@requires_array
|
|
72
|
-
def Average(s):
|
|
73
|
-
s_ = [float(f(x, c)) for x in s[1][1:] if is_numeric(f(x, c))]
|
|
74
|
-
# print(f"{s_} {sum(s_)}/{len(s_)}")
|
|
75
|
-
try:
|
|
76
|
-
return sum(s_) / len(s_)
|
|
77
|
-
except ZeroDivisionError:
|
|
78
|
-
return None
|
|
79
|
-
|
|
80
|
-
@requires_array
|
|
81
|
-
def Median(s):
|
|
82
|
-
return median([f(x, c) for x in s[1][1:]])
|
|
83
|
-
|
|
84
|
-
@requires_array
|
|
85
|
-
def Length(s):
|
|
86
|
-
return len([f(x, c) for x in s[1][1:]])
|
|
87
|
-
|
|
88
|
-
@requires_array
|
|
89
|
-
def Any(s):
|
|
90
|
-
return any([f(x, c) for x in s[1][1:]])
|
|
91
|
-
|
|
92
|
-
@requires_array
|
|
93
|
-
def All(s):
|
|
94
|
-
return all([f(x, c) for x in s[1][1:]])
|
|
95
|
-
|
|
96
|
-
def Int(s):
|
|
97
|
-
try:
|
|
98
|
-
return int(f(s[1], c))
|
|
99
|
-
except ValueError:
|
|
100
|
-
return int(float(f(s[1], c)))
|
|
101
|
-
|
|
102
|
-
def Float(s):
|
|
103
|
-
return float(f(s[1], c))
|
|
104
|
-
|
|
105
|
-
def Constants(s):
|
|
106
|
-
for x in s[1:-1]:
|
|
107
|
-
c[x[0]] = f(x[1], c)
|
|
108
|
-
return f(s[-1], c)
|
|
109
|
-
|
|
110
|
-
def Switch(s):
|
|
111
|
-
expression = f(s[1], c)
|
|
112
|
-
for x in s[3:]:
|
|
113
|
-
if len(x) != 2:
|
|
114
|
-
raise ValueError(
|
|
115
|
-
f"Case of 'Switch' should have exactly two parameters"
|
|
116
|
-
)
|
|
117
|
-
if expression == f(x[0], c):
|
|
118
|
-
return f(x[1], c)
|
|
119
|
-
else:
|
|
120
|
-
return f(s[2], c)
|
|
121
|
-
|
|
122
|
-
def If(s):
|
|
123
|
-
if len(s) < 3:
|
|
124
|
-
raise ValueError(f"Wrong parameters for 'If'")
|
|
125
|
-
for x in s[1:-1]:
|
|
126
|
-
if len(x) != 2:
|
|
127
|
-
raise ValueError(f"Wrong if or elif in 'If'")
|
|
128
|
-
try:
|
|
129
|
-
if f(x[0], c):
|
|
130
|
-
try:
|
|
131
|
-
return f(x[1], c)
|
|
132
|
-
except MathJSONException as e:
|
|
133
|
-
logging.error(
|
|
134
|
-
"MathJSONException: %s", traceback.format_exc()
|
|
135
|
-
)
|
|
136
|
-
continue
|
|
137
|
-
except MathJSONException as e:
|
|
138
|
-
logging.error("MathJSONException: %s", traceback.format_exc())
|
|
139
|
-
return f(s[-1], c) # return default value (else)
|
|
140
|
-
|
|
141
|
-
return f(s[-1], c)
|
|
142
|
-
|
|
143
|
-
def In(s):
|
|
144
|
-
if len(s) != 3:
|
|
145
|
-
raise ValueError(f"Wrong parameters for 'In'")
|
|
146
|
-
if type(s[2]) == list and s[2][0] == "Array":
|
|
147
|
-
return f(s[1], c) in [f(x, c) for x in s[2][1:]]
|
|
148
|
-
|
|
149
|
-
elif type(s[2]) == str:
|
|
150
|
-
return f(s[1], c) in f(s[2], c)
|
|
151
|
-
else:
|
|
152
|
-
raise ValueError(
|
|
153
|
-
f"Wrong parameters for 'In'. Parameter 2 must be a list."
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
def Not_in(s):
|
|
157
|
-
return not In(s)
|
|
158
|
-
|
|
159
|
-
def Contains_any_of(s):
|
|
160
|
-
if type(s[1]) == list and s[1][0] == "Array":
|
|
161
|
-
list1 = [f(x, c) for x in s[1][1:]]
|
|
162
|
-
elif type(s[1]) == str:
|
|
163
|
-
list1 = f(s[1], c)
|
|
164
|
-
|
|
165
|
-
if type(s[2]) == list and s[2][0] == "Array":
|
|
166
|
-
list2 = [f(x, c) for x in s[2][1:]]
|
|
167
|
-
elif type(s[2]) == str:
|
|
168
|
-
list2 = f(s[2], c)
|
|
169
|
-
|
|
170
|
-
if any(x in list1 for x in list2):
|
|
171
|
-
return True
|
|
172
|
-
return False
|
|
173
|
-
|
|
174
|
-
def Contains_all_of(s):
|
|
175
|
-
if type(s[1]) == list and s[1][0] == "Array":
|
|
176
|
-
list1 = [f(x, c) for x in s[1][1:]]
|
|
177
|
-
elif type(s[1]) == str:
|
|
178
|
-
list1 = f(s[1], c)
|
|
179
|
-
|
|
180
|
-
if type(s[2]) == list and s[2][0] == "Array":
|
|
181
|
-
list2 = [f(x, c) for x in s[2][1:]]
|
|
182
|
-
elif type(s[2]) == str:
|
|
183
|
-
list2 = f(s[2], c)
|
|
184
|
-
|
|
185
|
-
if all(x in list1 for x in list2):
|
|
186
|
-
return True
|
|
187
|
-
return False
|
|
188
|
-
|
|
189
|
-
def Contains_none_of(s):
|
|
190
|
-
return not Contains_any_of(s)
|
|
191
|
-
|
|
192
|
-
def Str(s):
|
|
193
|
-
if len(s) < 2:
|
|
194
|
-
raise ValueError(f"Wrong parameters for 'Str'")
|
|
195
|
-
return f"{f(s[1])}"
|
|
196
|
-
|
|
197
|
-
def Not(s):
|
|
198
|
-
return not f(s[1])
|
|
199
|
-
|
|
200
|
-
constructs = {
|
|
201
|
-
"Add": lambda s: sum([f(x, c) for x in s[1:]]),
|
|
202
|
-
"Sum": lambda s: sum([f(x, c) for x in s[1:]]),
|
|
203
|
-
"Subtract": lambda s: reduce(
|
|
204
|
-
lambda a, b: a - b, [f(x, c) for x in s[1:]]
|
|
205
|
-
),
|
|
206
|
-
"Constants": Constants,
|
|
207
|
-
"Switch": Switch,
|
|
208
|
-
"If": If,
|
|
209
|
-
"Multiply": lambda s: reduce(
|
|
210
|
-
lambda a, b: float(a) * float(b), [f(x, c) for x in s[1:]]
|
|
211
|
-
),
|
|
212
|
-
"Divide": lambda s: f(s[1], c) / f(s[2], c),
|
|
213
|
-
"Negate": lambda s: -f(s[1], c),
|
|
214
|
-
"Power": lambda s: pow(f(s[1], c), f(s[2], c)),
|
|
215
|
-
"Root": lambda s: pow(f(s[1], c), 1.0 / f(s[2], c)),
|
|
216
|
-
"Sqrt": lambda s: pow(f(s[1], c), 1.0 / 2),
|
|
217
|
-
"Square": lambda s: pow(f(s[1], c), 2),
|
|
218
|
-
"Exp": lambda s: math.exp(f(s[1], c)),
|
|
219
|
-
"Log": lambda s: math.log(f(s[1], c)),
|
|
220
|
-
"Log2": lambda s: math.log2(f(s[1], c)),
|
|
221
|
-
"Log10": lambda s: math.log10(f(s[1], c)),
|
|
222
|
-
"Equal": lambda s: f(s[1], c) == f(s[2], c),
|
|
223
|
-
"Greater": lambda s: f(s[1], c) > f(s[2], c),
|
|
224
|
-
"GreaterEqual": lambda s: f(s[1], c) >= f(s[2], c),
|
|
225
|
-
"Less": lambda s: f(s[1], c) < f(s[2], c),
|
|
226
|
-
"LessEqual": lambda s: f(s[1], c) <= f(s[2], c),
|
|
227
|
-
"NotEqual": lambda s: f(s[1], c) != f(s[2], c),
|
|
228
|
-
"Abs": lambda s: abs(f(s[1], c)),
|
|
229
|
-
"Round": lambda s: round(f(s[1], c), f(s[2], c))
|
|
230
|
-
if len(s) == 3
|
|
231
|
-
else int(round(f(s[1], c))),
|
|
232
|
-
"Max": Max,
|
|
233
|
-
"Min": Min,
|
|
234
|
-
"Average": Average,
|
|
235
|
-
"Median": Median,
|
|
236
|
-
"Length": Length,
|
|
237
|
-
"Any": Any,
|
|
238
|
-
"All": All,
|
|
239
|
-
"Array": Arr,
|
|
240
|
-
"In": In,
|
|
241
|
-
"Not_in": Not_in,
|
|
242
|
-
"Contains_any_of": Contains_any_of,
|
|
243
|
-
"Contains_all_of": Contains_all_of,
|
|
244
|
-
"Contains_none_of": Contains_none_of,
|
|
245
|
-
"NotIn": Not_in,
|
|
246
|
-
"ContainsAnyOf": Contains_any_of,
|
|
247
|
-
"ContainsAllOf": Contains_all_of,
|
|
248
|
-
"ContainsNoneOf": Contains_none_of,
|
|
249
|
-
"Int": Int,
|
|
250
|
-
"Float": Float,
|
|
251
|
-
"Str": Str,
|
|
252
|
-
"Not": Not,
|
|
253
|
-
"IsDefined": lambda s: s[1] in c,
|
|
254
|
-
}
|
|
255
|
-
if s[0] in constructs:
|
|
256
|
-
try:
|
|
257
|
-
return constructs[s[0]](s)
|
|
258
|
-
except Exception as e:
|
|
259
|
-
raise MathJSONException(e, s, mathjson_construct=s[0])
|
|
260
|
-
else:
|
|
261
|
-
# raise MathJSONException(
|
|
262
|
-
# NotImplementedError(f"'{s[0]}' is not supported"), s
|
|
263
|
-
# )
|
|
264
|
-
return s
|
|
265
|
-
elif s in solver_parameters:
|
|
266
|
-
return f(solver_parameters[s], c)
|
|
267
|
-
elif s in c:
|
|
268
|
-
return f(c[s], c)
|
|
269
|
-
else:
|
|
270
|
-
# raise KeyError(f"Parameter '{s}' is not defined")
|
|
271
|
-
return s
|
|
272
|
-
|
|
273
|
-
return f
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mathjson_solver-1.7.0 → mathjson_solver-1.9.0}/src/mathjson_solver.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|