klongpy 0.6.8__py3-none-any.whl → 0.7.0__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 +19 -1
- klongpy/adverbs.py +5 -5
- klongpy/autograd.py +308 -0
- klongpy/backend.py +167 -99
- klongpy/backends/__init__.py +94 -0
- klongpy/backends/base.py +320 -0
- klongpy/backends/numpy_backend.py +122 -0
- klongpy/backends/torch_backend.py +995 -0
- klongpy-0.6.8.data/scripts/kgpy → klongpy/cli.py +65 -88
- klongpy/core.py +228 -106
- klongpy/db/sys_fn_db.py +4 -3
- klongpy/dyads.py +173 -32
- klongpy/interpreter.py +31 -3
- klongpy/lib/help.kg +2 -2
- klongpy/monads.py +49 -12
- klongpy/repl.py +91 -0
- klongpy/sys_fn.py +129 -18
- klongpy/sys_fn_autograd.py +290 -0
- klongpy/sys_fn_ipc.py +18 -7
- klongpy/sys_fn_timer.py +13 -3
- klongpy/web/sys_fn_web.py +28 -6
- klongpy-0.7.0.dist-info/METADATA +493 -0
- klongpy-0.7.0.dist-info/RECORD +48 -0
- {klongpy-0.6.8.dist-info → klongpy-0.7.0.dist-info}/WHEEL +1 -1
- klongpy-0.7.0.dist-info/entry_points.txt +2 -0
- {klongpy-0.6.8.dist-info → klongpy-0.7.0.dist-info}/top_level.txt +0 -1
- klongpy-0.6.8.dist-info/METADATA +0 -412
- klongpy-0.6.8.dist-info/RECORD +0 -72
- 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_examples.py +0 -64
- tests/test_extra_suite.py +0 -382
- tests/test_file_cache.py +0 -185
- tests/test_interop.py +0 -181
- tests/test_kgtests.py +0 -65
- tests/test_known_bugs.py +0 -206
- tests/test_prog.py +0 -107
- tests/test_suite.py +0 -1479
- 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_util.py +0 -233
- tests/utils.py +0 -126
- {klongpy-0.6.8.dist-info → klongpy-0.7.0.dist-info/licenses}/LICENSE +0 -0
klongpy/sys_fn.py
CHANGED
|
@@ -12,8 +12,24 @@ from inspect import Parameter
|
|
|
12
12
|
import numpy
|
|
13
13
|
|
|
14
14
|
from .core import (KGChannel, KGChannelDir, KGLambda, KGSym, KlongException,
|
|
15
|
-
is_dict, is_empty, is_list,
|
|
15
|
+
is_dict, is_empty, is_list, kg_read, kg_write, np,
|
|
16
16
|
reserved_fn_args, reserved_fn_symbol_map, safe_eq, safe_inspect)
|
|
17
|
+
from .backend import to_numpy, get_default_backend, kg_asarray
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _to_display_value(x):
|
|
21
|
+
"""Convert backend tensors to numpy for cleaner display."""
|
|
22
|
+
backend = get_default_backend()
|
|
23
|
+
# Convert backend arrays (tensors) to numpy
|
|
24
|
+
if backend.is_backend_array(x):
|
|
25
|
+
return to_numpy(x)
|
|
26
|
+
# Handle numpy arrays with tensors inside (object arrays)
|
|
27
|
+
if isinstance(x, numpy.ndarray) and x.dtype == object:
|
|
28
|
+
return numpy.array([_to_display_value(item) for item in x], dtype=object)
|
|
29
|
+
# Handle lists with tensors
|
|
30
|
+
if isinstance(x, list):
|
|
31
|
+
return [_to_display_value(item) for item in x]
|
|
32
|
+
return x
|
|
17
33
|
|
|
18
34
|
|
|
19
35
|
def eval_sys_append_channel(x):
|
|
@@ -47,10 +63,25 @@ def eval_sys_display(klong, x):
|
|
|
47
63
|
|
|
48
64
|
.d(x) [Display]
|
|
49
65
|
|
|
50
|
-
|
|
66
|
+
Display the object "x". Tensors are converted to numpy for cleaner output.
|
|
67
|
+
Use .bkd() for raw backend-specific display.
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
x = _to_display_value(x)
|
|
71
|
+
r = kg_write(x, klong._backend, display=True)
|
|
72
|
+
klong['.sys.cout'].raw.write(r)
|
|
73
|
+
return r
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def eval_sys_backend_display(klong, x):
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
.bkd(x) [Backend-Display]
|
|
80
|
+
|
|
81
|
+
Display the object "x" in raw backend format (tensors shown as-is).
|
|
51
82
|
|
|
52
83
|
"""
|
|
53
|
-
r = kg_write(x, display=True)
|
|
84
|
+
r = kg_write(x, klong._backend, display=True)
|
|
54
85
|
klong['.sys.cout'].raw.write(r)
|
|
55
86
|
return r
|
|
56
87
|
|
|
@@ -272,10 +303,26 @@ def eval_sys_print(klong, x):
|
|
|
272
303
|
.p(x) [Print]
|
|
273
304
|
|
|
274
305
|
Pretty-print the object "x" (like Display) and then print a
|
|
275
|
-
newline sequence.
|
|
306
|
+
newline sequence. Tensors are converted to numpy for cleaner output.
|
|
307
|
+
Use .bkp() for raw backend-specific print.
|
|
308
|
+
|
|
309
|
+
"""
|
|
310
|
+
x = _to_display_value(x)
|
|
311
|
+
o = kg_write(x, klong._backend, display=True)
|
|
312
|
+
klong['.sys.cout'].raw.write(o+"\n")
|
|
313
|
+
return o
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def eval_sys_backend_print(klong, x):
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
.bkp(x) [Backend-Print]
|
|
320
|
+
|
|
321
|
+
Pretty-print the object "x" in raw backend format (tensors shown as-is)
|
|
322
|
+
and then print a newline sequence.
|
|
276
323
|
|
|
277
324
|
"""
|
|
278
|
-
o = kg_write(x, display=True)
|
|
325
|
+
o = kg_write(x, klong._backend, display=True)
|
|
279
326
|
klong['.sys.cout'].raw.write(o+"\n")
|
|
280
327
|
return o
|
|
281
328
|
|
|
@@ -341,20 +388,30 @@ def _handle_import(item):
|
|
|
341
388
|
if n_args <= len(reserved_fn_args):
|
|
342
389
|
item = KGLambda(item, args=reserved_fn_args[:n_args])
|
|
343
390
|
else:
|
|
344
|
-
|
|
345
|
-
if 'args' in
|
|
391
|
+
sig_args = safe_inspect(item, follow_wrapped=True)
|
|
392
|
+
if 'args' in sig_args:
|
|
346
393
|
item = KGLambda(item, args=None, wildcard=True)
|
|
347
394
|
n_args = 3
|
|
348
395
|
else:
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
#
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
396
|
+
# Get required args (no default)
|
|
397
|
+
required_args = [k for k,v in sig_args.items() if (v.kind == Parameter.POSITIONAL_OR_KEYWORD and v.default == Parameter.empty) or (v.kind == Parameter.POSITIONAL_ONLY)]
|
|
398
|
+
# Get optional args (have default)
|
|
399
|
+
optional_args = [k for k,v in sig_args.items() if v.kind == Parameter.POSITIONAL_OR_KEYWORD and v.default != Parameter.empty]
|
|
400
|
+
# Use required args count, but if there are optional args and no required args,
|
|
401
|
+
# use wildcard mode so the function can accept 0-3 args
|
|
402
|
+
if not required_args and optional_args:
|
|
403
|
+
item = KGLambda(item, args=None, wildcard=True)
|
|
404
|
+
n_args = 3
|
|
405
|
+
else:
|
|
406
|
+
args = required_args
|
|
407
|
+
n_args = len(args)
|
|
408
|
+
# if there are kwargs, then .pyc() must be used to call this function to override them
|
|
409
|
+
if 'klong' in args:
|
|
410
|
+
n_args -= 1
|
|
411
|
+
assert n_args <= len(reserved_fn_args)
|
|
412
|
+
item = KGLambda(item, args=reserved_fn_args[:n_args], provide_klong=True)
|
|
413
|
+
elif n_args <= len(reserved_fn_args):
|
|
414
|
+
item = KGLambda(item, args=reserved_fn_args[:n_args])
|
|
358
415
|
except Exception:
|
|
359
416
|
if hasattr(item, "__class__") and hasattr(item.__class__, '__module__') and item.__class__.__module__ == "builtins":
|
|
360
417
|
# LOOK AWAY. You didn't see this.
|
|
@@ -405,7 +462,7 @@ def _import_module(klong, x, from_set=None):
|
|
|
405
462
|
module = import_module_from_sys(x)
|
|
406
463
|
|
|
407
464
|
export_items = module.__dict__.get("klongpy_exports") or module.__dict__
|
|
408
|
-
ffn = lambda p: p[0] in from_set if from_set is not None else lambda p: not p[0].startswith("__")
|
|
465
|
+
ffn = (lambda p: p[0] in from_set) if from_set is not None else (lambda p: not p[0].startswith("__"))
|
|
409
466
|
|
|
410
467
|
ctx = klong._context.pop()
|
|
411
468
|
try:
|
|
@@ -415,6 +472,17 @@ def _import_module(klong, x, from_set=None):
|
|
|
415
472
|
except Exception as e:
|
|
416
473
|
# TODO: this should be logged
|
|
417
474
|
print(f"failed to import function: {name}", e)
|
|
475
|
+
|
|
476
|
+
# For from_set imports, also check for lazy-loaded attributes not in __dict__
|
|
477
|
+
# (e.g., numpy.random in numpy 2.x)
|
|
478
|
+
if from_set is not None:
|
|
479
|
+
for name in from_set:
|
|
480
|
+
if name not in export_items and hasattr(module, name):
|
|
481
|
+
try:
|
|
482
|
+
item = getattr(module, name)
|
|
483
|
+
klong[name] = _handle_import(item)
|
|
484
|
+
except Exception as e:
|
|
485
|
+
print(f"failed to import function: {name}", e)
|
|
418
486
|
finally:
|
|
419
487
|
klong._context.push(ctx)
|
|
420
488
|
|
|
@@ -578,6 +646,49 @@ def eval_sys_python_from(klong, x, y):
|
|
|
578
646
|
return _import_module(klong, x, from_set=set(y))
|
|
579
647
|
|
|
580
648
|
|
|
649
|
+
def eval_sys_backend_fn(klong, x):
|
|
650
|
+
"""
|
|
651
|
+
|
|
652
|
+
.bkf(x) [Backend-Function]
|
|
653
|
+
|
|
654
|
+
Import functions from the current backend's array module.
|
|
655
|
+
This is similar to .pyf() but uses backend-aware functions that
|
|
656
|
+
work with both numpy and torch backends.
|
|
657
|
+
|
|
658
|
+
When using the torch backend, these functions preserve gradient
|
|
659
|
+
tracking for autograd.
|
|
660
|
+
|
|
661
|
+
Example:
|
|
662
|
+
|
|
663
|
+
.bkf("exp")
|
|
664
|
+
exp(1.0) --> 2.718...
|
|
665
|
+
|
|
666
|
+
.bkf(["exp";"sin";"cos"])
|
|
667
|
+
sin(1.0) --> 0.841...
|
|
668
|
+
|
|
669
|
+
Common functions available: exp, sin, cos, tan, tanh, sqrt, abs,
|
|
670
|
+
log, log10, floor, ceil, round
|
|
671
|
+
|
|
672
|
+
"""
|
|
673
|
+
if isinstance(x, str):
|
|
674
|
+
x = [x]
|
|
675
|
+
if not (is_list(x) and all(map(lambda p: isinstance(p, str), x))):
|
|
676
|
+
raise RuntimeError("function name(s) must be a string or list of strings")
|
|
677
|
+
|
|
678
|
+
backend = klong._backend
|
|
679
|
+
ctx = klong._context.pop()
|
|
680
|
+
try:
|
|
681
|
+
for fn_name in x:
|
|
682
|
+
if hasattr(backend.np, fn_name):
|
|
683
|
+
fn = getattr(backend.np, fn_name)
|
|
684
|
+
klong[fn_name] = _handle_import(fn)
|
|
685
|
+
else:
|
|
686
|
+
raise RuntimeError(f"Backend does not have function: {fn_name}")
|
|
687
|
+
finally:
|
|
688
|
+
klong._context.push(ctx)
|
|
689
|
+
return None
|
|
690
|
+
|
|
691
|
+
|
|
581
692
|
def eval_sys_random_number():
|
|
582
693
|
"""
|
|
583
694
|
|
|
@@ -722,7 +833,7 @@ def eval_sys_write(klong, x):
|
|
|
722
833
|
sequence. Use .p (Print) to do so.
|
|
723
834
|
|
|
724
835
|
"""
|
|
725
|
-
r = kg_write(x)
|
|
836
|
+
r = kg_write(x, klong._backend)
|
|
726
837
|
klong['.sys.cout'].raw.write(r)
|
|
727
838
|
return x
|
|
728
839
|
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Autograd system functions for KlongPy.
|
|
3
|
+
|
|
4
|
+
Provides .jacobian() for Jacobian matrix computation.
|
|
5
|
+
Provides .compile() for function compilation and graph export (torch only).
|
|
6
|
+
|
|
7
|
+
For optimizers (SGD, Adam, etc.), see examples/autograd/optimizers.py
|
|
8
|
+
which can be copied to your project and customized.
|
|
9
|
+
"""
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
from .autograd import jacobian_of_fn, _invoke_fn
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def eval_sys_jacobian(klong, x, y):
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
.jacobian(x;y) [Jacobian]
|
|
19
|
+
|
|
20
|
+
Compute Jacobian matrix of function x at point y.
|
|
21
|
+
|
|
22
|
+
For f: R^n -> R^m, returns m x n matrix where J[i,j] = df_i/dx_j.
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
f::{[x@0^2 x@1^2]}
|
|
26
|
+
.jacobian(f;[1 2]) --> [[2 0] [0 4]]
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
return jacobian_of_fn(klong, x, y)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def eval_sys_compile(klong, x, y):
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
.compile(x;y) [Compile]
|
|
36
|
+
|
|
37
|
+
Compile a function for optimized execution using torch.compile.
|
|
38
|
+
Requires PyTorch backend (USE_TORCH=1).
|
|
39
|
+
|
|
40
|
+
Arguments:
|
|
41
|
+
x - Function to compile
|
|
42
|
+
y - Example input for tracing the computation graph
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Compiled function (faster execution)
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
f::{x^2}
|
|
49
|
+
cf::.compile(f;3.0) :" Returns compiled function
|
|
50
|
+
cf(5.0) :" 25.0 (optimized)
|
|
51
|
+
|
|
52
|
+
Notes:
|
|
53
|
+
- Only supported with PyTorch backend
|
|
54
|
+
- Raises error on NumPy backend
|
|
55
|
+
- See .export() for saving graphs to files
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
fn, example_input = x, y
|
|
59
|
+
|
|
60
|
+
backend = klong._backend
|
|
61
|
+
if not backend.supports_autograd():
|
|
62
|
+
raise RuntimeError(
|
|
63
|
+
".compile() requires PyTorch backend. "
|
|
64
|
+
"Run with USE_TORCH=1 environment variable."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Wrap the Klong function for torch
|
|
68
|
+
def wrapped_fn(v):
|
|
69
|
+
return _invoke_fn(klong, fn, [v])
|
|
70
|
+
|
|
71
|
+
return backend.compile_function(wrapped_fn, example_input, None)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def eval_sys_export(klong, x, y, z):
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
.export(x;y;z) [Export]
|
|
78
|
+
|
|
79
|
+
Export a function's computation graph to a file for inspection.
|
|
80
|
+
Requires PyTorch backend (USE_TORCH=1).
|
|
81
|
+
|
|
82
|
+
Arguments:
|
|
83
|
+
x - Function to export
|
|
84
|
+
y - Example input for tracing the computation graph
|
|
85
|
+
z - Path to save the graph (.pt2 file)
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Dictionary with:
|
|
89
|
+
"compiled_fn" - The compiled function
|
|
90
|
+
"export_path" - Path where graph was saved
|
|
91
|
+
"graph" - String representation of computation graph
|
|
92
|
+
|
|
93
|
+
Examples:
|
|
94
|
+
f::{x^2}
|
|
95
|
+
info::.export(f;3.0;"model.pt2")
|
|
96
|
+
.p(info@"graph") :" Print computation graph
|
|
97
|
+
|
|
98
|
+
Notes:
|
|
99
|
+
- Only supported with PyTorch backend
|
|
100
|
+
- The exported graph can be loaded with torch.export.load()
|
|
101
|
+
- Use .compile() for just compiling without export
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
fn, example_input, output_path = x, y, z
|
|
105
|
+
|
|
106
|
+
backend = klong._backend
|
|
107
|
+
if not backend.supports_autograd():
|
|
108
|
+
raise RuntimeError(
|
|
109
|
+
".export() requires PyTorch backend. "
|
|
110
|
+
"Run with USE_TORCH=1 environment variable."
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Wrap the Klong function for torch
|
|
114
|
+
def wrapped_fn(v):
|
|
115
|
+
return _invoke_fn(klong, fn, [v])
|
|
116
|
+
|
|
117
|
+
return backend.compile_function(wrapped_fn, example_input, output_path)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def eval_sys_compilex(klong, x, y, z):
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
.compilex(x;y;z) [Compile-Extended]
|
|
124
|
+
|
|
125
|
+
Compile a function with extended options for mode and backend.
|
|
126
|
+
Requires PyTorch backend (USE_TORCH=1).
|
|
127
|
+
|
|
128
|
+
Arguments:
|
|
129
|
+
x - Function to compile
|
|
130
|
+
y - Example input for tracing the computation graph
|
|
131
|
+
z - Options dictionary with compile settings
|
|
132
|
+
|
|
133
|
+
Options (z):
|
|
134
|
+
"mode" - Compilation mode:
|
|
135
|
+
"default" - Balanced (default)
|
|
136
|
+
"reduce-overhead" - Faster compile, less optimization
|
|
137
|
+
"max-autotune" - Slower compile, best runtime
|
|
138
|
+
"backend" - Compilation backend:
|
|
139
|
+
"inductor" - Default with C++/Triton codegen
|
|
140
|
+
"eager" - No compilation (debugging)
|
|
141
|
+
"cudagraphs" - CUDA graphs (GPU only)
|
|
142
|
+
"fullgraph" - 1 to require full graph compilation
|
|
143
|
+
"dynamic" - 1 for dynamic shapes, 0 for static
|
|
144
|
+
|
|
145
|
+
Mode Comparison:
|
|
146
|
+
| Mode | Compile | Runtime | Use Case |
|
|
147
|
+
|-----------------|---------|---------|-------------------|
|
|
148
|
+
| default | Medium | Good | General use |
|
|
149
|
+
| reduce-overhead | Fast | OK | Development |
|
|
150
|
+
| max-autotune | Slow | Best | Production |
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Compiled function
|
|
154
|
+
|
|
155
|
+
Examples:
|
|
156
|
+
f::{x^2}
|
|
157
|
+
|
|
158
|
+
:" Fast compilation for development
|
|
159
|
+
cf::.compilex(f;3.0;:{["mode" "reduce-overhead"]})
|
|
160
|
+
|
|
161
|
+
:" Maximum optimization for production
|
|
162
|
+
cf::.compilex(f;3.0;:{["mode" "max-autotune"]})
|
|
163
|
+
|
|
164
|
+
:" Debug mode (no compilation)
|
|
165
|
+
cf::.compilex(f;3.0;:{["backend" "eager"]})
|
|
166
|
+
|
|
167
|
+
Notes:
|
|
168
|
+
- Only supported with PyTorch backend
|
|
169
|
+
- Requires C++ compiler for inductor backend
|
|
170
|
+
- Use .cmodes() to see all available options
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
fn, example_input, options = x, y, z
|
|
174
|
+
|
|
175
|
+
backend = klong._backend
|
|
176
|
+
if not backend.supports_autograd():
|
|
177
|
+
raise RuntimeError(
|
|
178
|
+
".compilex() requires PyTorch backend. "
|
|
179
|
+
"Run with USE_TORCH=1 environment variable."
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Extract options from dictionary
|
|
183
|
+
mode = options.get("mode", "default") if isinstance(options, dict) else "default"
|
|
184
|
+
compile_backend = options.get("backend", "inductor") if isinstance(options, dict) else "inductor"
|
|
185
|
+
fullgraph = bool(options.get("fullgraph", 0)) if isinstance(options, dict) else False
|
|
186
|
+
dynamic = None
|
|
187
|
+
if isinstance(options, dict) and "dynamic" in options:
|
|
188
|
+
dynamic = bool(options["dynamic"])
|
|
189
|
+
|
|
190
|
+
# Wrap the Klong function for torch
|
|
191
|
+
def wrapped_fn(v):
|
|
192
|
+
return _invoke_fn(klong, fn, [v])
|
|
193
|
+
|
|
194
|
+
return backend.compile_function(
|
|
195
|
+
wrapped_fn, example_input, None,
|
|
196
|
+
mode=mode, backend=compile_backend, fullgraph=fullgraph, dynamic=dynamic
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def eval_sys_cmodes(klong):
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
.cmodes() [Compile-Modes]
|
|
204
|
+
|
|
205
|
+
Get information about available torch.compile modes and backends.
|
|
206
|
+
Requires PyTorch backend (USE_TORCH=1).
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Dictionary with:
|
|
210
|
+
"modes" - Available compilation modes
|
|
211
|
+
"backends" - Available compilation backends
|
|
212
|
+
"recommendations" - Suggested settings for common use cases
|
|
213
|
+
|
|
214
|
+
Examples:
|
|
215
|
+
info::.cmodes()
|
|
216
|
+
.p(info@"modes") :" Print available modes
|
|
217
|
+
.p(info@"recommendations") :" Print recommended settings
|
|
218
|
+
|
|
219
|
+
Mode Comparison:
|
|
220
|
+
| Mode | Compile Time | Runtime Speed | Best For |
|
|
221
|
+
|-----------------|--------------|---------------|--------------|
|
|
222
|
+
| default | Medium | Good | General use |
|
|
223
|
+
| reduce-overhead | Fast | Moderate | Development |
|
|
224
|
+
| max-autotune | Slow | Best | Production |
|
|
225
|
+
|
|
226
|
+
Backend Comparison:
|
|
227
|
+
| Backend | Description |
|
|
228
|
+
|------------|------------------------------------------|
|
|
229
|
+
| inductor | Default - C++/Triton code generation |
|
|
230
|
+
| eager | No compilation - for debugging |
|
|
231
|
+
| cudagraphs | CUDA graphs - reduces GPU launch overhead|
|
|
232
|
+
|
|
233
|
+
"""
|
|
234
|
+
backend = klong._backend
|
|
235
|
+
if not backend.supports_autograd():
|
|
236
|
+
raise RuntimeError(
|
|
237
|
+
".cmodes() requires PyTorch backend. "
|
|
238
|
+
"Run with USE_TORCH=1 environment variable."
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
return backend.get_compile_modes()
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def eval_sys_gradcheck(klong, x, y):
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
.gradcheck(x;y) [Gradcheck]
|
|
248
|
+
|
|
249
|
+
Verify that autograd gradients match numeric gradients.
|
|
250
|
+
Uses torch.autograd.gradcheck for rigorous verification.
|
|
251
|
+
Requires PyTorch backend (USE_TORCH=1).
|
|
252
|
+
|
|
253
|
+
Arguments:
|
|
254
|
+
x - Function to check (should return a scalar)
|
|
255
|
+
y - Input value or list of inputs to check
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
1 if gradients are correct
|
|
259
|
+
Raises error if gradients don't match
|
|
260
|
+
|
|
261
|
+
Examples:
|
|
262
|
+
f::{x^2}
|
|
263
|
+
.gradcheck(f;3.0) :" Returns 1
|
|
264
|
+
|
|
265
|
+
g::{(x@0^2)+(x@1^2)}
|
|
266
|
+
.gradcheck(g;[1.0 2.0]) :" Returns 1
|
|
267
|
+
|
|
268
|
+
Notes:
|
|
269
|
+
- Only supported with PyTorch backend
|
|
270
|
+
- Uses double precision (float64) when available (CPU/CUDA)
|
|
271
|
+
- Falls back to float32 with relaxed tolerances on MPS
|
|
272
|
+
- Useful for verifying custom gradient implementations
|
|
273
|
+
|
|
274
|
+
"""
|
|
275
|
+
return klong._backend.klong_gradcheck(klong, x, y)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def create_system_functions_autograd():
|
|
279
|
+
"""Create registry of autograd system functions."""
|
|
280
|
+
def _get_name(s):
|
|
281
|
+
i = s.index('.')
|
|
282
|
+
return s[i:i+s[i:].index('(')]
|
|
283
|
+
|
|
284
|
+
registry = {}
|
|
285
|
+
m = sys.modules[__name__]
|
|
286
|
+
for x in filter(lambda n: n.startswith("eval_sys_"), dir(m)):
|
|
287
|
+
fn = getattr(m, x)
|
|
288
|
+
registry[_get_name(fn.__doc__)] = fn
|
|
289
|
+
|
|
290
|
+
return registry
|
klongpy/sys_fn_ipc.py
CHANGED
|
@@ -919,7 +919,6 @@ def eval_sys_fn_create_ipc_server(klong, x):
|
|
|
919
919
|
if "x" is 0, then the server is closed and existing client connections are dropped.
|
|
920
920
|
|
|
921
921
|
"""
|
|
922
|
-
global _ipc_tcp_server
|
|
923
922
|
x = str(x)
|
|
924
923
|
parts = x.split(":")
|
|
925
924
|
bind = parts[0] if len(parts) > 1 else None
|
|
@@ -941,20 +940,31 @@ def eval_sys_fn_create_ipc_server(klong, x):
|
|
|
941
940
|
|
|
942
941
|
|
|
943
942
|
class KGAsyncCall(KGLambda):
|
|
944
|
-
def __init__(self, klongloop, fn, cb):
|
|
943
|
+
def __init__(self, klongloop, fn, cb, klong):
|
|
945
944
|
self.klongloop = klongloop
|
|
946
|
-
self.
|
|
947
|
-
|
|
948
|
-
|
|
945
|
+
self.klong = klong
|
|
946
|
+
|
|
947
|
+
# Wrap callbacks - KGFnWrapper now handles dynamic resolution automatically
|
|
948
|
+
self.fn = KGFnWrapper(klong, fn) if isinstance(fn, KGFn) else fn
|
|
949
|
+
self.cb = KGFnWrapper(klong, cb) if isinstance(cb, KGFn) else cb
|
|
950
|
+
|
|
951
|
+
arity = fn.get_arity() if issubclass(type(fn), KGLambda) else fn.arity
|
|
949
952
|
self.args = [reserved_fn_symbol_map[x] for x in reserved_fn_args[:arity]]
|
|
950
953
|
|
|
951
954
|
async def acall(self, klong, params):
|
|
955
|
+
# Execute the function based on its type
|
|
952
956
|
if issubclass(type(self.fn), KGLambda):
|
|
953
957
|
ctx = {reserved_fn_symbols[i]:params[i] for i in range(min(len(reserved_fn_args),len(params)))}
|
|
954
958
|
r = self.fn(klong, ctx)
|
|
959
|
+
elif callable(self.fn):
|
|
960
|
+
r = self.fn(*params)
|
|
955
961
|
else:
|
|
962
|
+
# Shouldn't reach here, but handle it
|
|
956
963
|
r = klong.call(KGCall(self.fn.a, [*params], self.fn.arity))
|
|
957
|
-
|
|
964
|
+
|
|
965
|
+
# Invoke callback - KGFnWrapper handles dynamic resolution automatically
|
|
966
|
+
if self.cb is not None:
|
|
967
|
+
self.cb(r)
|
|
958
968
|
|
|
959
969
|
def __call__(self, klong, ctx):
|
|
960
970
|
params = [ctx[x] for x in self.args]
|
|
@@ -980,7 +990,8 @@ def eval_sys_fn_create_async_wrapper(klong, x, y):
|
|
|
980
990
|
raise KlongException("y must be a function")
|
|
981
991
|
system = klong['.system']
|
|
982
992
|
klongloop = system['klongloop']
|
|
983
|
-
|
|
993
|
+
# KGAsyncCall will wrap the callbacks automatically
|
|
994
|
+
return KGAsyncCall(klongloop, x, y, klong)
|
|
984
995
|
|
|
985
996
|
|
|
986
997
|
def create_system_functions_ipc():
|
klongpy/sys_fn_timer.py
CHANGED
|
@@ -54,6 +54,8 @@ def eval_sys_fn_timer(klong, x, y, z):
|
|
|
54
54
|
|
|
55
55
|
The callback function returns 1 to continue, 0 to stop time timer.
|
|
56
56
|
|
|
57
|
+
If "z" is a named function, the timer re-resolves it on each tick so redefinitions take effect.
|
|
58
|
+
|
|
57
59
|
Example:
|
|
58
60
|
|
|
59
61
|
cb::{.p("hello")}
|
|
@@ -76,12 +78,20 @@ def eval_sys_fn_timer(klong, x, y, z):
|
|
|
76
78
|
y= int(y)
|
|
77
79
|
if y < 0:
|
|
78
80
|
return "x must be a non-negative integer"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
|
|
82
|
+
# Wrap the callback - KGFnWrapper now handles dynamic resolution automatically
|
|
83
|
+
if isinstance(z, KGCall):
|
|
84
|
+
return "z must be a function (not a function call)"
|
|
85
|
+
if isinstance(z, KGFn):
|
|
86
|
+
callback = KGFnWrapper(klong, z)
|
|
87
|
+
elif callable(z):
|
|
88
|
+
callback = z
|
|
89
|
+
else:
|
|
81
90
|
return "z must be a function"
|
|
91
|
+
|
|
82
92
|
system = klong['.system']
|
|
83
93
|
klongloop = system['klongloop']
|
|
84
|
-
return _call_periodic(klongloop, x, y,
|
|
94
|
+
return _call_periodic(klongloop, x, y, callback)
|
|
85
95
|
|
|
86
96
|
|
|
87
97
|
def eval_sys_fn_cancel_timer(x):
|
klongpy/web/sys_fn_web.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
import concurrent.futures
|
|
3
5
|
|
|
4
6
|
from aiohttp import web
|
|
5
7
|
|
|
@@ -67,9 +69,14 @@ def eval_sys_fn_create_web_server(klong, x, y, z):
|
|
|
67
69
|
if arity != 1:
|
|
68
70
|
logging.info(f"GET route {route} handler function requires arity 1, got {arity}")
|
|
69
71
|
continue
|
|
70
|
-
fn = fn if isinstance(fn, KGCall) else KGFnWrapper(klong, fn) if isinstance(fn, KGFn) else fn
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
# Wrap function - KGFnWrapper now handles dynamic resolution automatically
|
|
74
|
+
if isinstance(fn, KGCall):
|
|
75
|
+
logging.info(f"GET route {route} handler cannot be a function call")
|
|
76
|
+
continue
|
|
77
|
+
fn_wrapped = KGFnWrapper(klong, fn) if isinstance(fn, KGFn) else fn
|
|
78
|
+
|
|
79
|
+
async def _get(request: web.Request, fn=fn_wrapped, route=route):
|
|
73
80
|
try:
|
|
74
81
|
assert request.method == "GET"
|
|
75
82
|
return web.Response(text=str(fn(dict(request.rel_url.query))))
|
|
@@ -86,9 +93,14 @@ def eval_sys_fn_create_web_server(klong, x, y, z):
|
|
|
86
93
|
if arity != 1:
|
|
87
94
|
logging.info(f"POST route {route} handler function requires arity 1, got {arity}")
|
|
88
95
|
continue
|
|
89
|
-
fn = fn if isinstance(fn, KGCall) else KGFnWrapper(klong, fn) if isinstance(fn, KGFn) else fn
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
# Wrap function - KGFnWrapper now handles dynamic resolution automatically
|
|
98
|
+
if isinstance(fn, KGCall):
|
|
99
|
+
logging.info(f"POST route {route} handler cannot be a function call")
|
|
100
|
+
continue
|
|
101
|
+
fn_wrapped = KGFnWrapper(klong, fn) if isinstance(fn, KGFn) else fn
|
|
102
|
+
|
|
103
|
+
async def _post(request: web.Request, fn=fn_wrapped, route=route):
|
|
92
104
|
try:
|
|
93
105
|
assert request.method == "POST"
|
|
94
106
|
parameters = dict(await request.post())
|
|
@@ -113,7 +125,17 @@ def eval_sys_fn_create_web_server(klong, x, y, z):
|
|
|
113
125
|
site = web.TCPSite(runner, bind, port)
|
|
114
126
|
await site.start()
|
|
115
127
|
|
|
116
|
-
|
|
128
|
+
# create the server task in the ioloop thread and capture the task handle
|
|
129
|
+
server_loop = klong['.system']['ioloop']
|
|
130
|
+
task_future = concurrent.futures.Future()
|
|
131
|
+
|
|
132
|
+
def _start():
|
|
133
|
+
task = asyncio.create_task(start_server())
|
|
134
|
+
task_future.set_result(task)
|
|
135
|
+
|
|
136
|
+
server_loop.call_soon_threadsafe(_start)
|
|
137
|
+
server_task = task_future.result()
|
|
138
|
+
|
|
117
139
|
return WebServerHandle(bind, port, runner, server_task)
|
|
118
140
|
|
|
119
141
|
|
|
@@ -129,7 +151,7 @@ def eval_sys_fn_shutdown_web_server(klong, x):
|
|
|
129
151
|
x = x.a.fn
|
|
130
152
|
if isinstance(x, WebServerHandle) and x.runner is not None:
|
|
131
153
|
print("shutting down web server")
|
|
132
|
-
klong['.system']['ioloop'].
|
|
154
|
+
asyncio.run_coroutine_threadsafe(x.shutdown(), klong['.system']['ioloop']).result()
|
|
133
155
|
return 1
|
|
134
156
|
return 0
|
|
135
157
|
|