klongpy 0.6.9__py3-none-any.whl → 0.7.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- klongpy/__init__.py +17 -1
- klongpy/adverbs.py +84 -82
- klongpy/autograd.py +299 -0
- klongpy/backend.py +38 -103
- klongpy/backends/__init__.py +26 -0
- klongpy/backends/base.py +469 -0
- klongpy/backends/numpy_backend.py +123 -0
- klongpy/backends/registry.py +76 -0
- klongpy/backends/torch_backend.py +1047 -0
- klongpy-0.6.9.data/scripts/kgpy → klongpy/cli.py +110 -90
- klongpy/core.py +113 -974
- klongpy/db/sys_fn_db.py +7 -6
- klongpy/db/sys_fn_kvs.py +2 -4
- klongpy/dyads.py +332 -160
- klongpy/interpreter.py +60 -15
- klongpy/monads.py +121 -75
- klongpy/parser.py +328 -0
- klongpy/repl.py +23 -5
- klongpy/sys_fn.py +170 -21
- klongpy/sys_fn_autograd.py +290 -0
- klongpy/sys_fn_ipc.py +22 -15
- klongpy/sys_fn_timer.py +13 -3
- klongpy/types.py +503 -0
- klongpy/web/sys_fn_web.py +14 -4
- klongpy/writer.py +122 -0
- klongpy/ws/sys_fn_ws.py +5 -8
- klongpy-0.7.1.dist-info/METADATA +544 -0
- klongpy-0.7.1.dist-info/RECORD +52 -0
- {klongpy-0.6.9.dist-info → klongpy-0.7.1.dist-info}/WHEEL +1 -1
- klongpy-0.7.1.dist-info/entry_points.txt +2 -0
- {klongpy-0.6.9.dist-info → klongpy-0.7.1.dist-info}/top_level.txt +0 -1
- klongpy-0.6.9.dist-info/METADATA +0 -448
- klongpy-0.6.9.dist-info/RECORD +0 -77
- tests/__init__.py +0 -6
- tests/gen_join_over.py +0 -119
- tests/gen_py_suite.py +0 -77
- tests/gen_test_fn.py +0 -259
- tests/perf_async.py +0 -25
- tests/perf_avg.py +0 -18
- tests/perf_duckdb.py +0 -32
- tests/perf_gen.py +0 -38
- tests/perf_ipc_overhead.py +0 -34
- tests/perf_join.py +0 -53
- tests/perf_load.py +0 -17
- tests/perf_prog.py +0 -18
- tests/perf_serdes.py +0 -52
- tests/perf_sys_fn_db.py +0 -263
- tests/perf_vector.py +0 -40
- tests/test_accel.py +0 -227
- tests/test_df_cache.py +0 -85
- tests/test_eval_monad_list.py +0 -34
- tests/test_examples.py +0 -64
- tests/test_extra_suite.py +0 -382
- tests/test_file_cache.py +0 -185
- tests/test_interop.py +0 -180
- tests/test_kg_asarray.py +0 -94
- tests/test_kgtests.py +0 -65
- tests/test_known_bugs.py +0 -206
- tests/test_prog.py +0 -107
- tests/test_reshape_strings.py +0 -33
- tests/test_suite.py +0 -1480
- tests/test_suite_file.py +0 -153
- tests/test_sys_fn.py +0 -420
- tests/test_sys_fn_db.py +0 -88
- tests/test_sys_fn_ipc.py +0 -587
- tests/test_sys_fn_timer.py +0 -133
- tests/test_sys_fn_web.py +0 -50
- tests/test_util.py +0 -233
- tests/utils.py +0 -126
- {klongpy-0.6.9.dist-info → klongpy-0.7.1.dist-info}/licenses/LICENSE +0 -0
klongpy/dyads.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from .core import *
|
|
2
|
-
import
|
|
2
|
+
from .autograd import grad_of_fn, numeric_grad, jacobian_of_fn, multi_jacobian_of_fn, multi_grad_of_fn
|
|
3
|
+
import numpy
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
def eval_dyad_add(a, b):
|
|
6
|
+
def eval_dyad_add(a, b, backend):
|
|
6
7
|
"""
|
|
7
8
|
|
|
8
9
|
a+b [Plus]
|
|
@@ -17,10 +18,10 @@ def eval_dyad_add(a, b):
|
|
|
17
18
|
1+0.3 --> 1.3
|
|
18
19
|
|
|
19
20
|
"""
|
|
20
|
-
return np.add(a, b)
|
|
21
|
+
return backend.np.add(a, b)
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
def eval_dyad_amend(a, b):
|
|
24
|
+
def eval_dyad_amend(a, b, backend):
|
|
24
25
|
"""
|
|
25
26
|
|
|
26
27
|
a:=b [Amend]
|
|
@@ -46,13 +47,14 @@ def eval_dyad_amend(a, b):
|
|
|
46
47
|
"abc":="def",3 --> "abcdef"
|
|
47
48
|
|
|
48
49
|
"""
|
|
49
|
-
|
|
50
|
+
np_backend = backend.np
|
|
51
|
+
if not (isinstance(a, (str,list)) or np_backend.isarray(a)):
|
|
50
52
|
raise RuntimeError(f"a must be list or str: {a}")
|
|
51
53
|
if len(b) <= 1:
|
|
52
54
|
return a
|
|
53
55
|
if isinstance(a, str):
|
|
54
|
-
r = str_to_chr_arr(a)
|
|
55
|
-
q = str_to_chr_arr(b[0])
|
|
56
|
+
r = backend.str_to_chr_arr(a)
|
|
57
|
+
q = backend.str_to_chr_arr(b[0])
|
|
56
58
|
for i in b[1:]:
|
|
57
59
|
try:
|
|
58
60
|
r[i:i+len(q)] = q
|
|
@@ -61,29 +63,29 @@ def eval_dyad_amend(a, b):
|
|
|
61
63
|
if i > len(r):
|
|
62
64
|
RangeError(i)
|
|
63
65
|
elif i == len(r):
|
|
64
|
-
r =
|
|
66
|
+
r = numpy.append(r, b[0])
|
|
65
67
|
else:
|
|
66
68
|
r[i] = b[0]
|
|
67
69
|
return "".join(["".join(x) for x in r])
|
|
68
|
-
r =
|
|
69
|
-
if is_list(b[0]): # TOOD: use
|
|
70
|
+
r = np_backend.array(a) # clone
|
|
71
|
+
if is_list(b[0]): # TOOD: use bknp.put if we can
|
|
70
72
|
r = r.tolist()
|
|
71
73
|
for i in b[1:]:
|
|
72
74
|
r[i] = b[0]
|
|
73
|
-
r = kg_asarray(r)
|
|
75
|
+
r = backend.kg_asarray(r)
|
|
74
76
|
else:
|
|
75
|
-
|
|
77
|
+
numpy.put(r, numpy.asarray(b[1:],dtype=int), b[0])
|
|
76
78
|
return r
|
|
77
79
|
|
|
78
80
|
|
|
79
81
|
def _e_dyad_amend_in_depth(p, q, v):
|
|
80
|
-
if
|
|
82
|
+
if bknp.isarray(q) and len(q) > 1:
|
|
81
83
|
r = _e_dyad_amend_in_depth(p[q[0]], q[1:] if len(q) > 2 else q[1], v)
|
|
82
|
-
p =
|
|
84
|
+
p = bknp.array(p, dtype=r.dtype)
|
|
83
85
|
p[q[0]] = r
|
|
84
86
|
return p
|
|
85
87
|
else:
|
|
86
|
-
p =
|
|
88
|
+
p = bknp.array(p, dtype=object) if isinstance(v, (str, KGSym)) else bknp.array(p)
|
|
87
89
|
p[q] = v
|
|
88
90
|
return p
|
|
89
91
|
|
|
@@ -105,7 +107,7 @@ def eval_dyad_amend_in_depth(a, b):
|
|
|
105
107
|
return _e_dyad_amend_in_depth(a, b[1:], b[0])
|
|
106
108
|
|
|
107
109
|
|
|
108
|
-
def eval_dyad_cut(a, b):
|
|
110
|
+
def eval_dyad_cut(a, b, backend):
|
|
109
111
|
"""
|
|
110
112
|
|
|
111
113
|
a:_b [Cut]
|
|
@@ -127,12 +129,12 @@ def eval_dyad_cut(a, b):
|
|
|
127
129
|
|
|
128
130
|
"""
|
|
129
131
|
j = isinstance(b, str)
|
|
130
|
-
b =
|
|
131
|
-
a = a if
|
|
132
|
-
r =
|
|
132
|
+
b = bknp.asarray(backend.str_to_chr_arr(b) if j else b)
|
|
133
|
+
a = a if bknp.isarray(a) else [a]
|
|
134
|
+
r = bknp.array_split(b, a)
|
|
133
135
|
if len(b) == 0 and len(a) > 0:
|
|
134
136
|
r = r[1:]
|
|
135
|
-
return
|
|
137
|
+
return bknp.asarray(["".join(x) for x in r]) if j else backend.kg_asarray(r)
|
|
136
138
|
|
|
137
139
|
|
|
138
140
|
def eval_dyad_at_index(klong, a, b):
|
|
@@ -166,24 +168,25 @@ def eval_dyad_at_index(klong, a, b):
|
|
|
166
168
|
|
|
167
169
|
"""
|
|
168
170
|
if isinstance(a, (KGFn, KGSym)) or issubclass(type(a), KGLambda):
|
|
169
|
-
b = [x for x in b] if
|
|
171
|
+
b = [x for x in b] if klong._backend.is_array(b) else b
|
|
170
172
|
return klong.eval(KGCall(a, b, arity=1))
|
|
173
|
+
backend = klong._backend
|
|
171
174
|
j = isinstance(a,str)
|
|
172
|
-
a = str_to_chr_arr(a) if j else a
|
|
175
|
+
a = backend.str_to_chr_arr(a) if j else a
|
|
173
176
|
if is_list(b):
|
|
174
177
|
if is_empty(b):
|
|
175
|
-
r =
|
|
178
|
+
r = bknp.asarray([])
|
|
176
179
|
else:
|
|
177
180
|
# TODO: return None for missing keys? or raise?
|
|
178
|
-
r = kg_asarray([a[x] for x in b])
|
|
179
|
-
elif is_integer(b):
|
|
181
|
+
r = backend.kg_asarray([a[x] for x in b])
|
|
182
|
+
elif backend.is_integer(b):
|
|
180
183
|
r = a[b]
|
|
181
184
|
j = False
|
|
182
185
|
else:
|
|
183
186
|
r = a
|
|
184
187
|
if j:
|
|
185
|
-
if
|
|
186
|
-
return
|
|
188
|
+
if bknp.isarray(r) and r.ndim > 1:
|
|
189
|
+
return bknp.asarray(["".join(x) for x in r], dtype=object)
|
|
187
190
|
return "".join(r)
|
|
188
191
|
return r
|
|
189
192
|
|
|
@@ -208,7 +211,7 @@ def eval_dyad_define(klong, n, v):
|
|
|
208
211
|
return v
|
|
209
212
|
|
|
210
213
|
|
|
211
|
-
def eval_dyad_divide(a, b):
|
|
214
|
+
def eval_dyad_divide(a, b, backend):
|
|
212
215
|
"""
|
|
213
216
|
|
|
214
217
|
a%b [Divide]
|
|
@@ -222,7 +225,11 @@ def eval_dyad_divide(a, b):
|
|
|
222
225
|
10%8 --> 1.25
|
|
223
226
|
|
|
224
227
|
"""
|
|
225
|
-
|
|
228
|
+
if not is_list(a) and not is_list(b) and backend.is_number(b):
|
|
229
|
+
b_val = backend.scalar_to_python(b) if backend.is_backend_array(b) or (hasattr(b, 'ndim') and b.ndim == 0) else b
|
|
230
|
+
if b_val == 0:
|
|
231
|
+
return KLONG_UNDEFINED
|
|
232
|
+
return backend.np.divide(a, b)
|
|
226
233
|
|
|
227
234
|
|
|
228
235
|
def eval_dyad_drop(a, b):
|
|
@@ -254,7 +261,12 @@ def eval_dyad_drop(a, b):
|
|
|
254
261
|
return b[a:] if a >= 0 else b[:a]
|
|
255
262
|
|
|
256
263
|
|
|
257
|
-
def
|
|
264
|
+
def _safe_equal(x, y, backend):
|
|
265
|
+
"""Compare two values, handling backend arrays correctly."""
|
|
266
|
+
return kg_truth(backend.safe_equal(x, y))
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def eval_dyad_equal(a, b, backend):
|
|
258
270
|
"""
|
|
259
271
|
|
|
260
272
|
a=b [Equal]
|
|
@@ -279,7 +291,7 @@ def eval_dyad_equal(a, b):
|
|
|
279
291
|
[1 2 3]=[1 4 3] --> [1 0 1]
|
|
280
292
|
|
|
281
293
|
"""
|
|
282
|
-
return vec_fn2(a, b, lambda x, y:
|
|
294
|
+
return backend.vec_fn2(a, b, lambda x, y: _safe_equal(x, y, backend))
|
|
283
295
|
|
|
284
296
|
|
|
285
297
|
def finditer(s, sub):
|
|
@@ -292,7 +304,7 @@ def finditer(s, sub):
|
|
|
292
304
|
i += 1
|
|
293
305
|
|
|
294
306
|
|
|
295
|
-
def eval_dyad_find(a, b):
|
|
307
|
+
def eval_dyad_find(a, b, backend):
|
|
296
308
|
"""
|
|
297
309
|
|
|
298
310
|
a?b [Find]
|
|
@@ -321,44 +333,44 @@ def eval_dyad_find(a, b):
|
|
|
321
333
|
|
|
322
334
|
"""
|
|
323
335
|
if isinstance(a,str):
|
|
324
|
-
return
|
|
336
|
+
return bknp.asarray(list(finditer(a,str(b))))
|
|
325
337
|
elif is_dict(a):
|
|
326
338
|
v = a.get(b)
|
|
327
|
-
return
|
|
339
|
+
return KLONG_UNDEFINED if v is None else v
|
|
328
340
|
if is_list(b):
|
|
329
|
-
return
|
|
330
|
-
return
|
|
341
|
+
return bknp.asarray([i for i,x in enumerate(a) if backend.kg_equal(x, b)])
|
|
342
|
+
return bknp.where(bknp.asarray(a) == b)[0]
|
|
331
343
|
|
|
332
344
|
|
|
333
|
-
def __e_dyad_form(a, b):
|
|
345
|
+
def __e_dyad_form(a, b, backend):
|
|
334
346
|
if isinstance(a,KGSym):
|
|
335
347
|
if is_empty(b):
|
|
336
|
-
return
|
|
348
|
+
return KLONG_UNDEFINED
|
|
337
349
|
return KGSym(b[1:] if isinstance(b,str) and b.startswith(":") else b)
|
|
338
|
-
if is_integer(a):
|
|
339
|
-
if is_float(b) or is_empty(b) or ('.' in b and str_is_float(b)):
|
|
340
|
-
return
|
|
350
|
+
if backend.is_integer(a):
|
|
351
|
+
if backend.is_float(b) or is_empty(b) or ('.' in b and str_is_float(b)):
|
|
352
|
+
return KLONG_UNDEFINED
|
|
341
353
|
return int(b)
|
|
342
|
-
if is_float(a):
|
|
354
|
+
if backend.is_float(a):
|
|
343
355
|
if is_empty(b):
|
|
344
|
-
return
|
|
356
|
+
return KLONG_UNDEFINED
|
|
345
357
|
return float(b)
|
|
346
358
|
if isinstance(a,KGChar):
|
|
347
359
|
b = str(b)
|
|
348
360
|
if len(b) != 1:
|
|
349
|
-
return
|
|
361
|
+
return KLONG_UNDEFINED
|
|
350
362
|
return KGChar(str(b)[0])
|
|
351
363
|
return b
|
|
352
364
|
|
|
353
|
-
def _e_dyad_form(a, b):
|
|
365
|
+
def _e_dyad_form(a, b, backend):
|
|
354
366
|
"""
|
|
355
367
|
Unravel the broadcasting of a and b and apply __e_dyad_form
|
|
356
368
|
"""
|
|
357
|
-
if
|
|
358
|
-
return
|
|
359
|
-
return __e_dyad_form(a,b)
|
|
369
|
+
if bknp.isarray(a) and bknp.isarray(b):
|
|
370
|
+
return bknp.asarray([backend.vec_fn2(x, y, lambda x, y: _e_dyad_form(x, y, backend)) for x,y in zip(a,b)])
|
|
371
|
+
return __e_dyad_form(a, b, backend)
|
|
360
372
|
|
|
361
|
-
def eval_dyad_form(a, b):
|
|
373
|
+
def eval_dyad_form(a, b, backend):
|
|
362
374
|
"""
|
|
363
375
|
|
|
364
376
|
a:$b [Form]
|
|
@@ -385,13 +397,17 @@ def eval_dyad_form(a, b):
|
|
|
385
397
|
:x:$":symbol" --> :symbol
|
|
386
398
|
|
|
387
399
|
"""
|
|
388
|
-
return vec_fn2(a, b, _e_dyad_form)
|
|
400
|
+
return backend.vec_fn2(a, b, lambda x, y: _e_dyad_form(x, y, backend))
|
|
389
401
|
|
|
390
402
|
|
|
391
|
-
def __e_dyad_format2(a, b):
|
|
403
|
+
def __e_dyad_format2(a, b, backend):
|
|
404
|
+
if hasattr(a, 'ndim') and a.ndim == 0:
|
|
405
|
+
a = a.item()
|
|
406
|
+
if hasattr(b, 'ndim') and b.ndim == 0:
|
|
407
|
+
b = b.item()
|
|
392
408
|
if safe_eq(int(a), 0):
|
|
393
409
|
return str(b)
|
|
394
|
-
if (is_float(b) and not isinstance(b,int)) and (is_float(a) and not isinstance(a,int)):
|
|
410
|
+
if (backend.is_float(b) and not isinstance(b,int)) and (backend.is_float(a) and not isinstance(a,int)):
|
|
395
411
|
b = "{:Xf}".replace("X",str(a)).format(b)
|
|
396
412
|
p = b.split('.')
|
|
397
413
|
p[0] = p[0].rjust(int(a))
|
|
@@ -401,17 +417,17 @@ def __e_dyad_format2(a, b):
|
|
|
401
417
|
r = str(b).ljust(abs(a)) if a >= 0 else str(b).rjust(abs(a))
|
|
402
418
|
return r
|
|
403
419
|
|
|
404
|
-
def _e_dyad_format2(a, b):
|
|
420
|
+
def _e_dyad_format2(a, b, backend):
|
|
405
421
|
"""
|
|
406
422
|
Unravel the broadcasting of a and b and apply __e_dyad_format2
|
|
407
423
|
"""
|
|
408
424
|
if is_list(a) and is_list(b):
|
|
409
|
-
return kg_asarray([vec_fn2(x, y, _e_dyad_format2) for x, y in zip(to_list(a), to_list(b))])
|
|
410
|
-
if np.isarray(a) and np.isarray(b):
|
|
411
|
-
return np.asarray([vec_fn2(x, y, _e_dyad_format2) for x, y in zip(a, b)])
|
|
412
|
-
return __e_dyad_format2(a, b)
|
|
425
|
+
return backend.kg_asarray([backend.vec_fn2(x, y, lambda x, y: _e_dyad_format2(x, y, backend)) for x, y in zip(to_list(a), to_list(b))])
|
|
426
|
+
if backend.np.isarray(a) and backend.np.isarray(b):
|
|
427
|
+
return backend.np.asarray([backend.vec_fn2(x, y, lambda x, y: _e_dyad_format2(x, y, backend)) for x, y in zip(a, b)])
|
|
428
|
+
return __e_dyad_format2(a, b, backend)
|
|
413
429
|
|
|
414
|
-
def eval_dyad_format2(a, b):
|
|
430
|
+
def eval_dyad_format2(a, b, backend):
|
|
415
431
|
"""
|
|
416
432
|
|
|
417
433
|
a$b [Format2]
|
|
@@ -436,7 +452,7 @@ def eval_dyad_format2(a, b):
|
|
|
436
452
|
5.3$123.45 --> " 123.450"
|
|
437
453
|
|
|
438
454
|
"""
|
|
439
|
-
return vec_fn2(a, b, _e_dyad_format2)
|
|
455
|
+
return backend.vec_fn2(a, b, lambda x, y: _e_dyad_format2(x, y, backend))
|
|
440
456
|
|
|
441
457
|
|
|
442
458
|
def eval_dyad_index_in_depth(a, b):
|
|
@@ -456,15 +472,16 @@ def eval_dyad_index_in_depth(a, b):
|
|
|
456
472
|
{y+x*x}:@[2 3] --> 7
|
|
457
473
|
|
|
458
474
|
"""
|
|
459
|
-
return
|
|
475
|
+
return bknp.asarray(a)[tuple(b) if is_list(b) else b] if not is_empty(b) else b
|
|
460
476
|
|
|
461
477
|
|
|
462
|
-
def _e_dyad_integer_divide(x,y):
|
|
463
|
-
|
|
464
|
-
a =
|
|
465
|
-
|
|
478
|
+
def _e_dyad_integer_divide(x, y, backend):
|
|
479
|
+
np_backend = backend.np
|
|
480
|
+
a = np_backend.divide(x, y)
|
|
481
|
+
a = backend.kg_asarray(backend.rec_fn(a, np_backend.trunc)) if np_backend.isarray(a) else a
|
|
482
|
+
return backend.to_int_array(a)
|
|
466
483
|
|
|
467
|
-
def eval_dyad_integer_divide(a, b):
|
|
484
|
+
def eval_dyad_integer_divide(a, b, backend):
|
|
468
485
|
"""
|
|
469
486
|
|
|
470
487
|
a:%b [Integer-Divide]
|
|
@@ -480,7 +497,11 @@ def eval_dyad_integer_divide(a, b):
|
|
|
480
497
|
10:%8 --> 1
|
|
481
498
|
|
|
482
499
|
"""
|
|
483
|
-
|
|
500
|
+
if not is_list(a) and not is_list(b) and backend.is_number(b):
|
|
501
|
+
b_val = backend.scalar_to_python(b) if backend.is_backend_array(b) or (hasattr(b, 'ndim') and b.ndim == 0) else b
|
|
502
|
+
if b_val == 0:
|
|
503
|
+
return KLONG_UNDEFINED
|
|
504
|
+
return backend.vec_fn2(a, b, lambda x, y: _e_dyad_integer_divide(x, y, backend))
|
|
484
505
|
|
|
485
506
|
|
|
486
507
|
def _arr_to_list(a):
|
|
@@ -488,7 +509,7 @@ def _arr_to_list(a):
|
|
|
488
509
|
return a if is_list(a) else [a]# if not is_list(a) else a
|
|
489
510
|
|
|
490
511
|
|
|
491
|
-
def eval_dyad_join(a, b):
|
|
512
|
+
def eval_dyad_join(a, b, backend):
|
|
492
513
|
"""
|
|
493
514
|
|
|
494
515
|
a,b [Join]
|
|
@@ -539,22 +560,32 @@ def eval_dyad_join(a, b):
|
|
|
539
560
|
b[a[0]] = a[1]
|
|
540
561
|
return b
|
|
541
562
|
|
|
542
|
-
if
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
563
|
+
if bknp.isarray(a) and bknp.isarray(b):
|
|
564
|
+
# Only use fast path for 1D+ arrays (not 0D scalars)
|
|
565
|
+
a_is_1d_plus = hasattr(a, 'ndim') and a.ndim >= 1
|
|
566
|
+
b_is_1d_plus = hasattr(b, 'ndim') and b.ndim >= 1
|
|
567
|
+
if a_is_1d_plus and b_is_1d_plus:
|
|
568
|
+
if len(a) == 0:
|
|
569
|
+
return b
|
|
570
|
+
if len(a.shape) == len(b.shape) and a.shape[-1] == b.shape[-1]:
|
|
571
|
+
return bknp.concatenate((a,b))
|
|
547
572
|
|
|
548
573
|
aa = _arr_to_list(a)
|
|
549
574
|
bb = _arr_to_list(b)
|
|
550
575
|
|
|
551
576
|
r = [*aa,*bb]
|
|
552
|
-
nr = kg_asarray(r)
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
577
|
+
nr = backend.kg_asarray(r)
|
|
578
|
+
# Check dtype kind for compatibility across backends
|
|
579
|
+
dtype_kind = backend.get_dtype_kind(nr)
|
|
580
|
+
if dtype_kind in ('i', 'f', 'u'):
|
|
581
|
+
return nr
|
|
582
|
+
# Use numpy directly for object arrays (backends without object dtype need this)
|
|
583
|
+
# Convert backend arrays to numpy first (needed for device-backed arrays)
|
|
584
|
+
r_numpy = [backend.to_numpy(x) if backend.is_array(x) else x for x in r]
|
|
585
|
+
return numpy.asarray(r_numpy, dtype=object)
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def eval_dyad_less(a, b, backend):
|
|
558
589
|
"""
|
|
559
590
|
|
|
560
591
|
a<b [Less]
|
|
@@ -575,10 +606,10 @@ def eval_dyad_less(a, b):
|
|
|
575
606
|
[1 2 3]<[1 4 3] --> [0 1 0]
|
|
576
607
|
|
|
577
608
|
"""
|
|
578
|
-
return kg_truth(vec_fn2(a, b, lambda x,y: x < y if (isinstance(x,str) and isinstance(y,str)) else np.less(x,y)))
|
|
609
|
+
return kg_truth(backend.vec_fn2(a, b, lambda x,y: x < y if (isinstance(x,str) and isinstance(y,str)) else backend.np.less(x,y)))
|
|
579
610
|
|
|
580
611
|
|
|
581
|
-
def eval_dyad_match(a,b):
|
|
612
|
+
def eval_dyad_match(a, b, backend):
|
|
582
613
|
"""
|
|
583
614
|
|
|
584
615
|
a~b [Match]
|
|
@@ -612,10 +643,10 @@ def eval_dyad_match(a,b):
|
|
|
612
643
|
[1 [2] 3]~[1 [4] 3] --> 0
|
|
613
644
|
|
|
614
645
|
"""
|
|
615
|
-
return kg_truth(kg_equal(a,b))
|
|
646
|
+
return kg_truth(backend.kg_equal(a, b))
|
|
616
647
|
|
|
617
648
|
|
|
618
|
-
def eval_dyad_maximum(a, b):
|
|
649
|
+
def eval_dyad_maximum(a, b, backend):
|
|
619
650
|
"""
|
|
620
651
|
|
|
621
652
|
a|b [Max/Or]
|
|
@@ -639,10 +670,10 @@ def eval_dyad_maximum(a, b):
|
|
|
639
670
|
1.0|1.1 --> 1.1
|
|
640
671
|
|
|
641
672
|
"""
|
|
642
|
-
return np.maximum(a, b)
|
|
673
|
+
return backend.np.maximum(a, b)
|
|
643
674
|
|
|
644
675
|
|
|
645
|
-
def eval_dyad_minimum(a, b):
|
|
676
|
+
def eval_dyad_minimum(a, b, backend):
|
|
646
677
|
"""
|
|
647
678
|
|
|
648
679
|
a&b [Min/And]
|
|
@@ -666,10 +697,10 @@ def eval_dyad_minimum(a, b):
|
|
|
666
697
|
1.0&1.1 --> 1.0
|
|
667
698
|
|
|
668
699
|
"""
|
|
669
|
-
return np.minimum(a, b)
|
|
700
|
+
return backend.np.minimum(a, b)
|
|
670
701
|
|
|
671
702
|
|
|
672
|
-
def eval_dyad_more(a, b):
|
|
703
|
+
def eval_dyad_more(a, b, backend):
|
|
673
704
|
"""
|
|
674
705
|
|
|
675
706
|
a>b [More]
|
|
@@ -688,10 +719,10 @@ def eval_dyad_more(a, b):
|
|
|
688
719
|
[1 4 3]>[1 2 3] --> [0 1 0]
|
|
689
720
|
|
|
690
721
|
"""
|
|
691
|
-
return kg_truth(vec_fn2(a, b, lambda x,y: x > y if (isinstance(x,str) and isinstance(y,str)) else np.greater(x,y)))
|
|
722
|
+
return kg_truth(backend.vec_fn2(a, b, lambda x,y: x > y if (isinstance(x,str) and isinstance(y,str)) else backend.np.greater(x,y)))
|
|
692
723
|
|
|
693
724
|
|
|
694
|
-
def eval_dyad_multiply(a, b):
|
|
725
|
+
def eval_dyad_multiply(a, b, backend):
|
|
695
726
|
"""
|
|
696
727
|
|
|
697
728
|
a*b [Times]
|
|
@@ -705,15 +736,31 @@ def eval_dyad_multiply(a, b):
|
|
|
705
736
|
0.3*7 --> 2.1
|
|
706
737
|
|
|
707
738
|
"""
|
|
708
|
-
return np.multiply(a, b)
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
def _e_dyad_power(a,b):
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
739
|
+
return backend.np.multiply(a, b)
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def _e_dyad_power(a, b, backend):
|
|
743
|
+
# Check if input requires grad - if so, preserve float for autograd
|
|
744
|
+
input_has_grad = backend.has_gradient(a)
|
|
745
|
+
# Use backend power function which handles gradient-aware power
|
|
746
|
+
r = backend.power(a, b)
|
|
747
|
+
# If input had gradients, keep result as float to preserve autograd
|
|
748
|
+
if input_has_grad:
|
|
749
|
+
return r
|
|
750
|
+
# Check if result is integer using vectorized operations
|
|
751
|
+
r_val = backend.detach_if_needed(r)
|
|
752
|
+
if is_list(r_val):
|
|
753
|
+
# Vectorized check: trunc(r) == r for all elements
|
|
754
|
+
trunc_r = numpy.trunc(r_val) if isinstance(r_val, numpy.ndarray) else r_val.trunc()
|
|
755
|
+
br = bool((trunc_r == r_val).all())
|
|
756
|
+
else:
|
|
757
|
+
val = float(r_val) if hasattr(r_val, 'item') else r_val
|
|
758
|
+
br = numpy.trunc(val) == val
|
|
759
|
+
if br:
|
|
760
|
+
return backend.to_int_array(r)
|
|
761
|
+
return r
|
|
715
762
|
|
|
716
|
-
def eval_dyad_power(a, b):
|
|
763
|
+
def eval_dyad_power(a, b, backend):
|
|
717
764
|
"""
|
|
718
765
|
|
|
719
766
|
a^b [Power]
|
|
@@ -732,10 +779,10 @@ def eval_dyad_power(a, b):
|
|
|
732
779
|
2^0.5 --> 1.41421356237309504
|
|
733
780
|
|
|
734
781
|
"""
|
|
735
|
-
return vec_fn2(a, b, _e_dyad_power)
|
|
782
|
+
return backend.vec_fn2(a, b, lambda x, y: _e_dyad_power(x, y, backend))
|
|
736
783
|
|
|
737
784
|
|
|
738
|
-
def eval_dyad_remainder(a, b):
|
|
785
|
+
def eval_dyad_remainder(a, b, backend):
|
|
739
786
|
"""
|
|
740
787
|
|
|
741
788
|
a!b [Remainder]
|
|
@@ -753,10 +800,10 @@ def eval_dyad_remainder(a, b):
|
|
|
753
800
|
-7!-5 --> -2
|
|
754
801
|
|
|
755
802
|
"""
|
|
756
|
-
return np.fmod(a, b)
|
|
803
|
+
return backend.np.fmod(a, b)
|
|
757
804
|
|
|
758
805
|
|
|
759
|
-
def eval_dyad_reshape(a, b):
|
|
806
|
+
def eval_dyad_reshape(a, b, backend):
|
|
760
807
|
"""
|
|
761
808
|
|
|
762
809
|
a:^b [Reshape]
|
|
@@ -803,49 +850,52 @@ def eval_dyad_reshape(a, b):
|
|
|
803
850
|
[2]:^[[1 2 3]] --> [[1 2 3] [1 2 3]]
|
|
804
851
|
|
|
805
852
|
"""
|
|
853
|
+
np_backend = backend.np
|
|
806
854
|
j = isinstance(b, str)
|
|
807
|
-
b = str_to_chr_arr(b) if j else b
|
|
808
|
-
if
|
|
809
|
-
if
|
|
810
|
-
y =
|
|
855
|
+
b = backend.str_to_chr_arr(b) if j else b
|
|
856
|
+
if np_backend.isarray(a):
|
|
857
|
+
if np_backend.isarray(b):
|
|
858
|
+
y = np_backend.where(a < 0)[0]
|
|
811
859
|
if len(y) > 0:
|
|
812
|
-
a =
|
|
813
|
-
a[y] = b
|
|
814
|
-
b_s = b
|
|
815
|
-
a_s =
|
|
860
|
+
a = np_backend.copy(a)
|
|
861
|
+
a[y] = backend.array_size(b) // 2
|
|
862
|
+
b_s = backend.array_size(b)
|
|
863
|
+
a_s = int(np_backend.prod(a)) # Ensure it's a Python int for comparison
|
|
864
|
+
# Convert shape to tuple of ints for backend compatibility
|
|
865
|
+
a_shape = tuple(int(x) for x in (a.tolist() if hasattr(a, 'tolist') else a))
|
|
816
866
|
if a_s > b_s:
|
|
817
|
-
b =
|
|
818
|
-
b =
|
|
819
|
-
b_s = b
|
|
820
|
-
r = b.reshape(
|
|
821
|
-
r =
|
|
867
|
+
b = np_backend.tile(b.flatten(), (a_s // b_s))
|
|
868
|
+
b = np_backend.concatenate((b, b[:a_s - backend.array_size(b)]))
|
|
869
|
+
b_s = backend.array_size(b)
|
|
870
|
+
r = b.reshape(a_shape)
|
|
871
|
+
r = np_backend.asarray(["".join(x) for x in r]) if j else r
|
|
822
872
|
j = False
|
|
823
873
|
elif a_s == b_s:
|
|
824
|
-
r = b.reshape(
|
|
874
|
+
r = b.reshape(a_shape)
|
|
825
875
|
else:
|
|
826
|
-
r =
|
|
876
|
+
r = np_backend.resize(b, a_shape)
|
|
827
877
|
else:
|
|
828
|
-
r =
|
|
878
|
+
r = np_backend.full(a, b)
|
|
829
879
|
else:
|
|
830
880
|
if a == 0:
|
|
831
881
|
r = b
|
|
832
|
-
elif
|
|
882
|
+
elif np_backend.isarray(b):
|
|
833
883
|
if a < b.shape[0]:
|
|
834
|
-
r =
|
|
884
|
+
r = np_backend.resize(b, (a,))
|
|
835
885
|
else:
|
|
836
|
-
ns =
|
|
886
|
+
ns = np_backend.ones(len(b.shape),dtype=int)
|
|
837
887
|
ns[0] = a // b.shape[0]
|
|
838
|
-
r =
|
|
888
|
+
r = np_backend.concatenate((np_backend.tile(b,ns), b[:a - b.shape[0]*ns[0]]))
|
|
839
889
|
else:
|
|
840
|
-
r =
|
|
890
|
+
r = np_backend.full((a,), b)
|
|
841
891
|
if j:
|
|
842
|
-
if
|
|
843
|
-
return
|
|
892
|
+
if np_backend.isarray(r) and r.ndim > 1:
|
|
893
|
+
return np_backend.asarray(["".join(x) for x in r], dtype=object)
|
|
844
894
|
return "".join(r)
|
|
845
895
|
return r
|
|
846
896
|
|
|
847
897
|
|
|
848
|
-
def eval_dyad_rotate(a, b):
|
|
898
|
+
def eval_dyad_rotate(a, b, backend):
|
|
849
899
|
"""
|
|
850
900
|
|
|
851
901
|
a:+b [Rotate]
|
|
@@ -860,7 +910,7 @@ def eval_dyad_rotate(a, b):
|
|
|
860
910
|
rotated will be a!#b.
|
|
861
911
|
|
|
862
912
|
Note that n:+M rotates the rows of a matrix M (i.e. it rotates
|
|
863
|
-
it vertically); to rotate its columns (horizontally), use n
|
|
913
|
+
it vertically); to rotate its columns (horizontally), use n:+:\\M
|
|
864
914
|
(Rotate-Each-Left).
|
|
865
915
|
|
|
866
916
|
Examples: 1:+[1 2 3 4 5] --> [5 1 2 3 4]
|
|
@@ -872,12 +922,12 @@ def eval_dyad_rotate(a, b):
|
|
|
872
922
|
if a == 0 or not is_iterable(b):
|
|
873
923
|
return b
|
|
874
924
|
j = isinstance(b, str)
|
|
875
|
-
b = str_to_chr_arr(b) if j else b
|
|
876
|
-
r =
|
|
925
|
+
b = backend.str_to_chr_arr(b) if j else b
|
|
926
|
+
r = bknp.roll(b, a)
|
|
877
927
|
return "".join(r) if j else r
|
|
878
928
|
|
|
879
929
|
|
|
880
|
-
def eval_dyad_split(a, b):
|
|
930
|
+
def eval_dyad_split(a, b, backend):
|
|
881
931
|
"""
|
|
882
932
|
|
|
883
933
|
a:#b [Split]
|
|
@@ -896,12 +946,12 @@ def eval_dyad_split(a, b):
|
|
|
896
946
|
|
|
897
947
|
"""
|
|
898
948
|
if len(b) == 0:
|
|
899
|
-
return
|
|
949
|
+
return bknp.asarray([])
|
|
900
950
|
|
|
901
951
|
j = isinstance(b, str)
|
|
902
|
-
b = str_to_chr_arr(b) if j else b
|
|
952
|
+
b = backend.str_to_chr_arr(b) if j else b
|
|
903
953
|
|
|
904
|
-
a = a if
|
|
954
|
+
a = a if bknp.isarray(a) else [a]
|
|
905
955
|
if len(a) == 1:
|
|
906
956
|
if a[0] >= len(b):
|
|
907
957
|
r = [b]
|
|
@@ -909,7 +959,7 @@ def eval_dyad_split(a, b):
|
|
|
909
959
|
k = len(b) // a[0]
|
|
910
960
|
if (k*a[0]) < len(b):
|
|
911
961
|
k += 1
|
|
912
|
-
r =
|
|
962
|
+
r = bknp.array_split(b, k)
|
|
913
963
|
else:
|
|
914
964
|
p, q = 0, 0
|
|
915
965
|
r = []
|
|
@@ -920,10 +970,10 @@ def eval_dyad_split(a, b):
|
|
|
920
970
|
if p >= len(a):
|
|
921
971
|
p = 0
|
|
922
972
|
|
|
923
|
-
return
|
|
973
|
+
return bknp.asarray(["".join(x) for x in r],dtype=object) if j else backend.kg_asarray(r)
|
|
924
974
|
|
|
925
975
|
|
|
926
|
-
def eval_dyad_subtract(a, b):
|
|
976
|
+
def eval_dyad_subtract(a, b, backend):
|
|
927
977
|
"""
|
|
928
978
|
|
|
929
979
|
a-b [Minus]
|
|
@@ -938,10 +988,10 @@ def eval_dyad_subtract(a, b):
|
|
|
938
988
|
1-0.3 --> 0.7
|
|
939
989
|
|
|
940
990
|
"""
|
|
941
|
-
return np.subtract(a, b)
|
|
991
|
+
return backend.np.subtract(a, b)
|
|
942
992
|
|
|
943
993
|
|
|
944
|
-
def eval_dyad_take(a, b):
|
|
994
|
+
def eval_dyad_take(a, b, backend):
|
|
945
995
|
"""
|
|
946
996
|
|
|
947
997
|
a#b [Take]
|
|
@@ -964,32 +1014,154 @@ def eval_dyad_take(a, b):
|
|
|
964
1014
|
0#"" --> ""
|
|
965
1015
|
|
|
966
1016
|
"""
|
|
1017
|
+
np_backend = backend.np
|
|
967
1018
|
j = isinstance(b,str)
|
|
968
|
-
b = str_to_chr_arr(b) if j else
|
|
969
|
-
|
|
970
|
-
if
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1019
|
+
b = backend.str_to_chr_arr(b) if j else np_backend.asarray(b)
|
|
1020
|
+
abs_a = np_backend.abs(a)
|
|
1021
|
+
aa = int(abs_a) if hasattr(abs_a, 'item') else abs_a # Convert tensor to int
|
|
1022
|
+
b_size = backend.array_size(b)
|
|
1023
|
+
if b_size == 0:
|
|
1024
|
+
# Handle empty array/string case
|
|
1025
|
+
r = b
|
|
1026
|
+
elif aa > b_size:
|
|
1027
|
+
b = np_backend.tile(b, aa // len(b))
|
|
1028
|
+
b = np_backend.concatenate((b, b[:aa-backend.array_size(b)]) if a > 0 else (b[-(aa-backend.array_size(b)):], b))
|
|
1029
|
+
r = b[a:] if a < 0 else b[:a]
|
|
1030
|
+
else:
|
|
1031
|
+
r = b[a:] if a < 0 else b[:a]
|
|
974
1032
|
return "".join(r) if j else r
|
|
975
1033
|
|
|
976
1034
|
|
|
1035
|
+
def eval_dyad_grad(klong, a, b):
|
|
1036
|
+
"""
|
|
1037
|
+
|
|
1038
|
+
a∇b [Grad]
|
|
1039
|
+
|
|
1040
|
+
Compute the numeric gradient of the monadic function ``b`` at ``a``
|
|
1041
|
+
using finite differences. Always uses numeric differentiation.
|
|
1042
|
+
|
|
1043
|
+
For automatic differentiation, use the :> operator instead.
|
|
1044
|
+
|
|
1045
|
+
"""
|
|
1046
|
+
def call_fn(v):
|
|
1047
|
+
if isinstance(b, (KGSym, KGLambda, KGFn, KGCall)):
|
|
1048
|
+
return klong.call(KGCall(b, [v], 1))
|
|
1049
|
+
return b(v)
|
|
1050
|
+
|
|
1051
|
+
if isinstance(a, KGSym):
|
|
1052
|
+
orig = klong[a]
|
|
1053
|
+
|
|
1054
|
+
def func(v):
|
|
1055
|
+
klong[a] = v
|
|
1056
|
+
try:
|
|
1057
|
+
return call_fn(v)
|
|
1058
|
+
finally:
|
|
1059
|
+
klong[a] = orig
|
|
1060
|
+
|
|
1061
|
+
return numeric_grad(func, orig, klong._backend)
|
|
1062
|
+
else:
|
|
1063
|
+
return numeric_grad(call_fn, a, klong._backend)
|
|
1064
|
+
|
|
1065
|
+
|
|
1066
|
+
def eval_dyad_jacobian(klong, a, b):
|
|
1067
|
+
"""
|
|
1068
|
+
|
|
1069
|
+
a∂b [Jacobian]
|
|
1070
|
+
|
|
1071
|
+
Compute Jacobian matrix of function ``b`` at point ``a``.
|
|
1072
|
+
For f: R^n -> R^m, returns m x n matrix where J[i,j] = df_i/dx_j.
|
|
1073
|
+
|
|
1074
|
+
Two modes based on what ``a`` contains:
|
|
1075
|
+
1. Single point: [1 2]∂f -> Jacobian at that point
|
|
1076
|
+
2. List of symbols: [w b]∂f -> [J_w J_b] (multi-param mode)
|
|
1077
|
+
|
|
1078
|
+
In multi-param mode, ``b`` should be a niladic function
|
|
1079
|
+
that references the parameter symbols.
|
|
1080
|
+
|
|
1081
|
+
Examples:
|
|
1082
|
+
[1 2]∂{[x@0^2 x@1^2]} --> [[2 0] [0 4]]
|
|
1083
|
+
[w b]∂f --> [J_w J_b] (multi-param mode)
|
|
1084
|
+
|
|
1085
|
+
"""
|
|
1086
|
+
# Check if a is a list of symbols (multi-param mode)
|
|
1087
|
+
if is_list(a) and len(a) > 0 and all(isinstance(p, KGSym) for p in a):
|
|
1088
|
+
return multi_jacobian_of_fn(klong, b, list(a))
|
|
1089
|
+
else:
|
|
1090
|
+
return jacobian_of_fn(klong, b, a) # Note: a is point, b is function
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
def eval_dyad_autograd(klong, a, b):
|
|
1094
|
+
"""
|
|
1095
|
+
|
|
1096
|
+
a:>b [Autograd]
|
|
1097
|
+
|
|
1098
|
+
Compute gradient of function ``a`` with respect to ``b``.
|
|
1099
|
+
|
|
1100
|
+
Two modes based on what ``b`` contains:
|
|
1101
|
+
1. Single param/point: a:>x or a:>[1 2 3] -> gradient at that point
|
|
1102
|
+
2. List of symbols: a:>[w b] -> [grad_w grad_b] (multi-param mode)
|
|
1103
|
+
|
|
1104
|
+
In multi-param mode, ``a`` should be a niladic function (loss)
|
|
1105
|
+
that references the parameter symbols.
|
|
1106
|
+
|
|
1107
|
+
Examples:
|
|
1108
|
+
{x^2}:>3.0 --> 6.0 (derivative of x^2 at x=3)
|
|
1109
|
+
{x^3}:>2.0 --> 12.0 (derivative of x^3 at x=2)
|
|
1110
|
+
{+/x^2}:>[1 2 3] --> [2 4 6] (gradient of sum of squares)
|
|
1111
|
+
loss:>[w b] --> [grad_w grad_b] (multi-param mode)
|
|
1112
|
+
|
|
1113
|
+
"""
|
|
1114
|
+
# Check if b is a list of symbols (multi-param mode)
|
|
1115
|
+
if is_list(b) and len(b) > 0 and all(isinstance(p, KGSym) for p in b):
|
|
1116
|
+
return multi_grad_of_fn(klong, a, list(b))
|
|
1117
|
+
else:
|
|
1118
|
+
return grad_of_fn(klong, a, b)
|
|
1119
|
+
|
|
1120
|
+
|
|
977
1121
|
def create_dyad_functions(klong):
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1122
|
+
backend = klong._backend
|
|
1123
|
+
|
|
1124
|
+
# Simple dyads that don't need backend or klong
|
|
1125
|
+
simple = {
|
|
1126
|
+
':-': eval_dyad_amend_in_depth,
|
|
1127
|
+
'_': eval_dyad_drop,
|
|
1128
|
+
':@': eval_dyad_index_in_depth,
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
# Dyads needing backend
|
|
1132
|
+
backend_dyads = {
|
|
1133
|
+
'+': lambda a, b: eval_dyad_add(a, b, backend),
|
|
1134
|
+
'|': lambda a, b: eval_dyad_maximum(a, b, backend),
|
|
1135
|
+
'&': lambda a, b: eval_dyad_minimum(a, b, backend),
|
|
1136
|
+
'!': lambda a, b: eval_dyad_remainder(a, b, backend),
|
|
1137
|
+
'%': lambda a, b: eval_dyad_divide(a, b, backend),
|
|
1138
|
+
'*': lambda a, b: eval_dyad_multiply(a, b, backend),
|
|
1139
|
+
'-': lambda a, b: eval_dyad_subtract(a, b, backend),
|
|
1140
|
+
':=': lambda a, b: eval_dyad_amend(a, b, backend),
|
|
1141
|
+
':_': lambda a, b: eval_dyad_cut(a, b, backend),
|
|
1142
|
+
'=': lambda a, b: eval_dyad_equal(a, b, backend),
|
|
1143
|
+
'?': lambda a, b: eval_dyad_find(a, b, backend),
|
|
1144
|
+
':$': lambda a, b: eval_dyad_form(a, b, backend),
|
|
1145
|
+
'$': lambda a, b: eval_dyad_format2(a, b, backend),
|
|
1146
|
+
':%': lambda a, b: eval_dyad_integer_divide(a, b, backend),
|
|
1147
|
+
',': lambda a, b: eval_dyad_join(a, b, backend),
|
|
1148
|
+
'<': lambda a, b: eval_dyad_less(a, b, backend),
|
|
1149
|
+
'~': lambda a, b: eval_dyad_match(a, b, backend),
|
|
1150
|
+
'>': lambda a, b: eval_dyad_more(a, b, backend),
|
|
1151
|
+
'^': lambda a, b: eval_dyad_power(a, b, backend),
|
|
1152
|
+
':^': lambda a, b: eval_dyad_reshape(a, b, backend),
|
|
1153
|
+
':+': lambda a, b: eval_dyad_rotate(a, b, backend),
|
|
1154
|
+
':#': lambda a, b: eval_dyad_split(a, b, backend),
|
|
1155
|
+
'#': lambda a, b: eval_dyad_take(a, b, backend),
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
# Dyads needing klong
|
|
1159
|
+
klong_dyads = {
|
|
1160
|
+
'@': lambda a, b: eval_dyad_at_index(klong, a, b),
|
|
1161
|
+
'::': lambda a, b: eval_dyad_define(klong, a, b),
|
|
1162
|
+
'∇': lambda a, b: eval_dyad_grad(klong, a, b),
|
|
1163
|
+
'∂': lambda a, b: eval_dyad_jacobian(klong, a, b),
|
|
1164
|
+
':>': lambda a, b: eval_dyad_autograd(klong, a, b),
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
return {**simple, **backend_dyads, **klong_dyads}
|