From 3d2a5b181489657b5ba0175cb1ced8107e27d676 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 8 May 2026 07:56:58 +0000 Subject: [PATCH] =?UTF-8?q?mk:=20phase=206A=20=E2=80=94=20minimal=20FD=20(?= =?UTF-8?q?ino=20+=20all-distincto)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ino is membero with the constraint-store-friendly argument order (`(ino x dom)` reads as "x in dom"). all-distincto checks pairwise distinctness via nafc + membero on the recursive tail. These two are enough to express the enumerate-then-filter style of finite-domain solving: (fresh (a b c) (ino a (list 1 2 3)) (ino b (list 1 2 3)) (ino c (list 1 2 3)) (all-distincto (list a b c))) enumerates all 6 distinct triples from {1, 2, 3}. Full CLP(FD) with arc-consistency, fd-plus, etc. remains pending under Phase 6 proper. 9 new tests, 237/237 cumulative. --- lib/minikanren/fd.sx | 25 +++++++++++++ lib/minikanren/tests/fd.sx | 75 ++++++++++++++++++++++++++++++++++++++ plans/minikanren-on-sx.md | 12 +++++- 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 lib/minikanren/fd.sx create mode 100644 lib/minikanren/tests/fd.sx diff --git a/lib/minikanren/fd.sx b/lib/minikanren/fd.sx new file mode 100644 index 00000000..fb0c06ab --- /dev/null +++ b/lib/minikanren/fd.sx @@ -0,0 +1,25 @@ +;; lib/minikanren/fd.sx — Phase 6 piece A: minimal finite-domain helpers. +;; +;; A full CLP(FD) engine (arc consistency, native integer domains, fd-plus +;; etc.) is Phase 6 proper. For now we expose two small relations layered +;; on the existing list machinery — they're sufficient for permutation +;; puzzles, the N-queens-style core of constraint solving: +;; +;; (ino x dom) — x is a member of dom (alias for membero with the +;; constraint-store-friendly argument order). +;; (all-distincto l) — all elements of l are pairwise distinct. +;; +;; all-distincto uses nafc + membero on the tail — it requires the head +;; element of each recursive step to be ground enough for membero to be +;; finitary, so order matters: prefer (in x dom) goals BEFORE +;; (all-distincto (list x ...)) so values get committed first. + +(define ino (fn (x dom) (membero x dom))) + +(define + all-distincto + (fn + (l) + (conde + ((nullo l)) + ((fresh (a d) (conso a d l) (nafc (membero a d)) (all-distincto d)))))) diff --git a/lib/minikanren/tests/fd.sx b/lib/minikanren/tests/fd.sx new file mode 100644 index 00000000..4954d777 --- /dev/null +++ b/lib/minikanren/tests/fd.sx @@ -0,0 +1,75 @@ +;; lib/minikanren/tests/fd.sx — Phase 6 piece A: ino + all-distincto. + +;; --- ino --- + +(mk-test + "ino-element-in-domain" + (run* q (ino q (list 1 2 3))) + (list 1 2 3)) + +(mk-test "ino-empty-domain" (run* q (ino q (list))) (list)) + +(mk-test + "ino-singleton-domain" + (run* q (ino q (list 42))) + (list 42)) + +;; --- all-distincto --- + +(mk-test + "all-distincto-empty" + (run* q (all-distincto (list))) + (list (make-symbol "_.0"))) + +(mk-test + "all-distincto-singleton" + (run* q (all-distincto (list 1))) + (list (make-symbol "_.0"))) + +(mk-test + "all-distincto-distinct-three" + (run* q (all-distincto (list 1 2 3))) + (list (make-symbol "_.0"))) + +(mk-test + "all-distincto-duplicate-fails" + (run* q (all-distincto (list 1 2 1))) + (list)) + +(mk-test + "all-distincto-adjacent-duplicate-fails" + (run* q (all-distincto (list 1 1 2))) + (list)) + +;; --- ino + all-distincto: classic enumerate-all-permutations --- + +(mk-test + "fd-puzzle-three-distinct-from-domain" + (let + ((perms (run* q (fresh (a b c) (== q (list a b c)) (ino a (list 1 2 3)) (ino b (list 1 2 3)) (ino c (list 1 2 3)) (all-distincto (list a b c)))))) + (and + (= (len perms) 6) + (and + (some (fn (p) (= p (list 1 2 3))) perms) + (and + (some + (fn (p) (= p (list 1 3 2))) + perms) + (and + (some + (fn (p) (= p (list 2 1 3))) + perms) + (and + (some + (fn (p) (= p (list 2 3 1))) + perms) + (and + (some + (fn (p) (= p (list 3 1 2))) + perms) + (some + (fn (p) (= p (list 3 2 1))) + perms)))))))) + true) + +(mk-tests-run!) diff --git a/plans/minikanren-on-sx.md b/plans/minikanren-on-sx.md index c5761dfd..09af1701 100644 --- a/plans/minikanren-on-sx.md +++ b/plans/minikanren-on-sx.md @@ -142,7 +142,10 @@ Key semantic mappings: ### Phase 6 — arithmetic constraints CLP(FD) - [ ] Finite domain variables: `fd-var` with domain `[lo..hi]` -- [ ] `in` `x` `domain` — constrain x to domain +- [x] `ino` `x` `domain` — alias for `(membero x domain)` with the + constraint-store-friendly argument order. Sufficient for the + enumerate-then-filter style of finite-domain solving. +- [x] `all-distincto` `l` — pairwise-distinct elements via `nafc + membero`. - [ ] `fd-eq` `x` `y` — x = y (constraint propagation) - [ ] `fd-neq` `x` `y` — x ≠ y - [ ] `fd-lt` `fd-lte` `fd-gt` `fd-gte` — ordering constraints @@ -166,6 +169,13 @@ _(none yet)_ _Newest first._ +- **2026-05-08** — **Phase 6 piece A — minimal FD (ino + all-distincto)**: + `lib/minikanren/fd.sx`. `ino` is `membero` with the FD-style argument + order; `all-distincto` is `nafc + membero` walking the list. Together + they cover the enumerate-then-filter style of finite-domain solving — + `(fresh (a b c) (ino a D) (ino b D) (ino c D) (all-distincto (list a b c)))` + enumerates all distinct triples from D. Full FD with arc-consistency, + fd-plus etc. is still pending. 9 new tests, 237/237 cumulative. - **2026-05-08** — **Classic puzzles + matche keyword fix**: matche now emits keywords bare in the pattern->expr conversion so they self-evaluate to their string name and unify with the same-keyword target value (instead of becoming