mathai 2.5.2__tar.gz → 2.5.3__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 (46) hide show
  1. {mathai-2.5.2 → mathai-2.5.3}/PKG-INFO +3 -1
  2. {mathai-2.5.2 → mathai-2.5.3}/mathai/__init__.py +1 -0
  3. {mathai-2.5.2 → mathai-2.5.3}/mathai/ml.py +1 -1
  4. mathai-2.5.3/mathai/mldirect.py +1018 -0
  5. {mathai-2.5.2 → mathai-2.5.3}/mathai.egg-info/PKG-INFO +3 -1
  6. {mathai-2.5.2 → mathai-2.5.3}/mathai.egg-info/SOURCES.txt +2 -0
  7. mathai-2.5.3/mathai.egg-info/requires.txt +1 -0
  8. {mathai-2.5.2 → mathai-2.5.3}/setup.py +4 -1
  9. {mathai-2.5.2 → mathai-2.5.3}/README.md +0 -0
  10. {mathai-2.5.2 → mathai-2.5.3}/mathai/apart.py +0 -0
  11. {mathai-2.5.2 → mathai-2.5.3}/mathai/base.py +0 -0
  12. {mathai-2.5.2 → mathai-2.5.3}/mathai/benchmark.py +0 -0
  13. {mathai-2.5.2 → mathai-2.5.3}/mathai/decision.py +0 -0
  14. {mathai-2.5.2 → mathai-2.5.3}/mathai/diff.py +0 -0
  15. {mathai-2.5.2 → mathai-2.5.3}/mathai/electronic.py +0 -0
  16. {mathai-2.5.2 → mathai-2.5.3}/mathai/expand.py +0 -0
  17. {mathai-2.5.2 → mathai-2.5.3}/mathai/factor.py +0 -0
  18. {mathai-2.5.2 → mathai-2.5.3}/mathai/formula_applier.py +0 -0
  19. {mathai-2.5.2 → mathai-2.5.3}/mathai/formula_compiler.py +0 -0
  20. {mathai-2.5.2 → mathai-2.5.3}/mathai/fraction.py +0 -0
  21. {mathai-2.5.2 → mathai-2.5.3}/mathai/graphics.py +0 -0
  22. {mathai-2.5.2 → mathai-2.5.3}/mathai/integrate.py +0 -0
  23. {mathai-2.5.2 → mathai-2.5.3}/mathai/inverse.py +0 -0
  24. {mathai-2.5.2 → mathai-2.5.3}/mathai/lambda_calculus.py +0 -0
  25. {mathai-2.5.2 → mathai-2.5.3}/mathai/limit.py +0 -0
  26. {mathai-2.5.2 → mathai-2.5.3}/mathai/linear.py +0 -0
  27. {mathai-2.5.2 → mathai-2.5.3}/mathai/logic.py +0 -0
  28. {mathai-2.5.2 → mathai-2.5.3}/mathai/matrix.py +0 -0
  29. {mathai-2.5.2 → mathai-2.5.3}/mathai/mlscalar.py +0 -0
  30. {mathai-2.5.2 → mathai-2.5.3}/mathai/ncert_data.txt +0 -0
  31. {mathai-2.5.2 → mathai-2.5.3}/mathai/ncert_data_optimized.txt +0 -0
  32. {mathai-2.5.2 → mathai-2.5.3}/mathai/ode.py +0 -0
  33. {mathai-2.5.2 → mathai-2.5.3}/mathai/parser.py +0 -0
  34. {mathai-2.5.2 → mathai-2.5.3}/mathai/pde.py +0 -0
  35. {mathai-2.5.2 → mathai-2.5.3}/mathai/printeq.py +0 -0
  36. {mathai-2.5.2 → mathai-2.5.3}/mathai/qm.py +0 -0
  37. {mathai-2.5.2 → mathai-2.5.3}/mathai/simplify.py +0 -0
  38. {mathai-2.5.2 → mathai-2.5.3}/mathai/square_root.py +0 -0
  39. {mathai-2.5.2 → mathai-2.5.3}/mathai/statistics.py +0 -0
  40. {mathai-2.5.2 → mathai-2.5.3}/mathai/test.py +0 -0
  41. {mathai-2.5.2 → mathai-2.5.3}/mathai/tool.py +0 -0
  42. {mathai-2.5.2 → mathai-2.5.3}/mathai/trig.py +0 -0
  43. {mathai-2.5.2 → mathai-2.5.3}/mathai/univariate_inequality.py +0 -0
  44. {mathai-2.5.2 → mathai-2.5.3}/mathai.egg-info/dependency_links.txt +0 -0
  45. {mathai-2.5.2 → mathai-2.5.3}/mathai.egg-info/top_level.txt +0 -0
  46. {mathai-2.5.2 → mathai-2.5.3}/setup.cfg +0 -0
@@ -1,13 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mathai
3
- Version: 2.5.2
3
+ Version: 2.5.3
4
4
  Summary: Mathematics solving Ai tailored to NCERT
5
5
  Home-page: https://github.com/infinity390/mathai
6
6
  Requires-Python: >=3.7
7
7
  Description-Content-Type: text/markdown
8
+ Requires-Dist: numpy
8
9
  Dynamic: description
9
10
  Dynamic: description-content-type
10
11
  Dynamic: home-page
12
+ Dynamic: requires-dist
11
13
  Dynamic: requires-python
12
14
  Dynamic: summary
13
15
 
@@ -28,6 +28,7 @@ from .expand import expand
28
28
 
29
29
  from .ml import NeuralNetwork, diff_matrix_matrix
30
30
  from .mlscalar import NeuralNetworkScalar
31
+ from .mldirect import NeuralNetworkDirect
31
32
 
32
33
  from .parser import parse, remove_extra_brackets
33
34
 
@@ -675,7 +675,7 @@ class NeuralNetwork:
675
675
  A_list = A_list + flatten_list(item)
676
676
  exec(s, env2)
677
677
  return env2["fx"](A_list,flatten_list(given_x))
678
- def train(self, train_x, train_y, learning_rate, epoch, batch_size=1):
678
+ def train(self, train_x, train_y, learning_rate=12, epoch=10000, batch_size=1):
679
679
  global hadamard, zeros, transpose, matadd, sigmoid, identity, power, vec, diag, flatten,\
680
680
  matmul, reshape, kronecker, commutation, broadcast, im2col, conv, col2im
681
681
  print("gradients calculated")
