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/interpreter.py
CHANGED
|
@@ -2,10 +2,12 @@ import time
|
|
|
2
2
|
from collections import deque
|
|
3
3
|
|
|
4
4
|
from .adverbs import get_adverb_fn
|
|
5
|
+
from .backends import get_backend
|
|
5
6
|
from .core import *
|
|
6
7
|
from .dyads import create_dyad_functions
|
|
7
8
|
from .monads import create_monad_functions
|
|
8
9
|
from .sys_fn import create_system_functions
|
|
10
|
+
from .sys_fn_autograd import create_system_functions_autograd
|
|
9
11
|
from .sys_fn_ipc import create_system_functions_ipc, create_system_var_ipc
|
|
10
12
|
from .sys_fn_timer import create_system_functions_timer
|
|
11
13
|
from .sys_var import *
|
|
@@ -52,9 +54,10 @@ class KlongContext():
|
|
|
52
54
|
|
|
53
55
|
"""
|
|
54
56
|
|
|
55
|
-
def __init__(self, system_contexts):
|
|
57
|
+
def __init__(self, system_contexts, strict_mode=1):
|
|
56
58
|
self._context = deque([{}, *system_contexts])
|
|
57
59
|
self._min_ctx_count = len(system_contexts)
|
|
60
|
+
self._strict_mode = strict_mode
|
|
58
61
|
|
|
59
62
|
def start_module(self, name):
|
|
60
63
|
self.push(KGModule(name))
|
|
@@ -68,11 +71,28 @@ class KlongContext():
|
|
|
68
71
|
|
|
69
72
|
def __setitem__(self, k, v):
|
|
70
73
|
assert isinstance(k, KGSym)
|
|
74
|
+
|
|
71
75
|
if k not in reserved_fn_symbols:
|
|
76
|
+
# Check if variable exists in any scope
|
|
72
77
|
for d in self._context:
|
|
73
78
|
if in_map(k, d):
|
|
74
79
|
d[k] = v
|
|
75
80
|
return k
|
|
81
|
+
|
|
82
|
+
# Variable doesn't exist - check strict mode
|
|
83
|
+
if self._strict_mode >= 1:
|
|
84
|
+
# Check if we're inside a function (more than just global scope)
|
|
85
|
+
in_function = len(self._context) > self._min_ctx_count + 1
|
|
86
|
+
|
|
87
|
+
if in_function:
|
|
88
|
+
# Inside function - disallow creating new variables
|
|
89
|
+
raise KlongException(
|
|
90
|
+
f"undefined variable: {k}\n"
|
|
91
|
+
f" To create a local variable, declare it in the parameter list: {{[{k}]; ...}}\n"
|
|
92
|
+
f" To modify an existing global, ensure it exists before calling the function"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Create new variable in current scope
|
|
76
96
|
set_context_var(self._context[0], k, v)
|
|
77
97
|
return k
|
|
78
98
|
|
|
@@ -141,6 +161,7 @@ def create_system_contexts():
|
|
|
141
161
|
|
|
142
162
|
sys_d = {}
|
|
143
163
|
add_context_key_values(sys_d, create_system_functions())
|
|
164
|
+
add_context_key_values(sys_d, create_system_functions_autograd())
|
|
144
165
|
add_context_key_values(sys_d, create_system_functions_ipc())
|
|
145
166
|
add_context_key_values(sys_d, create_system_functions_timer())
|
|
146
167
|
set_context_var(sys_d, KGSym('.e'), eval_sys_var_epsilon()) # TODO: support lambda
|
|
@@ -210,13 +231,36 @@ def chain_adverbs(klong, arr):
|
|
|
210
231
|
|
|
211
232
|
class KlongInterpreter():
|
|
212
233
|
|
|
213
|
-
def __init__(self):
|
|
214
|
-
|
|
234
|
+
def __init__(self, backend=None, device=None):
|
|
235
|
+
"""
|
|
236
|
+
Initialize a Klong interpreter.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
backend : str, optional
|
|
241
|
+
Backend name ('numpy' or 'torch'). Defaults to 'numpy'.
|
|
242
|
+
device : str, optional
|
|
243
|
+
Device for torch backend ('cpu', 'cuda', 'mps'). Only applies
|
|
244
|
+
when backend='torch'. If None, auto-selects best available device.
|
|
245
|
+
"""
|
|
246
|
+
self._backend = get_backend(backend, device=device)
|
|
247
|
+
strict_mode = 0 # 0=unsafe (default for backward compat), 1=strict, 2=pedantic
|
|
248
|
+
self._context = KlongContext(create_system_contexts(), strict_mode=strict_mode)
|
|
215
249
|
self._vd = create_dyad_functions(self)
|
|
216
250
|
self._vm = create_monad_functions(self)
|
|
217
251
|
self._start_time = time.time()
|
|
218
252
|
self._module = None
|
|
219
253
|
|
|
254
|
+
@property
|
|
255
|
+
def backend(self):
|
|
256
|
+
"""Return the backend provider for this interpreter."""
|
|
257
|
+
return self._backend
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def np(self):
|
|
261
|
+
"""Return the numpy-compatible array module for this interpreter."""
|
|
262
|
+
return self._backend.np
|
|
263
|
+
|
|
220
264
|
def __setitem__(self, k, v):
|
|
221
265
|
k = k if isinstance(k, KGSym) else KGSym(k)
|
|
222
266
|
self._context[k] = v
|
|
@@ -224,7 +268,8 @@ class KlongInterpreter():
|
|
|
224
268
|
def __getitem__(self, k):
|
|
225
269
|
k = k if isinstance(k, KGSym) else KGSym(k)
|
|
226
270
|
r = self._context[k]
|
|
227
|
-
|
|
271
|
+
# Pass the symbol name to avoid O(n) context search
|
|
272
|
+
return KGFnWrapper(self, r, sym=k) if issubclass(type(r), KGFn) else r
|
|
228
273
|
|
|
229
274
|
def __delitem__(self, k):
|
|
230
275
|
k = k if isinstance(k, KGSym) else KGSym(k)
|
|
@@ -304,7 +349,7 @@ class KlongInterpreter():
|
|
|
304
349
|
return i+1,arr
|
|
305
350
|
k = i
|
|
306
351
|
while True:
|
|
307
|
-
ii,c = kg_read(t,i,ignore_newline=True,module=self.current_module())
|
|
352
|
+
ii,c = kg_read(t, i, ignore_newline=True, module=self.current_module())
|
|
308
353
|
if safe_eq(c, ';'):
|
|
309
354
|
i = ii
|
|
310
355
|
if k == i - 1:
|
|
@@ -352,7 +397,7 @@ class KlongInterpreter():
|
|
|
352
397
|
| V P
|
|
353
398
|
|
|
354
399
|
"""
|
|
355
|
-
i,a =
|
|
400
|
+
i,a = kg_read_array(t, i, self._backend, ignore_newline=ignore_newline, module=self.current_module())
|
|
356
401
|
if a is None:
|
|
357
402
|
return i,a
|
|
358
403
|
if safe_eq(a, '{'): # read fn
|
|
@@ -569,14 +614,14 @@ class KlongInterpreter():
|
|
|
569
614
|
ctx = {} if f_args is None else {reserved_fn_symbol_map[p]: self.call(q) for p,q in zip(reserved_fn_args,f_args)}
|
|
570
615
|
|
|
571
616
|
if is_list(f) and len(f) > 1 and is_list(f[0]) and len(f[0]) > 0:
|
|
572
|
-
|
|
573
|
-
for q in f[0]
|
|
574
|
-
|
|
575
|
-
have_locals = False
|
|
576
|
-
break
|
|
617
|
+
# Filter out semicolons and check if all remaining elements are symbols
|
|
618
|
+
params = [q for q in f[0] if isinstance(q, KGSym)]
|
|
619
|
+
have_locals = len(params) > 0 and all(isinstance(q, KGSym) for q in params)
|
|
577
620
|
if have_locals:
|
|
578
|
-
for q in
|
|
579
|
-
|
|
621
|
+
for q in params:
|
|
622
|
+
# Don't overwrite function parameters (x, y, z)
|
|
623
|
+
if q not in ctx:
|
|
624
|
+
ctx[q] = q
|
|
580
625
|
f = f[1:]
|
|
581
626
|
|
|
582
627
|
ctx[reserved_dot_f_symbol] = f
|
|
@@ -618,7 +663,7 @@ class KlongInterpreter():
|
|
|
618
663
|
f = self._get_op_fn(x.a.a, x.a.arity)
|
|
619
664
|
fa = (x.args if isinstance(x.args, list) else [x.args]) if x.args is not None else x.args
|
|
620
665
|
_y = self.eval(fa[1]) if x.a.arity == 2 else None
|
|
621
|
-
_x = fa[0] if x.a.a
|
|
666
|
+
_x = fa[0] if x.a.a in ['::','∇'] else self.eval(fa[0])
|
|
622
667
|
return f(_x) if x.a.arity == 1 else f(_x, _y)
|
|
623
668
|
elif x.is_adverb_chain():
|
|
624
669
|
return chain_adverbs(self, x.a)()
|
|
@@ -626,7 +671,7 @@ class KlongInterpreter():
|
|
|
626
671
|
return self._eval_fn(x)
|
|
627
672
|
elif isinstance(x, KGCond):
|
|
628
673
|
q = self.call(x[0])
|
|
629
|
-
p = not ((is_number(q) and q == 0) or is_empty(q))
|
|
674
|
+
p = not ((self._backend.is_number(q) and q == 0) or is_empty(q))
|
|
630
675
|
return self.call(x[1]) if p else self.call(x[2])
|
|
631
676
|
elif isinstance(x,list) and len(x) > 0:
|
|
632
677
|
return [self.call(y) for y in x][-1]
|
klongpy/monads.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from .core import *
|
|
2
|
-
import
|
|
2
|
+
from .autograd import grad_of_fn
|
|
3
|
+
|
|
3
4
|
|
|
4
5
|
def eval_monad_atom(a):
|
|
5
6
|
"""
|
|
@@ -18,7 +19,7 @@ def eval_monad_atom(a):
|
|
|
18
19
|
return kg_truth(is_atom(a))
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
def eval_monad_char(a):
|
|
22
|
+
def eval_monad_char(a, backend):
|
|
22
23
|
"""
|
|
23
24
|
|
|
24
25
|
:#a [Char]
|
|
@@ -31,10 +32,10 @@ def eval_monad_char(a):
|
|
|
31
32
|
:#10 --> :"newline character"
|
|
32
33
|
|
|
33
34
|
"""
|
|
34
|
-
return rec_fn(a, lambda x: KGChar(chr(x))) if is_list(a) else KGChar(chr(a))
|
|
35
|
+
return backend.rec_fn(a, lambda x: KGChar(chr(x))) if is_list(a) else KGChar(chr(a))
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
def eval_monad_enumerate(a):
|
|
38
|
+
def eval_monad_enumerate(a, backend):
|
|
38
39
|
"""
|
|
39
40
|
|
|
40
41
|
!a [Enumerate]
|
|
@@ -46,9 +47,9 @@ def eval_monad_enumerate(a):
|
|
|
46
47
|
!10 --> [0 1 2 3 4 5 6 7 8 9]
|
|
47
48
|
|
|
48
49
|
"""
|
|
49
|
-
if not is_integer(a):
|
|
50
|
+
if not backend.is_integer(a):
|
|
50
51
|
raise RuntimeError(f"enumerate: invalid type error: {a}")
|
|
51
|
-
return
|
|
52
|
+
return bknp.arange(int(a))
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
def eval_monad_expand_where(a):
|
|
@@ -75,7 +76,7 @@ def eval_monad_expand_where(a):
|
|
|
75
76
|
|
|
76
77
|
"""
|
|
77
78
|
arr = a if is_list(a) else [a]
|
|
78
|
-
return
|
|
79
|
+
return bknp.repeat(bknp.arange(len(arr)), arr)
|
|
79
80
|
|
|
80
81
|
|
|
81
82
|
def eval_monad_first(a):
|
|
@@ -97,7 +98,7 @@ def eval_monad_first(a):
|
|
|
97
98
|
return a if is_empty(a) or not is_iterable(a) else a[0]
|
|
98
99
|
|
|
99
100
|
|
|
100
|
-
def eval_monad_floor(a):
|
|
101
|
+
def eval_monad_floor(a, backend):
|
|
101
102
|
"""
|
|
102
103
|
|
|
103
104
|
_a [Floor]
|
|
@@ -115,10 +116,10 @@ def eval_monad_floor(a):
|
|
|
115
116
|
_1e100 --> 1.0e+100 :"if precision < 100 digits"
|
|
116
117
|
|
|
117
118
|
"""
|
|
118
|
-
return vec_fn(a,
|
|
119
|
+
return backend.vec_fn(a, backend.floor_to_int)
|
|
119
120
|
|
|
120
121
|
|
|
121
|
-
def eval_monad_format(a):
|
|
122
|
+
def eval_monad_format(a, backend):
|
|
122
123
|
"""
|
|
123
124
|
|
|
124
125
|
$a [Format]
|
|
@@ -136,10 +137,10 @@ def eval_monad_format(a):
|
|
|
136
137
|
$:foo --> ":foo"
|
|
137
138
|
|
|
138
139
|
"""
|
|
139
|
-
return f":{a}" if isinstance(a, KGSym) else vec_fn(a, eval_monad_format) if is_list(a) else str(a)
|
|
140
|
+
return f":{a}" if isinstance(a, KGSym) else backend.vec_fn(a, lambda x: eval_monad_format(x, backend)) if is_list(a) else str(a)
|
|
140
141
|
|
|
141
142
|
|
|
142
|
-
def eval_monad_grade_up(a):
|
|
143
|
+
def eval_monad_grade_up(a, backend):
|
|
143
144
|
"""
|
|
144
145
|
|
|
145
146
|
<a [Grade-Up]
|
|
@@ -164,10 +165,10 @@ def eval_monad_grade_up(a):
|
|
|
164
165
|
>[[1] [2] [3]] --> [2 1 0]
|
|
165
166
|
|
|
166
167
|
"""
|
|
167
|
-
return kg_argsort(kg_asarray(a))
|
|
168
|
+
return kg_argsort(backend.kg_asarray(a), backend)
|
|
168
169
|
|
|
169
170
|
|
|
170
|
-
def eval_monad_grade_down(a):
|
|
171
|
+
def eval_monad_grade_down(a, backend):
|
|
171
172
|
"""
|
|
172
173
|
|
|
173
174
|
>a [Grade-Down]
|
|
@@ -175,10 +176,10 @@ def eval_monad_grade_down(a):
|
|
|
175
176
|
See [Grade-Up].
|
|
176
177
|
|
|
177
178
|
"""
|
|
178
|
-
return kg_argsort(kg_asarray(a), descending=True)
|
|
179
|
+
return kg_argsort(backend.kg_asarray(a), backend, descending=True)
|
|
179
180
|
|
|
180
181
|
|
|
181
|
-
def eval_monad_groupby(a):
|
|
182
|
+
def eval_monad_groupby(a, backend):
|
|
182
183
|
"""
|
|
183
184
|
|
|
184
185
|
=a [Group]
|
|
@@ -195,15 +196,15 @@ def eval_monad_groupby(a):
|
|
|
195
196
|
="hello foo" --> [[0] [1] [2 3] [4 7 8] [5] [6]]
|
|
196
197
|
|
|
197
198
|
"""
|
|
198
|
-
arr = kg_asarray(a)
|
|
199
|
-
if arr
|
|
199
|
+
arr = backend.kg_asarray(a)
|
|
200
|
+
if backend.array_size(arr) == 0:
|
|
200
201
|
return arr
|
|
201
|
-
vals, inverse =
|
|
202
|
-
groups = [
|
|
203
|
-
return kg_asarray(groups)
|
|
202
|
+
vals, inverse = bknp.unique(arr, return_inverse=True)
|
|
203
|
+
groups = [bknp.where(inverse == i)[0] for i in range(len(vals))]
|
|
204
|
+
return backend.kg_asarray(groups)
|
|
204
205
|
|
|
205
206
|
|
|
206
|
-
def eval_monad_list(a):
|
|
207
|
+
def eval_monad_list(a, backend):
|
|
207
208
|
"""
|
|
208
209
|
|
|
209
210
|
,a [List]
|
|
@@ -215,14 +216,12 @@ def eval_monad_list(a):
|
|
|
215
216
|
,"xyz" --> ["xyz"]
|
|
216
217
|
,[1] --> [[1]]
|
|
217
218
|
"""
|
|
218
|
-
if
|
|
219
|
+
if is_char(a):
|
|
219
220
|
return str(a)
|
|
220
|
-
|
|
221
|
-
return np.asarray([a],dtype=object) # np interprets ':foo" as ':fo"
|
|
222
|
-
return np.asarray([a])
|
|
221
|
+
return backend.kg_asarray([a])
|
|
223
222
|
|
|
224
223
|
|
|
225
|
-
def eval_monad_negate(a):
|
|
224
|
+
def eval_monad_negate(a, backend):
|
|
226
225
|
"""
|
|
227
226
|
|
|
228
227
|
-a [Negate]
|
|
@@ -235,10 +234,10 @@ def eval_monad_negate(a):
|
|
|
235
234
|
-1.23 --> -1.23
|
|
236
235
|
|
|
237
236
|
"""
|
|
238
|
-
return vec_fn(a, lambda x: np.negative(kg_asarray(x)))
|
|
237
|
+
return backend.vec_fn(a, lambda x: backend.np.negative(backend.kg_asarray(x)))
|
|
239
238
|
|
|
240
239
|
|
|
241
|
-
def eval_monad_not(a):
|
|
240
|
+
def eval_monad_not(a, backend):
|
|
242
241
|
"""
|
|
243
242
|
|
|
244
243
|
~a [Not]
|
|
@@ -254,11 +253,11 @@ def eval_monad_not(a):
|
|
|
254
253
|
|
|
255
254
|
"""
|
|
256
255
|
def _neg(x):
|
|
257
|
-
return 1 if is_empty(x) else 0 if is_dict(x) or isinstance(x, (KGFn, KGSym)) else kg_truth(
|
|
258
|
-
return vec_fn(a, _neg) if not is_empty(a) else _neg(a)
|
|
256
|
+
return 1 if is_empty(x) else 0 if is_dict(x) or isinstance(x, (KGFn, KGSym)) else kg_truth(bknp.logical_not(bknp.asarray(x, dtype=object)))
|
|
257
|
+
return backend.vec_fn(a, _neg) if not is_empty(a) else _neg(a)
|
|
259
258
|
|
|
260
259
|
|
|
261
|
-
def eval_monad_range(a):
|
|
260
|
+
def eval_monad_range(a, backend):
|
|
262
261
|
"""
|
|
263
262
|
|
|
264
263
|
?a [Range]
|
|
@@ -271,28 +270,19 @@ def eval_monad_range(a):
|
|
|
271
270
|
?"aaabbcccd" --> "abcd"
|
|
272
271
|
|
|
273
272
|
"""
|
|
273
|
+
np_backend = backend.np
|
|
274
274
|
if isinstance(a, str):
|
|
275
|
-
return ''.join(
|
|
276
|
-
elif
|
|
277
|
-
|
|
278
|
-
|
|
275
|
+
return ''.join(bknp.unique(backend.str_to_chr_arr(a)))
|
|
276
|
+
elif np_backend.isarray(a):
|
|
277
|
+
dtype_kind = backend.get_dtype_kind(a)
|
|
278
|
+
if dtype_kind != 'O' and a.ndim > 1:
|
|
279
|
+
# Use numpy for unique with return_index across backends
|
|
280
|
+
a_np = backend.to_numpy(a) if backend.is_backend_array(a) else a
|
|
281
|
+
_, ids = bknp.unique(a_np, axis=0, return_index=True)
|
|
282
|
+
ids.sort()
|
|
283
|
+
return a[ids]
|
|
279
284
|
else:
|
|
280
285
|
# handle the jagged / mixed array case
|
|
281
|
-
# from functools import total_ordering
|
|
282
|
-
# @total_ordering
|
|
283
|
-
# class Wrapper:
|
|
284
|
-
# def __init__(self, x):
|
|
285
|
-
# self.x = x
|
|
286
|
-
# def __eq__(self,o):
|
|
287
|
-
# print("eq")
|
|
288
|
-
# return array_equal(self.x, o.x)
|
|
289
|
-
# def __ne__(self,o):
|
|
290
|
-
# return not array_equal(self.x, o.x)
|
|
291
|
-
# def __lt__(self, o):
|
|
292
|
-
# u = np.sort(np.asarray([self.x, o.x]))
|
|
293
|
-
# return u[0] == self.x
|
|
294
|
-
# # return u[0] if isinstance(u,np.ndarray) else u
|
|
295
|
-
# _,ids = np.unique([Wrapper(x) for x in a], return_index=True)
|
|
296
286
|
# TODO: Make UNIQUE work. this feels so dirty.
|
|
297
287
|
s = set()
|
|
298
288
|
arr = []
|
|
@@ -301,13 +291,11 @@ def eval_monad_range(a):
|
|
|
301
291
|
if sx not in s:
|
|
302
292
|
s.add(sx)
|
|
303
293
|
arr.append(x)
|
|
304
|
-
return
|
|
305
|
-
ids.sort()
|
|
306
|
-
a = a[ids]
|
|
294
|
+
return backend.kg_asarray(arr)
|
|
307
295
|
return a
|
|
308
296
|
|
|
309
297
|
|
|
310
|
-
def eval_monad_reciprocal(a):
|
|
298
|
+
def eval_monad_reciprocal(a, backend):
|
|
311
299
|
"""
|
|
312
300
|
|
|
313
301
|
%a [Reciprocal]
|
|
@@ -321,7 +309,11 @@ def eval_monad_reciprocal(a):
|
|
|
321
309
|
%0.1 --> 10.0
|
|
322
310
|
|
|
323
311
|
"""
|
|
324
|
-
|
|
312
|
+
if not is_list(a) and backend.is_number(a):
|
|
313
|
+
a_val = backend.scalar_to_python(a) if backend.is_backend_array(a) else a
|
|
314
|
+
if a_val == 0:
|
|
315
|
+
return KLONG_UNDEFINED
|
|
316
|
+
return backend.vec_fn(a, lambda x: bknp.reciprocal(bknp.asarray(x, dtype=float)))
|
|
325
317
|
|
|
326
318
|
|
|
327
319
|
def eval_monad_reverse(a):
|
|
@@ -341,7 +333,7 @@ def eval_monad_reverse(a):
|
|
|
341
333
|
return a[::-1]
|
|
342
334
|
|
|
343
335
|
|
|
344
|
-
def eval_monad_shape(a):
|
|
336
|
+
def eval_monad_shape(a, backend):
|
|
345
337
|
"""
|
|
346
338
|
|
|
347
339
|
^a [Shape]
|
|
@@ -393,12 +385,20 @@ def eval_monad_shape(a):
|
|
|
393
385
|
|
|
394
386
|
"""
|
|
395
387
|
|
|
388
|
+
def _normalize_backend_array(x):
|
|
389
|
+
return backend.to_numpy(x) if backend.is_backend_array(x) else x
|
|
390
|
+
|
|
396
391
|
def _a(x): # use numpy's natural shape by replacing all strings with arrays
|
|
397
|
-
|
|
398
|
-
|
|
392
|
+
x = _normalize_backend_array(x)
|
|
393
|
+
return bknp.asarray([
|
|
394
|
+
bknp.empty(len(y)) if isinstance(y, str) else (_a(y) if is_list(y) else _normalize_backend_array(y))
|
|
395
|
+
for y in x
|
|
396
|
+
])
|
|
397
|
+
a = _normalize_backend_array(a)
|
|
398
|
+
return 0 if is_atom(a) else bknp.asarray([len(a)]) if isinstance(a, str) else bknp.asarray(_a(a).shape)
|
|
399
399
|
|
|
400
400
|
|
|
401
|
-
def eval_monad_size(a):
|
|
401
|
+
def eval_monad_size(a, backend):
|
|
402
402
|
"""
|
|
403
403
|
|
|
404
404
|
#a [Size]
|
|
@@ -417,7 +417,7 @@ def eval_monad_size(a):
|
|
|
417
417
|
#0cA --> 65
|
|
418
418
|
|
|
419
419
|
"""
|
|
420
|
-
return np.abs(a) if is_number(a) else ord(a) if is_char(a) else len(a)
|
|
420
|
+
return backend.np.abs(a) if backend.is_number(a) else ord(a) if is_char(a) else len(a)
|
|
421
421
|
|
|
422
422
|
|
|
423
423
|
def eval_monad_transpose(a):
|
|
@@ -432,10 +432,10 @@ def eval_monad_transpose(a):
|
|
|
432
432
|
+[] --> []
|
|
433
433
|
|
|
434
434
|
"""
|
|
435
|
-
return
|
|
435
|
+
return bknp.transpose(bknp.asarray(a))
|
|
436
436
|
|
|
437
437
|
|
|
438
|
-
def eval_monad_undefined(a):
|
|
438
|
+
def eval_monad_undefined(a, backend):
|
|
439
439
|
"""
|
|
440
440
|
|
|
441
441
|
:_a [Undefined]
|
|
@@ -450,20 +450,66 @@ def eval_monad_undefined(a):
|
|
|
450
450
|
:_:valid --> 0
|
|
451
451
|
|
|
452
452
|
"""
|
|
453
|
-
return kg_truth(a is None or
|
|
453
|
+
return kg_truth(a is None or a is KLONG_UNDEFINED)
|
|
454
454
|
|
|
455
455
|
|
|
456
|
-
def
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
456
|
+
def eval_monad_track(a):
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
˙a [Track]
|
|
460
|
+
|
|
461
|
+
Identity operator used when marking values for gradient tracking.
|
|
462
|
+
|
|
463
|
+
"""
|
|
464
|
+
return a
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def eval_monad_grad(klong, a):
|
|
468
|
+
"""
|
|
460
469
|
|
|
461
|
-
|
|
470
|
+
∇a [Grad]
|
|
462
471
|
|
|
463
|
-
|
|
464
|
-
for x in filter(lambda n: n.startswith("eval_monad_"), dir(m)):
|
|
465
|
-
fn = getattr(m,x)
|
|
466
|
-
name = _get_name(fn.__doc__)
|
|
467
|
-
registry[name] = fn
|
|
472
|
+
Return a function that computes the numeric gradient of ``a``.
|
|
468
473
|
|
|
469
|
-
|
|
474
|
+
"""
|
|
475
|
+
return KGLambda(lambda x, fn=a, k=klong: grad_of_fn(k, fn, x))
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def create_monad_functions(klong):
|
|
479
|
+
backend = klong._backend
|
|
480
|
+
|
|
481
|
+
# Simple monads that don't need backend or klong
|
|
482
|
+
simple = {
|
|
483
|
+
'@': eval_monad_atom,
|
|
484
|
+
'&': eval_monad_expand_where,
|
|
485
|
+
'*': eval_monad_first,
|
|
486
|
+
'|': eval_monad_reverse,
|
|
487
|
+
'+': eval_monad_transpose,
|
|
488
|
+
'˙': eval_monad_track,
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
# Monads needing backend
|
|
492
|
+
backend_monads = {
|
|
493
|
+
',': lambda a: eval_monad_list(a, backend),
|
|
494
|
+
':#': lambda a: eval_monad_char(a, backend),
|
|
495
|
+
'!': lambda a: eval_monad_enumerate(a, backend),
|
|
496
|
+
'_': lambda a: eval_monad_floor(a, backend),
|
|
497
|
+
'$': lambda a: eval_monad_format(a, backend),
|
|
498
|
+
'<': lambda a: eval_monad_grade_up(a, backend),
|
|
499
|
+
'>': lambda a: eval_monad_grade_down(a, backend),
|
|
500
|
+
'=': lambda a: eval_monad_groupby(a, backend),
|
|
501
|
+
'-': lambda a: eval_monad_negate(a, backend),
|
|
502
|
+
'~': lambda a: eval_monad_not(a, backend),
|
|
503
|
+
'?': lambda a: eval_monad_range(a, backend),
|
|
504
|
+
'%': lambda a: eval_monad_reciprocal(a, backend),
|
|
505
|
+
'#': lambda a: eval_monad_size(a, backend),
|
|
506
|
+
':_': lambda a: eval_monad_undefined(a, backend),
|
|
507
|
+
'^': lambda a: eval_monad_shape(a, backend),
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
# Monads needing klong
|
|
511
|
+
klong_monads = {
|
|
512
|
+
'∇': lambda a: eval_monad_grad(klong, a),
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return {**simple, **backend_monads, **klong_monads}
|