You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
flowlog: native :merge for term-mode congruence (--native-merge)
Port the non-proof half of egglog PR egraphs-good#933 to the FlowLog backend:
replace the rule-encoded @congruence_rule* with an FD view keyed on
children + a UnionId-style side-effecting merge that unions colliding
outputs at FD-conflict time (drained at the iteration boundary). Behind
a --native-merge flag (FlowLog-only, term mode, implies --native-uf);
off by default with byte-identical rule encoding. Bit-exact to flowlog's
rule encoding; agent-measured ~1.16x (1.28x with --fast-rebuild) faster
than --native-uf on math-microbenchmark. Also fixes a pre-existing
clone_boxed panic on --native-uf --flowlog/feldera.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,8 @@
2
2
3
3
## [Unreleased] - ReleaseDate
4
4
5
+
- **`--native-merge`: inline congruence via a native backend merge (FlowLog, term mode).** Ports the non-proof half of egraphs-good/egglog PR #933 to the FlowLog dataflow backend: instead of desugaring congruence into a seminaive `@congruence_rule*` self-join run inside the per-`(run)` rebuild fixpoint, a CONSTRUCTOR's `@<F>View` is keyed on its CHILDREN ONLY (an FD view `(children) -> eclass`) with a side-effecting `MergeFn::UnionId` merge that, on an FD conflict (same children, two eclasses), UNIONS the two eclasses into the union-find and keeps the min — congruence done INLINE at insert time, and the `@congruence_rule*` rule dropped entirely. Implies `--native-uf` (the injected union edge goes into FlowLog's in-core union-find via `enqueue_union`, drained at the iteration boundary like every other union; the displaced-id signal drives the existing rebuild to re-canonicalize children, which re-triggers the merge for transitive congruence). The frontend records each FD view's name (`EncodingState::native_merge_views`) so lowering substitutes `MergeFn::UnionId` and `register_native_merge_view` tells the backend which UF owns the eclass column; the view body/set/delete/subsume forms switch to the `(= eclass (@view children))` shape via the new `view_body_atom`/`view_delete` helpers. Bit-exact with the bridge `--term-encoding` oracle (math-microbenchmark runs 1/3/5/7, hand-written congruence tests, a Herbie sample file) and faster than the rule-encoded congruence (math run 9: 1.14x vs `--native-uf`, 1.21x vs `--native-uf --fast-rebuild`). FlowLog-only, term mode, eq-sort-output constructors with ≥1 input; 0-input constructors (globals) keep the baseline all-columns view. Off by default — the rule-encoded congruence is byte-identical when off. Also fixes a pre-existing `--native-uf --flowlog`/`--feldera` panic (`clone_boxed` unimplemented) by skipping the cli `with_term_encoding_enabled` clone for those backends (they provision the typechecker at construction). (`src/proofs/proof_encoding.rs`, `src/lib.rs`, `src/cli.rs`, `egglog-bridge-flowlog/src/lib.rs`, `egglog-backend-trait/src/lib.rs`, `egglog-experimental/src/main.rs`.)
6
+
5
7
- **Proof mode now supports canonicalize-at-creation, and `--fast-rebuild` works with `--proofs` (native bridge).** Previously canon-at-creation and the `--fast-rebuild` `δview ⋈ uf_old` drop were gated off in proof mode, because the proof-mode flat UF index `@UF_Sf` is `(S) -> Pair(S, Proof)` (key/output types differ, so `DefaultVal::Identity` is unavailable and a lookup of a just-minted key fails). Proof-mode canon now goes through two per-eq-sort `ReadPrim`s declared via a new `:internal-uf-pair-prims` annotation on the `@UFPair_S` sort (so they survive the desugar round-trip): `@uf_find_leader_S` (`(S) -> S`, lookup-or-self leader for child columns) and `@uf_find_or_refl_S` (`(S Proof) -> @UFPair_S`, lookup-or-`(pair x fallback)` for the representative column, no row inserted — bit-exact tuple counts). The constructor term is still built from the original (non-canonical) args so the rule proof's claimed equality matches; the view's child/representative columns are canonicalized and the per-child `child = leader` proofs are threaded into the view proof via `Congr`/`Trans(Sym(rep_pf), …)`. Bridge-only (gated on `Backend::supports_inline_table_lookups`); constructors only (custom `:merge` function views keep the baseline rebuild-rule canonicalization and their `δview` term under fast-rebuild). Bit-exact with proof-mode full-rebuild, and proofs still check. (`src/proofs/proof_encoding.rs`, `src/typechecking.rs`.)
6
8
7
9
- **Fixed: `egglog-experimental` ignored `--threads`/`-j` and silently ran rayon at full width.** `egglog::cli` configures the rayon global pool via `set_num_threads` → `build_global`, but only after it's handed an already-constructed egraph; the experimental wrapper main builds the (experimental/backend) egraph first, and that construction touches rayon — lazily initializing the global pool at its default (all logical CPUs). So `cli`'s later `build_global(1)` failed (`GlobalPoolAlreadyInitialized`) and `--threads 1` was a no-op (e.g. ~257 threads spawned instead of running serial). Fix: the experimental main now calls `set_num_threads` (parsing `--threads`/`-j`, default 1) BEFORE constructing the egraph, and `EGraph::set_num_threads` is now idempotent (a `Once` guard — first call wins, later calls silently ignored per its doc, no spurious warning). The plain `egglog` binary was unaffected (it constructs `EGraph::default()`, which doesn't touch rayon, so its `set_num_threads` already won). (egglog-experimental + `EGraph::set_num_threads`.)
0 commit comments