artdag: Phase 7 optimisation laws as confluent maude module + 11 tests
lib/artdag/optimize-rules.sx — the effect-pipeline optimisation passes (identity elim, no-op/zero-radius elim, adjacent fusion, idempotent over dedup) as a maude module. Radius algebra is _+_ [assoc comm id: 0] (NOT Peano successor rules, which are non-confluent here); mau/confluent? certifies 0 non-joinable critical pairs, so the optimised pipeline's normal form / content id is rewrite-order stable. Consumes lib/maude/confluence.sx. maude-optimize 25/25, total 183/183. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,13 +35,15 @@ run_suite() {
|
|||||||
(load "lib/maude/matching.sx")
|
(load "lib/maude/matching.sx")
|
||||||
(load "lib/maude/conditional.sx")
|
(load "lib/maude/conditional.sx")
|
||||||
(load "lib/maude/fire.sx")
|
(load "lib/maude/fire.sx")
|
||||||
|
(load "lib/maude/confluence.sx")
|
||||||
(load "lib/maude/rewrite.sx")
|
(load "lib/maude/rewrite.sx")
|
||||||
(load "lib/maude/searchpath.sx")
|
(load "lib/maude/searchpath.sx")
|
||||||
(load "lib/maude/strategy.sx")
|
(load "lib/maude/strategy.sx")
|
||||||
(load "lib/maude/meta.sx")
|
(load "lib/maude/meta.sx")
|
||||||
(load "lib/maude/pretty.sx")
|
(load "lib/maude/pretty.sx")
|
||||||
(load "lib/maude/run.sx")'
|
(load "lib/maude/run.sx")'
|
||||||
BRIDGE_LOAD='(load "lib/artdag/maude-bridge.sx")'
|
BRIDGE_LOAD='(load "lib/artdag/maude-bridge.sx")
|
||||||
|
(load "lib/artdag/optimize-rules.sx")'
|
||||||
fi
|
fi
|
||||||
cat > "$TMP" << EPOCHS
|
cat > "$TMP" << EPOCHS
|
||||||
(epoch 1)
|
(epoch 1)
|
||||||
|
|||||||
69
lib/artdag/optimize-rules.sx
Normal file
69
lib/artdag/optimize-rules.sx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
; lib/artdag/optimize-rules.sx — Phase 7: optimisation laws as a confluent maude module.
|
||||||
|
; The optimised effect pipeline IS the normal form of the rule set, so confluence
|
||||||
|
; (mau/confluent?) is exactly content-id stability: every rewrite order reaches the
|
||||||
|
; same normal form. Media ops (blur/bright/id/over) are the opaque-op model from
|
||||||
|
; lib/maude/tests/effects.sx — the engine reasons about the pipeline algebra, never
|
||||||
|
; pixels. The radius algebra is an AC operator with identity 0 (unary 1s): Peano
|
||||||
|
; successor rules (s M + N = s(M+N), 0 + N = N) are NOT confluent here (the symbolic
|
||||||
|
; critical pairs M + 0 and (A+B)+C vs A+(B+C) stick), whereas [assoc comm id: 0]
|
||||||
|
; joins them via canonical form. maude (lib/maude) is a READ-ONLY consumed substrate:
|
||||||
|
; mau/parse-module, mau/creduce-term, mau/creduce->str, mau/ccanon, mau/confluent?,
|
||||||
|
; mau/non-joinable-pairs, mau/cp->str.
|
||||||
|
|
||||||
|
(define
|
||||||
|
artdag/opt-module-src
|
||||||
|
(str
|
||||||
|
"fmod ARTDAGOPT is\n"
|
||||||
|
" sorts Img Num .\n"
|
||||||
|
" op 0 : -> Num .\n"
|
||||||
|
" op 1 : -> Num .\n"
|
||||||
|
" op _+_ : Num Num -> Num [assoc comm id: 0] .\n"
|
||||||
|
" op blur : Img Num -> Img .\n"
|
||||||
|
" op bright : Img Num -> Img .\n"
|
||||||
|
" op id : Img -> Img .\n"
|
||||||
|
" op over : Img Img -> Img [comm] .\n"
|
||||||
|
" vars I J : Img .\n"
|
||||||
|
" vars M N : Num .\n"
|
||||||
|
" eq id(I) = I .\n"
|
||||||
|
" eq blur(I, 0) = I .\n"
|
||||||
|
" eq bright(I, 0) = I .\n"
|
||||||
|
" eq blur(blur(I, M), N) = blur(I, M + N) .\n"
|
||||||
|
" eq bright(bright(I, M), N) = bright(I, M + N) .\n"
|
||||||
|
" eq over(I, I) = I .\n"
|
||||||
|
"endfm"))
|
||||||
|
|
||||||
|
(define artdag/opt-module (mau/parse-module artdag/opt-module-src))
|
||||||
|
|
||||||
|
; ---- reduce a surface pipeline to its optimised normal form ----
|
||||||
|
|
||||||
|
(define
|
||||||
|
artdag/opt-reduce-term
|
||||||
|
(fn (src) (mau/creduce-term artdag/opt-module src)))
|
||||||
|
|
||||||
|
(define
|
||||||
|
artdag/opt-normal-form
|
||||||
|
(fn (src) (mau/creduce->str artdag/opt-module src)))
|
||||||
|
|
||||||
|
(define artdag/opt-canon (fn (src) (mau/ccanon artdag/opt-module src)))
|
||||||
|
|
||||||
|
; two surface pipelines optimise to the same pipeline (=> same content id) iff
|
||||||
|
; their normal forms coincide.
|
||||||
|
(define
|
||||||
|
artdag/opt-same-form?
|
||||||
|
(fn (a b) (= (artdag/opt-normal-form a) (artdag/opt-normal-form b))))
|
||||||
|
|
||||||
|
; ---- confluence / content-id stability (consume lib/maude/confluence.sx) ----
|
||||||
|
|
||||||
|
(define artdag/opt-confluent? (fn () (mau/confluent? artdag/opt-module)))
|
||||||
|
|
||||||
|
(define
|
||||||
|
artdag/opt-non-joinable
|
||||||
|
(fn () (mau/non-joinable-pairs artdag/opt-module)))
|
||||||
|
|
||||||
|
(define
|
||||||
|
artdag/opt-non-joinable->strs
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(map
|
||||||
|
(fn (cp) (mau/cp->str artdag/opt-module cp))
|
||||||
|
(artdag/opt-non-joinable))))
|
||||||
@@ -10,9 +10,9 @@
|
|||||||
"serialize": {"pass": 13, "fail": 0},
|
"serialize": {"pass": 13, "fail": 0},
|
||||||
"stats": {"pass": 12, "fail": 0},
|
"stats": {"pass": 12, "fail": 0},
|
||||||
"fault": {"pass": 14, "fail": 0},
|
"fault": {"pass": 14, "fail": 0},
|
||||||
"maude-optimize": {"pass": 14, "fail": 0}
|
"maude-optimize": {"pass": 25, "fail": 0}
|
||||||
},
|
},
|
||||||
"total_pass": 172,
|
"total_pass": 183,
|
||||||
"total_fail": 0,
|
"total_fail": 0,
|
||||||
"total": 172
|
"total": 183
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,5 +14,5 @@ _Generated by `lib/artdag/conformance.sh`_
|
|||||||
| serialize | 13 | 0 | 13 |
|
| serialize | 13 | 0 | 13 |
|
||||||
| stats | 12 | 0 | 12 |
|
| stats | 12 | 0 | 12 |
|
||||||
| fault | 14 | 0 | 14 |
|
| fault | 14 | 0 | 14 |
|
||||||
| maude-optimize | 14 | 0 | 14 |
|
| maude-optimize | 25 | 0 | 25 |
|
||||||
| **Total** | **172** | **0** | **172** |
|
| **Total** | **183** | **0** | **183** |
|
||||||
|
|||||||
@@ -110,3 +110,60 @@
|
|||||||
(artdag/dag-id mo-diamond "a")
|
(artdag/dag-id mo-diamond "a")
|
||||||
(keys (artdag/dag-nodes mo-diamond-rt)))
|
(keys (artdag/dag-nodes mo-diamond-rt)))
|
||||||
true)
|
true)
|
||||||
|
|
||||||
|
; ---- optimisation laws as a confluent maude module (optimize-rules.sx) ----
|
||||||
|
; The optimised pipeline is the normal form; confluence => stable content id.
|
||||||
|
|
||||||
|
(artdag-test "opt module is confluent" (artdag/opt-confluent?) true)
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"opt module has no non-joinable critical pairs"
|
||||||
|
(len (artdag/opt-non-joinable))
|
||||||
|
0)
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"law: identity elimination"
|
||||||
|
(artdag/opt-normal-form "id(src)")
|
||||||
|
"src")
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"law: zero-radius blur is a no-op"
|
||||||
|
(artdag/opt-normal-form "blur(src, 0)")
|
||||||
|
"src")
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"law: zero-radius bright is a no-op"
|
||||||
|
(artdag/opt-normal-form "bright(src, 0)")
|
||||||
|
"src")
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"law: adjacent blur fusion adds radii"
|
||||||
|
(artdag/opt-normal-form "blur(blur(src, 1), 1)")
|
||||||
|
"blur(src, _+_(1, 1))")
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"fusion normal form is rewrite-order stable"
|
||||||
|
(artdag/opt-same-form?
|
||||||
|
"blur(blur(blur(src, 1), 1), 1)"
|
||||||
|
"blur(blur(src, 1 + 1), 1)")
|
||||||
|
true)
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"laws compose: id + no-op + fusion"
|
||||||
|
(artdag/opt-normal-form "bright(id(blur(blur(src, 1), 1)), 0)")
|
||||||
|
"blur(src, _+_(1, 1))")
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"law: idempotent over dedup (CSE)"
|
||||||
|
(artdag/opt-normal-form "over(blur(src, 1), blur(src, 1))")
|
||||||
|
"blur(src, 1)")
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"distinct over operands do not dedup"
|
||||||
|
(artdag/opt-same-form? "over(blur(src, 1), blur(src, 1 + 1))" "blur(src, 1)")
|
||||||
|
false)
|
||||||
|
|
||||||
|
(artdag-test
|
||||||
|
"distinct pipelines stay distinct"
|
||||||
|
(artdag/opt-same-form? "blur(src, 1)" "bright(src, 1)")
|
||||||
|
false)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ edges.
|
|||||||
|
|
||||||
## Status (rolling)
|
## Status (rolling)
|
||||||
|
|
||||||
`bash lib/artdag/conformance.sh` → **172/172** (11 suites: dag, analyze, plan, execute, optimize, fed, cost, serialize, stats, fault, maude-optimize)
|
`bash lib/artdag/conformance.sh` → **183/183** (11 suites: dag, analyze, plan, execute, optimize, fed, cost, serialize, stats, fault, maude-optimize)
|
||||||
|
|
||||||
Base roadmap (Phases 1–6) COMPLETE. Now extending.
|
Base roadmap (Phases 1–6) COMPLETE. Now extending.
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ declaratively and prove it equivalent to the hand-written `optimize.sx`.
|
|||||||
- [ ] Equivalence: assert the maude-optimised DAG == `optimize.sx`'s output on
|
- [ ] Equivalence: assert the maude-optimised DAG == `optimize.sx`'s output on
|
||||||
the existing fixtures (same nodes, same content ids) — the rule set must cover
|
the existing fixtures (same nodes, same content ids) — the rule set must cover
|
||||||
at least the hand-written passes.
|
at least the hand-written passes.
|
||||||
- [ ] Confluence / CID-stability check: **consume `mau/confluent?` from
|
- [x] Confluence / CID-stability check: **consume `mau/confluent?` from
|
||||||
`lib/maude/confluence.sx`** — do NOT build your own checker. Assert the
|
`lib/maude/confluence.sx`** — do NOT build your own checker. Assert the
|
||||||
optimisation rule module is confluent (`(mau/confluent? rules-module)` is
|
optimisation rule module is confluent (`(mau/confluent? rules-module)` is
|
||||||
true) so different rewrite orders reach the same normal form and the optimised
|
true) so different rewrite orders reach the same normal form and the optimised
|
||||||
@@ -184,6 +184,26 @@ be an op token.
|
|||||||
|
|
||||||
## Progress log
|
## Progress log
|
||||||
|
|
||||||
|
- **2026-06-19 Phase 7 — optimisation laws + confluence** (maude-optimize 25/25,
|
||||||
|
total 183/183). `lib/artdag/optimize-rules.sx`: the effect-pipeline optimisation
|
||||||
|
passes as a maude module `ARTDAGOPT` — `id(I)=I`, `blur(I,0)=I`, `bright(I,0)=I`,
|
||||||
|
adjacent fusion `blur(blur(I,M),N)=blur(I,M+N)` (+bright), idempotent
|
||||||
|
`over(I,I)=I`. Key result: the radius algebra is `_+_ [assoc comm id: 0]` (unary
|
||||||
|
`1`s), NOT Peano successor rules — the Peano version is non-confluent (6
|
||||||
|
non-joinable critical pairs: `M+0` sticks, `(A+B)+C` vs `A+(B+C)` doesn't join),
|
||||||
|
whereas AC+id makes `(mau/confluent? artdag/opt-module)` true (0 non-joinable
|
||||||
|
pairs) by joining those overlaps via canonical form. So the optimised pipeline's
|
||||||
|
normal form — and hence its content id — is stable under any rewrite order.
|
||||||
|
`artdag/opt-normal-form`/`opt-reduce-term`/`opt-canon` reduce a surface pipeline;
|
||||||
|
`opt-same-form?` decides content-id equality; `opt-confluent?`/`opt-non-joinable`
|
||||||
|
/`opt-non-joinable->strs` consume `lib/maude/confluence.sx` (loaded into the
|
||||||
|
maude-optimize suite). Tests: confluence holds, every law fires, fusion is
|
||||||
|
rewrite-order stable, laws compose, dedup vs no-dedup, distinct pipelines stay
|
||||||
|
distinct. Gotcha: compare reduced *strings* (`mau/creduce->str`) — canon term
|
||||||
|
objects compare unequal under `=` even when the printed normal form is identical.
|
||||||
|
Remaining Phase 7: bridge the maude normal form back to a runnable DAG +
|
||||||
|
equivalence-with-`optimize.sx`.
|
||||||
|
|
||||||
- **2026-06-07 Phase 7 — maude-bridge** (maude-optimize suite 14/14, total 172/172).
|
- **2026-06-07 Phase 7 — maude-bridge** (maude-optimize suite 14/14, total 172/172).
|
||||||
`lib/artdag/maude-bridge.sx`: lossless adapter between an artdag effect DAG and a
|
`lib/artdag/maude-bridge.sx`: lossless adapter between an artdag effect DAG and a
|
||||||
maude term. `artdag/dag->term dag id` walks from a sink, emitting `(mau/app op
|
maude term. `artdag/dag->term dag id` walks from a sink, emitting `(mau/app op
|
||||||
|
|||||||
Reference in New Issue
Block a user