Skip to content

Commit 1bbb541

Browse files
authored
Merge pull request #1658 from AdaWorldAPI/ccr-d3ce1263-ied9yh
U4 (D-RU-4): co-addressed join operator over one JoinDomain
2 parents de6770f + faa0837 commit 1bbb541

9 files changed

Lines changed: 1068 additions & 3 deletions

File tree

‎.claude/board/STATUS_BOARD.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Plan: `.claude/plans/d-relalg-unified-v1.md`. Decisions recorded there: ADMISSIO
88
| **D-RU-1** | U1: JC statistical batch, distinct (selection, key, lane pair) triples over shared power-sum terminals | MEASURED (`crates/jc-batch-probe`; `entries/2026-10-20-u1-jc-batch.md`): 252 requests → 2 selection + 42 cross passes; at 1M rows 6.4× faster, 11.5× fewer rows traversed, 8.5× fewer values read, 126× fewer mask bytes; bit-identical to per-request; no R reference (R absent) | rows traversed and terminal passes below the per-request baseline; results bit-exact on integer lanes; overflow refused, not wrapped; R references |
99
| **D-RU-2** | U2: `let`-bound planes on the contract IR, immutable `Plane(i)`, versioned coordinates, no DAG inside a fold | MEASURED (`contract::set_program`, `relalg::program`; `entries/2026-10-21-u2-let-planes.md`): 16 queries sharing one conjunction at 1M rows: 3.1× faster, 14 vs 59 lane reads, 2 planes (250 kB) materialized, ≤ 2 scratch slots; answers equal recompute and a row oracle; other versions refused | rows traversed and planes materialized against recompute; width test ≤ 3 slots; answers and versions unchanged |
1010
| **D-RU-3** | cross-rule CSE in crepe-plan-probe (256-rule benchmark, separate from the hop benchmark) | MEASURED (`crepe-plan-probe/src/cse.rs`, `examples/rule_cse.rs`; `entries/2026-10-21-u3-cross-rule-cse.md`): 256 rules, 90 % shared core, 200k centers: rows laid out 15.3 M → 6.2 M, prepare 1.78 s → 1.22 s; lets cut ops 512 → 283 but folds are ≤ 2.4 ms and `share` costs 7 ms; one table for all rules is slower than per-rule when nothing is shared, so tables are per group of rules sharing a side; answers equal crepe, ascent and the reference | answers == crepe and ascent; traversals and materializations measured |
11-
| **D-RU-4** | co-addressed join operator over `JoinDomain` | Queued (after D-RU-2) | parity with the star path; no position taken from another table's values |
11+
| **D-RU-4** | co-addressed join operator over `JoinDomain` | FAILED GATE — speed (no-ms rule, `CLAUDE.md` P0); correctness MEASURED (`crepe-plan-probe/src/coaddressed.rs`, `examples/coaddressed.rs`; `entries/2026-10-21-u4-coaddressed-join.md`): per-side `Rel`s at query time over one unrestricted table, `GroupReduce Count` on each side's own slot lane, `And` of slot planes; answers equal the star path, the nested-loop reference and every arm; no program has a foreign hop (disable-verified). Every arm of the benchmark, the star path's included, ran in milliseconds; open: fold the per-side `Rel` into the projection terminal | parity with the star path; no position taken from another table's values |
1212
| **D-RU-5** | Quack physical pins (dense vs sorted, materialize vs recompute) | Queued (after D-RU-3/4) | can-fire and can-stay-silent per pin |
1313
| **D-RU-6** | recursion driver over BIND tiles (256-hop benchmark) | Queued/Lab (slot assignment must be argued as an attested frozen-codebook lookup under ADMISSION = B). Lab probe MEASURED (`crepe-plan-probe/examples/walk_tile.rs`, `entries/2026-10-21-d-ru-6-tile-walk-probe.md`): tuple sets equal ascent, crepe and walk_mask; execution gather-free; binding witness probe-local, so ADMISSION B is NOT demonstrated; count-only 1.0–9.3× slower than walk_mask, end to end 1.1–2.0× slower. Block-sparse arm C MEASURED (`examples/walk_sparse.rs`, `entries/2026-10-21-d-ru-6-sparse-compute.md`): faster than the dense tile path on every fixture (0.11–0.96× count-only); against walk_mask end to end it wins only on deep low-fan-out recursion (chain(230) 0.82×, chain(700) 0.59× UNCHECKED), is near parity on sparse graphs (1.03–1.07×) and loses on dense, cyclic, clustered and mixed-length graphs (1.20–2.43×). Arm A′ (walk_mask with a dirty-frontier skip, `examples/walk_dirty.rs`, `entries/2026-10-21-d-ru-6-dirty-frontier.md`) does not remove C's chain lead; the chain sweep (two runs, distinct fan-out, like-for-like timing) confines C's end-to-end win over A′ to unbranched chains of about 192 nodes or more (3–30 %); with fan-out 2 or 4 C loses end to end at most points. Recommendation: defer the codebook attestation | least fixpoint == datafrog; ≥ 200 iterations asserted; cycles terminate. Benchmark requirement (operator, 2026-10-21; refs D-RMAP-2 §5b, #1642): tuple materialization, count-only evaluation, memory growth, address binding and value-derived reads by origin reported separately; tuple-set equality with crepe and ascent wherever they run |
1414
| **D-RU-7** | prefetch and staging of Arm B ranges | Queued (after D-RU-6 or a non-recursive multi-stage plan) | critical-path overlap measured; bytes read unchanged |
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# 2026-10-21 — D-RU-4 (U4): the co-addressed join as a named operator
2+
3+
**Status:** FAILED GATE (no-ms rule, `CLAUDE.md` P0, 2026-10-21) — correctness MEASURED, speed not (lab; `crates/crepe-plan-probe/src/coaddressed.rs`, `examples/coaddressed.rs`). Plan: `.claude/plans/d-relalg-unified-v1.md` §5.3, §6, stage table (U4). Builds on U2 (`relalg::run_with_program`) and the D-MPS-8 table (`mork_pathmap_probe::join`).
4+
5+
## What landed
6+
7+
- `coaddressed::eval(table, plan, rels, current)`: a conjunction of per-table `Rel`s over one `JoinDomain`. For each side `i`:
8+
- `rel_i` runs as a relalg fold over side `i`'s own rows (lane 0 = the side's other column);
9+
- one mask-risc `GroupReduce Count` keyed by the side's slot lane turns the selected rows into per-slot counts;
10+
- a side whose `Rel` normalizes to `All` reads its presence plane and offsets as prepared, with no row read.
11+
- The joint is relalg `And(Plane(i))` over the per-side slot planes. Answers:
12+
- `distinct_centers`, the popcount of the joint;
13+
- `bag_count`, a co-addressed dot product of the per-side counts (`Σ_slot Π_i count_i[slot]`) that enumerates no row;
14+
- `matching(pattern)`, the derivations whose head reads as a pattern: a fold over the joint, no row built. (This replaced the first version's `bindings`, a tuple builder, in `1d1c0b77` after #1657 removed `native::bindings`.)
15+
- `CoTable::prepare` lays out every side without its restriction. `plan_rels` turns a `StarPlan`'s restrictions into `value == k` (or `Empty` for a constant above `u32::MAX`). A restriction is therefore a query-time `Rel` and no longer part of the layout. The `Rel` can be anything relalg lowers on a `U32` lane (`==`, `!=`, `Or`, `Not`), which the star path cannot express.
16+
- The slot lane is built from the side's own offsets, so it is non-decreasing. A row is counted in the run it already sits in, and no operand is read at a position taken from another operand's values. `CoEval::programs` keeps every program run so a caller can check that with `native::has_foreign_hop`.
17+
- Refusals: a table holding other sides than the plan reads, the wrong number of `Rel`s or generations, a stale side.
18+
- `mork_pathmap_probe::join::Side` gains two read-only accessors, `values()` and `offsets()`.
19+
20+
## Measured — failed gate
21+
22+
The benchmark (`examples/coaddressed.rs`: 64 queries `Out(x, c, z) <- R(x, c), S(c, z), T(c, q)`, 200,000 rows per relation, 20,000 centers) produced results in milliseconds for every arm: the per-query layout of the star path (`native::prepare`, with and without semijoin) and this operator alike. Under the `CLAUDE.md` P0 rule (2026-10-21) a millisecond result is a failed gate, not a measurement, so no timing is reported here. The first version of this entry and of #1658's body reported them; they are withdrawn.
23+
24+
What stays true and is not a timing: the operator lays the table out once and reads only the rows of sides whose `Rel` is not `All` (here T's value and slot lanes, 200k rows per query); the star path lays out all sides again per query.
25+
26+
**Where the operator leaves the mask-risc terminals** (from the code; not attributed by measurement):
27+
- each filtered side keeps a full row mask (`Measure::Keep`) instead of folding into the terminal;
28+
- the projection's `GroupReduce Count` sinks into an `i64` buffer of domain size (`8 × domain` bytes);
29+
- the slot plane is built from those counts by a scalar Rust loop over the domain;
30+
- `bag_count` / `matching` walk the joint bit by bit and multiply side counts in scalar code per slot;
31+
- each filtered side runs two programs (fold, then projection).
32+
33+
**Correctness.**
34+
- `parity_with_the_star_path`: for five restriction combinations (none; one; two; three; a constant above `u32::MAX`), the bag count, distinct centers and pinned per-derivation counts equal the star path (`native::matching` / `distinct_centers`). At least four of the five cases have answers.
35+
- `disjunctive_restrictions_match_the_reference`: `Or`, `Not` and `!=` restrictions equal a nested-loop reference, bag and set.
36+
- The benchmark asserts that the answers of every arm are equal on every rep, and `cargo test --example coaddressed` runs the same comparison on a small fixture in CI.
37+
- **Disables:** five, each red on the test it targets:
38+
- a projection that scatters by the slot lane (`ScatterOrU32`, answer-correct) fails only `no_gather`, at the gather assertion;
39+
- row bits ignored in tuple enumeration;
40+
- per-slot counts replaced by run lengths;
41+
- witness check removed;
42+
- stale-generation check removed.
43+
- The first scatter disable used `ScatterCountU32` with an `Out::I64`. It failed on an `ExecError` before the gather assertion ran, so it proved nothing about that assertion. It was replaced with the answer-correct `ScatterOrU32` version above.
44+
45+
## Open
46+
47+
- **The speed gate.** Bring the per-query path to the mask-risc terminals: fold each side's `Rel` straight into the slot projection, sink into a plane rather than an `i64` buffer, and answer counts through a terminal over the kept joint. Until that runs in nanoseconds per operation (microseconds per batch), D-RU-4 is not shipped.
48+
49+
- The projection writes per-slot counts as `i64` for every filtered side (`8 × domain` bytes), and builds the slot plane from them in a Rust loop over the domain. A fused "count into plane" terminal would remove both; not built, not measured.
50+
- Each filtered side runs two programs (fold, then projection) and keeps the row mask for tuple enumeration. When only counts are asked, the fold and the projection could be one program; not measured.
51+
- No restriction on the center itself (a `Rel` over the domain) yet.
52+
- §6 factorization beyond the star (a chain through two domains) is not built.

‎.claude/board/entries/README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ index row, (3) no duplicate entry id. Checks 1 and 2 are deliberately
2525
opposite directions; the stranding this convention prevents shows up in
2626
exactly one of them, never both.
2727

28-
409 entries, 2026-08-06 .. 2026-10-21.
28+
410 entries, 2026-08-06 .. 2026-10-21.
2929

3030
| date | entry id | finding | file |
3131
|---|---|---|---|
32+
| 2026-10-21 | `D-RU-4` | | [2026-10-21-u4-coaddressed-join.md](2026-10-21-u4-coaddressed-join.md) |
3233
| 2026-10-21 | `D-RU-3` | | [2026-10-21-u3-cross-rule-cse.md](2026-10-21-u3-cross-rule-cse.md) |
3334
| 2026-10-21 | `D-RU-2` | | [2026-10-21-u2-let-planes.md](2026-10-21-u2-let-planes.md) |
3435
| 2026-10-21 | `tuple-zero-copy-council` | | [2026-10-21-tuple-zero-copy-council.md](2026-10-21-tuple-zero-copy-council.md) |

‎.claude/plans/d-relalg-unified-v1.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,4 +298,4 @@ One workload, run under three toggles. Each toggle changes one mechanism only.
298298
| U7 | prefetch and staging of Arm B ranges | U6 or a non-recursive multi-stage plan | critical-path overlap measured; bytes read unchanged |
299299
| U8 | Wikidata and SAP-shaped fixtures; completeness refusals | U6, U7 | refusal tests; witness carried end to end |
300300

301-
U1 needs nothing new and comes first. **U1 MEASURED (2026-10-20, `crates/jc-batch-probe`, `entries/2026-10-20-u1-jc-batch.md`):** 252 requests reduce to 2 selection and 42 cross passes; at 1M rows the batch is 6.4× faster with 11.5× fewer rows traversed, 8.5× fewer values read and 126× fewer mask bytes, and its answers equal the per-request answers bit for bit. The R reference is replaced by JC's slice functions because R is not installed. **U2 MEASURED (2026-10-21, `contract::set_program` + `relalg::program`, `entries/2026-10-21-u2-let-planes.md`):** each shared junction subset is evaluated once into a scratch plane; for 16 queries sharing one conjunction at 1M rows the program is 3.1× faster than recompute and reads 14 lanes instead of 59, at the cost of 2 materialized planes (250 kB). Answers equal recompute and a row oracle, and a program for another version is refused. **U3 MEASURED (2026-10-21, `crepe-plan-probe/src/cse.rs` + `examples/rule_cse.rs`, `entries/2026-10-21-u3-cross-rule-cse.md`):** the §7 claim "one core join plus 256 ANDs" holds for the layout, not for the folds. At 90 % shared core the core is laid out once (15.3 M → 6.2 M rows, prepare 1.78 s → 1.22 s); the lets cut ops 512 → 283, but the folds cost ≤ 2.4 ms and the subset search 7 ms. One table for all rules is slower than per-rule tables when nothing is shared, so tables are formed per group of rules that share a side. Both are accepted only on measured row traversals, materializations and total cost (§ Decisions), not on operator counts.
301+
U1 needs nothing new and comes first. **U1 MEASURED (2026-10-20, `crates/jc-batch-probe`, `entries/2026-10-20-u1-jc-batch.md`):** 252 requests reduce to 2 selection and 42 cross passes; at 1M rows the batch is 6.4× faster with 11.5× fewer rows traversed, 8.5× fewer values read and 126× fewer mask bytes, and its answers equal the per-request answers bit for bit. The R reference is replaced by JC's slice functions because R is not installed. **U2 MEASURED (2026-10-21, `contract::set_program` + `relalg::program`, `entries/2026-10-21-u2-let-planes.md`):** each shared junction subset is evaluated once into a scratch plane; for 16 queries sharing one conjunction at 1M rows the program is 3.1× faster than recompute and reads 14 lanes instead of 59, at the cost of 2 materialized planes (250 kB). Answers equal recompute and a row oracle, and a program for another version is refused. **U3 MEASURED (2026-10-21, `crepe-plan-probe/src/cse.rs` + `examples/rule_cse.rs`, `entries/2026-10-21-u3-cross-rule-cse.md`):** the §7 claim "one core join plus 256 ANDs" holds for the layout, not for the folds. At 90 % shared core the core is laid out once (15.3 M → 6.2 M rows, prepare 1.78 s → 1.22 s); the lets cut ops 512 → 283, but the folds cost ≤ 2.4 ms and the subset search 7 ms. One table for all rules is slower than per-rule tables when nothing is shared, so tables are formed per group of rules that share a side. **U4 FAILED GATE on speed (2026-10-21, `crepe-plan-probe/src/coaddressed.rs` + `examples/coaddressed.rs`, `entries/2026-10-21-u4-coaddressed-join.md`):** the co-addressed join is a named operator, a conjunction of per-table `Rel`s over one `JoinDomain`, and a side's restriction runs at query time over one table laid out without it. Answers equal the star path and no program reads at a position taken from another operand's values, but every arm of its benchmark ran in milliseconds, which the no-ms rule makes a failed gate; the per-side `Rel` must fold into the projection terminal. Both are accepted only on measured row traversals, materializations and total cost (§ Decisions), not on operator counts.

‎.github/workflows/crepe-plan-probe.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ jobs:
6363
# The benchmark (`cargo run --release --example rule_cse`) is not run
6464
# in CI.
6565
- run: cargo test --example rule_cse
66+
# D-RU-4 (U4): the co-addressed join operator against the star path.
67+
# The benchmark (`cargo run --release --example coaddressed`) is not
68+
# run in CI.
69+
- run: cargo test --example coaddressed
6670
# D-RU-6 arm C: the block-sparse product, against arms A and B and the
6771
# oracles, its refusals and the semi-naive bound.
6872
- run: cargo test --example walk_sparse

0 commit comments

Comments
 (0)