sutra-dev 0.2.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.
@@ -0,0 +1,53 @@
1
+ // Canonical Sutra declaration for the Axon class.
2
+ //
3
+ // Spec: planning/sutra-spec/axons.md.
4
+ //
5
+ // An axon is a bundle of named variables passed as a single value —
6
+ // closer to a Haskell-monad-shaped structuring construct than a
7
+ // runtime hash map (no runtime input → output lookup, no iteration,
8
+ // no schema). The user-facing API resembles a dict in syntax only.
9
+ //
10
+ // At the substrate level the operations route to:
11
+ // axon_new() → a zero vector at runtime dim
12
+ // axon_add(a, k, v) → a + bind(basis_vector(k), v)
13
+ // axon_item(a, k) → unbind(basis_vector(k), a)
14
+ //
15
+ // String keys are compile-time identifiers; the runtime auto-embeds
16
+ // them into basis vectors at the call boundary.
17
+ //
18
+ // Surface forms wired today:
19
+ // - `Axon a;` → `a = _VSA.axon_new()`
20
+ // - `a.add(k, v);` → `a = _VSA.axon_add(a, k, v)`
21
+ // (statement form: augmented assignment;
22
+ // the instance method is "void-returning"
23
+ // from the user's POV — the compiler
24
+ // rebinds `a` to the returned new axon)
25
+ // - `a.item(k)` (expr) → `_VSA.axon_item(a, k)`
26
+ // - `Axon.axon_add(a, ...)` → `_VSA.axon_add(a, ...)` also works
27
+ // (static-class-method form, in case
28
+ // you want to be explicit)
29
+ //
30
+ // The augmented-assignment rule for void-returning instance methods
31
+ // is conceptually general (every class should work this way), but
32
+ // the wiring in codegen_base.py is currently axon-specific. Other
33
+ // classes still error if you try `obj.method(args)` on a
34
+ // non-class-namespace receiver. Generalizing the rule is a separate
35
+ // piece of work.
36
+ //
37
+ // What is deferred:
38
+ // - Per-entry class tags (deemed an IDE-level concern; the user's
39
+ // idiom is to declare the retrieval type explicitly:
40
+ // `Cat c = a.item("cat");`).
41
+ // - Lazy evaluation across function/program boundaries — the spec
42
+ // says only receiver-referenced keys materialize at the
43
+ // boundary; today the full bundle crosses. Within a single
44
+ // function, the SSA-elision pass DOES drop adds whose keys are
45
+ // never read (and the axon doesn't escape). Cross-function
46
+ // analysis is the unimplemented piece.
47
+
48
+
49
+ class Axon extends vector {
50
+ static intrinsic method Axon axon_new();
51
+ static intrinsic method Axon axon_add(Axon a, string key, vector value);
52
+ static intrinsic method vector axon_item(Axon a, string key);
53
+ }
@@ -0,0 +1,48 @@
1
+ // Canonical Sutra definition for the `embed` operation — turn a
2
+ // string into a semantic vector by running it through the frozen
3
+ // embedding model (Ollama / nomic-embed-text today).
4
+ //
5
+ // Today this is hardcoded as the `def embed(self, s)` method on the
6
+ // runtime VSA class and batched via `def embed_batch` for the
7
+ // compile-time prefetch pass. See `_emit_prelude` in codegen.py.
8
+ // This file is the target shape for the inliner. See stdlib/README.md.
9
+ //
10
+ // `embed` is a pure intrinsic — it has no Sutra-level body. The
11
+ // string goes to an external process (the embedding model), a
12
+ // vector comes back. There's nothing to inline; the function call
13
+ // is a leaf in the dataflow graph.
14
+ //
15
+ // Listing it here anyway for completeness — so the stdlib directory
16
+ // is the single source of truth for every system function's
17
+ // signature and status. Once the inliner lands, `embed` will be
18
+ // marked `@intrinsic` (or whatever the chosen syntax is) and pass
19
+ // through the inliner unchanged.
20
+
21
+
22
+ // ==========================================================================
23
+ // Section 3 — pure intrinsic (no Sutra body)
24
+ // ==========================================================================
25
+ //
26
+ // embed — string → semantic vector.
27
+ //
28
+ // @intrinsic
29
+ // function vector embed(string s) {
30
+ // // runtime: hits Ollama, caches to disk, mean-centers,
31
+ // // L2-normalizes, returns the semantic block padded with
32
+ // // zeros in the synthetic block.
33
+ // }
34
+ //
35
+ // Intrinsic primitive. The compile-time prefetch (collecting every
36
+ // `basis_vector("...")` / `embed("...")` string literal at module
37
+ // init and batching them into one Ollama call) stays a codegen-
38
+ // level optimization — it's transparent to the .su-level semantics.
39
+ //
40
+ // basis_vector is an alias for embed with a different spelling (VSA-
41
+ // role allocation vs. pure embedding). Both hit the same intrinsic.
42
+ //
43
+ // ==========================================================================
44
+
45
+
46
+ class Embedding extends vector {
47
+ static intrinsic method vector embed(string s);
48
+ }
@@ -0,0 +1,18 @@
1
+ // JavaScriptObject — the catch-all class for values that come out of
2
+ // JavaScript / TypeScript code without resolvable static types. A
3
+ // transpiled `function f(x) { return x + x; }` parameter `x` is
4
+ // declared `JavaScriptObject x` because the source had no type
5
+ // annotation and the value could be anything.
6
+ //
7
+ // Today this is intentionally a near-empty class. Sutra's substrate
8
+ // already handles vector arithmetic and the auto-promotion of
9
+ // scalars to vectors, so for numeric JS code, treating
10
+ // JavaScriptObject as "just a vector" is enough — `x + x` lowers to
11
+ // tensor addition without further help. Real JS semantics (type
12
+ // coercion in `+` between strings and numbers, ===-vs-==, prototype
13
+ // chains, etc.) live in deferred work.
14
+ //
15
+ // Spec: planning/sutra-spec/javascript-object.md (TBD).
16
+
17
+ class JavaScriptObject extends vector {
18
+ }
@@ -0,0 +1,202 @@
1
+ // Canonical Sutra definitions for truth-axis and logic operations.
2
+ //
3
+ // Today these are hardcoded as runtime methods in codegen.py —
4
+ // see the `def defuzzify(...)`, `def logical_and(...)`, `def eq(...)`,
5
+ // `def gt(...)` etc. sections of _emit_prelude. This file is where
6
+ // they want to live: as Sutra source the compiler parses, inlines
7
+ // into user programs, and unrolls/fuses at compile time. See
8
+ // stdlib/README.md for the broader plan.
9
+ //
10
+ // Two sections below:
11
+ // 1. IMPLEMENTED IN SUTRA — bodies the parser accepts today.
12
+ // 2. BLOCKED ON INTRINSICS — bodies that reach into primitives
13
+ // (dot, sqrt, tanh, axis projectors, cached polynomial
14
+ // matrices) that aren't language-level yet. These stay as
15
+ // commented-out pseudo-Sutra until an `intrinsic` mechanism
16
+ // or primitive-op surface lands.
17
+
18
+
19
+ // ==========================================================================
20
+ // Section 1 — implemented in pure Sutra
21
+ // ==========================================================================
22
+
23
+
24
+ // defuzzy — polarize any value along the truth axis.
25
+ //
26
+ // Iterates cosine equality with `true` ten times. Under the
27
+ // eps-guarded cosine form of `==`, inputs with truth=0 stay at 0
28
+ // (unknown stays unknown — the neutral point is a fixed point of
29
+ // equality-with-true) and inputs with truth!=0 sharpen toward ±1.
30
+ //
31
+ // Ten iterations is the spec-level definition. One pass mathematically
32
+ // suffices for well-separated inputs; the loop is kept because the
33
+ // fully-unrolled form is what the compiler targets for fusion.
34
+ //
35
+ // Historical note: the original formulation was just `v = v == true`
36
+ // (one pass, no loop). That had a pathology where `unknown` (truth=0)
37
+ // got pulled toward false because the zero-norm case in cosine
38
+ // similarity was handled with a branch. The eps-guarded divide in
39
+ // the current `==` fixes the fixed-point at 0 without branching, which
40
+ // is why the loop is safe to unroll.
41
+ function fuzzy defuzzy(fuzzy v) {
42
+ loop (10) {
43
+ v = v == true;
44
+ }
45
+ return v;
46
+ }
47
+
48
+
49
+ // logical_not — Kleene K₃ negation on the truth axis.
50
+ //
51
+ // Polynomial form: NOT(v) = -v. Exact on {-1, 0, +1}, preserves the
52
+ // neutral point (NOT(unknown) = unknown), differentiable everywhere.
53
+ // Sign-flip is the entirety of the operation.
54
+ function fuzzy logical_not(fuzzy v) {
55
+ return 0 - v;
56
+ }
57
+
58
+
59
+ // logical_and — Kleene K₃ AND. Lagrange polynomial, exact on
60
+ // {-1, 0, +1}², smooth everywhere:
61
+ //
62
+ // a ∧ b = (a + b + ab - a² - b² + a²b²) / 2
63
+ //
64
+ // No absolute value → no compile-time kink → no branch. Every
65
+ // subexpression stays a tensor op. This is the form the fusion pass
66
+ // wants to see once user-written `a && b` inlines to this body.
67
+ function fuzzy logical_and(fuzzy a, fuzzy b) {
68
+ return (a + b + a * b - a * a - b * b + a * a * b * b) * 0.5;
69
+ }
70
+
71
+
72
+ // logical_or — Kleene K₃ OR. Dual polynomial to AND:
73
+ //
74
+ // a ∨ b = (a + b - ab + a² + b² - a²b²) / 2
75
+ function fuzzy logical_or(fuzzy a, fuzzy b) {
76
+ return (a + b - a * b + a * a + b * b - a * a * b * b) * 0.5;
77
+ }
78
+
79
+
80
+ // logical_nand — NOT(AND). Composition over the existing primitives;
81
+ // the inliner expands the !(...) wrapper and then the AND polynomial,
82
+ // so the final emitted form is one polynomial subtracted from a
83
+ // constant — no new substrate primitive is introduced.
84
+ function fuzzy logical_nand(fuzzy a, fuzzy b) {
85
+ return !logical_and(a, b);
86
+ }
87
+
88
+
89
+ // logical_xor — Kleene K₃ XOR. Closed-form polynomial: -a·b.
90
+ //
91
+ // Verification on the K₃ grid {-1, 0, +1}²:
92
+ // (1, 1) -> -1 (T XOR T = F)
93
+ // (1, -1) -> +1 (T XOR F = T)
94
+ // (-1, 1) -> +1 (F XOR T = T)
95
+ // (-1,-1) -> -1 (F XOR F = F)
96
+ // (0, 1) -> 0 (U XOR T = U — unknown is a fixed point)
97
+ // (1, 0) -> 0 (T XOR U = U)
98
+ //
99
+ // Equivalent to the longer composition `(a && !b) || (!a && b)`,
100
+ // which observation: reduces to `-a·b` after polynomial folding —
101
+ // see docs/compilation.md § "Worked examples". Shipping the closed
102
+ // form directly so the simplifier doesn't have to discover it.
103
+ function fuzzy logical_xor(fuzzy a, fuzzy b) {
104
+ return 0 - a * b;
105
+ }
106
+
107
+
108
+ // logical_xnor — Kleene K₃ XNOR / IFF. NOT(XOR), which simplifies to
109
+ // the bare product:
110
+ //
111
+ // (1, 1) -> +1 (T <-> T = T)
112
+ // (1, -1) -> -1 (T <-> F = F)
113
+ // (-1,-1) -> +1 (F <-> F = T)
114
+ // (0, 1) -> 0 (unknown bilateral)
115
+ //
116
+ // Same shape as logical_xor with the sign flipped. `iff` is the
117
+ // natural-language alias the lexer also accepts.
118
+ function fuzzy logical_xnor(fuzzy a, fuzzy b) {
119
+ return a * b;
120
+ }
121
+
122
+
123
+ // neq lives in similarity.su — it's a composition over vector cosine
124
+ // similarity, not a truth-axis primitive. Cross-referenced there.
125
+
126
+
127
+ // lt — strictly less than. `>` with sides swapped.
128
+ function fuzzy lt(complex a, complex b) {
129
+ return b > a;
130
+ }
131
+
132
+
133
+ // ge / le — on the differentiable-tanh scheme, `>=` and `<=` collapse
134
+ // to `>` and `<`: both give tanh(0) = 0 on exact ties. Programs that
135
+ // need to distinguish strict from tie compose with `==` (cosine
136
+ // similarity, crisp on identical operands).
137
+ function fuzzy ge(complex a, complex b) {
138
+ return a > b;
139
+ }
140
+
141
+ function fuzzy le(complex a, complex b) {
142
+ return a < b;
143
+ }
144
+
145
+
146
+ // ==========================================================================
147
+ // Section 2 — blocked on intrinsics
148
+ // ==========================================================================
149
+ //
150
+ // defuzzify_trit — three-way polarizer (true / false / unknown) with
151
+ // a β-sharpening knob instead of the straight cosine-eq loop.
152
+ //
153
+ // function trit defuzzify_trit(trit v, int iters, scalar beta) {
154
+ // vector t = truth_projector @ v;
155
+ // loop (iters) {
156
+ // t = sign_polarize(t, beta);
157
+ // }
158
+ // return t;
159
+ // }
160
+ //
161
+ // Blocked on: `truth_projector` as a matrix literal, `sign_polarize`
162
+ // β primitive.
163
+
164
+
165
+ // gt — differentiable smooth sign on real-axis difference.
166
+ //
167
+ // function fuzzy gt(complex a, complex b) {
168
+ // vector diff = a - b;
169
+ // vector diff_r = real_projector @ diff;
170
+ // vector signed = tanh(100.0 * diff_r);
171
+ // return truth_from_real @ signed;
172
+ // }
173
+ //
174
+ // Blocked on: axis-projector matrix literals, `tanh` on vectors,
175
+ // `@` matmul as a Sutra operator.
176
+ //
177
+ // See also:
178
+ // similarity.su — `eq`, `neq`, cosine operations on vectors.
179
+ // numbers.su — `complex_mul`, `make_real`, `make_complex`, `make_char`.
180
+ // vectors.su — `bind`, `unbind`, `bundle`, permutation ops.
181
+ // memory.su — `zero_vector`, hashmap ops.
182
+ // rotation.su — rotation-matrix construction and eigenrotation loop.
183
+ // embed.su — the `embed` LLM-call intrinsic.
184
+
185
+
186
+ // ==========================================================================
187
+ // Section 3 — intrinsic declarations
188
+ // ==========================================================================
189
+ //
190
+ // `gt` and `make_truth` are runtime primitives. `gt` uses tanh on
191
+ // the real-axis difference (not expressible in pure Sutra until
192
+ // tanh has a language-level surface); `make_truth` writes a scalar
193
+ // into the truth-axis slot (not expressible until axis-indexed
194
+ // write lands).
195
+
196
+ intrinsic function fuzzy gt(complex a, complex b);
197
+ intrinsic function fuzzy make_truth(scalar t);
198
+ // truth_axis — read AXIS_TRUTH from a fuzzy vector and return it as
199
+ // a scalar. The transpiler-emitted if/else lowering uses this to
200
+ // pull a scalar out of `defuzzy(cond)` for use as a softmax score
201
+ // in `select`.
202
+ intrinsic function scalar truth_axis(vector v);
@@ -0,0 +1,12 @@
1
+
2
+
3
+
4
+ class Math extends vector {
5
+ static intrinsic method scalar log(scalar x);
6
+ static intrinsic method scalar sqrt(scalar x);
7
+ static intrinsic method scalar exp(scalar x);
8
+ static intrinsic method scalar sin(scalar x);
9
+ static intrinsic method scalar cos(scalar x);
10
+ static intrinsic method scalar tan(scalar x);
11
+ static intrinsic method scalar pow(scalar x, scalar y);
12
+ }
@@ -0,0 +1,82 @@
1
+ // Canonical Sutra definitions for memory / lookup operations:
2
+ // `zero_vector`, the rotation-hashmap `hashmap_get` / `hashmap_set`,
3
+ // and the identity-or-nearest-key map lookup.
4
+ //
5
+ // Today these are hardcoded as runtime methods in codegen.py — see
6
+ // `def zero_vector`, `def hashmap_get`, `def hashmap_set`, and the
7
+ // emitted `_vector_map_lookup` helper. This file is the target shape
8
+ // for the inliner. See stdlib/README.md.
9
+ //
10
+ // Every function below is currently blocked on intrinsics — they
11
+ // need indexed construction, cosine-based argmax, or functional-
12
+ // update semantics that aren't Sutra-callable yet.
13
+
14
+
15
+ // ==========================================================================
16
+ // Section 2 — blocked on intrinsics
17
+ // ==========================================================================
18
+ //
19
+ // zero_vector — the zero vector at runtime dim. Every component is 0,
20
+ // including every synthetic slot. Used as the neutral element for
21
+ // bundling and as the starting state for constructors like
22
+ // `make_real` / `make_complex`.
23
+ //
24
+ // function vector zero_vector() {
25
+ // return _VSA_zeros(runtime_dim);
26
+ // }
27
+ //
28
+ // Blocked on: `_VSA_zeros(N)` primitive — a d-dim zero vector
29
+ // literal. Could alternatively be expressed as an axis-indexed
30
+ // assignment starting from a fresh allocation once those surfaces
31
+ // land.
32
+
33
+
34
+ // hashmap_get — look up a key in a rotation hashmap, returning the
35
+ // bound value. Keys are vectors; lookup is cosine-nearest against
36
+ // the set of rotation-bound (key, value) pairs.
37
+ //
38
+ // function vector hashmap_get(map m, vector key) {
39
+ // vector bound = unbind(key, m.state);
40
+ // return snap(bound, m.values);
41
+ // }
42
+ //
43
+ // Blocked on: `map` type's field access (`m.state`, `m.values`),
44
+ // `unbind` inlining (see vectors.su), `snap` (substrate cleanup
45
+ // primitive — see similarity.su).
46
+
47
+
48
+ // hashmap_set — functional update. Returns a new map with the
49
+ // (key, value) pair added or overwritten.
50
+ //
51
+ // function map hashmap_set(map m, vector key, vector value) {
52
+ // vector fresh = bundle(m.state, bind(key, value));
53
+ // return map_with_state(m, fresh);
54
+ // }
55
+ //
56
+ // Blocked on: same as hashmap_get, plus `map_with_state` constructor
57
+ // for the map-type update.
58
+
59
+
60
+ // map_lookup — identity-first lookup over a list of (key, value)
61
+ // pairs. Checks each key against the query by identity (same Python
62
+ // object) first; falls back to cosine-nearest match against the
63
+ // stored keys.
64
+ //
65
+ // function vector map_lookup(pair[] pairs, vector key) {
66
+ // foreach (pair p in pairs) {
67
+ // if (p.key is key) {
68
+ // return p.value;
69
+ // }
70
+ // }
71
+ // return argmax_cosine(key, [p.key for p in pairs]).value;
72
+ // }
73
+ //
74
+ // Blocked on: `is` identity comparison, comprehension syntax,
75
+ // tuple/pair destructuring, and the `argmax_cosine` composition
76
+ // from similarity.su.
77
+
78
+
79
+
80
+ class Memory extends vector {
81
+ static intrinsic method vector zero_vector();
82
+ }
@@ -0,0 +1,99 @@
1
+ // Canonical Sutra definitions for the number family (int / float /
2
+ // complex / char). All four share the synthetic[AXIS_REAL] and
3
+ // synthetic[AXIS_IMAG] slots at the vector level; `complex` is the
4
+ // only one that occupies both, while `int` and `float` leave imag at
5
+ // 0 and `char` additionally sets synthetic[AXIS_CHAR_FLAG] = 1.
6
+ //
7
+ // Today the constructors (make_real, make_complex, make_char) and
8
+ // the arithmetic primitives (complex_mul and the three cached
9
+ // mul-matrices) are hardcoded as runtime methods in codegen.py —
10
+ // see `def make_real`, `def make_complex`, `def make_char`, and the
11
+ // complex_mul block. This file is the target shape for the inliner
12
+ // to consume. See stdlib/README.md.
13
+ //
14
+ // Every function here is currently blocked on intrinsics — the
15
+ // primitives they need (writing a scalar into a specific synthetic
16
+ // slot; matrix literals; the `@` matmul operator on a Sutra-level
17
+ // matrix) aren't exposed at the language level yet.
18
+
19
+
20
+ // ==========================================================================
21
+ // Section 2 — blocked on intrinsics
22
+ // ==========================================================================
23
+ //
24
+ // make_real — scalar on the real axis, zero elsewhere.
25
+ //
26
+ // function complex make_real(scalar x) {
27
+ // vector v = zero_vector();
28
+ // v[semantic_dim + AXIS_REAL] = x;
29
+ // return v;
30
+ // }
31
+ //
32
+ // Blocked on: `zero_vector()` surface, indexed write to a synthetic
33
+ // slot.
34
+
35
+
36
+ // make_complex — two scalars placed on the real and imag axes.
37
+ //
38
+ // function complex make_complex(scalar r, scalar i) {
39
+ // vector v = zero_vector();
40
+ // v[semantic_dim + AXIS_REAL] = r;
41
+ // v[semantic_dim + AXIS_IMAG] = i;
42
+ // return v;
43
+ // }
44
+ //
45
+ // Blocked on: same as make_real.
46
+
47
+
48
+ // make_char — integer code point on the real axis + char flag on the
49
+ // synthetic axis.
50
+ //
51
+ // function char make_char(int code) {
52
+ // vector v = zero_vector();
53
+ // v[semantic_dim + AXIS_REAL] = code;
54
+ // v[semantic_dim + AXIS_CHAR_FLAG] = 1.0;
55
+ // return v;
56
+ // }
57
+ //
58
+ // Blocked on: same as make_real.
59
+
60
+
61
+ // complex_mul — pure-matmul complex multiplication using three cached
62
+ // matrices: `_swap_ri` (real-imag swap), `_cm_real` (extract real
63
+ // product into real slot), `_cm_imag` (extract imag product into
64
+ // imag slot).
65
+ //
66
+ // function complex complex_mul(complex a, complex b) {
67
+ // vector ab = a * b;
68
+ // vector swapped_ab = (_swap_ri @ a) * b;
69
+ // return _cm_real @ ab + _cm_imag @ swapped_ab;
70
+ // }
71
+ //
72
+ // Blocked on: matrix literals for the three cached matrices, `@`
73
+ // matmul as a Sutra operator. The matrices themselves are
74
+ // d×d sparse with a handful of entries — the fusion pass wants
75
+ // to see them as compile-time constants so chains of complex_mul
76
+ // collapse into a single cached matrix.
77
+
78
+
79
+ // conj — complex conjugate. Flips the imag-axis sign, leaves the real
80
+ // axis and everything else alone.
81
+ //
82
+ // function complex conj(complex c) {
83
+ // vector out = c;
84
+ // out[semantic_dim + AXIS_IMAG] = -c[semantic_dim + AXIS_IMAG];
85
+ // return out;
86
+ // }
87
+ //
88
+ // Blocked on: indexed read/write to synthetic slots. Alternative
89
+ // formulation as a matrix (diag of 1s with a -1 at the imag slot)
90
+ // needs matmul + matrix-literal surfaces.
91
+
92
+
93
+
94
+ class Numbers extends vector {
95
+ static intrinsic method complex make_real(scalar x);
96
+ static intrinsic method complex make_complex(scalar r, scalar i);
97
+ static intrinsic method char make_char(int code);
98
+ static intrinsic method complex complex_mul(complex a, complex b);
99
+ }
@@ -0,0 +1,83 @@
1
+ // Canonical Sutra definitions for rotation construction and the
2
+ // eigenrotation loop that runs behind runtime-conditional `loop` /
3
+ // `while` constructs.
4
+ //
5
+ // Today these are hardcoded as runtime methods in codegen.py — see
6
+ // `def make_random_rotation`, `def compile_prototypes`, and `def loop`
7
+ // (the `_VSA.loop` geometric-rotation runner). This file is the
8
+ // target shape for the inliner. See stdlib/README.md.
9
+ //
10
+ // All of this is deeply substrate-level: block-diagonal Haar rotations
11
+ // built from QR decomposition of a Gaussian matrix, eigendecomposition
12
+ // for fractional powers, prototype codebooks with per-frame random
13
+ // rotations. Every function below is currently blocked on intrinsics.
14
+
15
+
16
+ // ==========================================================================
17
+ // Section 2 — blocked on intrinsics
18
+ // ==========================================================================
19
+ //
20
+ // make_random_rotation — block-diagonal Haar rotation scaled so its
21
+ // largest eigenphase ≈ `angle`. Haar-uniform in the semantic block,
22
+ // identity in the synthetic block — matches the bind-rotation layout
23
+ // so eigenrotation loops walk the semantic subspace while the
24
+ // synthetic subspace (real / imag / truth / char-flag) stays untouched.
25
+ //
26
+ // function matrix make_random_rotation(scalar angle, int n_planes,
27
+ // int seed) {
28
+ // matrix A = randn(semantic_dim, semantic_dim, seed);
29
+ // (Q_sem, _) = qr(A);
30
+ // (w, V) = eig(Q_sem);
31
+ // matrix phases = angle_of(w) * (angle / pi);
32
+ // matrix R_sem = real(V @ diag(exp(i * phases)) @ inverse(V));
33
+ // matrix R = identity(dim);
34
+ // R[:semantic_dim, :semantic_dim] = R_sem;
35
+ // return R;
36
+ // }
37
+ //
38
+ // Blocked on: `randn`, `qr`, `eig`, complex exponential, matrix slice
39
+ // assignment, `@` matmul. Whole-pipeline primitive.
40
+
41
+
42
+ // compile_prototypes — take a set of prototype vectors, apply a
43
+ // frame-seeded random rotation to each, and bundle the results into
44
+ // a codebook the eigenrotation loop can match against.
45
+ //
46
+ // function vector compile_prototypes(vector[] prototype_vectors,
47
+ // int frame_seed) {
48
+ // matrix frame = make_random_rotation(pi / 4, 20, frame_seed);
49
+ // vector[] framed = [frame @ p for p in prototype_vectors];
50
+ // return bundle(framed);
51
+ // }
52
+ //
53
+ // Blocked on: comprehension syntax, `@` matmul, variadic bundle.
54
+
55
+
56
+ // eigenrotation loop — what `loop(condition)` / `while` compile to.
57
+ // Takes a state vector, a rotation matrix, and a compiled prototype
58
+ // codebook; iterates `state = R @ state` until the state matches a
59
+ // prototype by the substrate's cleanup metric (cosine on the default
60
+ // CPU runtime).
61
+ //
62
+ // function (string, vector, int) eigenrotation_loop(
63
+ // vector initial_state,
64
+ // matrix R,
65
+ // vector prototype_codebook,
66
+ // int max_iters,
67
+ // scalar match_threshold) {
68
+ // vector state = initial_state;
69
+ // int i = 0;
70
+ // loop (max_iters as i) {
71
+ // state = R @ state;
72
+ // string match = match_prototype(state, prototype_codebook,
73
+ // match_threshold);
74
+ // if (match != unknown) {
75
+ // return (match, state, i);
76
+ // }
77
+ // }
78
+ // return (unknown, state, max_iters);
79
+ // }
80
+ //
81
+ // Blocked on: `@` matmul, `match_prototype` substrate primitive,
82
+ // tuple return types, early-return inside `loop`, `unknown` as a
83
+ // Sutra-level sentinel for the string type.