Loops¶
Sutra has four looping constructs: loop, while, do/while, and foreach. Each compiles to one of two patterns:
- Compile-time unroll — the loop body is emitted once per iteration directly into the output, no loop exists at runtime.
- Eigenrotation — the loop state is rotated by a fixed matrix each step; termination is a prototype-match test in vector space. The brain (or GPU tensor code) iterates; Python doesn't.
Which pattern a given loop uses is determined by whether the iteration count is known at compile time.
loop(N) — compile-time unroll¶
When N is an integer literal, the body is emitted three times:
No runtime loop. No iteration counter on the host. The emitted code is straight-line and the compiler can simplify across the copies.
With an index¶
The index variable i takes values 0, 1, 2. Emits a real for i in range(3) when the count isn't a pure literal, otherwise unrolls.
Use loop(N) for small fixed repetitions — stacking a few transformations, generating a short codebook, initializing a banks. It's syntactic sugar over straight-line code.
loop(condition) — eigenrotation¶
When the loop header is a condition (not an integer), the loop compiles to a geometric rotation on the state vector with termination by prototype match:
- Compile the body to a single rotation matrix
R. - Compile the condition into a codebook of target prototypes.
- Emit
_VSA.loop(state, R, prototypes)— the runtime appliesRto the state, checks against the prototypes, and terminates when the match crosses a threshold.
The iteration happens on the substrate. Python does not see the loop counter. The loop terminates because the rotated state enters the target prototype's basin, not because a host counter hit a limit. There's a max_iters safety cap in the runtime, but the semantic terminator is the prototype match.
This is the form that makes Sutra effectively a linear bounded automaton rather than just a feedforward circuit — runtime-conditional iteration over runtime-evolving state.
while(cond) — same eigenrotation machinery¶
Semantically identical to loop(condition). Kept as a keyword because people coming from C-family languages reach for while first. Compiles through the same _translate_while_as_geometric_loop path.
do { body } while (cond) — desugars¶
Desugars to: body executes once, then a while (cond) { body }. The while half compiles to eigenrotation. The desugar happens at the AST level — there's no do-while-specific runtime.
for(init; cond; step) — bounded geometric loop¶
Compiles to a bounded eigenrotation loop. The bound (i < 10) is extracted at compile time, and the rotation is pre-scaled so N iterations equals the intended total angle. Emits for i in range(N): … around a rotation application.
The structure is the same as loop(N) in spirit — fixed count, no runtime termination test — but with the geometric-rotation semantic that the brain expects.
foreach(x in [a, b, c]) — compile-time expansion over array literals¶
When the iterable is a compile-time-known array literal, the body is emitted once per element with step substituted. Three calls in straight line, no runtime iteration. This is how Sutra writes out a fixed sequence of operations that would be a for-each-item loop in other languages.
foreach over a runtime-known array (not a literal) is not currently supported — the array has to be known at compile time so the unrolling can happen.
Choosing between them¶
| you want | use |
|---|---|
| N fixed at compile time, small | loop(N) |
| N fixed, need the index | loop(N as i) |
| iterate a fixed list of items | foreach(x in [...]) |
| iterate until a state converges | loop(condition) or while(condition) |
| do-at-least-once with a continue-condition | do { } while |
| C-style three-part index loop | for(init; cond; step) |
The common theme: loops that unroll at compile time stay straight-line in the emitted code and let the compiler simplify across iterations. Loops that depend on runtime state evolve the state via rotation and terminate by prototype match.