Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
[Unreleased]
Section titled “[Unreleased]”-
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 ofschedule-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.RecurrenceSystemstores a list of branches per name withresolve(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 piecewisexx) 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 exactAx=bsolution[1,1,1](Jacobi within1e-3, Gauss–Seidel within1e-6, atK=8sweeps); 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 (accLonj<i,accUoni<j<N), so thek-vs-k-1choice 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.
Changed
Section titled “Changed”- Stationary iteration (Jacobi) — plane-shift reformulation (issue #53):
docs/SURE/stationary.surenow moves the residual divide one plane up (to thej = Nupdate plane) so it reads the completed reductionacc(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 (thexs→xsself-broadcast became a uniformk-carry; only the honest matrix-vector gatherxs→accstays affine — a SARE, since the DSL can’t feed a solvedxback 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_sureand 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 repoREADME.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 withnpm 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 builtdist/viaastro preview, drives the viewer through a new global capture hook (window.__scheduleViewers[i].setFrame(f)/.frameCount, with each container taggeddata-sv-index), reads each frame’s pixels withcanvas.toDataURL(rendererpreserveDrawingBuffer:true; avoids the Playwright-screenshot deadlock against the page’srequestAnimationFrameloops, 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-legalityoverlay indocs-site/src/components/schedule-anim.jsthat 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 violationsand 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 thematmulfree 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 bymountCompareAllindocs-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 thematmulpage. -
Schedule animation — τ-normal wavefront plane (issue #142, Phase 3): an opt-in
data-planeoverlay indocs-site/src/components/schedule-anim.jsthat 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σ = τ·pof 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 thematmullinear-schedule embed viadata-plane. -
RDG viewer — one channel per arc between a node pair (issue #143):
docs-site/src/components/rdg.jsnow 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 foura ↔ rarcs (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-linep ↦ A·p + bmatrix 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), viatext-anchorand block alignment rather than a centered anchor that straddles the arc. The QR-Givens (lstsq) graph’sa ↔ rmaps now sit clear to the left and right of the two arcs. Addeddocs-site/test/rdg-no-overlap.mjs(wired asnpm 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-fixlstsqoverlap and a mis-placed affine map.jsdomis now a declared devDependency. -
Schedule animation — dependency arrows (issue #142, Phase 3): an opt-in
data-edgesoverlay indocs-site/src/components/schedule-anim.jsthat draws each recurrence’s taps (the affine mapA·p + bper 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 adependencies: N firingreadout and the legend a swatch. Mutually exclusive with tile mode (tiles draw their own cross-tile edges). Demonstrated on thematmulpage via<div class="schedule-anim" … data-edges>. -
Singular Value Decomposition — one-sided Jacobi rotation (issue #46):
docs/SURE/svd.sure, the derivationdocs/SURE/svd.md(Theory),test_svd_sure(dual-compiler, zero warnings), adfactl_sure_svdemit test, and the committed RDG + schedule animation. The SURE-native route toA = 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 the2×2Gram matrix (α=a₀·a₀,β=a₁·a₁,γ=a₀·a₁— three dot reductions over rows), and the one-sided update right-multiplies byJ(only columns0,1change), so it’s simpler than the two-sided eigenvalue rotation — a SARE. VerifiedA' = AJto 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 derivationdocs/SURE/eig_qr.md(Theory),test_eig_qr_sure(dual-compiler, zero warnings), adfactl_sure_eig_qremit 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 symmetricAto tridiagonalT. 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. VerifiedA' = HAHto machine precision (column 0 zeroed below the subdiagonal, spectrum preserved: trace18, 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.
- schedule animation. Phase 1 of the QR-iteration eigensolver: one two-sided Householder
reflection
-
Symmetric eigensolver — cyclic Jacobi rotation (issue #47):
docs/SURE/eig_jacobi.sure, the derivationdocs/SURE/eig_jacobi.md(Theory),test_eig_jacobi_sure(dual-compiler, zero warnings), adfactl_sure_eig_jacobiemit test, and the committed RDG + schedule animation. One two-sided Givens rotationA' = JᵀAJon 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 usessqrt/abs/gt/select(noatan), and the pivot rows/columns are fixed-index affine taps — a SARE. VerifiedA' = JᵀAJto 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 ofA[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 derivationdocs/SURE/cg.md(Theory),test_cg_sure(dual-compiler, zero warnings), adfactl_sure_cgemit 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), twodots, threeaxpys — stitched by the scalarsα^k = (r·r)/(p·Ap)andβ^k = (r'·r')/(r·r)feeding back over the iteration indexk. Its index space(i,j,k)carries two reduction directions at once — the SpMV along+jand 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-cellp^k = r^k + …are zero-slack). Verified to converge to the exact solution in≤ Nsteps 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 derivationdocs/SURE/stationary.md(Theory),test_stationary_sure(dual-compiler, zero warnings), adfactl_sure_stationaryemit test, and the committed RDG + schedule animation. The first iterative catalog entry: a recurrence over the iteration indexkwhose body is agemv(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 sweepk-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, whosex^k_j(j<i) read turns the sweep into an intra-sweep triangular wavefront — the same body, different concurrency. VerifiedK=8sweeps converge tox=[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 derivationdocs/SURE/lstsq.md(Theory),test_lstsq_sure(dual-compiler, zero warnings), adfactl_sure_lstsqemit test, and the committed RDG + schedule animation. Solvesmin ‖Ax − b‖₂for a tallAasR x = Qᵀbby augmentingbas an extra column ofA: the same Givens rotations that triangularizeAcarrybintoQᵀb, so one QR pass yields bothRandc = Qᵀbwith no explicitQ— the QR and applyQᵀstages fused into one domain flow. The third stage, back-substitutionR x = c(trsv’s upper-triangular mirror), completes the pipeline. A SARE (the affine rotation broadcast, likeqr_givens) with a benign forward tap, soτ = [1,1,1]is legal. Verified without ever formingQby the sign-robust invariantsRᵀR = AᵀAandRᵀc = Aᵀb, then the normal equationsAᵀA x = Aᵀbafter 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 derivationdocs/SURE/lu_neighbor.md(Theory),test_lu_neighbor_sure(dual-compiler, zero warnings), adfactl_sure_lu_neighboremit 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 pivotedPLUa 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, viagt/select/abs. Every tap is a constant offset, so it is a genuine SURE (RDG all translation vectors,0affine arcs) with a linear scheduleτ = [1,1]— uniformization of the pivot broadcast made visible againstlu’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-pivotingPLUis 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, andtrsveach 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/ldltare one node with three affine pivot arcs (projections onto row/columnk);trsvis four nodes with a single affine arc — the solvedxbroadcast 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, andsymmeach 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;trmmis 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, andsyr2each 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/syr2are 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, androteach 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 arcs → vis uniform whenA = I(a translationθ = −b, nearest-neighbour) or affine whenA ≠ 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 committeddocs/rdg/<op>.jsonfor all 19 catalog operators;sync-content.mjscopies them topublic/rdg/.- the formalism doc
docs/SURE/reduced_dependency_graph.mdunder 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/ldltare SAREs with 3 affine pivot arcs;trsolveis a SARE with 1 affine arc) plusdfactl_rdg_*emit tests. The RDG is the natural setting for the upcoming neighbour-pivoting LU revisit.
-
Triangular solve —
trsv(issue #49): forward substitutionL x = bfor a lower-triangularL, the substitution engine behind every direct solver —docs/SURE/trsolve.sure, the derivationdocs/SURE/trsolve.md(Theory), the lean referencedocs/SURE/triangular_solve.md(SURE Algorithms / Linear Algebra, with a schedule animation),test_trsolve_sure(dual-compiler, zero warnings) and adfactl_sure_trsolveemit test. Structurallytrmvrun backwards: the reduction accumulatesΣ_{j<i} L(i,j)·x_j, butx_jis now a solved value read via an affine tap onto the diagonal(j,j)— a SARE (the solvedxis broadcast down the columns). Free-schedule-only, for a reason distinct fromtrmm: 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. VerifiedL x = breconstructsx = [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 derivationdocs/SURE/ldlt.md(Theory), the lean referencedocs/SURE/ldlt_decomposition.md(SURE Algorithms / Linear Algebra, with a schedule animation),test_ldlt_sure(dual-compiler, zero warnings) and adfactl_sure_ldltemit test. The square-root-free sibling of Cholesky: the elimination is the same symmetric Schur update, but factoring out the diagonalDavoids the√, so it handles indefiniteA(mixed-sign pivots). Extraction uses two faces — unit-lowerL(i,j) = a(i,j,j-1)/a(j,j,j-1)and the signed diagonalD(j) = a(j,j,j-1). A SARE withτ = [1,1,2](same benign forward affine dependence). VerifiedL·D·Lᵀ = Aon an indefiniteA(D = diag(2,-3,4)); the test assertsDis 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-definiteAas an executable recurrence system —docs/SURE/cholesky.sure, the derivationdocs/SURE/cholesky.md(Theory), the lean referencedocs/SURE/cholesky_decomposition.md(SURE Algorithms / Linear Algebra, with a schedule animation),test_cholesky_sure(dual-compiler, zero warnings) and adfactl_sure_choleskyemit test. It is LU specialized toA = Aᵀ: the symmetric Schur updatea(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 ofL: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). VerifiedL·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 uniformizeddocs/SURE/lu_propagate.sure, the derivationdocs/SURE/lu.md(under Theory),test_lu_sureandtest_lu_propagate_sure(dual-compiler, zero warnings), anddfactl_sure_lu/dfactl_sure_lu_propagateemit 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 reconstructsL,Ufrom super-diagonal output faces and verifiesL·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 inputA, 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
gemv…syr2, BLAS-3gemm…symm, 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 productC = β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) andsrc/dfa/tests/sim/symm_sure.cpp(test_symm_sure, dual-compiler, zero warnings) with adfactl_sure_symmschedule-emit test. It isgemmfor a symmetricA: the operand tap reads the stored triangle via a two-face feed — the lower cells readA[i][k], the upper cells read the reflectedA[k][i]— so cell(i,j,k)always seesA_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 productB := αTB(in place,Tlower-triangular) as a pure SURE —docs/SURE/trmm.sure(+ a lean page under SURE Algorithms / BLAS L3 with a triangular-prism animation) andsrc/dfa/tests/sim/trmm_sure.cpp(test_trmm_sure, dual-compiler, zero warnings) with adfactl_sure_trmmschedule-emit test. It isgemmwhose reduction overkhas 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 diagonali-k = 0. Free-schedule-only: the super-diagonalB-feed and the diagonal output are parallel faces (same normal(-1,0,1)) with opposite flux, so no linearτsatisfies both (τ_k < τ_ifor the feed vsτ_k > τ_ifor the output) — the data-flow-earliest free schedule is required, as for the MGSqr.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 updateC = βC + α(ABᵀ + BAᵀ)on the triangular outputj ≤ i, as a pure SURE —docs/SURE/syr2k.sure(+ a lean page under SURE Algorithms / BLAS L3 with a triangular-prism animation) andsrc/dfa/tests/sim/syr2k_sure.cpp(test_syr2k_sure, dual-compiler, zero warnings) with adfactl_sure_syr2kschedule-emit test. It issyrkwith 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 assyrk(τ = [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 updateC = βC + αAAᵀon the triangular outputj ≤ i, as a pure SURE —docs/SURE/syrk.sure(+ a lean page under SURE Algorithms / BLAS L3 with a triangular-prism animation) andsrc/dfa/tests/sim/syrk_sure.cpp(test_syrk_sure, dual-compiler, zero warnings) with adfactl_sure_syrkschedule-emit test. It isgemmwithB = Aᵀ— a single operandAfeeds 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, theA(j,k)tap enters on the super-diagonal haloi-j = -1(a clean halo since the output leaves onk=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, mirroringtrmvin the opposite direction. A symmetric rank-k update (theAAᵀ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 productC = β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) andsrc/dfa/tests/sim/gemm_sure.cpp(test_gemm_sure, dual-compiler, zero warnings) with adfactl_sure_gemmschedule-emit test. Generalizes the canonical systolicmatmul(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 incomingCscaled byβjoins the completed sum in a terminal-face epilogue onk = K-1. Every dependence is a constant offset → a pure SURE. Verified numerically (C = [[126,148],[308,348]]forαAB + βCwithα=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.
Changed
Section titled “Changed”- 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.mdcollects 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.
- an animation) for
- 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.
- 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
- BLAS-2
syr2(issue #36): the symmetric rank-2 updateA += α(xyᵀ + yxᵀ)on the triangular domainj ≤ i, as a pure SURE —docs/SURE/syr2.sure(+ derivationdocs/SURE/syr2.mdunder Theory / BLAS L2 nav) andsrc/dfa/tests/sim/syr2_sure.cpp(test_syr2_sure, dual-compiler, zero warnings) with adfactl_sure_syr2schedule-emit test. It issyrwith two vectors: the two rank-1 outer productsxyᵀandyxᵀare fused into one symmetric update. Same structure assyr(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 vectorsXandY(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 behindsyr2kand 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 updateA += αxxᵀon the triangular domainj ≤ i, as a pure SURE —docs/SURE/syr.sure(+ derivationdocs/SURE/syr.mdunder Theory / BLAS L2 nav) andsrc/dfa/tests/sim/syr_sure.cpp(test_syr_sure, dual-compiler, zero warnings) with adfactl_sure_syremit test. It isgerwithy = 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. Combinesger’s inject → add → drain (a depth-2 feed axis,αinjected once on thek=-1halo) withtrmv’s triangular domain; the single vector feeds both axes per cell on the(i,j)face (a+ibroadcast would need a diagonal seed face on a triangular domain). The result drains on thek=1face, 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 matvecy = αAx + βy(A = Aᵀ, reading only one stored triangle) as a pure SURE —docs/SURE/symv.sure(+ derivationdocs/SURE/symv.mdunder Theory / BLAS L2 nav) andsrc/dfa/tests/sim/symv_sure.cpp(test_symv_sure, dual-compiler, zero warnings) with adfactl_sure_symvemit test. Numerically it isgemv; the interest is the symmetric-storage confluence pattern — each stored elementA(i,j)(j ≤ i) is routed to two accumulations (y(i) += A(i,j)x(j)andy(j) += A(i,j)x(i)), realized as a two-face feed over the full square: the lower cells read the storedA[i][j], the upper cells read the reflectedA[j][i], so cell(i,j)always seesA_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 matvecx := Txas a pure SURE —docs/SURE/trmv.sure(+ derivationdocs/SURE/trmv.mdunder Theory / BLAS L2 nav) andsrc/dfa/tests/sim/trmv_sure.cpp(test_trmv_sure, dual-compiler, zero warnings) with adfactl_sure_trmvemit test. The catalog’s first non-box index space: agemv-style reduction on the triangular domainj ≤ i, so each row’s sum has a variable extentj = 0..iand the result leaves on the diagonal facei-j = 0(not a flatj = N-1face).Tand the input vector both feed on the(i,j)face (xis 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 updateA += αxyᵀas a pure SURE —docs/SURE/ger.sure(+ derivationdocs/SURE/ger.mdunder Theory) andsrc/dfa/tests/sim/ger_sure.cpp(test_ger_sure, dual-compiler, zero warnings) with adfactl_sure_geremit test. The outer-product counterpart togemv: a 2-D fully-parallel update (no reduction), withxbroadcast along+jandyalong+i. The matrixA— 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 inscal/axpy):Aseeds the accumulator on thek=-1halo, the rank-1 term is added exactly once (αinjected only on that halo, zero in the interior), and the updated matrix drains on thek=1face. 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 bysyr/syr2. - docs-site: BLAS L2 catalog nav — a new BLAS L2 subsection under SURE
Algorithms surfaces
gemvandgerin 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), sogemv/ger/gemm/syr2become 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 + αAxas a pure SURE —docs/SURE/gemv.sure(+ derivationdocs/SURE/gemv.mdunder Theory) andsrc/dfa/tests/sim/gemv_sure.cpp(test_gemv_sure, dual-compiler, zero warnings) with adfactl_sure_gemvemit test. Structurally matmul with the second matrix collapsed to a vector: a reduction over the columnj, withxreused across rows (pipelined along+i) andA— 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βyterm combines with the completed sum in a terminal-face epilogue on the output facej = 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-scheduleemits each variable’s dependence taps (the affine mapA·p + bper producer read), and theschedule-anim.jsviewer 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 (nowN=12,T=4) draws ~2288 halo and ~640 collective edges along its two affine diagonal tapsr(i-1,p,p)anda(i,p-1,p). Both are embedded on the tiling and uniformization pages. AddsAffineDependency::matrix()/offset()accessors; extends the emitter and viewer (edge buckets by firing time, a cross-tile HUD counter, a halo/collective legend). Builds on thedata-tileoverlay 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.mddefines 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.mddefines 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 existingtargeting-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 inconv2d.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/asumreductions,axpy’sαbroadcast, QR’ssrp). 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 (haloO(1/T)amortizes; a collective has anO(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: becausematmul.sureis uniform, itsa/b/cdependences are constant offsets, so every dependence a tile cut severs is a halo (O(T²)surface exchange with a neighbour), never a collective — even theC-accumulation acrossK-blocks is a±1pipelined 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 adata-tilemode 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, CTesttest_qr_givens_sure,dfactl_sure_qr_givens, and a schedule animation on the uniformization page. Each row ofAis merged into the accumulating upper-triangularRby a nearest-neighbour sweep of Givens rotations over(i,p,q),p ≤ q— no reduction. Verified: exactRfor the classic 3×3 and the sign-robustRᵀR = AᵀAon tall inputs. It is still a SARE in the DSL (the two diagonal tapsr(i-1,p,p),a(i,p-1,p)read the pivot column), but those are pure diagonal broadcasts along+qwith no reduction — nearest- neighbour, so they tile with local communication, unlike MGS-QR’s globalΣ_iall-reduce. A pure SURE needs only the internal-confluence extension to seed the rotation propagation. Thedocs/scaling/uniformization.mdpage 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’sr_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 = τ·plinear, or the data-flow-earliest free time), bounds, and latency; a Three.js viewer (docs-site/src/components/schedule-anim.js, modeled on cortex’sScene3D) 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 bydocs-site/make-schedules.mjsand committed underdocs/schedules/(copied topublic/at build, so the docs CI stays toolchain-free). Phase 1 wires thematmulpage 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 usesInstancedMesh(~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,iamaxas a wavefront point sweeping the accumulation chain; the mapsaxpy,scal,copy,swap,rotas a wavefront over the systolic strip). Visualization scale is now applied bymake-schedules.mjsitself — 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.surespecs are checked in (the dedicatedmatmul15.sureis retired;matmulrenders frommatmul.sureat 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).
- Phase 2 (issue #64): animations across the whole BLAS L1 catalog —
all nine operator pages embed a schedule animation (the reductions
- 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
.surespec, a regression test, and a docs-site Theory page.axpy(issue #22): the first entry — BLAS-1y := alpha*x + yas a two-cell systolic map (docs/SURE/axpy.sure,docs/SURE/axpy.md, CTesttest_axpy_sure,dfactl_sure_axpy). The fully-parallel vector map rides the length-N result on theiaxis and injectsxon aj = -1halo so the scaled contribution is added once asystreams to the terminal face. All three operands are uniform flows — in particular the scalaralphais projected onto thei = -1edge 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). VerifiedR = alpha*X + Ywith derived face normals and free/linear legality.SURE_DOCS_DIRis now directory-scoped in the sim tests so each new<op>_sure.cppis a drop-in.dot(issue #23): the canonical reduction — BLAS-1s = xᵀy(docs/SURE/dot.sure,docs/SURE/dot.md, CTesttest_dot_sure,dfactl_sure_dot). The multiply-add chains(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 pointi = N-1. The operands are indexed by the reduction coordinate, so they enter on a depth-1 feed axisj(a reduction reuses no operand, unlike matmul’s propagating A/B). Verifieds = 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, CTesttest_nrm2_sure,dfactl_sure_nrm2). Thedotsum-of-squares reduction with a fused output-facesqrtepilogue: 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 andqr’sRdiag. Verified‖x‖₂ = 5through 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, CTesttest_asum_sure,dfactl_sure_asum). Thedotreduction with a fused input-faceabsprologue: the absolute value maps each operand as it enters (x = abs(X[i])) — the mirror image ofnrm2’s output-facesqrtepilogue, with the interior a clean uniform sum in both. VerifiedΣ|x| = 10on signed data; O(1) accumulator footprint.scal(issue #26): the vector scale — BLAS-1x := α·x(docs/SURE/scal.sure,docs/SURE/scal.md, CTesttest_scal_sure,dfactl_sure_scal). Exactlyaxpyspecialized 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 ofY. VerifiedR = α·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, CTesttest_swap_sure,dfactl_sure_swap). No arithmetic: two value-preserving flows carryXandYstraight through, and the swap is realized entirely in the crossed output confluences (theX-named face reads they-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-1y := x(docs/SURE/copy.sure,docs/SURE/copy.md, CTesttest_copy_sure,dfactl_sure_copy). The catalog’s simplest operator: a single value-preserving recurrencex(i,j) = x(i,j-1)carriesXfrom the input halo to the terminal face, where it leaves asY— one input face, one output face, no arithmetic. The elementary building block of every value-preserving flow (swapis two crossedcopyflows). VerifiedY = 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, CTesttest_rot_sure,dfactl_sure_rot).axpygeneralized 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²whenc²+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-1argmaxᵢ|xᵢ|(docs/SURE/iamax.sure,docs/SURE/iamax.md, CTesttest_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) andselect(c,x,y)— which stay pointwise value ops, so the recurrence remains uniform. Magnitude enters via theabsprologue; the index enters as data (Idx[i]=iprojected onto the feed halo, since bodies cannot read the loop index). Strictgtkeeps 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 thesystem((i,j,k) | constraints) { equations }notation used in the theory documents into executableRecurrenceSystems, wired intodfactl --sure <file.sure>with the existing--schedule/--tau/--quietflags. 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 < 0influx /> 0outflux, revalidated for CLI--tauoverrides viavalidateSureFlux), 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,qembedded as a diagonal-tapped normalized flow,Rleaving through two oriented faces including the non-axis-alignedk = jdiagonal; verifiedRexact,Q^T*Q = IandQ*R = Ato ~1e-15), andconv2d.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
MATMULnode can declare a pointwise activation viaattribute["activation"](e.g."relu"), recorded as an epilogue on the terminalk = K-1output-faceConfluence; bias enters via the existing 3rd operandCin.Confluencegained an optional epilogue field, MATMUL elaboration now populates the input/output face confluences (previously constructed and discarded), and the simulator importer executes the fused formn(i,j) = act(c(i,j,K-1) + Cin(i,j)). Attributes already round-trip through.dfgserialization. FUSED_MATMUL_BIAS_ACToperator (issue #2, PR #14): dedicated IR operator making the fusedY = activation(A*B + bias)semantics explicit — a single(i,j,k)domain where, unlike 3-input MATMUL (Cin seeds the accumulator atk = 0), the bias confluence sits on the terminal face where the epilogue executes. Shares a newbuildMatmulHull()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-stationarytau = [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’sdocs/tree bydocs-site/sync-content.mjs(sections: getting started, architecture, SURE simulator, theory, changelog), with KaTeX math rendering and a landing page atdocs/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 >= 1with violation reports), memory-cardinality (peak live values) analysis, eviction-based execution, and a.dfgimport path. Includes thedfactlCLI (sim/) with built-inmatmul,matvec, andqrspecs, plus tests undersrc/dfa/tests/sim/. - SURE simulator documentation (
docs/sure-simulator.md): why/what/how guide with workeddfactlexamples (legal vs illegal schedules, stage offsets, heterogeneous-rank SAREs,.dfgimport) and a spec-authoring walkthrough. - CLAUDE.md (PR #5): guidance file for AI-assisted development with the
verified build/test workflow (preset scheme,
compile_alltest naming, single-test invocation), code layout, and repo conventions.
Changed
Section titled “Changed”- 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.mjsmaps the L1 pages undersure-algorithms/blas-l1/and the sidebar is defined inastro.config.mjs. (Also fixed twoqrcross-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,
dfactlhelp/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 newllvm-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
llvmjob now owns the cache build/save per OS; the compiler matrix depends on it and does a read-onlyactions/cache/restorewith 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 toactions/cache@v5; workflow runs with least-privilegepermissions: contents: read.
Security
Section titled “Security”- 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 sidebaritemssyntax.
-
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-fpson the.schedule-animdiv) 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
\( … \)/\[ … \], whichremark-mathdoes 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), andRecurrenceSystem(duplicate equation names); schedule rank mismatches now throw instead of silently truncating (could mask illegal schedules); dependency-cycle detection ineval()/computeFreeSchedule(); drained outputs no longer counted as resident in the memory analysis;.dfgimporter validates unary/binary operand shapes with per-axis broadcast compatibility and fails fast on multi-output producers;dfactl --quietnow 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 realBUILD_TESTING/DOMAINFLOW_BUILD_TESTINGwith accurate defaults. -
Compiler warnings cleanup: Eliminated all 6,720 compiler warnings with
-Wall -Wextraflags- Added
default:cases to switch statements indomain_flow_node.hppanddomain_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_tfor 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.hppandenergy_estimator.cpp(4 warnings)
- Added
-
Bug fix in
domain_flow_edge.hpp: Constructor now properly initializessrcSlotanddstSlotmember variables from parameters (previously ignored)