From 4ec81473cc3e50a01778234bae537315fc489839 Mon Sep 17 00:00:00 2001 From: Victor Balegas Date: Tue, 21 Jul 2026 12:45:22 +0100 Subject: [PATCH] viz: coherent operator notation across the logical and circuit views MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three operator-notation fixes so the same operator reads the same in both the logical view and the dbsp circuit view. 1. Aggregation Σ → γ. The visualizer already uses relational-algebra symbols (σ filter, ⋈ join, π projection); aggregation was labelled Σ, which is summation, not the aggregation operator. γ is the standard grouping/ aggregation symbol. Only the operator markers change (legend "aggregate"/ "fold", the agg/fold node tags, README). The Σ that denotes genuine summation is kept — "Σ of Z-set weights", `fold(Σ value·w)` — so a γ node now reads as the aggregation operator implemented as a fold computing Σ. 2. The engine's circuit-view fold label dropped its `Σ ` prefix (introspection.rs): it emitted "Σ COUNT(*)" while the logical view showed "COUNT(*)". Now both views show "COUNT(*)"; the γ operator identity lives in the node tag (γ FOLD · STATE), verified live in the visualizer. 3. Subquery inner set: arrange → distinct in the logical view. The logical node was tagged IN-SET ARRANGE while its circuit-view counterpart is DISTINCT (and the node's own formula/sublabel already said distinct), so a reader tracing the inner set saw two names. Unify on distinct — the DBSP operator (paper Table 4.2 spells `distinct`, not δ; the blog diagram agrees). "arrange" stays only for op-arrange, the genuinely different physical node (feed set / params index). Verified: pipeline-viz build-graph + build-circuit tests pass (15); the rebuilt engine emits "COUNT(*)" and both views render γ FOLD / distinct, confirmed in the running visualizer. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/engine/src/engine/introspection.rs | 2 +- apps/pipeline-viz/README.md | 6 +++--- apps/pipeline-viz/src/App.tsx | 8 ++++---- apps/pipeline-viz/src/build-circuit.ts | 4 ++-- apps/pipeline-viz/src/node-meta.ts | 8 ++++---- apps/pipeline-viz/src/styles.css | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/engine/src/engine/introspection.rs b/apps/engine/src/engine/introspection.rs index 523165d..d819c96 100644 --- a/apps/engine/src/engine/introspection.rs +++ b/apps/engine/src/engine/introspection.rs @@ -369,7 +369,7 @@ pub(crate) fn circuit_ops( let snk_id = format!("snk:{sid}"); if let Some(agg) = &s.aggregate { - let fn_label = format!("Σ {}({})", format!("{:?}", agg.func).to_uppercase(), agg.col.as_deref().unwrap_or("*")); + let fn_label = format!("{}({})", format!("{:?}", agg.func).to_uppercase(), agg.col.as_deref().unwrap_or("*")); if s.circuit.as_ref().is_some_and(|p| p.counts) { // Counts-served: the fold's input is the counts pipeline's group deltas // (`map_index(group) → weighted_count` in the circuit), NOT a σ over row diff --git a/apps/pipeline-viz/README.md b/apps/pipeline-viz/README.md index 4bf3809..0768218 100644 --- a/apps/pipeline-viz/README.md +++ b/apps/pipeline-viz/README.md @@ -11,7 +11,7 @@ of every node** on the canvas. the same ids, so nothing is reconstructed client-side — what flashes is what ran, and the counts you see are the engine's counters. - **dbsp circuit** — the exploded operator dataflow, **emitted by the engine** (`/graph`'s - `operators`/`opEdges`): source → Δ → σ/↦/arrange/⋈/distinct/Σ → π → sink, one box per real + `operators`/`opEdges`): source → Δ → σ/↦/arrange/⋈/distinct/γ → π → sink, one box per real execution step. Each operator carries the trace-hop id it animates under and (for the state-bearing operator only) the state-summary id its chips show — declared bindings, not client-side guesses. Dashed edges are stateful arrangements feeding joins; shared structure @@ -26,9 +26,9 @@ In the Logical view each node card carries its dbsp identity and its live state: - **↦⋈ route join · STATE** — the shared equality router: an arrangement of predicate keys → shapes, one per family (`WHERE k = const` compiles to an index entry, not a circuit). Chips: live routing-index keys + routed shapes. -- **IN-set arrange · STATE** — a shared subquery inner set (`value → contributing pks`), one per +- **IN-set distinct · STATE** — a shared subquery inner set (`value → contributing pks`), one per distinct `IN (SELECT …)`. Chips: distinct values + refcount. -- **Σ fold · STATE** — a scalar aggregation maintained as an incremental fold. Chips: the current +- **γ fold · STATE** — a scalar aggregation maintained as an incremental fold. Chips: the current value (live), matching-row count, and the MIN/MAX retraction-multiset size. - **shape out · π** — the per-shape output stream (grouped by pk into upsert/delete envelopes). Chip: envelopes emitted (backfill + live). diff --git a/apps/pipeline-viz/src/App.tsx b/apps/pipeline-viz/src/App.tsx index dc2e777..5cc74c0 100644 --- a/apps/pipeline-viz/src/App.tsx +++ b/apps/pipeline-viz/src/App.tsx @@ -890,8 +890,8 @@ export default function App() { table · Δ source σ filter ↦⋈ route join - IN-set arrange - Σ aggregate + IN-set distinct + γ aggregate shape out {graph?.arrangements ? ( @@ -905,9 +905,9 @@ export default function App() { Δ change σ filter ↦ key - arrange (state) + distinct (state) ⋈ join - Σ fold + γ fold π · sink {graph?.arrangements ? dbsp index : null} {graph?.arrangements ? dbsp counts : null} diff --git a/apps/pipeline-viz/src/build-circuit.ts b/apps/pipeline-viz/src/build-circuit.ts index 332631c..413bf49 100644 --- a/apps/pipeline-viz/src/build-circuit.ts +++ b/apps/pipeline-viz/src/build-circuit.ts @@ -204,7 +204,7 @@ function restrictToSelection( // 2. Subquery templates: subquery shapes whose maintained pipeline is structurally identical // (same outer table, predicate template, projection) — differing only in their bound parameter // and thus their materialized inner set — fold their inner-set chain (`sqf`/`sqp`/`dist`) into -// one stacked IN-SET ARRANGE and their outer chain (`sj`/`pi`/`snk`) into one stacked SINK. +// one stacked IN-SET DISTINCT and their outer chain (`sj`/`pi`/`snk`) into one stacked SINK. // Both dimensions reduce to the same mechanism: a `redirect` map (collapsed operator id → the // representative node that stands in for it) plus the representative nodes themselves. `collapse` // then drops the collapsed operators, adds the representatives, and rewrites every edge endpoint @@ -316,7 +316,7 @@ function planGroups(g: EngineGraph): GroupPlan { ref, }, }) - // The inner-set chain (σ inner where → π proj → distinct) collapses to the IN-SET ARRANGE rep… + // The inner-set chain (σ inner where → π proj → distinct) collapses to the IN-SET DISTINCT rep… for (const sig of sigs) { redirect.set(`sqf:${sig}`, distId) redirect.set(`sqp:${sig}`, distId) diff --git a/apps/pipeline-viz/src/node-meta.ts b/apps/pipeline-viz/src/node-meta.ts index e373f8e..1a16447 100644 --- a/apps/pipeline-viz/src/node-meta.ts +++ b/apps/pipeline-viz/src/node-meta.ts @@ -64,7 +64,7 @@ export const KIND_META: Record = { sqnode: { color: '#7e22ce', bg: '#f3e8ff', - tag: 'IN-SET ARRANGE · STATE', + tag: 'IN-SET DISTINCT · STATE', formula: 'distinct(π(proj) · σ(where) · inner)', stateful: true, inside: @@ -92,7 +92,7 @@ export const KIND_META: Record = { agg: { color: '#0d9488', bg: '#ccfbf1', - tag: 'Σ FOLD · STATE', + tag: 'γ FOLD · STATE', formula: 'fold(Σ value·w over σ(where))', stateful: true, inside: @@ -192,7 +192,7 @@ export const KIND_META: Record = { 'op-fold': { color: '#0d9488', bg: '#ccfbf1', - tag: 'Σ FOLD · STATE', + tag: 'γ FOLD · STATE', formula: 'fold(Σ value·w)', stateful: true, inside: @@ -256,7 +256,7 @@ export const KIND_META: Record = { 'arr-counts': { color: '#6d28d9', bg: '#ede9fe', - tag: 'DBSP COUNTS · Σ STATE', + tag: 'DBSP COUNTS · γ STATE', formula: 'map_index(group) · weighted_count', stateful: true, inside: diff --git a/apps/pipeline-viz/src/styles.css b/apps/pipeline-viz/src/styles.css index 7b2297b..f59affe 100644 --- a/apps/pipeline-viz/src/styles.css +++ b/apps/pipeline-viz/src/styles.css @@ -690,7 +690,7 @@ body, box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); } } -/* Aggregation detail: the prominent live scalar stat (teal, matching the Σ node). */ +/* Aggregation detail: the prominent live scalar stat (teal, matching the γ node). */ .dp-agg { margin: 2px 0 10px; padding: 8px 12px 9px;