diff --git a/lib/minikanren/tests/graph.sx b/lib/minikanren/tests/graph.sx new file mode 100644 index 00000000..a1b3e989 --- /dev/null +++ b/lib/minikanren/tests/graph.sx @@ -0,0 +1,70 @@ +;; lib/minikanren/tests/graph.sx — directed-graph reachability via patho. + +(define + test-edges + (list (list :a :b) (list :b :c) (list :c :d) (list :a :c) (list :d :e))) + +(define edgeo (fn (from to) (membero (list from to) test-edges))) + +(define + patho + (fn + (x y path) + (conde + ((edgeo x y) (== path (list x y))) + ((fresh (z mid-path) (edgeo x z) (patho z y mid-path) (conso x mid-path path)))))) + +;; --- direct edges --- + +(mk-test "patho-direct" (run* q (patho :a :b q)) (list (list :a :b))) + +(mk-test "patho-no-direct-edge" (run* q (patho :e :a q)) (list)) + +;; --- indirect --- + +(mk-test + "patho-multi-hop" + (let + ((paths (run* q (patho :a :d q)))) + (and + (= (len paths) 2) + (and + (some (fn (p) (= p (list :a :b :c :d))) paths) + (some (fn (p) (= p (list :a :c :d))) paths)))) + true) + +(mk-test + "patho-to-leaf" + (let + ((paths (run* q (patho :a :e q)))) + (and + (= (len paths) 2) + (and + (some (fn (p) (= p (list :a :b :c :d :e))) paths) + (some (fn (p) (= p (list :a :c :d :e))) paths)))) + true) + +;; --- enumeration with multiplicity --- +;; Each path contributes one tuple, so reachable nodes can repeat. Here +;; targets are: b (1 path), c (2 paths), d (2 paths), e (2 paths) = 7. + +(mk-test + "patho-enumerate-from-a-with-multiplicity" + (let + ((targets (run* q (fresh (path) (patho :a q path))))) + (and + (= (len targets) 7) + (and + (some (fn (t) (= t :b)) targets) + (and + (some (fn (t) (= t :c)) targets) + (and + (some (fn (t) (= t :d)) targets) + (some (fn (t) (= t :e)) targets)))))) + true) + +;; --- unreachable target --- + +(mk-test "patho-unreachable" (run* q (patho :a :z q)) (list)) + +(mk-tests-run!) diff --git a/plans/minikanren-on-sx.md b/plans/minikanren-on-sx.md index cd7c3b79..dbce01e9 100644 --- a/plans/minikanren-on-sx.md +++ b/plans/minikanren-on-sx.md @@ -173,6 +173,11 @@ _(none yet)_ _Newest first._ +- **2026-05-08** — **Graph reachability via patho**: classic miniKanren + graph search. `edgeo` looks up edges in a fact list via `membero`; `patho` + recursively builds paths via direct-edge OR (one edge + recurse + cons). + Enumerates all paths between two nodes, including alternates through + shortcuts. 6 new tests, 316/316 cumulative. - **2026-05-08** — **everyo / someo (predicate-style relations)**: `(everyo rel l)` — every element of l satisfies rel; `(someo rel l)` — some element does. Both compose with intarith for numeric tests: