From b89b0def931ae5558368c24079fbcded9facfb23 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 8 May 2026 11:00:12 +0000 Subject: [PATCH] =?UTF-8?q?mk:=202x2=20Latin=20square=20=E2=80=94=20small?= =?UTF-8?q?=20classic=20FD=20constraint=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defines latin-2x2 over 4 cells and 4 all-distincto constraints. Enumerates exactly 2 squares ((1 2)(2 1)) and ((2 1)(1 2)); a corner clue narrows to one. 3 new tests, 269/269 cumulative. 3x3 (12 squares, the natural showcase) is too slow under naive enumerate- then-filter — that is the motivating test for Phase 6 arc-consistency. --- lib/minikanren/tests/latin.sx | 61 +++++++++++++++++++++++++++++++++++ plans/minikanren-on-sx.md | 6 ++++ 2 files changed, 67 insertions(+) create mode 100644 lib/minikanren/tests/latin.sx diff --git a/lib/minikanren/tests/latin.sx b/lib/minikanren/tests/latin.sx new file mode 100644 index 00000000..8a0afabd --- /dev/null +++ b/lib/minikanren/tests/latin.sx @@ -0,0 +1,61 @@ +;; lib/minikanren/tests/latin.sx — 2x2 Latin square via ino + all-distincto. +;; +;; A 2x2 Latin square has 2 distinct fillings: +;; ((1 2) (2 1)) and ((2 1) (1 2)). +;; The 3x3 version has 12 fillings but takes minutes under naive search; +;; full CLP(FD) (Phase 6 proper) would handle it in milliseconds. + +(define + latin-2x2 + (fn + (cells) + (let + ((c11 (nth cells 0)) + (c12 (nth cells 1)) + (c21 (nth cells 2)) + (c22 (nth cells 3)) + (dom (list 1 2))) + (mk-conj + (ino c11 dom) + (ino c12 dom) + (ino c21 dom) + (ino c22 dom) + (all-distincto (list c11 c12)) + (all-distincto (list c21 c22)) + (all-distincto (list c11 c21)) + (all-distincto (list c12 c22)))))) ;; col 2 + +(mk-test + "latin-2x2-count" + (let + ((squares (run* q (fresh (a b c d) (== q (list a b c d)) (latin-2x2 (list a b c d)))))) + (len squares)) + 2) + +(mk-test + "latin-2x2-as-set" + (let + ((squares (run* q (fresh (a b c d) (== q (list a b c d)) (latin-2x2 (list a b c d)))))) + (and + (= (len squares) 2) + (and + (some + (fn (s) (= s (list 1 2 2 1))) + squares) + (some + (fn (s) (= s (list 2 1 1 2))) + squares)))) + true) + +(mk-test + "latin-2x2-with-clue" + (run* + q + (fresh + (a b c d) + (== a 1) + (== q (list a b c d)) + (latin-2x2 (list a b c d)))) + (list (list 1 2 2 1))) + +(mk-tests-run!) diff --git a/plans/minikanren-on-sx.md b/plans/minikanren-on-sx.md index 117f88b7..82f2c568 100644 --- a/plans/minikanren-on-sx.md +++ b/plans/minikanren-on-sx.md @@ -173,6 +173,12 @@ _(none yet)_ _Newest first._ +- **2026-05-08** — **2x2 Latin square**: small classic constraint demo using + `ino` + 4 `all-distincto` constraints. Enumerates exactly 2 squares + (`((1 2)(2 1))` and `((2 1)(1 2))`); a clue (top-left = 1) narrows to one. + 3 new tests, 269/269 cumulative. Note: 3x3 (12 squares) is the natural + showcase but too slow under naive enumerate-then-filter — needs Phase 6 + arc-consistency. - **2026-05-08** — **rembero / assoco / nth-o**: more standard list relations. rembero removes the first occurrence (uses `nafc (== a x)` to gate the skip clause, so it's well-defined on ground lists). assoco is alist