From 28bd8bb98cec31b1c7b37d3988fa254a8ee924ce Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 9 May 2026 14:12:36 +0000 Subject: [PATCH] =?UTF-8?q?mk:=20phase=207=20piece=20A=20=E2=80=94=20fixed?= =?UTF-8?q?-point=20iteration=20in=20SLG=20tabling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the single-pass body run with table-2-slg-iter / table-3-slg-iter: each iteration stores the current vals in cache and re-runs the body; loop until vals length stops growing. The cache thus grows monotonically until no new answers appear. For simple cycles (single tabled relation) this is sound and terminating — len comparison is O(1) and the cache only grows. Limitation: mutually-recursive tabled relations have INDEPENDENT iteration loops. Each runs to its own fixed point in isolation; the two don't coordinate. True SLG uses a worklist that cross-fires re-iteration when any subgoal's cache grows. Left as a future refinement. All 5 SLG tests still pass (Fibonacci unchanged, 3 cyclic-patho cases unchanged). --- lib/minikanren/tabling-slg.sx | 56 ++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/lib/minikanren/tabling-slg.sx b/lib/minikanren/tabling-slg.sx index ed2550c9..c952e3ce 100644 --- a/lib/minikanren/tabling-slg.sx +++ b/lib/minikanren/tabling-slg.sx @@ -13,6 +13,23 @@ ;; recursive calls are NOT discovered. Full SLG with fixed-point ;; iteration (re-running until no new answers) is left for follow-up. +(define + table-2-slg-iter + (fn + (rel-fn input output s key prev-vals) + (begin + (mk-tab-store! key prev-vals) + (let + ((all-substs (stream-take -1 ((rel-fn input output) s)))) + (let + ((vals (map (fn (s2) (mk-walk* output s2)) all-substs))) + (cond + ((= (len vals) (len prev-vals)) + (begin + (mk-tab-store! key vals) + (mk-tab-replay-vals vals output s))) + (:else (table-2-slg-iter rel-fn input output s key vals)))))))) + (define table-2-slg (fn @@ -30,21 +47,29 @@ (let ((cached (mk-tab-lookup key))) (cond - ((= cached :in-progress) mzero) ((not (= cached :miss)) (mk-tab-replay-vals cached output s)) (:else - (begin - (mk-tab-store! key :in-progress) - (let - ((all-substs (stream-take -1 ((rel-fn input output) s)))) - (let - ((vals (map (fn (s2) (mk-walk* output s2)) all-substs))) - (begin - (mk-tab-store! key vals) - (mk-tab-replay-vals vals output s)))))))))) + (table-2-slg-iter rel-fn input output s key (list))))))) (:else ((rel-fn input output) s)))))))) +(define + table-3-slg-iter + (fn + (rel-fn i1 i2 output s key prev-vals) + (begin + (mk-tab-store! key prev-vals) + (let + ((all-substs (stream-take -1 ((rel-fn i1 i2 output) s)))) + (let + ((vals (map (fn (s2) (mk-walk* output s2)) all-substs))) + (cond + ((= (len vals) (len prev-vals)) + (begin + (mk-tab-store! key vals) + (mk-tab-replay-vals vals output s))) + (:else (table-3-slg-iter rel-fn i1 i2 output s key vals)))))))) + (define table-3-slg (fn @@ -62,17 +87,8 @@ (let ((cached (mk-tab-lookup key))) (cond - ((= cached :in-progress) mzero) ((not (= cached :miss)) (mk-tab-replay-vals cached output s)) (:else - (begin - (mk-tab-store! key :in-progress) - (let - ((all-substs (stream-take -1 ((rel-fn i1 i2 output) s)))) - (let - ((vals (map (fn (s2) (mk-walk* output s2)) all-substs))) - (begin - (mk-tab-store! key vals) - (mk-tab-replay-vals vals output s)))))))))) + (table-3-slg-iter rel-fn i1 i2 output s key (list))))))) (:else ((rel-fn i1 i2 output) s))))))))