sutra-dev 0.9.1__tar.gz → 0.9.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.
- {sutra_dev-0.9.1/sutra_dev.egg-info → sutra_dev-0.9.3}/PKG-INFO +4 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/pyproject.toml +11 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/__init__.py +1 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/__main__.py +73 -19
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/codegen.py +34 -16
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/codegen_base.py +254 -39
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/codegen_pytorch.py +121 -19
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/codegen_thrml.py +7 -5
- sutra_dev-0.9.3/sutra_compiler/embedding.py +150 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/fv.py +24 -0
- sutra_dev-0.9.3/sutra_compiler/fv_loop_convergence.py +177 -0
- sutra_dev-0.9.3/sutra_compiler/fv_sampler_convergence.py +199 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/lexer.py +10 -11
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/multi_process.py +1 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/parser.py +38 -8
- sutra_dev-0.9.3/sutra_compiler/repl.py +303 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/simplify.py +11 -26
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/axons.su +3 -3
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/bitwise.su +1 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/embed.su +3 -6
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/logic.su +2 -3
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/numbers.su +2 -2
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/rotation.su +2 -2
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/similarity.su +35 -15
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/strings.su +6 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/vectors.su +0 -14
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib_loader.py +1 -1
- sutra_dev-0.9.3/sutra_compiler/symbol_table.py +550 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/validator.py +300 -4
- {sutra_dev-0.9.1 → sutra_dev-0.9.3/sutra_dev.egg-info}/PKG-INFO +4 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_dev.egg-info/SOURCES.txt +27 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_dev.egg-info/requires.txt +4 -0
- sutra_dev-0.9.3/tests/test_arity_diagnostic.py +82 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_axon_keys.py +4 -4
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_axon_project.py +3 -3
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_branchless_loop.py +1 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_cached_compile.py +3 -3
- sutra_dev-0.9.3/tests/test_cast_codegen.py +183 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_codegen.py +85 -85
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_codegen_pytorch.py +19 -18
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_dimension_audit.py +2 -2
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_dimension_audit_warning.py +1 -1
- sutra_dev-0.9.3/tests/test_embedding_provider.py +109 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_fv_key_soundness.py +2 -2
- sutra_dev-0.9.3/tests/test_fv_loop_convergence.py +164 -0
- sutra_dev-0.9.3/tests/test_fv_sampler_convergence.py +121 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_fv_termination.py +1 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_inliner.py +2 -2
- sutra_dev-0.9.3/tests/test_int_to_string.py +84 -0
- sutra_dev-0.9.3/tests/test_interp_string_codegen.py +86 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_list_ops.py +14 -0
- sutra_dev-0.9.3/tests/test_make_string_idempotent.py +62 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_mini_wasm_machine.py +1 -1
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_multi_process_runtime.py +2 -2
- sutra_dev-0.9.3/tests/test_ntm_ram.py +492 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_optional_llm_model.py +2 -2
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_parser.py +30 -5
- sutra_dev-0.9.3/tests/test_python_builtin_escape.py +66 -0
- sutra_dev-0.9.3/tests/test_repl.py +116 -0
- sutra_dev-0.9.3/tests/test_run_error_diagnostics.py +53 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_simplify.py +56 -56
- sutra_dev-0.9.3/tests/test_snap_diagnostic.py +143 -0
- sutra_dev-0.9.3/tests/test_string_plus_concat.py +84 -0
- sutra_dev-0.9.3/tests/test_symbol_table.py +250 -0
- sutra_dev-0.9.3/tests/test_terminal_string_decode.py +53 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_transcendentals.py +14 -63
- sutra_dev-0.9.3/tests/test_type_inference.py +105 -0
- sutra_dev-0.9.3/tests/test_unknown_function_diagnostic.py +100 -0
- sutra_dev-0.9.3/tests/test_unknown_type_diagnostic.py +89 -0
- sutra_dev-0.9.3/tests/test_unknown_variable_diagnostic.py +114 -0
- sutra_dev-0.9.3/tests/test_unsafe_override_codegen.py +42 -0
- sutra_dev-0.9.3/tests/test_wrong_arg_type_diagnostic.py +81 -0
- sutra_dev-0.9.1/tests/test_ntm_ram.py +0 -263
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/LICENSE +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/README.md +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/setup.cfg +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/ast_nodes.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/axon_keys.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/cached_compile.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/diagnostics.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/fv_key_soundness.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/fv_obligation_checker.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/fv_poly_bound.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/inliner.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/loop_capture.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/loop_desugar.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/preeval.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/promise_desugar.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/review.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/simplify_egglog.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/bigint.su +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/javascript_object.su +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/javascript_primitives.su +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/math.su +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/memory.su +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/modulus.su +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/promises.su +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/stdlib/tensor.su +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/sutradb_embedded.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/tabulate.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/trace.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_compiler/workspace.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_dev.egg-info/dependency_links.txt +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_dev.egg-info/entry_points.txt +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/sutra_dev.egg-info/top_level.txt +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_arrow_functions.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_await_midfunction.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_await_substrate_pure.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_axon_build.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_axon_device_coherence.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_axon_item_call_result.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_buffer_transcendentals.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_class_fields_runtime.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_codegen_thrml.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_corpus.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_equality_autograd.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_fused_nn.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_fv_general_checker.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_fv_kleene_grid_exactness.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_fv_poly_obligation_checker.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_fv_public_api.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_fv_worked_example.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_higher_order_functions.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_implicit_loop_desugar.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_int_dict.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_jvm_core.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_lexer.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_load_matrix.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_loop_capture.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_loop_function_decl.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_matrix_literal.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_measurement_claim_sweep.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_native_recursion.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_no_host_readout.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_preeval.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_process_pool_runtime.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_ram_direct_tensor.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_rotation_prewarm.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_simplify_egglog.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_simplify_egglog_cse.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_stdlib_loader.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_string_equality.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_substrate_leak_sweep.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_sutradb_embedded.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_tabulate.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_torch_compile_wrap.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_trainable_matrix.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_type_test_gap.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_vector_literal.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_wasm_core.py +0 -0
- {sutra_dev-0.9.1 → sutra_dev-0.9.3}/tests/test_workspace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sutra-dev
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: Compiler for the Sutra programming language — tensor ops on frozen-LLM embedding vectors.
|
|
5
5
|
Author: Emma Leonhart
|
|
6
6
|
License-Expression: AGPL-3.0-only
|
|
@@ -23,6 +23,9 @@ License-File: LICENSE
|
|
|
23
23
|
Requires-Dist: numpy>=1.26
|
|
24
24
|
Provides-Extra: runtime
|
|
25
25
|
Requires-Dist: torch>=2.1; extra == "runtime"
|
|
26
|
+
Provides-Extra: embed
|
|
27
|
+
Requires-Dist: sentence-transformers>=3.0; extra == "embed"
|
|
28
|
+
Requires-Dist: einops>=0.7; extra == "embed"
|
|
26
29
|
Provides-Extra: ts
|
|
27
30
|
Requires-Dist: sutra-from-ts>=0.1.0; extra == "ts"
|
|
28
31
|
Provides-Extra: fv
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sutra-dev"
|
|
7
|
-
version = "0.9.
|
|
7
|
+
version = "0.9.3"
|
|
8
8
|
description = "Compiler for the Sutra programming language — tensor ops on frozen-LLM embedding vectors."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -44,6 +44,16 @@ dependencies = [
|
|
|
44
44
|
runtime = [
|
|
45
45
|
"torch>=2.1",
|
|
46
46
|
]
|
|
47
|
+
# In-process embedding backend. sentence-transformers loads the SAME frozen
|
|
48
|
+
# nomic-embed-text model HuggingFace serves, so semantic Sutra programs run with
|
|
49
|
+
# NO Ollama daemon — this is the default backend (sutra_compiler/embedding.py).
|
|
50
|
+
# einops is needed by nomic-bert's custom modeling code. Pure make_real / matrix
|
|
51
|
+
# / arithmetic programs embed nothing and need none of this.
|
|
52
|
+
# Install with: pip install sutra-dev[runtime,embed]
|
|
53
|
+
embed = [
|
|
54
|
+
"sentence-transformers>=3.0",
|
|
55
|
+
"einops>=0.7",
|
|
56
|
+
]
|
|
47
57
|
# TypeScript→Sutra transpiler. `pip install sutra-dev[ts]` pulls in
|
|
48
58
|
# the working ts2su CLI from the sister package, so the recommended
|
|
49
59
|
# way to get a complete Sutra dev environment for browser-layer work
|
|
@@ -28,7 +28,7 @@ The compiler is intentionally liberal where the spec is still open
|
|
|
28
28
|
forms and flags the clearly-forbidden ones.
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
|
-
__version__ = "0.9.
|
|
31
|
+
__version__ = "0.9.3"
|
|
32
32
|
|
|
33
33
|
from .diagnostics import Diagnostic, DiagnosticLevel, DiagnosticBag
|
|
34
34
|
from .lexer import Lexer, Token, TokenKind
|
|
@@ -23,6 +23,7 @@ from typing import List
|
|
|
23
23
|
|
|
24
24
|
from . import __version__
|
|
25
25
|
from . import ast_nodes as ast
|
|
26
|
+
from .codegen_base import CodegenNotSupported
|
|
26
27
|
from .codegen_pytorch import translate_module as translate_pytorch
|
|
27
28
|
from .diagnostics import Diagnostic, DiagnosticLevel
|
|
28
29
|
from .lexer import Lexer
|
|
@@ -292,10 +293,20 @@ def _compile_to_python(path: str, *, runtime_dim: int,
|
|
|
292
293
|
tabulate_module(module)
|
|
293
294
|
if loop_T is None:
|
|
294
295
|
loop_T = _read_atman_loop_T(path) or 50
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
296
|
+
try:
|
|
297
|
+
return translate_pytorch(
|
|
298
|
+
module, runtime_dim=runtime_dim, runtime_seed=runtime_seed,
|
|
299
|
+
loop_max_iterations=loop_T,
|
|
300
|
+
)
|
|
301
|
+
except CodegenNotSupported as exc:
|
|
302
|
+
# The backend can't lower this construct (an unsupported builtin like
|
|
303
|
+
# `snap`, an unsupported node, etc.). `exc` already formats as
|
|
304
|
+
# `line:col: codegen: <message>`; prepend the file path so it reads
|
|
305
|
+
# like every other Sutra diagnostic (`file:line:col: …`) instead of
|
|
306
|
+
# surfacing to the user as an uncaught Python traceback. Single choke
|
|
307
|
+
# point: --run / --emit / runtime-viz all route through here.
|
|
308
|
+
print(f"{path}:{exc}", file=sys.stderr)
|
|
309
|
+
return None
|
|
299
310
|
|
|
300
311
|
|
|
301
312
|
def _run_execute(path: str, *, runtime_dim: int, runtime_seed: int,
|
|
@@ -314,26 +325,48 @@ def _run_execute(path: str, *, runtime_dim: int, runtime_seed: int,
|
|
|
314
325
|
return 1
|
|
315
326
|
mod = types.ModuleType("_sutra_run")
|
|
316
327
|
mod.__file__ = f"<generated from {path}>"
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
328
|
+
try:
|
|
329
|
+
exec(compile(py_src, mod.__file__, "exec"), mod.__dict__)
|
|
330
|
+
if hasattr(mod, "main") and callable(mod.main):
|
|
331
|
+
result = mod.main()
|
|
332
|
+
if result is not None:
|
|
333
|
+
print(_decode_terminal_result(mod, result))
|
|
334
|
+
else:
|
|
335
|
+
# The substrate program is the body of main(); a file without one
|
|
336
|
+
# has nothing to run. Say so rather than exiting silently with no
|
|
337
|
+
# output (a newcomer otherwise can't tell why nothing happened).
|
|
338
|
+
print(f"{path}: no main() found - nothing to run", file=sys.stderr)
|
|
339
|
+
except Exception as exc:
|
|
340
|
+
# A runtime error in the generated module — e.g. a type mismatch the v0.1
|
|
341
|
+
# validator can't catch yet — should read like a Sutra diagnostic, not an
|
|
342
|
+
# uncaught Python traceback. KeyboardInterrupt / SystemExit are not caught
|
|
343
|
+
# (they subclass BaseException, not Exception), so Ctrl-C still works.
|
|
344
|
+
print(f"{path}: runtime error: {type(exc).__name__}: {exc}", file=sys.stderr)
|
|
345
|
+
return 1
|
|
322
346
|
return 0
|
|
323
347
|
|
|
324
348
|
|
|
325
349
|
def _decode_terminal_result(mod, result):
|
|
326
|
-
"""Terminal/output boundary: decode a
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
350
|
+
"""Terminal/output boundary: decode a `main()` result to a host value for
|
|
351
|
+
display. The language has no scalar-readout accessor (`real()` was removed —
|
|
352
|
+
substrate purity); the host reading the FINAL result for display is the one
|
|
353
|
+
external terminal boundary, the same as printing a returned value in any
|
|
354
|
+
runtime.
|
|
355
|
+
|
|
356
|
+
Both a String and a number-vector are 1-D tensors of length `_VSA.dim`; the
|
|
357
|
+
AXIS_STRING_FLAG tells them apart. The string check MUST come first: a String
|
|
358
|
+
stores its codepoints on synthetic axes and the real axis coincides with the
|
|
359
|
+
first codepoint, so a String read as a number prints that char's codepoint
|
|
360
|
+
(e.g. `return "hello world"` would print 104.0 = 'h'). A String decodes to its
|
|
361
|
+
Python text via the sanctioned `string_to_python` terminal decode; a
|
|
362
|
+
number-vector decodes to its real-axis value; anything else prints as-is."""
|
|
332
363
|
try:
|
|
333
364
|
import torch as _t
|
|
334
365
|
vsa = getattr(mod, "_VSA", None)
|
|
335
366
|
if (vsa is not None and isinstance(result, _t.Tensor)
|
|
336
367
|
and result.ndim == 1 and result.shape[0] == vsa.dim):
|
|
368
|
+
if vsa.is_string(result):
|
|
369
|
+
return vsa.string_to_python(result)
|
|
337
370
|
return float(result[vsa.semantic_dim + vsa.AXIS_REAL])
|
|
338
371
|
except Exception:
|
|
339
372
|
pass
|
|
@@ -367,7 +400,7 @@ def _run_viz(path: str, *, runtime_dim: int, runtime_seed: int,
|
|
|
367
400
|
# Inject tracing shim: after the _VSA = _TorchVSA(...) line,
|
|
368
401
|
# wrap every method with a tracing version.
|
|
369
402
|
shim = '''
|
|
370
|
-
#
|
|
403
|
+
# -- Tracing shim (injected by --run-viz) --
|
|
371
404
|
_orig_embed = _VSA.embed
|
|
372
405
|
_orig_bind = _VSA.bind
|
|
373
406
|
_orig_unbind = _VSA.unbind
|
|
@@ -397,7 +430,7 @@ _VSA.embed = _traced_embed
|
|
|
397
430
|
_VSA.bind = _traced_bind
|
|
398
431
|
_VSA.unbind = _traced_unbind
|
|
399
432
|
_VSA.bundle = _traced_bundle
|
|
400
|
-
#
|
|
433
|
+
# -- End tracing shim --
|
|
401
434
|
'''
|
|
402
435
|
# Find the _VSA = _TorchVSA(...) line and inject after it
|
|
403
436
|
lines = py_src.split('\n')
|
|
@@ -483,9 +516,30 @@ def _emit_thrml(path: str) -> int:
|
|
|
483
516
|
|
|
484
517
|
|
|
485
518
|
def main(argv: List[str] | None = None) -> int:
|
|
519
|
+
# `sutrac repl` (or `--repl`) launches the interactive evaluator. Intercepted
|
|
520
|
+
# before argparse because the validator requires a `paths` positional and the
|
|
521
|
+
# REPL takes no file.
|
|
522
|
+
_args = argv if argv is not None else sys.argv[1:]
|
|
523
|
+
if _args and _args[0] in ("repl", "--repl"):
|
|
524
|
+
from .repl import main_repl
|
|
525
|
+
return main_repl()
|
|
526
|
+
|
|
486
527
|
parser = argparse.ArgumentParser(
|
|
487
528
|
prog="sutrac",
|
|
488
529
|
description="Validate Sutra (.su) source files.",
|
|
530
|
+
epilog=(
|
|
531
|
+
"interactive REPL:\n"
|
|
532
|
+
" sutrac repl Launch the interactive evaluator (takes no\n"
|
|
533
|
+
" file). Type an expression to see its value;\n"
|
|
534
|
+
" declarations accumulate as session state.\n"
|
|
535
|
+
"\n"
|
|
536
|
+
"examples:\n"
|
|
537
|
+
" sutrac file.su Validate a source file.\n"
|
|
538
|
+
" sutrac --run file.su Compile and execute (needs torch).\n"
|
|
539
|
+
" sutrac --emit file.su Print the generated PyTorch module.\n"
|
|
540
|
+
" sutrac repl Explore interactively."
|
|
541
|
+
),
|
|
542
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
489
543
|
)
|
|
490
544
|
parser.add_argument(
|
|
491
545
|
"paths",
|
|
@@ -513,7 +567,7 @@ def main(argv: List[str] | None = None) -> int:
|
|
|
513
567
|
help=(
|
|
514
568
|
"Compile the first input file to self-contained torch Python and "
|
|
515
569
|
"print it to stdout. Picks CUDA at module init if available; "
|
|
516
|
-
"falls back to CPU otherwise. This is the one main codegen target
|
|
570
|
+
"falls back to CPU otherwise. This is the one main codegen target - "
|
|
517
571
|
"PyTorch is the runtime and the tensor-op library Sutra compiles "
|
|
518
572
|
"against."
|
|
519
573
|
),
|
|
@@ -536,7 +590,7 @@ def main(argv: List[str] | None = None) -> int:
|
|
|
536
590
|
"stdout. The default PyTorch path (--emit/--run) is untouched; this is "
|
|
537
591
|
"the Extropic thermodynamic-sampling compile target (queue.md approach "
|
|
538
592
|
"G). Requires jax + the thrml submodule. Lowering coverage is the "
|
|
539
|
-
"validated subset only
|
|
593
|
+
"validated subset only - see codegen_thrml.py."
|
|
540
594
|
),
|
|
541
595
|
)
|
|
542
596
|
parser.add_argument(
|
|
@@ -89,7 +89,7 @@ class Codegen(BaseCodegen):
|
|
|
89
89
|
self._semantic_dim = runtime_dim
|
|
90
90
|
self._synthetic_dim = (synthetic_dim if synthetic_dim is not None
|
|
91
91
|
else self.DEFAULT_SYNTHETIC_DIM)
|
|
92
|
-
# List of strings that appear in `
|
|
92
|
+
# List of strings that appear in `embed("...")` calls,
|
|
93
93
|
# populated by translate_module() between simplify and codegen.
|
|
94
94
|
# The codegen emits a batched Ollama pre-fetch at module init
|
|
95
95
|
# to replace N sequential HTTP round-trips with one call.
|
|
@@ -119,6 +119,18 @@ class Codegen(BaseCodegen):
|
|
|
119
119
|
"array_filter",
|
|
120
120
|
})
|
|
121
121
|
|
|
122
|
+
# Steering hints attached to the CodegenNotSupported error for
|
|
123
|
+
# spec'd-but-unimplemented builtins. `snap` is the cleanup primitive
|
|
124
|
+
# backed by an attractor circuit the substrate doesn't have yet;
|
|
125
|
+
# argmax_cosine against an explicit codebook is the implemented form
|
|
126
|
+
# the demos use today (tutorial 03). Keeps the rejection message from
|
|
127
|
+
# being a dead end.
|
|
128
|
+
_UNSUPPORTED_BUILTIN_HINTS = {
|
|
129
|
+
"snap": "use `argmax_cosine(query, [a, b, c])` to clean a vector up "
|
|
130
|
+
"against an explicit codebook; `snap` (attractor-circuit "
|
|
131
|
+
"cleanup) is a future substrate primitive",
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
def _char_literal_src(self, expr: ast.CharLiteral) -> str:
|
|
123
135
|
"""Lower `'a'` to a runtime make_char call with the code point."""
|
|
124
136
|
return f"_VSA.make_char({int(expr.value)})"
|
|
@@ -444,10 +456,12 @@ class Codegen(BaseCodegen):
|
|
|
444
456
|
callee = call.callee
|
|
445
457
|
if isinstance(callee, ast.Identifier):
|
|
446
458
|
if callee.name in self._UNSUPPORTED_BUILTINS:
|
|
459
|
+
hint = self._UNSUPPORTED_BUILTIN_HINTS.get(callee.name)
|
|
460
|
+
detail = f" — {hint}" if hint else ""
|
|
447
461
|
raise CodegenNotSupported(
|
|
448
462
|
call,
|
|
449
|
-
f"`{callee.name}` is not supported on the
|
|
450
|
-
f"
|
|
463
|
+
f"`{callee.name}` is not yet supported on the substrate "
|
|
464
|
+
f"(no runtime lowering on this backend){detail}",
|
|
451
465
|
)
|
|
452
466
|
if (isinstance(callee, ast.MemberAccess)
|
|
453
467
|
and callee.member in self._REMOVED_SCALAR_ACCESSORS):
|
|
@@ -512,7 +526,7 @@ class Codegen(BaseCodegen):
|
|
|
512
526
|
self._emit("# repeated bind/unbind with the same role O(d^2) lookup + matmul.")
|
|
513
527
|
self._emit("self._rot_cache = {}")
|
|
514
528
|
self._emit("# On-disk embedding cache. Second-and-later runs load every")
|
|
515
|
-
self._emit("# previously-seen
|
|
529
|
+
self._emit("# previously-seen embed(...) string from disk instead of")
|
|
516
530
|
self._emit("# hitting Ollama. Cache is keyed by (model, dim) so changing")
|
|
517
531
|
self._emit("# either invalidates cleanly (different cache file).")
|
|
518
532
|
self._emit("import os as _os")
|
|
@@ -524,9 +538,13 @@ class Codegen(BaseCodegen):
|
|
|
524
538
|
self._emit("_os.makedirs(self._cache_dir, exist_ok=True)")
|
|
525
539
|
self._emit("# Sanitize model name for use as filename.")
|
|
526
540
|
self._emit("_safe_model = llm_model.replace('/', '_').replace(':', '_')")
|
|
541
|
+
self._emit("# Backend-aware: in-process (transformers) and ollama realize the")
|
|
542
|
+
self._emit("# same model with slightly different geometry, so they must not")
|
|
543
|
+
self._emit("# share a cache file or one backend reads the other's vectors.")
|
|
544
|
+
self._emit("_emb_backend = _os.environ.get('SUTRA_EMBED_BACKEND', 'auto').strip().lower() or 'auto'")
|
|
527
545
|
self._emit("self._cache_path = _os.path.join(")
|
|
528
546
|
self._indent += 1
|
|
529
|
-
self._emit("self._cache_dir, f'{_safe_model}-d{self.dim}.npz')")
|
|
547
|
+
self._emit("self._cache_dir, f'{_safe_model}-d{self.dim}-{_emb_backend}.npz')")
|
|
530
548
|
self._indent -= 1
|
|
531
549
|
self._emit("self._load_disk_cache()")
|
|
532
550
|
self._emit("# Transcendental lookup tables — substrate-pure interpolation per")
|
|
@@ -641,9 +659,9 @@ class Codegen(BaseCodegen):
|
|
|
641
659
|
self._emit('starts at zero."""')
|
|
642
660
|
self._emit("if name not in self._codebook:")
|
|
643
661
|
self._indent += 1
|
|
644
|
-
self._emit("import
|
|
645
|
-
self._emit("r =
|
|
646
|
-
self._emit("v = _np.array(r[
|
|
662
|
+
self._emit("from sutra_compiler.embedding import embed_texts as _embed_texts")
|
|
663
|
+
self._emit("r = _embed_texts([name], self.llm_model)")
|
|
664
|
+
self._emit("v = _np.array(r[0], dtype=_np.float64)")
|
|
647
665
|
self._emit("# Mean-center. Raw LLM embeddings cluster in a cone (all-")
|
|
648
666
|
self._emit("# positive-ish); centering keeps rotation/bind algebra")
|
|
649
667
|
self._emit("# well-behaved.")
|
|
@@ -685,11 +703,11 @@ class Codegen(BaseCodegen):
|
|
|
685
703
|
self._indent += 1
|
|
686
704
|
self._emit("return")
|
|
687
705
|
self._indent -= 1
|
|
688
|
-
self._emit("import
|
|
689
|
-
self._emit("r =
|
|
706
|
+
self._emit("from sutra_compiler.embedding import embed_texts as _embed_texts")
|
|
707
|
+
self._emit("r = _embed_texts(missing, self.llm_model)")
|
|
690
708
|
self._emit("for i, name in enumerate(missing):")
|
|
691
709
|
self._indent += 1
|
|
692
|
-
self._emit("v = _np.array(r[
|
|
710
|
+
self._emit("v = _np.array(r[i], dtype=_np.float64)")
|
|
693
711
|
self._emit("v = v - _np.mean(v)")
|
|
694
712
|
self._emit("n = _np.linalg.norm(v)")
|
|
695
713
|
self._emit("if n > 0: v = v / n")
|
|
@@ -1682,7 +1700,7 @@ class Codegen(BaseCodegen):
|
|
|
1682
1700
|
self._emit("distinguish `'a'` (97 with flag) from `97` (97 without).")
|
|
1683
1701
|
self._emit("Arithmetic on chars works the same as on ints — both")
|
|
1684
1702
|
self._emit("live on the number axis. Downstream code that cares")
|
|
1685
|
-
self._emit("about the distinction can read the flag via `
|
|
1703
|
+
self._emit("about the distinction can read the flag via `is_string`.")
|
|
1686
1704
|
self._emit('"""')
|
|
1687
1705
|
self._emit("v = _np.zeros(self.dim, dtype=_np.float64)")
|
|
1688
1706
|
self._emit("v[self.semantic_dim + self.AXIS_REAL] = float(codepoint)")
|
|
@@ -2146,7 +2164,7 @@ class Codegen(BaseCodegen):
|
|
|
2146
2164
|
f"seed={self.runtime_seed}, "
|
|
2147
2165
|
f"llm_model={self._llm_model!r})"
|
|
2148
2166
|
)
|
|
2149
|
-
# Batched pre-fetch of every
|
|
2167
|
+
# Batched pre-fetch of every embed("...") string argument
|
|
2150
2168
|
# the program uses. One Ollama round-trip instead of N sequential
|
|
2151
2169
|
# ones. Collected by the simplify pass (see translate_module).
|
|
2152
2170
|
if self._prefetch_strings:
|
|
@@ -2233,11 +2251,11 @@ def translate_module(module: ast.Module, **kwargs) -> str:
|
|
|
2233
2251
|
Runs the simplification pass over the AST before handing to the
|
|
2234
2252
|
codegen so identity rewrites (bundle(v) -> v, bundle flattening)
|
|
2235
2253
|
happen in source-to-source form rather than in the emitted
|
|
2236
|
-
Python. Also collects every `
|
|
2254
|
+
Python. Also collects every `embed("...")` string literal
|
|
2237
2255
|
so the codegen can emit a batched Ollama pre-fetch at module init
|
|
2238
2256
|
(N HTTP round-trips collapse into one batched embed call).
|
|
2239
2257
|
"""
|
|
2240
|
-
from .simplify import simplify_module,
|
|
2258
|
+
from .simplify import simplify_module, collect_embedded_strings
|
|
2241
2259
|
from .inliner import inline_stdlib_calls
|
|
2242
2260
|
from .promise_desugar import desugar_promises
|
|
2243
2261
|
from .loop_desugar import desugar_implicit_loops
|
|
@@ -2263,7 +2281,7 @@ def translate_module(module: ast.Module, **kwargs) -> str:
|
|
|
2263
2281
|
# absorption, which can fold parts of the inlined form.
|
|
2264
2282
|
inline_stdlib_calls(module)
|
|
2265
2283
|
simplify_module(module)
|
|
2266
|
-
strings =
|
|
2284
|
+
strings = collect_embedded_strings(module)
|
|
2267
2285
|
cg = Codegen(**kwargs)
|
|
2268
2286
|
cg._prefetch_strings = strings
|
|
2269
2287
|
cg._axon_keys_bound = bound_keys
|