relations: route enumeration — all-paths (all simple directed paths a->b) + 9 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Cycle-safe DFS in explain.sx, complements shortest-path relations-path. 135/135. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,4 +29,5 @@ SUITES=(
|
|||||||
"fed:lib/relations/tests/fed.sx:(relations-fed-tests-run!)"
|
"fed:lib/relations/tests/fed.sx:(relations-fed-tests-run!)"
|
||||||
"shape:lib/relations/tests/shape.sx:(relations-shape-tests-run!)"
|
"shape:lib/relations/tests/shape.sx:(relations-shape-tests-run!)"
|
||||||
"tree:lib/relations/tests/tree.sx:(relations-tree-tests-run!)"
|
"tree:lib/relations/tests/tree.sx:(relations-tree-tests-run!)"
|
||||||
|
"routes:lib/relations/tests/routes.sx:(relations-routes-tests-run!)"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -69,6 +69,28 @@
|
|||||||
|
|
||||||
;; --- current-db convenience layer ---
|
;; --- current-db convenience layer ---
|
||||||
|
|
||||||
|
(define
|
||||||
|
relations-ap-dfs
|
||||||
|
(fn
|
||||||
|
(db b kind path node)
|
||||||
|
(if
|
||||||
|
(= node b)
|
||||||
|
(list path)
|
||||||
|
(relations-concat-map
|
||||||
|
(fn
|
||||||
|
(nbr)
|
||||||
|
(if
|
||||||
|
(relations-eng-member? nbr path)
|
||||||
|
(list)
|
||||||
|
(relations-ap-dfs db b kind (append path (list nbr)) nbr)))
|
||||||
|
(relations-children-of db node kind)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
relations-all-paths
|
||||||
|
(fn
|
||||||
|
(db a b kind)
|
||||||
|
(if (= a b) (list (list a)) (relations-ap-dfs db b kind (list a) a))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
relations/path
|
relations/path
|
||||||
(fn (a b kind) (relations-path (relations-ensure-db!) a b kind)))
|
(fn (a b kind) (relations-path (relations-ensure-db!) a b kind)))
|
||||||
@@ -84,3 +106,7 @@
|
|||||||
(define
|
(define
|
||||||
relations/reachable-any?
|
relations/reachable-any?
|
||||||
(fn (a b) (relations-reachable-any? (relations-ensure-db!) a b)))
|
(fn (a b) (relations-reachable-any? (relations-ensure-db!) a b)))
|
||||||
|
|
||||||
|
(define
|
||||||
|
relations/all-paths
|
||||||
|
(fn (a b kind) (relations-all-paths (relations-ensure-db!) a b kind)))
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"lang": "relations",
|
"lang": "relations",
|
||||||
"total_passed": 126,
|
"total_passed": 135,
|
||||||
"total_failed": 0,
|
"total_failed": 0,
|
||||||
"total": 126,
|
"total": 135,
|
||||||
"suites": [
|
"suites": [
|
||||||
{"name":"direct","passed":22,"failed":0,"total":22},
|
{"name":"direct","passed":22,"failed":0,"total":22},
|
||||||
{"name":"reach","passed":24,"failed":0,"total":24},
|
{"name":"reach","passed":24,"failed":0,"total":24},
|
||||||
{"name":"path","passed":24,"failed":0,"total":24},
|
{"name":"path","passed":24,"failed":0,"total":24},
|
||||||
{"name":"fed","passed":22,"failed":0,"total":22},
|
{"name":"fed","passed":22,"failed":0,"total":22},
|
||||||
{"name":"shape","passed":18,"failed":0,"total":18},
|
{"name":"shape","passed":18,"failed":0,"total":18},
|
||||||
{"name":"tree","passed":16,"failed":0,"total":16}
|
{"name":"tree","passed":16,"failed":0,"total":16},
|
||||||
|
{"name":"routes","passed":9,"failed":0,"total":9}
|
||||||
],
|
],
|
||||||
"generated": "2026-06-07T13:07:14+00:00"
|
"generated": "2026-06-07T13:18:20+00:00"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# relations scoreboard
|
# relations scoreboard
|
||||||
|
|
||||||
**126 / 126 passing** (0 failure(s)).
|
**135 / 135 passing** (0 failure(s)).
|
||||||
|
|
||||||
| Suite | Passed | Total | Status |
|
| Suite | Passed | Total | Status |
|
||||||
|-------|--------|-------|--------|
|
|-------|--------|-------|--------|
|
||||||
@@ -10,3 +10,4 @@
|
|||||||
| fed | 22 | 22 | ok |
|
| fed | 22 | 22 | ok |
|
||||||
| shape | 18 | 18 | ok |
|
| shape | 18 | 18 | ok |
|
||||||
| tree | 16 | 16 | ok |
|
| tree | 16 | 16 | ok |
|
||||||
|
| routes | 9 | 9 | ok |
|
||||||
|
|||||||
130
lib/relations/tests/routes.sx
Normal file
130
lib/relations/tests/routes.sx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
;; lib/relations/tests/routes.sx — extension: all simple paths (route enumeration).
|
||||||
|
|
||||||
|
(define relations-ro-pass 0)
|
||||||
|
(define relations-ro-fail 0)
|
||||||
|
(define relations-ro-failures (list))
|
||||||
|
|
||||||
|
(define
|
||||||
|
relations-ro-check!
|
||||||
|
(fn
|
||||||
|
(name got expected)
|
||||||
|
(if
|
||||||
|
(= got expected)
|
||||||
|
(set! relations-ro-pass (+ relations-ro-pass 1))
|
||||||
|
(do
|
||||||
|
(set! relations-ro-fail (+ relations-ro-fail 1))
|
||||||
|
(append!
|
||||||
|
relations-ro-failures
|
||||||
|
(str name "\n expected: " expected "\n got: " got))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
relations-ro-subset?
|
||||||
|
(fn
|
||||||
|
(xs ys)
|
||||||
|
(cond
|
||||||
|
((= (len xs) 0) true)
|
||||||
|
((relations-member? (first xs) ys)
|
||||||
|
(relations-ro-subset? (rest xs) ys))
|
||||||
|
(else false))))
|
||||||
|
|
||||||
|
;; Order-insensitive set equality; elements compared structurally (works for
|
||||||
|
;; lists-of-paths since `=` is structural).
|
||||||
|
(define
|
||||||
|
relations-ro-set=?
|
||||||
|
(fn
|
||||||
|
(xs ys)
|
||||||
|
(and
|
||||||
|
(= (len xs) (len ys))
|
||||||
|
(relations-ro-subset? xs ys)
|
||||||
|
(relations-ro-subset? ys xs))))
|
||||||
|
|
||||||
|
;; Diamond + branch + a cycle with an exit.
|
||||||
|
;; parent: a->b, a->c, b->d, c->d, b->e
|
||||||
|
;; member: a->z (a different kind, to test isolation)
|
||||||
|
;; parent cycle: g->h, h->g, h->out
|
||||||
|
(define
|
||||||
|
relations-ro-fixture
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(relations-build-db
|
||||||
|
(list
|
||||||
|
(relations-rel (quote a) (quote b) (quote parent))
|
||||||
|
(relations-rel (quote a) (quote c) (quote parent))
|
||||||
|
(relations-rel (quote b) (quote d) (quote parent))
|
||||||
|
(relations-rel (quote c) (quote d) (quote parent))
|
||||||
|
(relations-rel (quote b) (quote e) (quote parent))
|
||||||
|
(relations-rel (quote a) (quote z) (quote member))
|
||||||
|
(relations-rel (quote g) (quote h) (quote parent))
|
||||||
|
(relations-rel (quote h) (quote g) (quote parent))
|
||||||
|
(relations-rel (quote h) (quote out) (quote parent))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
relations-ro-run-all!
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(let
|
||||||
|
((db (relations-ro-fixture)))
|
||||||
|
(do
|
||||||
|
(relations-ro-check!
|
||||||
|
"two routes a->d"
|
||||||
|
(relations-ro-set=?
|
||||||
|
(relations-all-paths db (quote a) (quote d) (quote parent))
|
||||||
|
(list
|
||||||
|
(list (quote a) (quote b) (quote d))
|
||||||
|
(list (quote a) (quote c) (quote d))))
|
||||||
|
true)
|
||||||
|
(relations-ro-check!
|
||||||
|
"single route a->e"
|
||||||
|
(relations-all-paths db (quote a) (quote e) (quote parent))
|
||||||
|
(list (list (quote a) (quote b) (quote e))))
|
||||||
|
(relations-ro-check!
|
||||||
|
"no route -> empty"
|
||||||
|
(relations-all-paths db (quote a) (quote zzz) (quote parent))
|
||||||
|
(list))
|
||||||
|
(relations-ro-check!
|
||||||
|
"self route is the singleton path"
|
||||||
|
(relations-all-paths db (quote a) (quote a) (quote parent))
|
||||||
|
(list (list (quote a))))
|
||||||
|
(relations-ro-check!
|
||||||
|
"route through a cycle terminates"
|
||||||
|
(relations-all-paths db (quote g) (quote out) (quote parent))
|
||||||
|
(list (list (quote g) (quote h) (quote out))))
|
||||||
|
(relations-ro-check!
|
||||||
|
"route count a->d is 2"
|
||||||
|
(len (relations-all-paths db (quote a) (quote d) (quote parent)))
|
||||||
|
2)
|
||||||
|
(relations-ro-check!
|
||||||
|
"kind isolation: no parent route to member target"
|
||||||
|
(relations-all-paths db (quote a) (quote z) (quote parent))
|
||||||
|
(list))
|
||||||
|
(relations-ro-check!
|
||||||
|
"member route a->z"
|
||||||
|
(relations-all-paths db (quote a) (quote z) (quote member))
|
||||||
|
(list (list (quote a) (quote z))))
|
||||||
|
(do
|
||||||
|
(relations/load!
|
||||||
|
(list
|
||||||
|
(relations-rel (quote p) (quote q) (quote parent))
|
||||||
|
(relations-rel (quote p) (quote r) (quote parent))
|
||||||
|
(relations-rel (quote q) (quote s) (quote parent))
|
||||||
|
(relations-rel (quote r) (quote s) (quote parent))))
|
||||||
|
(relations-ro-check!
|
||||||
|
"api all-paths two routes p->s"
|
||||||
|
(relations-ro-set=?
|
||||||
|
(relations/all-paths (quote p) (quote s) (quote parent))
|
||||||
|
(list
|
||||||
|
(list (quote p) (quote q) (quote s))
|
||||||
|
(list (quote p) (quote r) (quote s))))
|
||||||
|
true)
|
||||||
|
(relations/load! (list)))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
relations-routes-tests-run!
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(do
|
||||||
|
(set! relations-ro-pass 0)
|
||||||
|
(set! relations-ro-fail 0)
|
||||||
|
(set! relations-ro-failures (list))
|
||||||
|
(relations-ro-run-all!)
|
||||||
|
{:failures relations-ro-failures :total (+ relations-ro-pass relations-ro-fail) :passed relations-ro-pass :failed relations-ro-fail})))
|
||||||
@@ -18,7 +18,7 @@ links. Reuses `lib/datalog/` — does not reimplement the engine.
|
|||||||
|
|
||||||
## Status (rolling)
|
## Status (rolling)
|
||||||
|
|
||||||
`bash lib/relations/conformance.sh` → **126/126** (Phases 1–4 complete + extensions)
|
`bash lib/relations/conformance.sh` → **135/135** (Phases 1–4 complete + extensions)
|
||||||
|
|
||||||
## Ground rules
|
## Ground rules
|
||||||
|
|
||||||
@@ -108,9 +108,18 @@ lib/relations/federation.sx
|
|||||||
(lowest common ancestors — a set; tree → singleton, DAG → may be several),
|
(lowest common ancestors — a set; tree → singleton, DAG → may be several),
|
||||||
`topo-order` (Kahn-style; nil for cyclic kinds). New `lib/relations/tree.sx`,
|
`topo-order` (Kahn-style; nil for cyclic kinds). New `lib/relations/tree.sx`,
|
||||||
computed in SX over `reach`/`ancestors`/`rnode`. `lib/relations/tests/tree.sx`.
|
computed in SX over `reach`/`ancestors`/`rnode`. `lib/relations/tests/tree.sx`.
|
||||||
|
- [x] **route enumeration** — `all-paths` (all simple directed paths a→b, not just
|
||||||
|
the shortest; cycle-safe DFS) in explain.sx. `lib/relations/tests/routes.sx`.
|
||||||
|
|
||||||
## Progress log
|
## Progress log
|
||||||
|
|
||||||
|
- **Extension: route enumeration** (135/135). `relations-all-paths(db,a,b,kind)`
|
||||||
|
in explain.sx — every simple (no repeated node) directed path a→b, not just the
|
||||||
|
shortest one `relations-path` returns; DFS that skips nodes already on the
|
||||||
|
current path so cyclic data terminates; a=b → `((a))`, no route → `()`. Reuses
|
||||||
|
engine's `relations-concat-map`/`-eng-member?`/`children-of`. + `relations/all-paths`
|
||||||
|
wrapper, `lib/relations/tests/routes.sx` (9 tests: two-route diamond, single
|
||||||
|
route, no route, self, route-through-cycle, route count, kind isolation).
|
||||||
- **Extension: tree/DAG queries** (126/126). New `lib/relations/tree.sx`:
|
- **Extension: tree/DAG queries** (126/126). New `lib/relations/tree.sx`:
|
||||||
`relations-common-ancestors` (intersection of the two ancestor sets),
|
`relations-common-ancestors` (intersection of the two ancestor sets),
|
||||||
`relations-lca` (common ancestors with no other common ancestor reachable below
|
`relations-lca` (common ancestors with no other common ancestor reachable below
|
||||||
|
|||||||
Reference in New Issue
Block a user