Skip to content

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog.

  • Schedule animation — per-variable check-mark controls + gray index space (issue #142): with 3+ recurrence variables the coloured lattice reads as a dense blur. The legend of every schedule-anim (and the shared legend of schedule-compare) is now a set of check-mark controls, one per variable — checked shows that variable’s activity wavefront in colour; unchecked hides its coloured dots. The index space is drawn as a single gray dot at each index point of the domain of execution — one dot per lattice site, not one per variable (a static backdrop over the deduplicated point set; e.g. matmul’s 15³ cube shows 3375 backdrop dots rather than 3×3375 jittered ones). Only the fired and firing points of checked variables carry colour on top; pending points and hidden variables show just the gray backdrop, so the wavefront you care about stands out. (Tile mode is unchanged — colour encodes the tile block, so there is no gray backdrop and pending points keep a dim tile colour.) Dependency/legality/tile overlay edges are filtered to the visible variables too. (docs-site/src/components/schedule-anim.js, src/styles/custom.css.)

  • SURE DSL — piecewise variables + Jacobi/Gauss–Seidel as pure SUREs (issue #53): a variable may now have more than one equation, each on a disjoint sub-domain — a conditional/piecewise recurrence, e.g. a pipelined state routed differently for i>j, i<j, i=j. RecurrenceSystem stores a list of branches per name with resolve(name,p) / coversAny(name,p); the simulator, legality, RDG, coverage check, and schedule emit all dispatch by sub-domain. This makes it possible to write Jacobi and Gauss–Seidel as pure SUREs (docs/SURE/jacobi_systolic.sure, gauss_seidel_systolic.sure, test_systolic_sure, committed RDG + free schedule): instead of an affine matrix-vector gather, the state is pipelined cell-to-cell (produced at the diagonal, broadcast up/down as a piecewise xx) and the reduction is split (accL/accU) so the diagonal solve reads adjacent columns — leaving every dependence a constant displacement (kind: SURE, zero affine arcs), the property a regular spatial mapping needs. Both still converge toward the exact Ax=b solution [1,1,1] (Jacobi within 1e-3, Gauss–Seidel within 1e-6, at K=8 sweeps); the Stationary iteration page gains a systolic SURE forms section contrasting them with the SARE forms. Dual-compiler, full suite green.

  • SURE DSL — per-equation domains and the Gauss–Seidel operator (issue #53): the SURE parser (include/dfa/sim/sure_parser.hpp) now accepts an optional per-equation domain restriction on an equation’s LHS — name(i,j,k | j <= i) = … — intersecting that equation’s domain with the system domain (the coverage check and equation build honor it; the simulator and legality already enumerated per-equation domains). This unlocks Gauss–Seidel (docs/SURE/gauss_seidel.sure, test_gauss_seidel_sure, committed RDG + free schedule), whose lower/upper sum split — Σ_{j<i} A_ij x^k_j (this sweep) and Σ_{j>i} A_ij x^{k-1}_j (previous sweep) — becomes two reductions on complementary sub-domains (accL on j<i, accU on i<j<N), so the k-vs-k-1 choice is structural rather than a per-point conditional. It’s a free-schedule-only SARE (the lower reduction is a triangular substitution wavefront, so no linear τ orders it) and converges to the exact [1,1,1] in fewer sweeps than Jacobi. The Stationary iteration page now nests each method’s Schedule + RDG under its own ## section, with Jacobi’s (free + linear) and Gauss–Seidel’s (free) animations. Dual-compiler.

  • Stationary iteration (Jacobi) — plane-shift reformulation (issue #53): docs/SURE/stationary.sure now moves the residual divide one plane up (to the j = N update plane) so it reads the completed reduction acc(i,N-1,k) as a translation [0,+1,0] instead of a [0,0,0] fusion at the finishing cell, and reads the propagated operands one step back along their carries (the gemv trick). This breaks the zero-slack fusion: Jacobi is now linearly schedulable (τ = [-1, 1, 2N], was free-only), the RDG drops from two affine arcs to one (the xs→xs self-broadcast became a uniform k-carry; only the honest matrix-vector gather xs→acc stays affine — a SARE, since the DSL can’t feed a solved x back through a uniform pipeline), and peak live memory falls ~5×. The docs page (docs/SURE/stationary.md) now embeds the RDG plus both the free and linear wavefront animations; test_stationary_sure and the emit/RDG goldens were updated to assert the new structure (linear τ legal, one affine arc). Dual-compiler, zero warnings.
  • Free-vs-linear comparison clip + README showcase: a second offline render, docs-site/public/videos/matmul-compare.mp4 (the side-by-side compare — free finishes and holds while linear keeps sweeping, the latency gap on one clock). It now leads the docs landing page (replacing the single τ-plane clip) and heads a new Visualizing schedules section in the repo README.md, followed there by the τ-plane single-schedule clip. The README embeds these as inline GIFs (GitHub’s markdown doesn’t play a <video> pointing at a raw file URL); the docs landing page uses the higher-quality .mp4. Generated with npm run video -- --page theory/matmul --index 2 --out public/videos/matmul-compare.mp4 --width 1280 --height 620 — the compare embed composites both panes (the 1248×836 output is that viewport’s two-pane composite).

  • Schedule animation — offline PNG→video path + landing-page clip (issue #142, Phase 3): docs-site/make-video.mjs (npm run video) renders one embedded schedule viewer frame-by-frame in a headless browser and stitches the frames with ffmpeg into a committed .mp4 / .webm / .gif — the same offline path cortex uses. It serves the built dist/ via astro preview, drives the viewer through a new global capture hook (window.__scheduleViewers[i].setFrame(f) / .frameCount, with each container tagged data-sv-index), reads each frame’s pixels with canvas.toDataURL (renderer preserveDrawingBuffer:true; avoids the Playwright-screenshot deadlock against the page’s requestAnimationFrame loops, and captures the canvas only), and encodes with ffmpeg. Playwright is a devDependency; its browsers and ffmpeg stay out-of-band, so the Pages build stays toolchain-free. The first clip (public/videos/matmul-linear.mp4) is committed and embedded on the landing page — the matmul linear schedule with its τ-plane sweep. Flags cover the target page, viewer index, selector, output/codec, fps, size, and loops.

  • Schedule animation — legality coloring (issue #142, Phase 3): an opt-in data-legality overlay in docs-site/src/components/schedule-anim.js that replays each recurrence’s taps and colours every dependence by its slack Δt = t(consumer) − t(producer)red for a violation (Δt ≤ 0: the schedule would read a value before it exists), bright green for a tight dependence (Δt = 1, latency-binding / on the critical path), and dim green for slack (Δt > 1). The HUD certifies the whole schedule ✓ legal / ✗ N violations and reports the firing wavefront’s tight/slack/violated tallies. Reuses the tap-replay + per-frame bucketing of the edge overlay; mutually exclusive with the tile/edge overlays; composes with the τ-plane and the side-by-side compare. Demonstrated on the matmul free schedule, which mixes tight and slack dependences (the linear schedule is all-tight).

  • Schedule animation — side-by-side comparison (issue #142, Phase 3): a new <div class="schedule-compare" data-src-a=… data-src-b=…> embed (mounted by mountCompareAll in docs-site/src/components/schedule-anim.js) renders two schedule viewers driven by one shared clock at the same wall-clock step rate. The shorter schedule finishes and holds its completed lattice while the longer keeps sweeping, so the latency↔parallelism trade reads directly — the extra steps you watch the fast one wait are the latency gap (matmul free 29 vs linear 43 = 14 steps). Overlays (data-plane, data-edges) pass through to both panes, each guarded downstream (the τ-plane lights up only the linear pane). Per-pane HUD + header (operator · kind · latency), a shared legend, and a responsive grid that stacks on narrow screens. Demonstrated on the matmul page.

  • Schedule animation — τ-normal wavefront plane (issue #142, Phase 3): an opt-in data-plane overlay in docs-site/src/components/schedule-anim.js that draws the classic domain-flow signature plane — a translucent quad whose normal is the scheduling vector τ, positioned at the firing wavefront and swept through the lattice as time advances. The plane is oriented once (τ̂ via a quaternion) and each frame slid along τ̂ to the mean signature σ = τ·p of the firing set, so it tracks the bright wavefront (β offsets and gaps included); it hides on any frame where nothing fires. Only meaningful for a linear schedule (a free schedule has no single τ, so the overlay is inert there). Composes with the tile / edge overlays. Demonstrated on the matmul linear-schedule embed via data-plane.

  • RDG viewer — one channel per arc between a node pair (issue #143): docs-site/src/components/rdg.js now groups arcs by the unordered node pair and assigns each its own parallel channel round-robin against a canonical (sorted-endpoint) normal. Previously arcs were grouped by the directed (from,to), so reciprocal arcs laid out against opposite edge normals and collided — the least-squares-via-Givens-QR graph (lstsq) has four a ↔ r arcs (two each way) but only two curves appeared, drawn on top of each other with stacked maps. Now all four render on distinct channels; affine arcs take the outermost channels so the multi-line p ↦ A·p + b matrix has open peripheral space rather than being buried on an inner channel; and every arc’s map is placed outboard of its own arc — grown left/right for a vertical node pair, above/below for a horizontal one (the channel normal’s orientation decides), via text-anchor and block alignment rather than a centered anchor that straddles the arc. The QR-Givens (lstsq) graph’s a ↔ r maps now sit clear to the left and right of the two arcs. Added docs-site/test/rdg-no-overlap.mjs (wired as npm test, jsdom): it mounts the real viewer on the committed RDG JSON and asserts no two arcs share a (node-pair, control-point) channel and that every affine arc sits outside every uniform arc of its pair — lstsq, eig_qr, and the rest of the catalog pass; the test flags both the pre-fix lstsq overlap and a mis-placed affine map. jsdom is now a declared devDependency.

  • Schedule animation — dependency arrows (issue #142, Phase 3): an opt-in data-edges overlay in docs-site/src/components/schedule-anim.js that draws each recurrence’s taps (the affine map A·p + b per source) as producer→consumer arrows into every firing cell. It replays the same tap data the tile overlay uses — already emitted in every schedule JSON — but in non-tile mode, bucketed by consumer firing time so only the wavefront’s dependences draw at each step. Direction reads via a colour gradient (producer end dim → consumer end bright) rather than per-edge arrowhead geometry; the HUD gains a dependencies: N firing readout and the legend a swatch. Mutually exclusive with tile mode (tiles draw their own cross-tile edges). Demonstrated on the matmul page via <div class="schedule-anim" … data-edges>.

  • Singular Value Decomposition — one-sided Jacobi rotation (issue #46): docs/SURE/svd.sure, the derivation docs/SURE/svd.md (Theory), test_svd_sure (dual-compiler, zero warnings), a dfactl_sure_svd emit test, and the committed RDG + schedule animation. The SURE-native route to A = UΣVᵀ: one Givens rotation on the fixed column pair (0,1) that makes those columns orthogonal (the sweep’s kernel — at convergence the column norms are the singular values). The angle comes from the 2×2 Gram matrix (α=a₀·a₀, β=a₁·a₁, γ=a₀·a₁ — three dot reductions over rows), and the one-sided update right-multiplies by J (only columns 0,1 change), so it’s simpler than the two-sided eigenvalue rotation — a SARE. Verified A' = AJ to machine precision (columns orthogonalized, singular values preserved via Frobenius invariance). The doc derives both routes (one-sided Jacobi and Golub–Kahan bidiagonalization + QR) and explains why neither full algorithm is a single SURE (data-dependent column pair / shift).

  • Symmetric eigensolver — Householder tridiagonalization step (issue #48): docs/SURE/eig_qr.sure, the derivation docs/SURE/eig_qr.md (Theory), test_eig_qr_sure (dual-compiler, zero warnings), a dfactl_sure_eig_qr emit test, and the committed RDG

    • schedule animation. Phase 1 of the QR-iteration eigensolver: one two-sided Householder reflection A' = HAH (H = I − βvvᵀ) that zeros a column below its subdiagonal — the building block that reduces a symmetric A to tridiagonal T. A rank-2 update composing three row-reductions (‖A[1:,0]‖², vᵀv, vᵀAv) and a column-reduction (Av) with fed row masks and fixed-index affine taps — a SARE. Verified A' = HAH to machine precision (column 0 zeroed below the subdiagonal, spectrum preserved: trace 18, Frobenius² 110). The doc explains why the full reduction sweep and phase-2 QR iteration are not single SUREs (data-dependent pivot column / shift), and contrasts convergence and parallelism with the Jacobi eigensolver.
  • Symmetric eigensolver — cyclic Jacobi rotation (issue #47): docs/SURE/eig_jacobi.sure, the derivation docs/SURE/eig_jacobi.md (Theory), test_eig_jacobi_sure (dual-compiler, zero warnings), a dfactl_sure_eig_jacobi emit test, and the committed RDG + schedule animation. One two-sided Givens rotation A' = JᵀAJ on the fixed pivot (0,1) that zeros the off-diagonal pair — the executable heart of cyclic Jacobi. Done as two one-sided rotations (right-multiply the columns, then left-multiply the rows); the “column/row is p or q?” test reads fed indicator vectors + select, the angle uses sqrt/abs/gt/select (no atan), and the pivot rows/columns are fixed-index affine taps — a SARE. Verified A' = JᵀAJ to machine precision (pivot zeroed, symmetry + trace + Frobenius preserved ⇒ spectrum invariant). The doc explains why the full sweep is not a single SURE: a changing pivot needs data-dependent addressing of A[p][p], A[q][q], A[p][q] (the partial-pivoting obstruction), so the sweep is a scheduled composition of these rotations.

  • Conjugate Gradient solver (issue #52): docs/SURE/cg.sure, the derivation docs/SURE/cg.md (Theory), test_cg_sure (dual-compiler, zero warnings), a dfactl_sure_cg emit test, and the committed RDG + schedule animation. The most tightly coupled spec in the catalog: one CG step is a domain-flow graph of L1/L2 kernels — a SpMV (gemv), two dots, three axpys — stitched by the scalars α^k = (r·r)/(p·Ap) and β^k = (r'·r')/(r·r) feeding back over the iteration index k. Its index space (i,j,k) carries two reduction directions at once — the SpMV along +j and the two dots along +i — with the scalar ratios broadcast back to every component, so CG is a SARE with 11 affine arcs (the most of any operator). Free-schedule only (the scalar couplings and the same-cell p^k = r^k + … are zero-slack). Verified to converge to the exact solution in ≤ N steps on an SPD tridiagonal system (‖AX−b‖ = 0). Completes the iterative-solver pair (with Jacobi #53).

  • Stationary iterative solver — Jacobi (issue #53): docs/SURE/stationary.sure, the derivation docs/SURE/stationary.md (Theory), test_stationary_sure (dual-compiler, zero warnings), a dfactl_sure_stationary emit test, and the committed RDG + schedule animation. The first iterative catalog entry: a recurrence over the iteration index k whose body is a gemv (residual) + axpy (update), x^k = x^{k-1} + D⁻¹(b − Ax^{k-1}). Reading the whole previous iterate is the matrix-vector gather (affine taps projecting onto the solution face), so Jacobi is a SARE — a benign forward one (each sweep reads only sweep k-1). Free-schedule only (the reduction and residual divide fuse at the finishing cell, like the triangular solve): the free schedule is a wide wavefront within a sweep (Jacobi’s parallelism) that serializes across sweeps. The doc contrasts Gauss–Seidel, whose x^k_j (j<i) read turns the sweep into an intra-sweep triangular wavefront — the same body, different concurrency. Verified K=8 sweeps converge to x=[1,1,1] on a strongly diagonally dominant system.

  • Least squares via QR — the first multi-operator pipeline (issue #50): docs/SURE/lstsq.sure, the derivation docs/SURE/lstsq.md (Theory), test_lstsq_sure (dual-compiler, zero warnings), a dfactl_sure_lstsq emit test, and the committed RDG + schedule animation. Solves min ‖Ax − b‖₂ for a tall A as R x = Qᵀb by augmenting b as an extra column of A: the same Givens rotations that triangularize A carry b into Qᵀb, so one QR pass yields both R and c = Qᵀb with no explicit Q — the QR and apply Qᵀ stages fused into one domain flow. The third stage, back-substitution R x = c (trsv’s upper-triangular mirror), completes the pipeline. A SARE (the affine rotation broadcast, like qr_givens) with a benign forward tap, so τ = [1,1,1] is legal. Verified without ever forming Q by the sign-robust invariants RᵀR = AᵀA and Rᵀc = Aᵀb, then the normal equations AᵀA x = Aᵀb after back-substitution (x = [2.75,-1.45,0.75] on a tall Vandermonde).

  • Neighbour pivoting — the pivot compare-exchange as a SURE (issue #42): docs/SURE/lu_neighbor.sure, the derivation docs/SURE/lu_neighbor.md (Theory), test_lu_neighbor_sure (dual-compiler, zero warnings), a dfactl_sure_lu_neighbor emit test, and the committed RDG. Partial pivoting selects a pivot by a global argmax and moves it an arbitrary distance — an affine broadcast that makes pivoted PLU a SARE (inexpressible as a static uniform system). Neighbour pivoting restricts every comparison and interchange to adjacent rows: a bubble-down compare-exchange carries the max-magnitude value (and its original row index) to the pivot position while each loser settles in place — iamax’s nearest-neighbour argmax carry, extended to also exchange, via gt/select/abs. Every tap is a constant offset, so it is a genuine SURE (RDG all translation vectors, 0 affine arcs) with a linear schedule τ = [1,1] — uniformization of the pivot broadcast made visible against lu’s three affine pivot arcs. Verified: x = [1,-5,3,-2] → pivot -5 (row 1), compare-exchanged column [1,3,-2,-5]. Documents the resolution of #42 (partial-pivoting PLU is a SARE a static SURE cannot express; neighbour pivoting is the SURE a domain-flow fabric can run).

  • RDG sections for the factorizations & solver (issues #119–#122, epic #102): lu, cholesky, ldlt, and trsv each gain a Reduced dependency graph section in their Theory derivation — node set, arc table, and the embedded RDG. These are the SARE cases: the affine pivot/broadcast arcs render as matrix maps (dashed) alongside the uniform Schur translation (solid). lu/cholesky/ldlt are one node with three affine pivot arcs (projections onto row/column k); trsv is four nodes with a single affine arc — the solved x broadcast off the diagonal (i,j,k) ↦ (j,j,k). Completes the per-operator RDG rollout (all 19 catalog operators).

  • RDG sections for the BLAS L3 operators (issues #114–#118, epic #102): gemm, syrk, syr2k, trmm, and symm each gain a Reduced dependency graph section — node set, arc table with the translation vector θ of every dependence, SURE classification, and the embedded RDG. All five are genuine SUREs; trmm is the teaching case where the RDG is uniform yet the operator is free-schedule-only (a flux conflict on the triangular geometry, not an affine dependence).

  • RDG sections for the BLAS L2 operators (issues #108–#113, epic #102): gemv, ger, trmv, symv, syr, and syr2 each gain a Reduced dependency graph section — the node set, an arc table with the translation vector θ of every dependence, the SURE classification, and the embedded RDG visualization. All six are genuine SUREs; syr/syr2 are the fully-parallel case where every arc is the same feed-axis translation [0,0,1]ᵀ.

  • RDG sections for the BLAS L1 operators (issues #104–#107, epic #102): axpy, dot, nrm2, and rot each gain a Reduced dependency graph section in their derivation — the node set, an arc table with the translation vector θ of every dependence, the SURE classification, and the embedded RDG visualization. All four are confirmed genuine SUREs (every arc a translation vector; no affine/broadcast arc).

  • Reduced Dependency Graph (RDG) — infrastructure (issue #103, epic #102): the compact, finite representation of a SURE/SARE — one node per recurrence variable, one arc per dependence, each arc carrying its affine map p ↦ Ap+b. Ships:

    • include/dfa/sim/rdg.hpp — the shared RDG model (buildRdg, emitRdgJson): an arc s → v is uniform when A = I (a translation θ = −b, nearest-neighbour) or affine when A ≠ I (a projection/broadcast); any affine arc makes the system a SARE. Merges identical arcs; schedule- and size-independent.
    • dfactl --emit-rdg <file> — write a SURE/SARE’s RDG as JSON.
    • docs-site/src/components/rdg.js — a self-contained (no-dependency) SVG viewer, embedded via <div class="rdg" data-src="rdg/<op>.json">; arcs are labeled with the operand tuple reconstructed from (A,b) exactly as written in the spec (a(i,j-1,k), a(k,k,k-1)), uniform solid / affine dashed, with a SURE/SARE badge.
    • docs-site/make-rdg.mjs (npm run rdg) generating the committed docs/rdg/<op>.json for all 19 catalog operators; sync-content.mjs copies them to public/rdg/.
    • the formalism doc docs/SURE/reduced_dependency_graph.md under Theory, with worked examples (gemv — a genuine SURE; lu — a SARE with three affine pivot arcs).
    • test_rdg_sure (dual-compiler, zero warnings) asserting the classification across the catalog (all BLAS L1/L2/L3 are SUREs; lu/cholesky/ldlt are SAREs with 3 affine pivot arcs; trsolve is a SARE with 1 affine arc) plus dfactl_rdg_* emit tests. The RDG is the natural setting for the upcoming neighbour-pivoting LU revisit.
  • Triangular solve — trsv (issue #49): forward substitution L x = b for a lower-triangular L, the substitution engine behind every direct solver — docs/SURE/trsolve.sure, the derivation docs/SURE/trsolve.md (Theory), the lean reference docs/SURE/triangular_solve.md (SURE Algorithms / Linear Algebra, with a schedule animation), test_trsolve_sure (dual-compiler, zero warnings) and a dfactl_sure_trsolve emit test. Structurally trmv run backwards: the reduction accumulates Σ_{j<i} L(i,j)·x_j, but x_j is now a solved value read via an affine tap onto the diagonal (j,j) — a SARE (the solved x is broadcast down the columns). Free-schedule-only, for a reason distinct from trmm: the off-diagonal reduction and the divide fuse at the same diagonal lattice point, a zero-slack self-dependence no linear τ can order (τ·0 = 0). The flux-legal τ = [1,2,1] is rejected by the legality check, not by flux; the free schedule is the sequential substitution wavefront. Verified L x = b reconstructs x = [1,2,3,4]. trsm (multiple RHS) is this replicated across an independent, fully parallel RHS axis.

  • LDLᵀ factorization (issue #44): A = L·D·Lᵀ for a symmetric (possibly indefinite) matrix — docs/SURE/ldlt.sure, the derivation docs/SURE/ldlt.md (Theory), the lean reference docs/SURE/ldlt_decomposition.md (SURE Algorithms / Linear Algebra, with a schedule animation), test_ldlt_sure (dual-compiler, zero warnings) and a dfactl_sure_ldlt emit test. The square-root-free sibling of Cholesky: the elimination is the same symmetric Schur update, but factoring out the diagonal D avoids the , so it handles indefinite A (mixed-sign pivots). Extraction uses two faces — unit-lower L(i,j) = a(i,j,j-1)/a(j,j,j-1) and the signed diagonal D(j) = a(j,j,j-1). A SARE with τ = [1,1,2] (same benign forward affine dependence). Verified L·D·Lᵀ = A on an indefinite A (D = diag(2,-3,4)); the test asserts D is indefinite. (True symmetric-indefinite robustness needs Bunch–Kaufman pivoting — a data-dependent choice a static SURE can’t express, like LU’s partial pivoting.)

  • Cholesky factorization (issue #43): A = L·Lᵀ for symmetric positive-definite A as an executable recurrence system — docs/SURE/cholesky.sure, the derivation docs/SURE/cholesky.md (Theory), the lean reference docs/SURE/cholesky_decomposition.md (SURE Algorithms / Linear Algebra, with a schedule animation), test_cholesky_sure (dual-compiler, zero warnings) and a dfactl_sure_cholesky emit test. It is LU specialized to A = Aᵀ: the symmetric Schur update a(i,j,k) = a(i,j,k-1) - a(i,k,k-1)·a(j,k,k-1)/a(k,k,k-1) on the trailing lower triangle. A SARE with the same benign forward affine dependence as LU, so τ = [1,1,2] is legal. Elegantly, a single output face gives all of L: a(i,j,j-1)/√a(j,j,j-1) yields (pivot) on the diagonal and the multiplier below. Because SPD guarantees positive pivots, Cholesky needs no pivoting — a clean SARE (unlike LU, whose honest form would need the inexpressible partial pivoting). Verified L·Lᵀ = A (L = [[2,0,0],[1,2,0],[1,1,2]]).

  • docs-site: Theory / Linear-Algebra split for the factorizations. Following the BLAS Theory-vs-reference pattern (#87): the factorization derivations stay under Theory (lu.md, cholesky.md), and a new SURE Algorithms → Linear Algebra subsection holds lean reference pages (lu_decomposition, cholesky_decomposition) with the SURE and its animation. The LU page was split accordingly (derivation in Theory; SURE + both animations moved to the reference).

  • LU factorization (issue #42, first of the factorizations): Gaussian elimination A = L·U (no pivoting) as an executable recurrence system — docs/SURE/lu.sure + the uniformized docs/SURE/lu_propagate.sure, the derivation docs/SURE/lu.md (under Theory), test_lu_sure and test_lu_propagate_sure (dual-compiler, zero warnings), and dfactl_sure_lu / dfactl_sure_lu_propagate emit tests. The right-looking Schur elimination is a SARE (the pivot / pivot-row / multiplier- column are affine taps that broadcast over the trailing submatrix) — but a benign one: every consumer sits strictly below-and-right of the pivot, so a linear schedule τ = [1,1,2] exists (LU tiles far more readily than QR). The test reconstructs L, U from super-diagonal output faces and verifies L·U = A. A step 2 (lu_propagate.sure) uniformizes the pivot broadcast into a nearest-neighbour propagation for a single elimination step (the pivot row / multiplier column come from the input A, so they seed input-face propagations) — a pure SURE whose free schedule shows the pivot/multiplier hopping across the array as a diagonal wavefront. Two honest findings recorded: partial pivoting cannot be a SURE (a data-dependent row permutation), and the full multi-step propagation needs the internal-confluence DSL extension (later pivots are computed) — the same gap that keeps the MGS QR a SARE.

  • Complete BLAS operator catalog (L1 + L2 + L3) and the Scaling & Distribution docs section. This spans the individual entries below (BLAS-2 gemvsyr2, BLAS-3 gemmsymm, the tiling/hierarchy/DMM/composition pages, the Givens-QR flagship, the docs-site restructure, and the geometry-doc math-rendering repair). Session log: docs/sessions/2026-07-14-scaling-epic-and-blas-l2-l3-catalog.md.

  • BLAS-3 symm (issue #41, completes BLAS Level-3): the symmetric matrix-matrix product C = βC + αAB (A = Aᵀ read from one triangle) as a pure SURE — docs/SURE/symm.sure (+ a lean page under SURE Algorithms / BLAS L3 with a 3-D-cube animation) and src/dfa/tests/sim/symm_sure.cpp (test_symm_sure, dual-compiler, zero warnings) with a dfactl_sure_symm schedule-emit test. It is gemm for a symmetric A: the operand tap reads the stored triangle via a two-face feed — the lower cells read A[i][k], the upper cells read the reflected A[k][i] — so cell (i,j,k) always sees A_sym(i,k) and the reduction is the ordinary gemm sweep (symv’s confluence pattern on gemm’s 3-D cube). Verified numerically (C = [[82,112],[120,156],[166,212]]). This completes the BLAS Level-3 catalog (#37–#41: gemm, syrk, syr2k, trmm, symm).

  • BLAS-3 trmm (issue #40): the triangular matrix-matrix product B := αTB (in place, T lower-triangular) as a pure SURE — docs/SURE/trmm.sure (+ a lean page under SURE Algorithms / BLAS L3 with a triangular-prism animation) and src/dfa/tests/sim/trmm_sure.cpp (test_trmm_sure, dual-compiler, zero warnings) with a dfactl_sure_trmm schedule-emit test. It is gemm whose reduction over k has a triangular extent (k ≤ i) — a non-box 3-D domain: T(i,k) propagates +j, B(k,j) propagates +i (super-diagonal feed), and the result leaves on the diagonal i-k = 0. Free-schedule-only: the super-diagonal B-feed and the diagonal output are parallel faces (same normal (-1,0,1)) with opposite flux, so no linear τ satisfies both (τ_k < τ_i for the feed vs τ_k > τ_i for the output) — the data-flow-earliest free schedule is required, as for the MGS qr.sure. The test asserts the free schedule is legal and every linear τ is rejected. Verified numerically (αTB = [[2,4],[15,22],[58,76]]).

  • BLAS-3 syr2k (issue #39): the symmetric rank-2k update C = βC + α(ABᵀ + BAᵀ) on the triangular output j ≤ i, as a pure SURE — docs/SURE/syr2k.sure (+ a lean page under SURE Algorithms / BLAS L3 with a triangular-prism animation) and src/dfa/tests/sim/syr2k_sure.cpp (test_syr2k_sure, dual-compiler, zero warnings) with a dfactl_sure_syr2k schedule-emit test. It is syrk with two operands: four operand streams (A(i,k)/B(i,k) along +j, A(j,k)/B(j,k) on the super-diagonal along +i) feed the two-term product α(A(i,k)B(j,k) + B(i,k)A(j,k)). Same triangular-domain schedule constraint as syrk (τ = [2,1,1] legal, τ = [1,1,1] rejected). Used in blocked eigen/SVD trailing updates. Verified numerically (lower triangle [[14],[30,46],[56,76,104]]).

  • BLAS-3 syrk (issue #38): the symmetric rank-k update C = βC + αAAᵀ on the triangular output j ≤ i, as a pure SURE — docs/SURE/syrk.sure (+ a lean page under SURE Algorithms / BLAS L3 with a triangular-prism animation) and src/dfa/tests/sim/syrk_sure.cpp (test_syrk_sure, dual-compiler, zero warnings) with a dfactl_sure_syrk schedule-emit test. It is gemm with B = Aᵀ — a single operand A feeds both taps (A(i,k) along +j, A(j,k) along +i) — and a triangular output (AAᵀ is symmetric). Because the triangle’s columns begin at the diagonal, the A(j,k) tap enters on the super-diagonal halo i-j = -1 (a clean halo since the output leaves on k=K-1); its normal (-1,1,0) makes the canonical τ = [2,1,1] legal while the box-default τ = [1,1,1] is rejected — the triangular geometry constrains the schedule, mirroring trmv in the opposite direction. A symmetric rank-k update (the AAᵀ term is positive semidefinite) — the kernel behind blocked Cholesky’s trailing update. Verified numerically (lower triangle [[20],[42,80],[74,128,182]]).

  • BLAS-3 gemm (issue #37): the general matrix–matrix product C = βC + αAB, the first Level-3 catalog operator — docs/SURE/gemm.sure (+ a lean page under SURE Algorithms / BLAS L3 with a 3-D-cube schedule animation) and src/dfa/tests/sim/gemm_sure.cpp (test_gemm_sure, dual-compiler, zero warnings) with a dfactl_sure_gemm schedule-emit test. Generalizes the canonical systolic matmul (C = AB) with the catalog’s matrix–vector patterns lifted to the 3-D reduction cube: α is projected onto a face and folded into the reduction, and the incoming C scaled by β joins the completed sum in a terminal-face epilogue on k = K-1. Every dependence is a constant offset → a pure SURE. Verified numerically (C = [[126,148],[308,348]] for αAB + βC with α=2, β=10) and for free/linear schedule legality.

  • docs-site: math rendering in the Theory geometry docs. The “Aligning convex hulls” (alignment_formalism.md) and “Geometric Transformation” (anchoring.md) pages showed raw LaTeX from three compounding issues in the original brain-dump markdown: (1) several display equations were concatenated on one line ($$…$$$$…$$), which remark-math cannot parse; (2) the rest used the single-line $$…$$ form that this site renders inline rather than as a centered display block; and (3) deeply indented nested list items (4+ spaces) were parsed as code blocks, so the inline $…$ math in those bullets rendered as literal text. Split the concatenated blocks, converted display equations to the multi-line block form, and flattened the list structure to top level (de-indenting prose/math while preserving the fenced Python code block). Both pages now render 0 visible raw-TeX (alignment: 15 display blocks + full inline math; anchoring: 5), matching the rest of the site.
  • docs-site: Theory vs SURE Algorithms restructure (issue #87). Separated derivation from reference:
    • Theory is now an explicit, ordered methodology track (Tensor shape and structure → Matmul and Tensormul → Aligning convex hulls → Geometric Transformation → Pipeline Schedules → Conv2D → QR Decomposition → Matrix–vector derivations), no longer autogenerated. A new consolidated docs/matrix_vector_operators.md collects the L2 derivation patterns (the feed axis, project-and-pipeline, the epilogue, triangular domains + the diagonal-output constraint, the symmetric confluence) in one place.
    • SURE Algorithms / BLAS L2 now hosts dedicated lean pages (intro + the SURE
      • an animation) for gemv, ger, trmv, symv, syr, syr2 — moved out of Theory, with the derivation prose relocated to the Theory doc above. Each links back for the why, so a designer gets immediate reference without wading through derivation.
    • Schedule animations for all six L2 operators — previously absent — are generated (make-schedules.mjs) and embedded on the lean pages, so the free/linear schedule (the (i,j)-plane / triangular wavefront) reads at a glance.
  • BLAS-2 syr2 (issue #36): the symmetric rank-2 update A += α(xyᵀ + yxᵀ) on the triangular domain j ≤ i, as a pure SURE — docs/SURE/syr2.sure (+ derivation docs/SURE/syr2.md under Theory / BLAS L2 nav) and src/dfa/tests/sim/syr2_sure.cpp (test_syr2_sure, dual-compiler, zero warnings) with a dfactl_sure_syr2 schedule-emit test. It is syr with two vectors: the two rank-1 outer products xyᵀ and yxᵀ are fused into one symmetric update. Same structure as syr (a depth-2 feed/drain axis over the triangle, α injected once), but each cell needs four per-cell feeds — x(i), x(j), y(i), y(j) — from the two input vectors X and Y (each drives two feeds); the rank-2 combination α(x(i)y(j) + y(i)x(j)) is formed in the recurrence from uniform taps. The kernel behind syr2k and the LDLᵀ / eigensolver trailing updates. Verified numerically (lower triangle [[5],[7,7],[14,16,30]]). This completes the BLAS Level-2 catalog (#31–#36).
  • BLAS-2 syr (issue #35): the symmetric rank-1 update A += αxxᵀ on the triangular domain j ≤ i, as a pure SURE — docs/SURE/syr.sure (+ derivation docs/SURE/syr.md under Theory / BLAS L2 nav) and src/dfa/tests/sim/syr_sure.cpp (test_syr_sure, dual-compiler, zero warnings) with a dfactl_sure_syr emit test. It is ger with y = x (a single vector on both axes) and the output restricted to one triangle (A + αxxᵀ is symmetric) — the SPD-accumulation primitive behind Cholesky’s trailing update. Combines ger’s inject → add → drain (a depth-2 feed axis, α injected once on the k=-1 halo) with trmv’s triangular domain; the single vector feeds both axes per cell on the (i,j) face (a +i broadcast would need a diagonal seed face on a triangular domain). The result drains on the k=1 face, so τ=[1,1,1] is legal (no diagonal-output constraint). Verified numerically (lower triangle [[3],[6,11],[10,17,24]]).
  • BLAS-2 symv (issue #34): the symmetric matvec y = αAx + βy (A = Aᵀ, reading only one stored triangle) as a pure SURE — docs/SURE/symv.sure (+ derivation docs/SURE/symv.md under Theory / BLAS L2 nav) and src/dfa/tests/sim/symv_sure.cpp (test_symv_sure, dual-compiler, zero warnings) with a dfactl_sure_symv emit test. Numerically it is gemv; the interest is the symmetric-storage confluence pattern — each stored element A(i,j) (j ≤ i) is routed to two accumulations (y(i) += A(i,j)x(j) and y(j) += A(i,j)x(i)), realized as a two-face feed over the full square: the lower cells read the stored A[i][j], the upper cells read the reflected A[j][i], so cell (i,j) always sees A_sym(i,j) and the reduction is the ordinary gemv sweep. The symmetry lives entirely in the confluence, not the recurrence. Verified numerically (Ax = [23,29,38], y = [56,78,106]) and for free/linear schedule legality.
  • BLAS-2 trmv (issue #33): the triangular matvec x := Tx as a pure SURE — docs/SURE/trmv.sure (+ derivation docs/SURE/trmv.md under Theory / BLAS L2 nav) and src/dfa/tests/sim/trmv_sure.cpp (test_trmv_sure, dual-compiler, zero warnings) with a dfactl_sure_trmv emit test. The catalog’s first non-box index space: a gemv-style reduction on the triangular domain j ≤ i, so each row’s sum has a variable extent j = 0..i and the result leaves on the diagonal face i-j = 0 (not a flat j = N-1 face). T and the input vector both feed on the (i,j) face (x is fed rather than pipelined along +i, which on a triangular domain would need a diagonal seed face parallel to the output — no linear schedule). The diagonal output constrains the schedule: its outflux needs τ_j > τ_i, so the canonical τ = [1,2,1] is legal while the box-default τ = [1,1,1] is rejected (zero diagonal flux). Verified numerically (Tx = [2,11,38,100]).
  • BLAS-2 ger (issue #32): the rank-1 update A += αxyᵀ as a pure SURE — docs/SURE/ger.sure (+ derivation docs/SURE/ger.md under Theory) and src/dfa/tests/sim/ger_sure.cpp (test_ger_sure, dual-compiler, zero warnings) with a dfactl_sure_ger emit test. The outer-product counterpart to gemv: a 2-D fully-parallel update (no reduction), with x broadcast along +j and y along +i. The matrix A — indexed by both axes and consumed once — enters and exits on the (i,j) face of a depth-2 feed/drain axis (inject → add → drain, as in scal/axpy): A seeds the accumulator on the k=-1 halo, the rank-1 term is added exactly once (α injected only on that halo, zero in the interior), and the updated matrix drains on the k=1 face. Verified numerically ([[21,42,63],[44,85,126]]) and for free/linear schedule legality. The rank-1 building block reused by LU’s Schur update and by syr/syr2.
  • docs-site: BLAS L2 catalog nav — a new BLAS L2 subsection under SURE Algorithms surfaces gemv and ger in the catalog navigation (referencing their Theory pages) alongside the existing BLAS L1 operators.
  • docs: “Reading BLAS names” — a reference page (docs/SURE/blas-naming.md, top of the SURE Algorithms section) that decodes the BLAS shorthand: the three levels (vector–vector / matrix–vector / matrix–matrix by work-to-data ratio) and the [type][structure][operation] name anatomy (ge/sy/tr × mv/mm/r/r2), so gemv/ger/gemm/syr2 become self-documenting. Includes a decoded table of every catalog operator and a note on how the name’s fields predict the recurrence shape (reduction vs. outer product vs. the 3-D cube; triangular/symmetric domains).
  • BLAS-2 gemv (issue #31, first Level-2 catalog operator): y = βy + αAx as a pure SURE — docs/SURE/gemv.sure (+ derivation docs/SURE/gemv.md under Theory) and src/dfa/tests/sim/gemv_sure.cpp (test_gemv_sure, dual-compiler, zero warnings) with a dfactl_sure_gemv emit test. Structurally matmul with the second matrix collapsed to a vector: a reduction over the column j, with x reused across rows (pipelined along +i) and A — indexed by both axes and read once — entering on the (i,j) face of a depth-1 feed axis. The scalars α, β are project-and-pipelined (never literals in a recurrence body): α folds into the reduction product, and the βy term combines with the completed sum in a terminal-face epilogue on the output face j = N-1. Verified numerically (y = [70,160,250] for the bundled data) and for free/linear schedule legality.
  • Tiled schedule animations — halo vs collective edges (issue #75, closes the Scaling & Distribution epic #68): the schedule animator now visualizes the tiling dichotomy. dfactl --emit-schedule emits each variable’s dependence taps (the affine map A·p + b per producer read), and the schedule-anim.js viewer replays them per firing wavefront to draw the cross-tile dependency edges — green when a dependence crosses to the nearest tile (|Δtile|∞ = 1, a halo), red when it jumps farther (a collective). A uniform operator shows only green; an affine tap lights up red. Verified on the assets: matmul (15³, T=5) draws 2250 halo and 0 collective edges — it tiles cleanly; the near-uniform Givens QR (now N=12, T=4) draws ~2288 halo and ~640 collective edges along its two affine diagonal taps r(i-1,p,p) and a(i,p-1,p). Both are embedded on the tiling and uniformization pages. Adds AffineDependency::matrix()/offset() accessors; extends the emitter and viewer (edge buckets by firing time, a cross-tile HUD counter, a halo/collective legend). Builds on the data-tile overlay from #70.
  • Composition across the hierarchy (issue #74, the section capstone): docs/scaling/composition.md — how tiled operators chain into a full domain flow graph and where inter-operator collectives land. Establishes that a DFG edge’s cost is set by whether the producer’s output tiling and the consumer’s input tiling agree: an aligned edge flows with no collective (what the alignment/anchoring formalism computes), a mismatched edge needs a transpose/all-to-all reshuffle. The fuse-vs-materialize choice (elementwise ops fuse for free; layout/axis changes force a boundary), a two-layer MLP worked example showing the same graph paying an all-reduce or nothing depending purely on the producer’s tiling choice, and the coupled tiling+placement mapping problem as a global (not greedy) optimization. Ties back to the repo’s DFG/RDG tooling and the alignment, anchoring, and pipelining-schedule docs. This closes the Scaling & Distribution walkthrough.
  • The memory & communication hierarchy and the DMM (issue #73): two greenfield scaling pages. docs/scaling/hierarchy.md defines the levels (PE → tile → KPU → SoC → cluster), establishes that there is no shared memory above the tile, and gives the energy–delay–distance cost model — each level up costs ~an order of magnitude more per word — with the key argument that locality compounds: a uniform halo stays pinned to the bottom level at any scale, while an affine collective is forced to climb the hierarchy as the problem grows, so the uniform-vs-affine gap widens with scale. docs/scaling/dmm.md defines the Distributed Memory Machine execution model (partitioned memory + collective primitives: reduce, broadcast, all-reduce, scatter/gather, transpose), their cost tiers (level dominates count; all-to-all is the cliff), and the compiler’s four-step lowering contract. Reconciles the acronym collision honestly: EDDO is Explicit Decoupled Data Orchestration (the architecture class, per the existing targeting-EDDO-with-Polyhedral.md), distinct from the energy–delay–distance cost model. Also wires up the previously-placeholder cross-links from the tiling, halo-vs-collective, and landing pages.
  • Halo vs collective (issue #71): docs/scaling/halo-vs-collective.md — the worked-out cross-tile dependency dichotomy at the heart of the scaling section. A uniform (constant-offset) dependence becomes a nearest-neighbour halo (grounded in conv2d.sure’s stencil and matmul); an affine/domain-spanning one becomes a collective — with a taxonomy (reduce/all-reduce, broadcast, transpose/all-to-all) mapped onto the catalog (dot/nrm2/asum reductions, axpy’s α broadcast, QR’s srp). Includes the honest reduction subtlety (an in-tile accumulation chain is a pipeline; the collective appears only when the reduction axis is split — a gather for a single consumer, a full all-reduce when the result is read back), and the surface-to-volume cost argument (halo O(1/T) amortizes; a collective has an O(log P)O(P) payload-independent latency floor whose energy scales with interconnect distance).
  • Tiling the index space (issue #70): docs/scaling/tiling.md — a blocked-matmul worked example showing that tiling is a partition of the same index space, not a new recurrence: because matmul.sure is uniform, its a/b/c dependences are constant offsets, so every dependence a tile cut severs is a halo (O(T²) surface exchange with a neighbour), never a collective — even the C-accumulation across K-blocks is a ±1 pipelined hand-off. Covers index-set partitioning, the two-level (across-tile + within-tile) schedule, matmul’s self-similarity under tiling, and a 4×4-as-2×2-blocks numeric example. The schedule-animation viewer gains a data-tile mode that colours each activation by its tile block and draws the tile boundaries, embedded here as a 15³ cube partitioned into a 3×3×3 grid.
  • QR by Givens rotations (issue #72, the Scaling & Distribution flagship): an executable, verified Gentleman–Kung QR — docs/SURE/qr_givens.sure, CTest test_qr_givens_sure, dfactl_sure_qr_givens, and a schedule animation on the uniformization page. Each row of A is merged into the accumulating upper-triangular R by a nearest-neighbour sweep of Givens rotations over (i,p,q), p ≤ qno reduction. Verified: exact R for the classic 3×3 and the sign-robust RᵀR = AᵀA on tall inputs. It is still a SARE in the DSL (the two diagonal taps r(i-1,p,p), a(i,p-1,p) read the pivot column), but those are pure diagonal broadcasts along +q with no reduction — nearest- neighbour, so they tile with local communication, unlike MGS-QR’s global Σ_i all-reduce. A pure SURE needs only the internal-confluence extension to seed the rotation propagation. The docs/scaling/uniformization.md page carries the full MGS-vs-Givens comparison.
  • “Scaling & Distribution” docs section (epic #68): a new top-level section on mapping SUREs across KPU tiles and SoCs. The landing page (#69) frames the scale-out problem and its organizing thesis — uniform deps become halo exchanges and tile linearly, affine/broadcast deps become collectives on the Distributed Memory Machine and only tile after uniformization. The Uniformization page (#72) works the affine→uniform transform on the two broadcast flavours the catalog contains and reports an honest finding: a broadcast of a known input (axpy’s α) is uniformizable in the confluence DSL (the pipeline is seeded from data), but a broadcast of a computed reduction (QR’s r_kj) is not — an input confluence cannot seed a propagation axis from a recurrence variable — so QR is a genuine SARE, and its executable uniform form is the Gentleman–Kung Givens array (a tracked follow-on) rather than a pipelined MGS.
  • Interactive schedule animations in the docs-site (issue #64, Phase 1): a SURE’s schedule can now be watched — the wavefront sweeping through the index lattice, one timestep at a time, so parallelism (wavefront width) and latency read at a glance. dfactl --sure <f> --emit-schedule <out.json> exports the index points per variable tagged with their signature time (t = τ·p linear, or the data-flow-earliest free time), bounds, and latency; a Three.js viewer (docs-site/src/components/schedule-anim.js, modeled on cortex’s Scene3D) animates it with play/pause/scrub, mounted on any <div class="schedule-anim" data-src=...> in the (synced, plain-.md) docs via a lazy-loaded <Head> override — three.js loads only on pages that embed one. Assets are generated by docs-site/make-schedules.mjs and committed under docs/schedules/ (copied to public/ at build, so the docs CI stays toolchain-free). Phase 1 wires the matmul page with its free (latency 29) vs linear τ=[1,1,1] (latency 43) schedules at 15×15×15 visualization scale (matmul15.sure, the same recurrence with dummy operands — the schedule geometry is value-independent), large enough for the wavefront geometry to materialize; the viewer uses InstancedMesh (~10k activations in one draw call) and handles 1–3-D index spaces. Reference implementation: branes-ai/cortex.
    • Phase 2 (issue #64): animations across the whole BLAS L1 catalog — all nine operator pages embed a schedule animation (the reductions dot, nrm2, asum, iamax as a wavefront point sweeping the accumulation chain; the maps axpy, scal, copy, swap, rot as a wavefront over the systolic strip). Visualization scale is now applied by make-schedules.mjs itself — it scales the size parameter and scalar-fills the operand data of a throwaway copy of the committed spec (the schedule depends only on the domain), so no duplicate .sure specs are checked in (the dedicated matmul15.sure is retired; matmul renders from matmul.sure at 15³). The QR page also gets an animation as a stress test of the approach on a genuine SARE — a triangular-prism domain with affine/broadcast taps and no global linear schedule, so the free-schedule wavefront threads the sequential Gram-Schmidt dependencies (M = N = 8, latency 144).
  • SURE linear-algebra operator catalog (epic #21): a catalog of executable SURE derivations of the key linear-algebra operators (BLAS L1/L2/L3, factorizations, solvers), each shipping the established triple — a .sure spec, a regression test, and a docs-site Theory page.
    • axpy (issue #22): the first entry — BLAS-1 y := alpha*x + y as a two-cell systolic map (docs/SURE/axpy.sure, docs/SURE/axpy.md, CTest test_axpy_sure, dfactl_sure_axpy). The fully-parallel vector map rides the length-N result on the i axis and injects x on a j = -1 halo so the scaled contribution is added once as y streams to the terminal face. All three operands are uniform flows — in particular the scalar alpha is projected onto the i = -1 edge and pipelined across the lanes (a(i,j) = a(i-1,j)) rather than baked into an equation body as a broadcast constant (which would be an affine, non-uniform dependence). Verified R = alpha*X + Y with derived face normals and free/linear legality. SURE_DOCS_DIR is now directory-scoped in the sim tests so each new <op>_sure.cpp is a drop-in.
    • dot (issue #23): the canonical reduction — BLAS-1 s = xᵀy (docs/SURE/dot.sure, docs/SURE/dot.md, CTest test_dot_sure, dfactl_sure_dot). The multiply-add chain s(i,j) = s(i-1,j) + x(i,j-1)*y(i,j-1) accumulates along +i, seeded with the additive identity, and the scalar result leaves through the single terminal point i = N-1. The operands are indexed by the reduction coordinate, so they enter on a depth-1 feed axis j (a reduction reuses no operand, unlike matmul’s propagating A/B). Verified s = X·Y = 20; the memory analysis confirms the reduction signature (peakLiveValues = 2, an O(1) accumulator), and the doc contrasts the latency-bound linear chain with the tree reduction.
    • nrm2 (issue #24): the Euclidean norm — BLAS-1 ‖x‖₂ = sqrt(Σ xᵢ²) (docs/SURE/nrm2.sure, docs/SURE/nrm2.md, CTest test_nrm2_sure, dfactl_sure_nrm2). The dot sum-of-squares reduction with a fused output-face sqrt epilogue: the square root is a pointwise op applied once where the result drains (R[0] = sqrt(s(N-1))), not an interior recurrence edge — the same confluence pattern as the MATMUL activation epilogue and qr’s Rdiag. Verified ‖x‖₂ = 5 through the epilogue; O(1) accumulator footprint. The doc notes the scaled/robust (overflow-avoiding) variant as an extension.
    • asum (issue #25): the ℓ₁ norm — BLAS-1 Σ|xᵢ| (docs/SURE/asum.sure, docs/SURE/asum.md, CTest test_asum_sure, dfactl_sure_asum). The dot reduction with a fused input-face abs prologue: the absolute value maps each operand as it enters (x = abs(X[i])) — the mirror image of nrm2’s output-face sqrt epilogue, with the interior a clean uniform sum in both. Verified Σ|x| = 10 on signed data; O(1) accumulator footprint.
    • scal (issue #26): the vector scale — BLAS-1 x := α·x (docs/SURE/scal.sure, docs/SURE/scal.md, CTest test_scal_sure, dfactl_sure_scal). Exactly axpy specialized to a zero addend (α·x = α·x + 0): the same two-cell systolic map with the projected + pipelined scalar and one-shot injection, but the result stream is seeded with the additive identity (no incoming vector) instead of Y. Verified R = α·X = {3,6,9,12}.
    • swap (issue #27): the vector exchange — BLAS-1 (x,y) := (y,x) (docs/SURE/swap.sure, docs/SURE/swap.md, CTest test_swap_sure, dfactl_sure_swap). No arithmetic: two value-preserving flows carry X and Y straight through, and the swap is realized entirely in the crossed output confluences (the X-named face reads the y-flow and vice versa) — a multi-tensor confluence, two input faces in, two output faces out. Verified (Xout,Yout) = (Y,X); shows that data-movement operators are pure confluence routing, no interior compute.
    • copy (issue #28): the identity flow — BLAS-1 y := x (docs/SURE/copy.sure, docs/SURE/copy.md, CTest test_copy_sure, dfactl_sure_copy). The catalog’s simplest operator: a single value-preserving recurrence x(i,j) = x(i,j-1) carries X from the input halo to the terminal face, where it leaves as Y — one input face, one output face, no arithmetic. The elementary building block of every value-preserving flow (swap is two crossed copy flows). Verified Y = X.
    • rot (issue #29): the Givens rotation — BLAS-1 (x,y) := (c·x+s·y, −s·x+c·y) (docs/SURE/rot.sure, docs/SURE/rot.md, CTest test_rot_sure, dfactl_sure_rot). axpy generalized to a 2×2 linear map: two projected + pipelined scalars (c,s) in place of the single α, two one-shot-injected operands (x,y), and two additive-identity-seeded result streams (rx = c·x+s·y, ry = −s·x+c·y). Six input confluences and two output confluences — the widest L1 structure. Verified against the rotation matrix and the norm-preserving invariant (rx²+ry² = x²+y² when c²+s²=1). The catalog’s rotation primitive, reused by the Jacobi/Givens sweeps in QR, SVD, and the symmetric eigensolvers.
    • iamax (issue #30): the argmax — BLAS-1 argmaxᵢ|xᵢ| (docs/SURE/iamax.sure, docs/SURE/iamax.md, CTest test_iamax_sure, dfactl_sure_iamax) — completes BLAS Level-1 (9 operators). A select reduction carrying an (index, value) pair: the fold chooses the larger magnitude rather than accumulating. This required two exact selection built-ins added to the DSL — gt(a,b) (strict > indicator) and select(c,x,y) — which stay pointwise value ops, so the recurrence remains uniform. Magnitude enters via the abs prologue; the index enters as data (Idx[i]=i projected onto the feed halo, since bodies cannot read the loop index). Strict gt keeps the earliest index on ties (BLAS convention); verified against tie/sign/last-element/all-zeros cases. The pivot-selection primitive behind LU partial pivoting, LP simplex, and eigensolver thresholds.
  • SURE DSL: executable docs/SURE notation (issue #15, PR #16; issue #17, PR #18): a header-only text front-end (include/dfa/sim/sure_parser.hpp) parses the system((i,j,k) | constraints) { equations } notation used in the theory documents into executable RecurrenceSystems, wired into dfactl --sure <file.sure> with the existing --schedule/--tau/--quiet flags. A same-day design review (issue #17) replaced the v1 boundary/table-input/projected-output statements with v2 symmetric input/output confluence declarations: equality-pinned face regions in the domain’s own coordinates (extent from inequalities, location from the equality) with the orientation derived as the domain’s outward normal, validated as a true supporting hyperplane. Machine-checked contracts, all with line-numbered diagnostics: tap-image coverage over declared input faces (face-dispatched boundaries — no silent data fabrication), face well-formedness, flux consistency (tau.n < 0 influx / > 0 outflux, revalidated for CLI --tau overrides via validateSureFlux), element range, and tensor/data binding.
  • Executable theory documents (PR #19): all three docs/SURE/ documents migrated to the v2 DSL with runnable kernels — matmul.sure (legality demos), qr.sure (Modified Gram-Schmidt uniformized onto one shared triangular domain: reductions as first-class variables, q embedded as a diagonal-tapped normalized flow, R leaving through two oriented faces including the non-axis-aligned k = j diagonal; verified R exact, Q^T*Q = I and Q*R = A to ~1e-15), and conv2d.sure (image as a value-preserving anti-diagonal flow seeded from two disjoint halo faces, padding as explicit data; verified against a direct correlation reference). QR_decomposition.md and conv2d.md rewritten around the uniformization decisions; the sim suite grew to 73 tests.
  • Fused MATMUL output-face epilogue (issue #1, PR #13): a MATMUL node can declare a pointwise activation via attribute["activation"] (e.g. "relu"), recorded as an epilogue on the terminal k = K-1 output-face Confluence; bias enters via the existing 3rd operand Cin. Confluence gained an optional epilogue field, MATMUL elaboration now populates the input/output face confluences (previously constructed and discarded), and the simulator importer executes the fused form n(i,j) = act(c(i,j,K-1) + Cin(i,j)). Attributes already round-trip through .dfg serialization.
  • FUSED_MATMUL_BIAS_ACT operator (issue #2, PR #14): dedicated IR operator making the fused Y = activation(A*B + bias) semantics explicit — a single (i,j,k) domain where, unlike 3-input MATMUL (Cin seeds the accumulator at k = 0), the bias confluence sits on the terminal face where the epilogue executes. Shares a new buildMatmulHull() helper, the MATMUL constraint-set case, and the simulator lowering path. Coexists with the attribute form: attribute for MLIR-imported graphs, dedicated operator for explicit fused-IR construction (KPU simulator path). Verified wavefronts under output-stationary tau = [1,1,1].
  • Documentation site (docs-site/): Astro + Starlight site published to GitHub Pages at https://branes-ai.github.io/domain_flow/ via .github/workflows/docs.yml. Content is synced from the repo’s docs/ tree by docs-site/sync-content.mjs (sections: getting started, architecture, SURE simulator, theory, changelog), with KaTeX math rendering and a landing page at docs/site/index.mdx.
  • SURE simulator (include/dfa/sim/, PR #3): standalone header-only functional simulator for Systems of Uniform/Affine Recurrence Equations — numeric evaluation with boundary/operand semantics, free schedule derivation, schedule legality checking (tau.theta >= 1 with violation reports), memory-cardinality (peak live values) analysis, eviction-based execution, and a .dfg import path. Includes the dfactl CLI (sim/) with built-in matmul, matvec, and qr specs, plus tests under src/dfa/tests/sim/.
  • SURE simulator documentation (docs/sure-simulator.md): why/what/how guide with worked dfactl examples (legal vs illegal schedules, stage offsets, heterogeneous-rank SAREs, .dfg import) and a spec-authoring walkthrough.
  • CLAUDE.md (PR #5): guidance file for AI-assisted development with the verified build/test workflow (preset scheme, compile_all test naming, single-test invocation), code layout, and repo conventions.
  • docs-site navigation: new “SURE Algorithms” section. The nine BLAS Level-1 operator derivations moved out of the flat “Theory” list into a two-level SURE Algorithms → BLAS L1 section, ordered pedagogically (axpy → dot → nrm2 → asum → scal → swap → copy → rot → iamax) rather than alphabetically. Theory now holds the background material and the not-yet-cataloged operator derivations (matmul, conv2d, QR). Content still originates in docs/SURE/; sync-content.mjs maps the L1 pages under sure-algorithms/blas-l1/ and the sidebar is defined in astro.config.mjs. (Also fixed two qr cross-links to reference the source filename so they survive the move.) Sidebar section order is now Getting Started → Architecture → Theory → SURE Simulator → SURE Algorithms → Changelog.
  • Terminology: drop the “ASAP” gloss on the free schedule. The free schedule is the long-established (since the 1960s) term for the unencumbered, data-flow-earliest execution order; the parenthetical “(ASAP)” was inaccurate and has been removed from all docs and code (simulator comments, dfactl help/output strings, sim-test comments, and the architecture/simulator/QR theory docs).
  • CI overhaul (issue #7, PR #9): docs-only changes (**.md, docs/**, docs-site/**) no longer trigger the LLVM build matrix; a weekly scheduled run on main keeps the shared LLVM cache warm inside GitHub’s 7-day eviction window; restore-keys + an install-tree probe (mlir-tblgen + clang) let prefix-restored caches skip the ~4h LLVM build (validated: warm matrix jobs complete in ~2-4 min); the cache key now fingerprints the real configure flags via the new llvm-build-config.txt; concurrency group auto-cancels superseded PR runs while protecting main/scheduled cache builds. Issue #10 tracks the remaining cold-cache duplicate build across ubuntu compiler legs.
  • CI: LLVM built once per OS (issue #10, PR #12): a dedicated llvm job now owns the cache build/save per OS; the compiler matrix depends on it and does a read-only actions/cache/restore with a fail-fast verify, eliminating the cold-cache race where the ubuntu gcc and clang legs each built LLVM (~4h x 2). All cache steps upgraded to actions/cache@v5; workflow runs with least-privilege permissions: contents: read.
  • docs-site Dependabot alerts resolved (PR #11): upgraded astro 5.18 → 7.0.6 and @astrojs/starlight 0.34 → 0.41.3, clearing all six open alerts (2 high: Host-header SSRF, reflected XSS via slot names; plus XSS mediums and an esbuild dev-server low, now at 0.28.1). npm audit: 0 vulnerabilities. One migration: Starlight 0.39’s sidebar items syntax.
  • QR page overclaimed “SURE”. Modified Gram-Schmidt QR is a System of Affine Recurrence Equations (SARE), not uniform: the projection coefficients are broadcast reductions and each column reads the previous column’s q — affine, non-local dependencies. Retitled the page and added an upfront caution; the honest classification (affine, hence no global linear schedule, hence a tiling challenge) is now stated plainly.

  • Schedule-animation playback rate is tunable (data-fps on the .schedule-anim div) and the adaptive default cap was lowered so fine-grained schedules stay legible — the QR animation (144 short steps) now plays at 4 fps instead of flickering past at the old 18 fps cap.

  • Math not rendering on the QR and tensor-structure Theory pages. Both docs wrapped their LaTeX in \( … \) / \[ … \], which remark-math does not recognize (it only handles $ … $ / $$ … $$), so every \sum, \sqrt, \begin{cases}, etc. printed as raw text. Converted the delimiters (and fixed a malformed \( |k| on the QR page); the pages now typeset (316 and 459 KaTeX spans). New docs already use the $ delimiters.

  • SURE simulator review hardening (PR #3, 18 CodeRabbit findings): fail-fast validation in Domain (axis bounds, constraint dimensions), AffineDependency (ragged matrices), and RecurrenceSystem (duplicate equation names); schedule rank mismatches now throw instead of silently truncating (could mask illegal schedules); dependency-cycle detection in eval()/computeFreeSchedule(); drained outputs no longer counted as resident in the memory analysis; .dfg importer validates unary/binary operand shapes with per-axis broadcast compatibility and fails fast on multi-output producers; dfactl --quiet now works with --dfg; sim tests reuse the canonical spec builders.

  • Build documentation (issue #4, PR #5): ARCHITECTURE.md, README.md, and SETUP.md referenced nonexistent CMake options (DOMAINFLOW_ENABLE_TESTS, DOMAINFLOW_BUILD_TESTS); corrected to the real BUILD_TESTING / DOMAINFLOW_BUILD_TESTING with accurate defaults.

  • Compiler warnings cleanup: Eliminated all 6,720 compiler warnings with -Wall -Wextra flags

    • Added default: cases to switch statements in domain_flow_node.hpp and domain_of_computation.hpp (4,545 warnings)
    • Fixed unused variable warnings with [[maybe_unused]] attributes across multiple headers (1,274 warnings)
    • Fixed signed/unsigned comparison warnings by using size_t for loop indices (567 warnings)
    • Fixed unused parameter warnings with [[maybe_unused]] attributes (319 warnings)
    • Removed unused local typedef declarations from test files (11 warnings)
    • Added missing struct field initializers in energy_estimator.hpp and energy_estimator.cpp (4 warnings)
  • Bug fix in domain_flow_edge.hpp: Constructor now properly initializes srcSlot and dstSlot member variables from parameters (previously ignored)