@@ -0,0 +1,1018 @@
1
+ from .formula_compiler import formula_list_compiler
2
+ import itertools
3
+ from .parser import parse, remove_extra_brackets
4
+ from .simplify import simplify, addition_node_mat
5
+ from .ml import diff_matrix_matrix
6
+ from .base import *
7
+ import random
8
+ import copy
9
+ import math
10
+ import numpy as np
11
+ class TreeNode2:
12
+ count = 0
13
+ count_x = 0
14
+ count_y = 0
15
+ def __new__(cls, name=None, children=None):
16
+ if name is None:
17
+ return super().__new__(cls)
18
+ if children and len(children) == 2:
19
+ c1, c2 = children[0], children[1]
20
+ if hasattr(c1, 'name') and c1.name.startswith("d_") and \
21
+ hasattr(c2, 'name') and c2.name.startswith("d_"):
22
+ try:
23
+ val1 = int(c1.name[2:])
24
+ val2 = int(c2.name[2:])
25
+ res = None
26
+ if name == "f_add":
27
+ res = val1 + val2
28
+ elif name == "f_sub":
29
+ res = val1 - val2
30
+ elif name == "f_mul":
31
+ res = val1 * val2
32
+ elif name == "f_pow":
33
+ if val2 >= 0:
34
+ res = val1 ** val2
35
+ else:
36
+ if val1 == 1:
37
+ res = 1
38
+ elif val1 == -1:
39
+ res = 1 if val2 % 2 == 0 else -1
40
+ if res is not None:
41
+ obj = super().__new__(cls)
42
+ obj.name = f"d_{res}"
43
+ obj.children = []
44
+ return obj
45
+ except ValueError:
46
+ pass
47
+ if name == "f_mul" and children and any(hasattr(child, 'name') and child.name == "d_0" for child in children):
48
+ obj = super().__new__(cls)
49
+ obj.name = "d_0"
50
+ obj.children = []
51
+ return obj
52
+ if name == "f_mul" and children and len(children) == 2:
53
+ if hasattr(children[0], 'name') and children[0].name == "d_1":
54
+ return children[1]
55
+ if hasattr(children[1], 'name') and children[1].name == "d_1":
56
+ return children[0]
57
+ if name == "f_add" and children and len(children) == 2:
58
+ if hasattr(children[0], 'name') and children[0].name == "d_0":
59
+ return children[1]
60
+ if hasattr(children[1], 'name') and children[1].name == "d_0":
61
+ return children[0]
62
+ return super().__new__(cls)
63
+ def __init__(self, name=None, children=None):
64
+ if hasattr(self, 'name'):
65
+ return
66
+ if name is None:
67
+ return
68
+ self.name = name
69
+ self.children = children
70
+ self.compare = None
71
+ def normal(self):
72
+ commutative={"f_add","f_mul"}
73
+ stack=[(self,False)]
74
+ while stack:
75
+ node,visited=stack.pop()
76
+ if visited:
77
+ if node.children:
78
+ if node.name in commutative:
79
+ node.children.sort(key=lambda c:c.compare)
80
+ node.compare=(node.name,*(c.compare for c in node.children))
81
+ else:
82
+ node.compare=(node.name,)
83
+ else:
84
+ stack.append((node,True))
85
+ stack.extend((c,False) for c in node.children)
86
+ def __repr__(self):
87
+ action_stack = [(self, False)]
88
+ value_stack = []
89
+ while action_stack:
90
+ node, children_processed = action_stack.pop()
91
+ if node.name.startswith("v_"):
92
+ var_idx = node.name.split("_")[1]
93
+ value_stack.append(f"A[{var_idx}]")
94
+ continue
95
+ if node.name.startswith("x_"):
96
+ var_idx = node.name.split("_")[1]
97
+ value_stack.append(f"X[{var_idx}]")
98
+ continue
99
+ if node.name.startswith("y_"):
100
+ var_idx = node.name.split("_")[1]
101
+ value_stack.append(f"Y[{var_idx}]")
102
+ continue
103
+ if node.name.startswith("w_"):
104
+ value_stack.append(node.name.split("_")[1])
105
+ continue
106
+ if node.name.startswith("d_"):
107
+ value_stack.append(node.name.split("_")[1])
108
+ continue
109
+ if not children_processed:
110
+ action_stack.append((node, True))
111
+ for child in reversed(node.children):
112
+ action_stack.append((child, False))
113
+ continue
114
+ num_children = len(node.children)
115
+ child_strs = []
116
+ for _ in range(num_children):
117
+ child_strs.append(value_stack.pop())
118
+ child_strs.reverse()
119
+ if node.name == "f_add":
120
+ value_stack.append(f"({'+'.join(child_strs)})")
121
+ elif node.name == "f_mul":
122
+ value_stack.append(f"({'*'.join(child_strs)})")
123
+ elif node.name == "f_pow":
124
+ value_stack.append(f"({'**'.join(child_strs)})")
125
+ elif node.name == "f_div":
126
+ value_stack.append(f"({'/'.join(child_strs)})")
127
+ elif node.name == "f_exp":
128
+ value_stack.append(f"exp({child_strs[0]})")
129
+ elif node.name == "f_neg":
130
+ value_stack.append(f"(-{child_strs[0]})")
131
+ elif node.name == "f_sigmoid":
132
+ value_stack.append(f"sigmoid({child_strs[0]})")
133
+ else:
134
+ value_stack.append(f"{node.name}({','.join(child_strs)})")
135
+ return value_stack[0]
136
+ def zeros(*shape):
137
+ if len(shape) == 0:
138
+ return TreeNode2("d_0",[])
139
+ return [zeros(*shape[1:]) for _ in range(shape[0])]
140
+ def flatten(A):
141
+ B = len(A)
142
+ H = len(A[0])
143
+ W = len(A[0][0])
144
+ C = len(A[0][0][0])
145
+ out = []
146
+ for b in range(B):
147
+ row = []
148
+ for i in range(H):
149
+ for j in range(W):
150
+ for c in range(C):
151
+ row.append(A[b][i][j][c])
152
+ out.append(row)
153
+ return out
154
+ def conv(X, K):
155
+ B = len(X)
156
+ H = len(X[0])
157
+ W = len(X[0][0])
158
+ C = len(X[0][0][0])
159
+ P = len(K[0])
160
+ OH = H - P + 1
161
+ OW = W - P + 1
162
+ out = []
163
+ for b in range(B):
164
+ batch = []
165
+ for i in range(OH):
166
+ row = []
167
+ for j in range(OW):
168
+ s = TreeNode2("d_0",[])
169
+ for u in range(P):
170
+ for v in range(P):
171
+ for c in range(C):
172
+ s = TreeNode2("f_add", [s, TreeNode2("f_mul",[X[b][i + u][j + v][c] , K[0][u][v][c]])])
173
+ row.append([s])
174
+ batch.append(row)
175
+ out.append(batch)
176
+ return out
177
+ def im2col(image, kernel):
178
+ B = len(image)
179
+ H = len(image[0])
180
+ W = len(image[0][0])
181
+ C = len(image[0][0][0])
182
+ Kh = len(kernel[0])
183
+ Kw = len(kernel[0][0])
184
+ out_h = H - Kh + 1
185
+ out_w = W - Kw + 1
186
+ P = Kh * Kw * C
187
+ cols = []
188
+ for b in range(B):
189
+ for i in range(out_h):
190
+ for j in range(out_w):
191
+ row = [TreeNode2("d_0",[]) for _ in range(B * P)]
192
+ idx = b * P
193
+ for r in range(Kh):
194
+ for c in range(Kw):
195
+ for ch in range(C):
196
+ row[idx] = image[b][i+r][j+c][ch]
197
+ idx += 1
198
+ cols.append(row)
199
+ return cols
200
+ def col2im(kernel, image):
201
+ B = len(image)
202
+ H = len(image[0])
203
+ W = len(image[0][0])
204
+ C = len(image[0][0][0])
205
+ K = len(kernel)
206
+ Kh = len(kernel[0])
207
+ Kw = len(kernel[0][0])
208
+ assert K == 1 or K == B
209
+ OH = H - Kh + 1
210
+ OW = W - Kw + 1
211
+ rows = B * OH * OW
212
+ cols = B * H * W * C
213
+ J = [[TreeNode2("d_0",[]) for _ in range(cols)] for _ in range(rows)]
214
+ row = 0
215
+ for b in range(B):
216
+ kb = 0 if K == 1 else b
217
+ for i in range(OH):
218
+ for j in range(OW):
219
+ for u in range(Kh):
220
+ for v in range(Kw):
221
+ for ch in range(C):
222
+ col = (
223
+ (((b * H + (i + u)) * W + (j + v)) * C)
224
+ + ch
225
+ )
226
+ J[row][col] = kernel[kb][u][v][ch]
227
+ row += 1
228
+ return J
229
+ def commutation(m, n):
230
+ size = m * n
231
+ K = [[TreeNode2("d_0", []) for _ in range(size)] for _ in range(size)]
232
+ for i in range(m):
233
+ for j in range(n):
234
+ src_idx = j * m + i
235
+ dest_idx = i * n + j
236
+ K[dest_idx][src_idx] = TreeNode2("d_1",[])
237
+ return K
238
+ def hadamard_h(A, B):
239
+ if isinstance(A, list) and isinstance(B, list):
240
+ assert len(A) == len(B)
241
+ return [hadamard_h(a, b) for a, b in zip(A, B)]
242
+ return TreeNode2("f_mul",[A,B])
243
+ def hadamard(*lst_prod):
244
+ result = lst_prod[0]
245
+ for x in lst_prod[1:]:
246
+ if isinstance(result, list) and isinstance(x, list):
247
+ result = hadamard_h(result, x)
248
+ elif isinstance(result, list):
249
+ result = apply(result, lambda y: TreeNode2("f_mul",[y,x]))
250
+ elif isinstance(x, list):
251
+ result = apply(x, lambda y: TreeNode2("f_mul",[result,y]))
252
+ else:
253
+ result = TreeNode2("f_mul",[result,x])
254
+ return result
255
+ def matadd_h(A, B):
256
+ if isinstance(A, list) and isinstance(B, list):
257
+ assert len(A) == len(B)
258
+ return [matadd_h(a, b) for a, b in zip(A, B)]
259
+ return TreeNode2("f_add",[A,B])
260
+ def matadd(*lst_prod):
261
+ result = lst_prod[0]
262
+ for x in lst_prod[1:]:
263
+ if isinstance(result, list) and isinstance(x, list):
264
+ result = matadd_h(result, x)
265
+ elif isinstance(result, list):
266
+ result = apply(result, lambda y: TreeNode2("f_add",[y,x]))
267
+ elif isinstance(x, list):
268
+ result = apply(x, lambda y: TreeNode2("f_add",[result,y]))
269
+ else:
270
+ result = TreeNode2("f_add",[result,x])
271
+ return result
272
+ def matmul_h(A, B):
273
+ assert len(A[0])==len(B)
274
+ rows = len(A)
275
+ inner = len(A[0])
276
+ cols = len(B[0])
277
+ C = []
278
+ for i in range(rows):
279
+ row = []
280
+ for j in range(cols):
281
+ expr = TreeNode2("d_0",[])
282
+ for k in range(inner):
283
+ left = A[i][k]
284
+ right = B[k][j]
285
+ expr = TreeNode2("f_add",[expr,TreeNode2("f_mul",[left,right])])
286
+ row.append(expr)
287
+ C.append(row)
288
+ return C
289
+ def depth(x):
290
+ d = 0
291
+ while isinstance(x, list):
292
+ d += 1
293
+ x = x[0]
294
+ return d
295
+ def matmul(*lst_prod):
296
+ result = lst_prod[0]
297
+ for index, x in enumerate(lst_prod[1:]):
298
+ if not isinstance(result, list) or not isinstance(x, list):
299
+ if isinstance(result, list):
300
+ result = apply(result, lambda y: TreeNode2("f_mul",[y,x]))
301
+ elif isinstance(x, list):
302
+ result = apply(x, lambda y: TreeNode2("f_mul",[result,y]))
303
+ else:
304
+ result = TreeNode2("f_mul",[result,x])
305
+ continue
306
+ r1 = depth(result)
307
+ r2 = depth(x)
308
+ if r1 == 2 and r2 == 2:
309
+ result = matmul_h(result, x)
310
+ elif r1 == 3 and r2 == 2:
311
+ result = [matmul_h(A, x) for A in result]
312
+ elif r1 == 2 and r2 == 3:
313
+ result = [matmul_h(result, B) for B in x]
314
+ elif r1 == 3 and r2 == 3:
315
+ if len(result) == 1:
316
+ result = [matmul_h(result[0], B) for B in x]
317
+ elif len(x) == 1:
318
+ result = [matmul_h(A, x[0]) for A in result]
319
+ else:
320
+ assert len(result) == len(x)
321
+ result = [
322
+ matmul_h(A, B)
323
+ for A, B in zip(result, x)
324
+ ]
325
+ else:
326
+ raise ValueError(
327
+ f"Unsupported matmul ranks ({r1}, {r2})"
328
+ )
329
+ return result
330
+ def kronecker_h(A, B):
331
+ m = len(A)
332
+ n = len(A[0])
333
+ p = len(B)
334
+ q = len(B[0])
335
+ result = [[TreeNode2("d_0", []) for _ in range(n * q)] for _ in range(m * p)]
336
+ for j in range(n):
337
+ for c in range(q):
338
+ col_target = (j*q)+c
339
+ for i in range(m):
340
+ for r in range(p):
341
+ row_target = (i*p)+r
342
+ result[row_target][col_target] = TreeNode2("f_mul",[A[i][j],B[r][c]])
343
+ return result
344
+ def kronecker(*lst_prod):
345
+ result = lst_prod[0]
346
+ for x in lst_prod[1:]:
347
+ result = kronecker_h(result, x)
348
+ return result
349
+ def transpose(A):
350
+ rows = len(A)
351
+ cols = len(A[0])
352
+ return [[A[i][j] for i in range(rows)] for j in range(cols)]
353
+ def apply(arr, fx):
354
+ if isinstance(arr, list):
355
+ return [apply(item, fx) for item in arr]
356
+ return fx(arr)
357
+ def sigmoid(arg):
358
+ def fx_apply(x):
359
+ return TreeNode2("f_sigmoid",[x])
360
+ return apply(arg, fx_apply)
361
+ def identity(size):
362
+ arr = zeros(size,size)
363
+ for i in range(size):
364
+ for j in range(size):
365
+ if i == j:
366
+ arr[i][j] = TreeNode2("d_1",[])
367
+ return arr
368
+ def vec(A):
369
+ out = []
370
+ def visit(x):
371
+ if isinstance(x, list):
372
+ for y in x:
373
+ visit(y)
374
+ else:
375
+ out.append([x])
376
+ visit(A)
377
+ return out
378
+ def diag(v):
379
+ if len(v) > 0 and isinstance(v[0], list):
380
+ if len(v[0]) != 1:
381
+ raise ValueError("diag expects a column vector.")
382
+ v = [x[0] for x in v]
383
+ for x in v:
384
+ if isinstance(x, list):
385
+ raise ValueError("diag received a tensor instead of a vector.")
386
+ n = len(v)
387
+ result = [[TreeNode2("d_0", []) for _ in range(n)] for _ in range(n)]
388
+ for i in range(n):
389
+ result[i][i] = v[i]
390
+ return result
391
+ def broadcast(M, rows):
392
+ if not isinstance(M, list):
393
+ raise ValueError("Cannot broadcast a scalar.")
394
+ r = len(M)
395
+ if r == rows:
396
+ return M
397
+ if r != 1:
398
+ raise ValueError(f"Cannot broadcast first dimension {r} to {rows}")
399
+ return [copy.deepcopy(M[0]) for _ in range(rows)]
400
+ def reshape(data, *shape):
401
+ def get_shape(x):
402
+ if not isinstance(x, list):
403
+ return []
404
+ return [len(x)] + get_shape(x[0])
405
+ in_shape = get_shape(data)
406
+ def get_element(x, indices):
407
+ for idx in indices:
408
+ x = x[idx]
409
+ return x
410
+ flat = []
411
+ if in_shape:
412
+ num_elements = 1
413
+ for s in in_shape:
414
+ num_elements *= s
415
+ for i in range(num_elements):
416
+ indices = [0] * len(in_shape)
417
+ rem = i
418
+ for d in reversed(range(len(in_shape))):
419
+ indices[d] = rem % in_shape[d]
420
+ rem //= in_shape[d]
421
+ flat.append(get_element(data, indices))
422
+ else:
423
+ flat = [data]
424
+ total = 1
425
+ for s in shape:
426
+ total *= s
427
+ if len(flat) != total:
428
+ raise ValueError(f"Cannot reshape: incompatible dimensions. Total size {len(flat)} vs {total}")
429
+ def make_empty(shp):
430
+ if len(shp) == 1:
431
+ return [None] * shp[0]
432
+ return [make_empty(shp[1:]) for _ in range(shp[0])]
433
+ out_structure = make_empty(shape)
434
+ def set_element(x, indices, val):
435
+ for idx in indices[:-1]:
436
+ x = x[idx]
437
+ x[indices[-1]] = val
438
+ for i in range(total):
439
+ indices = [0] * len(shape)
440
+ rem = i
441
+ for d in reversed(range(len(shape))):
442
+ indices[d] = rem % shape[d]
443
+ rem //= shape[d]
444
+ set_element(out_structure, indices, flat[i])
445
+ return out_structure
446
+ def gen2(eq, w):
447
+ def from_treenode(eq,keep_integer=False):
448
+ nonlocal w
449
+ alter = {"f_wadd":"matadd", "f_transpose":"transpose", "f_diag":"diag", "f_identity":"identity","f_wmul":"matmul",\
450
+ "f_hadamard":"hadamard","f_commutation":"commutation","f_kronecker":"kronecker",\
451
+ "f_conv":"conv", "f_im2col":"im2col","f_col2im":"col2im", "f_flatten":"flatten",\
452
+ "f_sigmoid":"sigmoid", "f_vec":"vec"\
453
+ }
454
+ if eq.name in alter.keys():
455
+ return alter[eq.name]+"("+",".join([from_treenode(child,keep_integer) for child in eq.children])+")"
456
+ if eq.name in ["f_reshape", "f_broadcast"]:
457
+ return eq.name[2:]+"("+",".join([from_treenode(eq.children[0])]+\
458
+ [from_treenode(child,True) for child in eq.children[1:]])+")"
459
+ if eq.name in ["f_zeros","f_len"]:
460
+ return eq.name[2:]+"("+",".join([from_treenode(child,True) for child in eq.children])+")"
461
+ alter2 = ["f_add", "f_pow", "f_mul"]
462
+ if eq.name in alter2:
463
+ return f"TreeNode2('{eq.name}',["+",".join([from_treenode(child,keep_integer) for child in eq.children])+"])"
464
+ if not keep_integer and eq.name in [parse("m").name, parse("n").name]:
465
+ return "TreeNode2(f'd_{m}',[])"
466
+ if eq.name == "f_index":
467
+ return from_treenode(eq.children[0],keep_integer)+"["+str(eq.children[1])+"]"
468
+ if eq in w:
469
+ return f"w[{w.index(eq)}]"
470
+ if not keep_integer and eq.name.startswith("d_"):
471
+ return f"TreeNode2('d_{eq.name[2:]}',[])"
472
+ return str(eq)
473
+ return from_treenode(eq)
474
+ def gpu_var(*shape):
475
+ if len(shape) == 0:
476
+ TreeNode2.count += 1
477
+ return TreeNode2(f"v_{TreeNode2.count-1}", [])
478
+ return [gpu_var(*shape[1:]) for _ in range(shape[0])]
479
+ def gpu_var_x(*shape):
480
+ if len(shape) == 0:
481
+ TreeNode2.count_x += 1
482
+ return TreeNode2(f"x_{TreeNode2.count_x-1}", [])
483
+ return [gpu_var_x(*shape[1:]) for _ in range(shape[0])]
484
+ def gpu_var_y(*shape):
485
+ if len(shape) == 0:
486
+ TreeNode2.count_y += 1
487
+ return TreeNode2(f"y_{TreeNode2.count_y-1}", [])
488
+ return [gpu_var_y(*shape[1:]) for _ in range(shape[0])]
489
+ def gpu_var_write(content,*shape):
490
+ def gpu_var_helper(*shape):
491
+ if len(shape) == 0:
492
+ return content.pop(0)
493
+ return [gpu_var_helper(*shape[1:]) for _ in range(shape[0])]
494
+ return gpu_var_helper(*shape)
495
+ def flatten_list(nested_list):
496
+ flat = []
497
+ def _traverse(items):
498
+ for item in items:
499
+ if isinstance(item, list):
500
+ _traverse(item)
501
+ else:
502
+ flat.append(item)
503
+ _traverse(nested_list)
504
+ return flat
505
+ def shape(nested_list):
506
+ if not isinstance(nested_list, list):
507
+ return []
508
+ return [len(nested_list)] + shape(nested_list[0])
509
+ def flatten_list2(nested_list):
510
+ flat = []
511
+ def _traverse(items):
512
+ for item in items:
513
+ if isinstance(item, list):
514
+ _traverse(item)
515
+ else:
516
+ flat.append(item)
517
+ output = []
518
+ for item in nested_list:
519
+ flat = []
520
+ _traverse(item)
521
+ output += flat
522
+ return output
523
+
524
+ def _t_const(k):
525
+ return TreeNode2(f"d_{k}", [])
526
+ def _t_add(terms):
527
+ terms = [t for t in terms if t.name != "d_0"]
528
+ if not terms:
529
+ return _t_const(0)
530
+ r = terms[0]
531
+ for t in terms[1:]:
532
+ r = TreeNode2("f_add", [r, t])
533
+ return r
534
+ def _t_mul(factors):
535
+ r = factors[0]
536
+ for f in factors[1:]:
537
+ r = TreeNode2("f_mul", [r, f])
538
+ return r
539
+ def _diff_rule(node, dch, var):
540
+ name = node.name
541
+ ch = node.children or []
542
+ if not ch:
543
+ return _t_const(1) if name == var else _t_const(0)
544
+ if name == "f_add":
545
+ return _t_add(dch)
546
+ if name == "f_mul":
547
+ terms = []
548
+ for i in range(len(ch)):
549
+ if dch[i].name == "d_0":
550
+ continue
551
+ terms.append(_t_mul([dch[i]] + ch[:i] + ch[i+1:]))
552
+ return _t_add(terms)
553
+ if name == "f_pow":
554
+ base, expo = ch
555
+ if not expo.name.startswith("d_"):
556
+ raise NotImplementedError("derivative of a pow with a non-constant exponent is not supported")
557
+ k = int(expo.name[2:])
558
+ if k == 0 or dch[0].name == "d_0":
559
+ return _t_const(0)
560
+ if k == 1:
561
+ return dch[0]
562
+ return _t_mul([_t_const(k), TreeNode2("f_pow", [base, _t_const(k-1)]), dch[0]])
563
+ if name == "f_sigmoid":
564
+ if dch[0].name == "d_0":
565
+ return _t_const(0)
566
+ one_minus = TreeNode2("f_add", [_t_const(1), TreeNode2("f_mul", [_t_const(-1), node])])
567
+ return _t_mul([node, one_minus, dch[0]])
568
+ if name == "f_exp":
569
+ if dch[0].name == "d_0":
570
+ return _t_const(0)
571
+ return _t_mul([node, dch[0]])
572
+ if name == "f_neg":
573
+ return _t_mul([_t_const(-1), dch[0]])
574
+ if name == "f_div":
575
+ a, b = ch
576
+ t1 = _t_mul([dch[0], TreeNode2("f_pow", [b, _t_const(-1)])])
577
+ t2 = _t_mul([_t_const(-1), a, dch[1], TreeNode2("f_pow", [b, _t_const(-2)])])
578
+ return _t_add([t1, t2])
579
+ raise NotImplementedError(f"no derivative rule for {name}")
580
+ def diff_node(root, var, memo):
581
+ stack = [root]
582
+ while stack:
583
+ node = stack[-1]
584
+ if id(node) in memo:
585
+ stack.pop()
586
+ continue
587
+ kids = node.children or []
588
+ pending = [c for c in kids if id(c) not in memo]
589
+ if pending:
590
+ stack.extend(pending)
591
+ continue
592
+ stack.pop()
593
+ memo[id(node)] = _diff_rule(node, [memo[id(c)] for c in kids], var)
594
+ return memo[id(root)]
595
+
596
+ _DAG_OPS = {"f_add":"add", "f_mul":"mul", "f_pow":"pow", "f_div":"div",
597
+ "f_exp":"exp", "f_neg":"neg", "f_sigmoid":"sigmoid"}
598
+ def compile_dag(roots):
599
+ index = {}
600
+ prog = []
601
+ for root in roots:
602
+ stack = [root]
603
+ while stack:
604
+ node = stack[-1]
605
+ if id(node) in index:
606
+ stack.pop()
607
+ continue
608
+ kids = node.children or []
609
+ pending = [c for c in kids if id(c) not in index]
610
+ if pending:
611
+ stack.extend(pending)
612
+ continue
613
+ stack.pop()
614
+ name = node.name
615
+ if not kids:
616
+ if name.startswith("d_"):
617
+ entry = ("const", float(name[2:]))
618
+ elif name.startswith("v_"):
619
+ entry = ("A", int(name[2:]))
620
+ elif name.startswith("x_"):
621
+ entry = ("X", int(name[2:]))
622
+ elif name.startswith("y_"):
623
+ entry = ("Y", int(name[2:]))
624
+ else:
625
+ raise ValueError(f"cannot evaluate leaf {name}")
626
+ else:
627
+ if name not in _DAG_OPS:
628
+ raise ValueError(f"cannot evaluate node {name}")
629
+ entry = (_DAG_OPS[name], tuple(index[id(c)] for c in kids))
630
+ index[id(node)] = len(prog)
631
+ prog.append(entry)
632
+ return prog, [index[id(r)] for r in roots]
633
+ def sigmoid_num(x):
634
+ return 0.5 * (1.0 + np.tanh(0.5 * x))
635
+ def run_dag(prog, outs, A, X, Y):
636
+ vals = [0.0] * len(prog)
637
+ with np.errstate(all="ignore"):
638
+ for i, (op, arg) in enumerate(prog):
639
+ if op == "const":
640
+ vals[i] = arg
641
+ elif op == "A":
642
+ vals[i] = A[arg]
643
+ elif op == "X":
644
+ vals[i] = X[arg]
645
+ elif op == "Y":
646
+ vals[i] = Y[arg]
647
+ elif op == "add":
648
+ s = 0.0
649
+ for k in arg:
650
+ s += vals[k]
651
+ vals[i] = s
652
+ elif op == "mul":
653
+ p = 1.0
654
+ for k in arg:
655
+ p *= vals[k]
656
+ vals[i] = p
657
+ elif op == "pow":
658
+ vals[i] = np.power(vals[arg[0]], vals[arg[1]])
659
+ elif op == "div":
660
+ vals[i] = np.divide(vals[arg[0]], vals[arg[1]])
661
+ elif op == "exp":
662
+ vals[i] = np.exp(vals[arg[0]])
663
+ elif op == "neg":
664
+ vals[i] = -vals[arg[0]]
665
+ elif op == "sigmoid":
666
+ vals[i] = sigmoid_num(vals[arg[0]])
667
+ return [float(vals[k]) for k in outs]
668
+
669
+ class NeuralNetworkDirect:
670
+ def __init__(self, struct):
671
+ self.struct = struct
672
+ self.var_list = [tree_form(f"v_-{i}") for i in range(1,26+1-4)]
673
+ self.o = None
674
+ self.lst_w = None
675
+ self.gradient = None
676
+ self.weight_equation = None
677
+ self.hessian_equation = None
678
+ self.critical_points = None
679
+ self.critical_point_info = None
680
+ self.learn = None
681
+ self.learn_sym = None
682
+ self.learn_var = None
683
+ self.loss = None
684
+ self.model_type = None
685
+ self.init_mat = lambda *args: gpu_var(*args)
686
+ self._eq_key = None
687
+ self._flat_grad = None
688
+ self._grad_prog = None
689
+ self._grad_outs = None
690
+ self._hess_prog = None
691
+ self._hess_outs = None
692
+ self.loss_equation = None
693
+ self._loss_prog = None
694
+ self._loss_outs = None
695
+ self.minima = None
696
+ self.num_gradients = 0
697
+ self.diff_matrix_count = 0
698
+ self.weight_equation_count = 0
699
+ TreeNode2.count = 0
700
+ TreeNode2.count_x = 0
701
+ TreeNode2.count_y = 0
702
+ def model(self, t="image"):
703
+ self.model_type = t
704
+ if t == "image":
705
+ return self.model_image()
706
+ def model_image(self):
707
+ lst_z = []
708
+ lst_w = []
709
+ x = parse("X")
710
+ y = parse("Y")
711
+ lst_z.append(x)
712
+ flattening_needed = False
713
+ for i in range(1,len(self.struct)):
714
+ if self.struct[i]["type"] == "convolution":
715
+ flattening_needed = True
716
+ lst_w.append(self.var_list.pop(0))
717
+ eq = TreeNode("f_broadcast", [lst_w[-1], parse("m")])
718
+ tmp = TreeNode("f_conv", [lst_z[-1], eq])
719
+ tmp = replace(self.struct[i]["activation"], parse("Z"), tmp)
720
+ lst_z[-1] = tmp
721
+ elif self.struct[i]["type"] == "dense":
722
+ lst_w.append(self.var_list.pop(0))
723
+ lst_w.append(self.var_list.pop(0))
724
+ if flattening_needed:
725
+ flattening_needed = False
726
+ lst_z[-1] = lst_z[-1].fx("flatten")
727
+ eq = TreeNode("f_wmul", [lst_z[-1], lst_w[-2]])
728
+ eq = TreeNode("f_wadd", [eq, TreeNode("f_broadcast", [lst_w[-1], parse("m")])])
729
+ lst_z.append(eq)
730
+ tmp = replace(self.struct[i]["activation"], parse("Z"), lst_z[-1])
731
+ lst_z[-1] = tmp
732
+ self.lst_w = lst_w
733
+ self.o = lst_z[-1]
734
+ eq = TreeNode("f_hadamard", [tree_form("d_-1"), y])
735
+ eq = TreeNode("f_wadd", [self.o, eq])
736
+ eq = eq.fx("vec")
737
+ eq = TreeNode("f_wmul", [eq.fx("transpose"), eq])
738
+ L = TreeNode("f_hadamard", [eq, (parse("m") * tree_form("d_2")) ** tree_form("d_-1")])
739
+ self.loss = L
740
+ gradient = []
741
+ index = 0
742
+ self.learn = []
743
+ for i in range(1,len(self.struct)):
744
+ if self.struct[i]["type"] == "dense":
745
+ prev = None
746
+ if self.struct[i-1]["type"] == "convolution":
747
+ a,b,c = self.struct[0]["dim"]
748
+ for j in range(1,i):
749
+ d,e,_ = self.struct[j]["dim"]
750
+ a,b = a-d + 1, b-e + 1
751
+ prev = a*b
752
+ else:
753
+ prev = self.struct[i-1]["dim"][0]
754
+ curr = self.struct[i]["dim"][0]
755
+ self.learn.append(self.init_mat(prev, curr))
756
+ self.learn.append(self.init_mat(1, curr))
757
+ elif self.struct[i]["type"] == "convolution":
758
+ self.learn.append(self.init_mat(*([1]+self.struct[i]["dim"])))
759
+ self.learn_sym = self.learn
760
+ self.num_gradients = len(lst_w)
761
+ self.diff_matrix_count = 0
762
+ print(f"gradients to calculate: {self.num_gradients}")
763
+ for j in range(len(lst_w)):
764
+ item = lst_w[j]
765
+ tmp = TreeNode("f_pdif", [L, item.fx("vec")])
766
+ tmp = diff_matrix_matrix(tmp)
767
+ self.diff_matrix_count += 1
768
+ print(f"diff_matrix_matrix {self.diff_matrix_count}/{self.num_gradients} done")
769
+ d = []
770
+ k = self.learn[j]
771
+ while isinstance(k, list):
772
+ d.append(len(k))
773
+ k = k[0]
774
+ tmp = TreeNode("f_reshape", [tmp]+[tree_form(f"d_{item}") for item in d])
775
+ gradient.append(tmp)
776
+ self.gradient = gradient
777
+ return self
778
+ def predict(self, given_x):
779
+ global hadamard, zeros, transpose, matadd, sigmoid, identity, vec, diag, flatten,\
780
+ matmul, reshape, kronecker, commutation, broadcast, im2col, conv, col2im, extract_values, shape
781
+ TreeNode2.count = 0
782
+ TreeNode2.count_x = 0
783
+ TreeNode2.count_y = 0
784
+ learn2 = []
785
+ for item in self.learn:
786
+ learn2.append(gpu_var(*shape(item)))
787
+ given_x = [given_x]
788
+ given2 = gpu_var_x(*shape(given_x))
789
+ env = {
790
+ "w": learn2,
791
+ "m": 1,
792
+ "identity":identity,
793
+ "X": given2,
794
+ "zeros":zeros,
795
+ "hadamard": hadamard,
796
+ "sigmoid":sigmoid,
797
+ "matadd": matadd,
798
+ "diag":diag,
799
+ "vec":vec,
800
+ "matmul":matmul,
801
+ "reshape":reshape,
802
+ "kronecker":kronecker,
803
+ "commutation":commutation,
804
+ "transpose":transpose,
805
+ "broadcast":broadcast,
806
+ "conv":conv,
807
+ "im2col":im2col,
808
+ "col2im": col2im,
809
+ "flatten":flatten
810
+ }
811
+ if self.model_type == "image":
812
+ tmp = gen2(self.o, self.lst_w)
813
+ eq = eval(tmp, {}, env)[0]
814
+ s = "fx = lambda A,X: "+str(eq)
815
+ env2 = {
816
+ "exp": np.exp,
817
+ "sigmoid": sigmoid_num
818
+ }
819
+ A_list = []
820
+ for item in self.learn:
821
+ A_list = A_list + flatten_list(item)
822
+ exec(s, env2)
823
+ return env2["fx"](A_list,flatten_list(given_x))
824
+ def build_equations(self, train_x, train_y, batch_size):
825
+ key = (batch_size, tuple(shape(train_x[:batch_size])), tuple(shape(train_y[:batch_size])))
826
+ if self._eq_key == key and self.weight_equation is not None:
827
+ return
828
+ TreeNode2.count_x = 0
829
+ TreeNode2.count_y = 0
830
+ data_x = gpu_var_x(*shape(train_x[:batch_size]))
831
+ data_y = gpu_var_y(*shape(train_y[:batch_size]))
832
+ env = {
833
+ "identity":identity,
834
+ "sigmoid":sigmoid,
835
+ "matadd": matadd,
836
+ "zeros":zeros,
837
+ "hadamard": hadamard,
838
+ "diag":diag,
839
+ "vec":vec,
840
+ "matmul":matmul,
841
+ "reshape":reshape,
842
+ "kronecker":kronecker,
843
+ "commutation":commutation,
844
+ "transpose":transpose,
845
+ "broadcast":broadcast,
846
+ "conv":conv,
847
+ "im2col":im2col,
848
+ "col2im":col2im,
849
+ "flatten":flatten,
850
+ "TreeNode2":TreeNode2,
851
+ "m":batch_size,
852
+ "w":self.learn_sym,
853
+ "X":data_x,
854
+ "Y":data_y
855
+ }
856
+ self.weight_equation_count = 0
857
+ print(f"gradients: {self.num_gradients}, diff_matrix_matrix done: {self.diff_matrix_count}/{self.num_gradients}")
858
+ weight_equation = []
859
+ for g in self.gradient:
860
+ weight_equation.append(eval(gen2(g, self.lst_w), {}, env))
861
+ self.weight_equation_count += 1
862
+ print(f"weight equation {self.weight_equation_count}/{self.num_gradients} done")
863
+ self.weight_equation = weight_equation
864
+ self._flat_grad = flatten_list(self.weight_equation)
865
+ n_weights = len(flatten_list(self.learn_sym))
866
+ assert len(self._flat_grad) == n_weights, "gradient size does not match number of weights"
867
+ self._grad_prog, self._grad_outs = compile_dag(self._flat_grad)
868
+ loss_flat = flatten_list(eval(gen2(self.loss, self.lst_w), {}, env))
869
+ assert len(loss_flat) == 1, "loss must be a scalar"
870
+ self.loss_equation = loss_flat[0]
871
+ self._loss_prog, self._loss_outs = compile_dag([self.loss_equation])
872
+ self.hessian_equation = None
873
+ self._hess_prog = None
874
+ self._hess_outs = None
875
+ self.critical_points = None
876
+ self.critical_point_info = None
877
+ self._eq_key = key
878
+ def _eval_gradient(self, A, X, Y):
879
+ return run_dag(self._grad_prog, self._grad_outs, A, X, Y)
880
+ def loss_at(self, A, X, Y):
881
+ return run_dag(self._loss_prog, self._loss_outs, A, X, Y)[0]
882
+ def _unflatten(self, flat):
883
+ it = iter(flat)
884
+ def build(shp):
885
+ if not shp:
886
+ return next(it)
887
+ return [build(shp[1:]) for _ in range(shp[0])]
888
+ return [build(shape(item)) for item in self.learn_sym]
889
+ def build_hessian(self):
890
+ if self._flat_grad is None:
891
+ raise RuntimeError("call build_equations / find_critical_points / train first")
892
+ flat = self._flat_grad
893
+ N = len(flat)
894
+ H = [[None] * N for _ in range(N)]
895
+ step = max(1, N // 10)
896
+ print(f"building hessian: {N} x {N}")
897
+ for j in range(N):
898
+ if j % step == 0:
899
+ print(f"hessian column {j}/{N}")
900
+ memo = {}
901
+ var = f"v_{j}"
902
+ for i in range(j + 1):
903
+ H[i][j] = diff_node(flat[i], var, memo)
904
+ H[j][i] = H[i][j]
905
+ self.hessian_equation = H
906
+ roots = [H[i][j] for j in range(N) for i in range(j + 1)]
907
+ self._hess_prog, self._hess_outs = compile_dag(roots)
908
+ return H
909
+ def hessian_at(self, A, X, Y):
910
+ if self._hess_prog is None:
911
+ raise RuntimeError("call build_hessian() first")
912
+ vals = run_dag(self._hess_prog, self._hess_outs, A, X, Y)
913
+ N = len(self._flat_grad)
914
+ Hn = np.empty((N, N))
915
+ k = 0
916
+ for j in range(N):
917
+ for i in range(j + 1):
918
+ Hn[i, j] = Hn[j, i] = vals[k]
919
+ k += 1
920
+ return Hn
921
+ def _newton(self, x, X, Y, grad_tol, max_iter):
922
+ g = np.array(self._eval_gradient(x.tolist(), X, Y))
923
+ for it in range(max_iter):
924
+ if not np.all(np.isfinite(g)):
925
+ return x, it, False
926
+ gnorm = np.linalg.norm(g, np.inf)
927
+ if gnorm < grad_tol:
928
+ return x, it, True
929
+ Hn = self.hessian_at(x.tolist(), X, Y)
930
+ if not np.all(np.isfinite(Hn)):
931
+ return x, it, False
932
+ try:
933
+ dx = np.linalg.solve(Hn, -g)
934
+ except np.linalg.LinAlgError:
935
+ dx = np.linalg.lstsq(Hn, -g, rcond=None)[0]
936
+ t = 1.0
937
+ while True:
938
+ x_new = x + t * dx
939
+ g_new = np.array(self._eval_gradient(x_new.tolist(), X, Y))
940
+ if np.all(np.isfinite(g_new)) and np.linalg.norm(g_new, np.inf) < gnorm:
941
+ break
942
+ t *= 0.5
943
+ if t < 1e-10:
944
+ return x, it, False
945
+ x, g = x_new, g_new
946
+ return x, max_iter, bool(np.linalg.norm(g, np.inf) < grad_tol)
947
+ def find_critical_points(self, train_x, train_y, n_starts=50, init_scale=1.0, seed=None,
948
+ grad_tol=1e-8, dedup_tol=1e-5, max_iter=100):
949
+ batch_size = len(train_x)
950
+ self.build_equations(train_x, train_y, batch_size)
951
+ if self._hess_prog is None:
952
+ self.build_hessian()
953
+ X = flatten_list(train_x)
954
+ Y = flatten_list(train_y)
955
+ N = len(self._flat_grad)
956
+ rng = np.random.default_rng(seed)
957
+ found = []
958
+ for s in range(n_starts):
959
+ x0 = rng.normal(0.0, init_scale, N)
960
+ x, iters, ok = self._newton(x0, X, Y, grad_tol, max_iter)
961
+ if s % n_starts//10 == 0:
962
+ print(f"newton start {s+1}/{n_starts}: {'converged' if ok else 'failed'} after {iters} iterations")
963
+ if not ok:
964
+ continue
965
+ if any(np.linalg.norm(x - p) <= dedup_tol * (1.0 + np.linalg.norm(p)) for p in found):
966
+ continue
967
+ found.append(x)
968
+ self.critical_points = found
969
+ return found
970
+ def classify_critical_points(self, train_x, train_y, points=None, eig_tol=1e-6):
971
+ batch_size = len(train_x)
972
+ self.build_equations(train_x, train_y, batch_size)
973
+ if self._hess_prog is None:
974
+ self.build_hessian()
975
+ if points is None:
976
+ points = self.critical_points
977
+ X = flatten_list(train_x)
978
+ Y = flatten_list(train_y)
979
+ results = []
980
+ for p in points:
981
+ Hn = self.hessian_at(np.asarray(p).tolist(), X, Y)
982
+ eig = np.linalg.eigvalsh(Hn)
983
+ thr = eig_tol * max(1.0, float(np.max(np.abs(eig))))
984
+ if eig.min() > thr:
985
+ kind = "local minimum"
986
+ elif eig.max() < -thr:
987
+ kind = "local maximum"
988
+ elif eig.min() < -thr and eig.max() > thr:
989
+ kind = "saddle point"
990
+ else:
991
+ kind = "inconclusive (singular Hessian)"
992
+ grad_norm = float(np.linalg.norm(self._eval_gradient(np.asarray(p).tolist(), X, Y), np.inf))
993
+ results.append({"point": np.asarray(p), "type": kind, "eigenvalues": eig,
994
+ "grad_norm": grad_norm,
995
+ "loss": self.loss_at(np.asarray(p).tolist(), X, Y)})
996
+ self.critical_point_info = results
997
+ return results
998
+ def analyze_critical_points(self, train_x, train_y, **find_kwargs):
999
+ points = self.find_critical_points(train_x, train_y, **find_kwargs)
1000
+ return self.classify_critical_points(train_x, train_y, points)
1001
+ def train(self, train_x, train_y, check_hessian=False, **find_kwargs):
1002
+ points = self.find_critical_points(train_x, train_y, **find_kwargs)
1003
+ if not points:
1004
+ raise RuntimeError("no critical point found; try a larger n_starts or a different init_scale")
1005
+ if check_hessian:
1006
+ results = [r for r in self.classify_critical_points(train_x, train_y, points)
1007
+ if r["type"] == "local minimum"]
1008
+ else:
1009
+ X = flatten_list(train_x)
1010
+ Y = flatten_list(train_y)
1011
+ results = [{"point": p, "loss": self.loss_at(p.tolist(), X, Y)} for p in points]
1012
+ results.sort(key=lambda r: r["loss"])
1013
+ print(f"{len(points)} critical points found, {len(results)} kept")
1014
+ if not results:
1015
+ raise RuntimeError("no local minimum passed the Hessian test")
1016
+ self.minima = results
1017
+ self.learn = self._unflatten(results[0]["point"].tolist())
1018
+ return results
@@ -1,13 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mathai
3
- Version: 2.5.2
3
+ Version: 2.5.3
4
4
  Summary: Mathematics solving Ai tailored to NCERT
5
5
  Home-page: https://github.com/infinity390/mathai
6
6
  Requires-Python: >=3.7
7
7
  Description-Content-Type: text/markdown
8
+ Requires-Dist: numpy
8
9
  Dynamic: description
9
10
  Dynamic: description-content-type
10
11
  Dynamic: home-page
12
+ Dynamic: requires-dist
11
13
  Dynamic: requires-python
12
14
  Dynamic: summary
13
15
 
@@ -22,6 +22,7 @@ mathai/linear.py
22
22
  mathai/logic.py
23
23
  mathai/matrix.py
24
24
  mathai/ml.py
25
+ mathai/mldirect.py
25
26
  mathai/mlscalar.py
26
27
  mathai/ncert_data.txt
27
28
  mathai/ncert_data_optimized.txt
@@ -40,4 +41,5 @@ mathai/univariate_inequality.py
40
41
  mathai.egg-info/PKG-INFO
41
42
  mathai.egg-info/SOURCES.txt
42
43
  mathai.egg-info/dependency_links.txt
44
+ mathai.egg-info/requires.txt
43
45
  mathai.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ numpy
@@ -3,12 +3,15 @@ from setuptools import setup, find_packages
3
3
 
4
4
  setup(
5
5
  name="mathai",
6
- version="2.5.2",
6
+ version="2.5.3",
7
7
  description="Mathematics solving Ai tailored to NCERT",
8
8
  long_description=Path("README.md").read_text(encoding="utf-8"),
9
9
  long_description_content_type="text/markdown",
10
10
  url="https://github.com/infinity390/mathai",
11
11
  packages=find_packages(),
12
+ install_requires=[
13
+ "numpy",
14
+ ],
12
15
  include_package_data=True,
13
16
  package_data={
14
17
  "mathai": ["ncert_data.txt", "ncert_data_optimized.txt"],
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes