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.
- sutra_compiler/__init__.py +49 -0
- sutra_compiler/__main__.py +514 -0
- sutra_compiler/ast_nodes.py +553 -0
- sutra_compiler/codegen.py +1811 -0
- sutra_compiler/codegen_base.py +2436 -0
- sutra_compiler/codegen_pytorch.py +1472 -0
- sutra_compiler/diagnostics.py +145 -0
- sutra_compiler/inliner.py +581 -0
- sutra_compiler/lexer.py +821 -0
- sutra_compiler/parser.py +2112 -0
- sutra_compiler/review.py +322 -0
- sutra_compiler/simplify.py +1046 -0
- sutra_compiler/simplify_egglog.py +674 -0
- sutra_compiler/stdlib/axons.su +53 -0
- sutra_compiler/stdlib/embed.su +48 -0
- sutra_compiler/stdlib/javascript_object.su +18 -0
- sutra_compiler/stdlib/logic.su +202 -0
- sutra_compiler/stdlib/math.su +12 -0
- sutra_compiler/stdlib/memory.su +82 -0
- sutra_compiler/stdlib/numbers.su +99 -0
- sutra_compiler/stdlib/rotation.su +83 -0
- sutra_compiler/stdlib/similarity.su +97 -0
- sutra_compiler/stdlib/strings.su +56 -0
- sutra_compiler/stdlib/tensor.su +82 -0
- sutra_compiler/stdlib/vectors.su +119 -0
- sutra_compiler/stdlib_loader.py +219 -0
- sutra_compiler/sutradb_embedded.py +273 -0
- sutra_compiler/trace.py +135 -0
- sutra_compiler/validator.py +552 -0
- sutra_compiler/workspace.py +655 -0
- sutra_dev-0.2.0.dist-info/METADATA +80 -0
- sutra_dev-0.2.0.dist-info/RECORD +36 -0
- sutra_dev-0.2.0.dist-info/WHEEL +5 -0
- sutra_dev-0.2.0.dist-info/entry_points.txt +2 -0
- sutra_dev-0.2.0.dist-info/licenses/LICENSE +201 -0
- sutra_dev-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -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.
|