maude: witness-path search for puzzle solvers (8 tests, 221 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
lib/maude/searchpath.sx — mau/search-path returns the shortest sequence of states from start to goal (the solution moves), mau/search-length its step count. BFS over all one-step successors, threading the path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ PRELOADS=(
|
||||
lib/maude/conditional.sx
|
||||
lib/maude/fire.sx
|
||||
lib/maude/rewrite.sx
|
||||
lib/maude/searchpath.sx
|
||||
lib/maude/strategy.sx
|
||||
lib/maude/meta.sx
|
||||
lib/maude/pretty.sx
|
||||
@@ -25,6 +26,7 @@ SUITES=(
|
||||
"matching:lib/maude/tests/matching.sx:(mau-matching-tests-run!)"
|
||||
"conditional:lib/maude/tests/conditional.sx:(mau-conditional-tests-run!)"
|
||||
"rewrite:lib/maude/tests/rewrite.sx:(mau-rewrite-tests-run!)"
|
||||
"searchpath:lib/maude/tests/searchpath.sx:(mau-searchpath-tests-run!)"
|
||||
"strategy:lib/maude/tests/strategy.sx:(mau-strategy-tests-run!)"
|
||||
"meta:lib/maude/tests/meta.sx:(mau-meta-tests-run!)"
|
||||
"pretty:lib/maude/tests/pretty.sx:(mau-pretty-tests-run!)"
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"lang": "maude",
|
||||
"total_passed": 213,
|
||||
"total_passed": 221,
|
||||
"total_failed": 0,
|
||||
"total": 213,
|
||||
"total": 221,
|
||||
"suites": [
|
||||
{"name":"parse","passed":65,"failed":0,"total":65},
|
||||
{"name":"reduce","passed":26,"failed":0,"total":26},
|
||||
{"name":"matching","passed":28,"failed":0,"total":28},
|
||||
{"name":"conditional","passed":19,"failed":0,"total":19},
|
||||
{"name":"rewrite","passed":21,"failed":0,"total":21},
|
||||
{"name":"searchpath","passed":8,"failed":0,"total":8},
|
||||
{"name":"strategy","passed":19,"failed":0,"total":19},
|
||||
{"name":"meta","passed":18,"failed":0,"total":18},
|
||||
{"name":"pretty","passed":11,"failed":0,"total":11},
|
||||
{"name":"run","passed":6,"failed":0,"total":6}
|
||||
],
|
||||
"generated": "2026-06-07T15:33:57+00:00"
|
||||
"generated": "2026-06-07T15:36:28+00:00"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# maude scoreboard
|
||||
|
||||
**213 / 213 passing** (0 failure(s)).
|
||||
**221 / 221 passing** (0 failure(s)).
|
||||
|
||||
| Suite | Passed | Total | Status |
|
||||
|-------|--------|-------|--------|
|
||||
@@ -9,6 +9,7 @@
|
||||
| matching | 28 | 28 | ok |
|
||||
| conditional | 19 | 19 | ok |
|
||||
| rewrite | 21 | 21 | ok |
|
||||
| searchpath | 8 | 8 | ok |
|
||||
| strategy | 19 | 19 | ok |
|
||||
| meta | 18 | 18 | ok |
|
||||
| pretty | 11 | 11 | ok |
|
||||
|
||||
96
lib/maude/searchpath.sx
Normal file
96
lib/maude/searchpath.sx
Normal file
@@ -0,0 +1,96 @@
|
||||
;; lib/maude/searchpath.sx — reachability search returning the witness path.
|
||||
;;
|
||||
;; mau/search (rewrite.sx) answers yes/no. For puzzle solvers you want the
|
||||
;; actual sequence of states from start to goal. mau/search-path runs the same
|
||||
;; BFS but threads the path so far; it returns the list of canonical states
|
||||
;; start..goal (shortest by step count) or nil if unreachable within depth.
|
||||
|
||||
(define mau/reverse2 (fn (xs) (mau/rev-acc xs (list))))
|
||||
|
||||
(define
|
||||
mau/rev-acc
|
||||
(fn
|
||||
(xs acc)
|
||||
(if (empty? xs) acc (mau/rev-acc (rest xs) (cons (first xs) acc)))))
|
||||
|
||||
;; find a frontier path whose current state (its head) matches the goal canon
|
||||
(define
|
||||
mau/path-hit
|
||||
(fn
|
||||
(theory frontier goal)
|
||||
(cond
|
||||
((empty? frontier) nil)
|
||||
((= (mau/canon theory (first (first frontier))) goal)
|
||||
(first frontier))
|
||||
(else (mau/path-hit theory (rest frontier) goal)))))
|
||||
|
||||
(define
|
||||
mau/bfs-path
|
||||
(fn
|
||||
(theory eqs rules frontier seen goal depth)
|
||||
(let
|
||||
((hit (mau/path-hit theory frontier goal)))
|
||||
(cond
|
||||
((not (= hit nil)) hit)
|
||||
((<= depth 0) nil)
|
||||
((empty? frontier) nil)
|
||||
(else
|
||||
(let
|
||||
((newf (list)) (newseen seen))
|
||||
(for-each
|
||||
(fn
|
||||
(path)
|
||||
(for-each
|
||||
(fn
|
||||
(succ)
|
||||
(let
|
||||
((c (mau/canon theory succ)))
|
||||
(when
|
||||
(not (mau/member? c newseen))
|
||||
(do
|
||||
(set! newseen (cons c newseen))
|
||||
(append! newf (cons succ path))))))
|
||||
(mau/all-successors theory eqs rules (first path))))
|
||||
frontier)
|
||||
(mau/bfs-path
|
||||
theory
|
||||
eqs
|
||||
rules
|
||||
newf
|
||||
newseen
|
||||
goal
|
||||
(- depth 1))))))))
|
||||
|
||||
(define
|
||||
mau/search-path
|
||||
(fn
|
||||
(m start-src goal-src max-depth)
|
||||
(let
|
||||
((theory (mau/build-theory m))
|
||||
(eqs (mau/module-eqs m))
|
||||
(rules (mau/module-rules m)))
|
||||
(let
|
||||
((start (mau/cnormalize theory eqs (mau/parse-term-in m start-src) mau/reduce-fuel))
|
||||
(goal
|
||||
(mau/canon
|
||||
theory
|
||||
(mau/cnormalize
|
||||
theory
|
||||
eqs
|
||||
(mau/parse-term-in m goal-src)
|
||||
mau/reduce-fuel))))
|
||||
(let
|
||||
((res (mau/bfs-path theory eqs rules (list (list start)) (list (mau/canon theory start)) goal max-depth)))
|
||||
(if
|
||||
(= res nil)
|
||||
nil
|
||||
(map (fn (t) (mau/canon theory t)) (mau/reverse2 res))))))))
|
||||
|
||||
;; number of steps in the shortest solution (nil if unreachable)
|
||||
(define
|
||||
mau/search-length
|
||||
(fn
|
||||
(m start-src goal-src max-depth)
|
||||
(let
|
||||
((p (mau/search-path m start-src goal-src max-depth)))
|
||||
(if (= p nil) nil (- (len p) 1)))))
|
||||
66
lib/maude/tests/searchpath.sx
Normal file
66
lib/maude/tests/searchpath.sx
Normal file
@@ -0,0 +1,66 @@
|
||||
;; lib/maude/tests/searchpath.sx — search returning the witness path.
|
||||
|
||||
(define msp-pass 0)
|
||||
(define msp-fail 0)
|
||||
(define msp-failures (list))
|
||||
|
||||
(define
|
||||
msp-check!
|
||||
(fn
|
||||
(name got expected)
|
||||
(if
|
||||
(= got expected)
|
||||
(set! msp-pass (+ msp-pass 1))
|
||||
(do
|
||||
(set! msp-fail (+ msp-fail 1))
|
||||
(append!
|
||||
msp-failures
|
||||
(str name " expected: " expected " got: " got))))))
|
||||
|
||||
(define
|
||||
msp-ndet
|
||||
(mau/parse-module
|
||||
"mod NDET is\n sort S .\n ops a b c d goal : -> S .\n rl [r1] : a => b .\n rl [r2] : a => c .\n rl [r3] : b => d .\n rl [r4] : c => goal .\nendm"))
|
||||
|
||||
;; shortest path a -> c -> goal
|
||||
(msp-check!
|
||||
"path-to-goal"
|
||||
(mau/search-path msp-ndet "a" "goal" 5)
|
||||
(list "a" "c" "goal"))
|
||||
(msp-check!
|
||||
"path-length"
|
||||
(mau/search-length msp-ndet "a" "goal" 5)
|
||||
2)
|
||||
(msp-check!
|
||||
"path-self"
|
||||
(mau/search-path msp-ndet "a" "a" 3)
|
||||
(list "a"))
|
||||
(msp-check!
|
||||
"path-one-step"
|
||||
(mau/search-path msp-ndet "a" "b" 3)
|
||||
(list "a" "b"))
|
||||
(msp-check!
|
||||
"path-unreachable"
|
||||
(mau/search-path msp-ndet "d" "goal" 5)
|
||||
nil)
|
||||
(msp-check!
|
||||
"path-depth-limited"
|
||||
(mau/search-path msp-ndet "a" "goal" 1)
|
||||
nil)
|
||||
|
||||
;; a counter that ticks up: path shows each state
|
||||
(define
|
||||
msp-walk
|
||||
(mau/parse-module
|
||||
"mod WALK is\n sort Pos .\n op z : -> Pos .\n op s : Pos -> Pos .\n op p : Pos -> Pos .\n var X : Pos .\n rl [step] : p(X) => p(s(X)) .\nendm"))
|
||||
|
||||
(msp-check!
|
||||
"walk-path"
|
||||
(mau/search-path msp-walk "p(z)" "p(s(s(z)))" 5)
|
||||
(list "p(z)" "p(s(z))" "p(s(s(z)))"))
|
||||
(msp-check!
|
||||
"walk-length"
|
||||
(mau/search-length msp-walk "p(z)" "p(s(s(s(z))))" 6)
|
||||
3)
|
||||
|
||||
(define mau-searchpath-tests-run! (fn () {:failures msp-failures :total (+ msp-pass msp-fail) :passed msp-pass :failed msp-fail}))
|
||||
@@ -109,6 +109,9 @@ The novel substrate stress: equational matching. Pattern `X + Y` against `1 + 2
|
||||
a module plus trailing `reduce`/`red`/`rewrite`/`rew TERM .` commands
|
||||
(`... in MOD : TERM` qualifier accepted) and execute them, rendering results
|
||||
in surface syntax. Runs an idiomatic `.maude` file end-to-end. 6 tests.
|
||||
- [x] Witness-path search (`lib/maude/searchpath.sx`) — `mau/search-path` /
|
||||
`mau/search-length` return the shortest sequence of states start..goal (the
|
||||
solution moves), not just yes/no. 8 tests.
|
||||
|
||||
### Phase 8 — Propose `lib/guest/rewriting/`
|
||||
- [ ] Extract equational matching engine (the most reusable piece).
|
||||
|
||||
Reference in New Issue
Block a user