From 49eb22243a1c4090745737e3a5c6aca7e4563aab Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 7 May 2026 05:07:25 +0000 Subject: [PATCH] =?UTF-8?q?apl:=20mandelbrot=20real-axis=20batched=20z=3Dz?= =?UTF-8?q?=C2=B2+c=20(+9=20tests,=20296/296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/apl/runtime.sx | 29 +++++++++++++++++ lib/apl/scoreboard.json | 6 ++-- lib/apl/scoreboard.md | 4 +-- lib/apl/tests/programs.sx | 45 +++++++++++++++++++++++++++ lib/apl/tests/programs/mandelbrot.apl | 29 +++++++++++++++++ plans/apl-on-sx.md | 3 +- 6 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 lib/apl/tests/programs/mandelbrot.apl diff --git a/lib/apl/runtime.sx b/lib/apl/runtime.sx index f2a6eb54..166b9123 100644 --- a/lib/apl/runtime.sx +++ b/lib/apl/runtime.sx @@ -836,6 +836,35 @@ (apl-eq sum-board (apl-scalar 3)) (apl-and board (apl-eq sum-board (apl-scalar 4)))))))) +(define + apl-mandelbrot-step + (fn + (cs z counts alive iters-left) + (if + (= iters-left 0) + counts + (let + ((still-alive (apl-and alive (apl-le (apl-mul z z) (apl-scalar 4))))) + (let + ((new-z (apl-mul still-alive (apl-add (apl-mul z z) cs)))) + (let + ((new-counts (apl-add counts still-alive))) + (apl-mandelbrot-step + cs + new-z + new-counts + still-alive + (- iters-left 1)))))))) + +(define + apl-mandelbrot-1d + (fn + (cs max-iter) + (let + ((zero (apl-mul cs (apl-scalar 0))) + (ones (apl-add (apl-mul cs (apl-scalar 0)) (apl-scalar 1)))) + (apl-mandelbrot-step cs zero zero ones max-iter)))) + (define apl-reduce (fn diff --git a/lib/apl/scoreboard.json b/lib/apl/scoreboard.json index e38be0b9..d5fb6c82 100644 --- a/lib/apl/scoreboard.json +++ b/lib/apl/scoreboard.json @@ -5,9 +5,9 @@ "dfn": {"pass": 24, "fail": 0}, "tradfn": {"pass": 20, "fail": 0}, "valence": {"pass": 14, "fail": 0}, - "programs": {"pass": 18, "fail": 0} + "programs": {"pass": 27, "fail": 0} }, - "total_pass": 287, + "total_pass": 296, "total_fail": 0, - "total": 287 + "total": 296 } diff --git a/lib/apl/scoreboard.md b/lib/apl/scoreboard.md index 8de76152..7ae9cd83 100644 --- a/lib/apl/scoreboard.md +++ b/lib/apl/scoreboard.md @@ -9,8 +9,8 @@ _Generated by `lib/apl/conformance.sh`_ | dfn | 24 | 0 | 24 | | tradfn | 20 | 0 | 20 | | valence | 14 | 0 | 14 | -| programs | 18 | 0 | 18 | -| **Total** | **287** | **0** | **287** | +| programs | 27 | 0 | 27 | +| **Total** | **296** | **0** | **296** | ## Notes diff --git a/lib/apl/tests/programs.sx b/lib/apl/tests/programs.sx index 9c59c544..877c9ccd 100644 --- a/lib/apl/tests/programs.sx +++ b/lib/apl/tests/programs.sx @@ -192,3 +192,48 @@ 0 0 0)) + +(apl-test + "mandelbrot c=0 stays bounded" + (mkrv (apl-mandelbrot-1d (make-array (list 1) (list 0)) 100)) + (list 100)) + +(apl-test + "mandelbrot c=-1 cycle bounded" + (mkrv (apl-mandelbrot-1d (make-array (list 1) (list -1)) 100)) + (list 100)) + +(apl-test + "mandelbrot c=-2 boundary stays bounded" + (mkrv (apl-mandelbrot-1d (make-array (list 1) (list -2)) 100)) + (list 100)) + +(apl-test + "mandelbrot c=0.25 boundary stays bounded" + (mkrv (apl-mandelbrot-1d (make-array (list 1) (list 0.25)) 100)) + (list 100)) + +(apl-test + "mandelbrot c=1 escapes at iter 3" + (mkrv (apl-mandelbrot-1d (make-array (list 1) (list 1)) 100)) + (list 3)) + +(apl-test + "mandelbrot c=0.5 escapes at iter 5" + (mkrv (apl-mandelbrot-1d (make-array (list 1) (list 0.5)) 100)) + (list 5)) + +(apl-test + "mandelbrot batched grid (rank-polymorphic)" + (mkrv (apl-mandelbrot-1d (make-array (list 5) (list -2 -1 0 1 2)) 10)) + (list 10 10 10 3 2)) + +(apl-test + "mandelbrot batched preserves shape" + (mksh (apl-mandelbrot-1d (make-array (list 5) (list -2 -1 0 1 2)) 10)) + (list 5)) + +(apl-test + "mandelbrot c=-1.5 stays bounded" + (mkrv (apl-mandelbrot-1d (make-array (list 1) (list -1.5)) 100)) + (list 100)) diff --git a/lib/apl/tests/programs/mandelbrot.apl b/lib/apl/tests/programs/mandelbrot.apl new file mode 100644 index 00000000..03bfad4e --- /dev/null +++ b/lib/apl/tests/programs/mandelbrot.apl @@ -0,0 +1,29 @@ +⍝ Mandelbrot — real-axis subset +⍝ +⍝ For complex c, the Mandelbrot set is { c : |z_n| stays bounded } where +⍝ z_0 = 0, z_{n+1} = z_n² + c. +⍝ Restricting c (and z) to ℝ gives the segment c ∈ [-2, 1/4] +⍝ where the iteration stays bounded. +⍝ +⍝ Rank-polymorphic batched-iteration form: +⍝ mandelbrot ← {⍵ ⍵⍵ ⍺⍺ +,(⍺⍺ × ⍺⍺) } +⍝ +⍝ Pseudocode (as we don't have ⎕ system fns yet): +⍝ z ← 0×c ⍝ start at zero +⍝ alive ← 1+0×c ⍝ all "still in" +⍝ for k iterations: +⍝ alive ← alive ∧ 4 ≥ z×z ⍝ still bounded? +⍝ z ← alive × c + z×z ⍝ freeze escaped via mask +⍝ count ← count + alive ⍝ tally surviving iters +⍝ +⍝ Examples (count after 100 iterations): +⍝ c=0 : 100 (z stays at 0) +⍝ c=-1 : 100 (cycles 0,-1,0,-1,...) +⍝ c=-2 : 100 (settles at 2 — boundary) +⍝ c=0.25 : 100 (boundary — converges to 0.5) +⍝ c=0.5 : 5 (escapes by iteration 6) +⍝ c=1 : 3 (escapes quickly) +⍝ +⍝ Real-axis Mandelbrot set: bounded for c ∈ [-2, 0.25]. + +mandelbrot ← {z←alive←count←0×⍵ ⋄ {alive←alive∧4≥z×z ⋄ z←alive×⍵+z×z ⋄ count+←alive}⍣⍺⊢⍵} diff --git a/plans/apl-on-sx.md b/plans/apl-on-sx.md index e19ca99e..4c788e18 100644 --- a/plans/apl-on-sx.md +++ b/plans/apl-on-sx.md @@ -96,7 +96,7 @@ Core mapping: ### Phase 6 — classic programs + drive corpus - [ ] Classic programs in `lib/apl/tests/programs/`: - [x] `life.apl` — Conway's Game of Life as a one-liner using `⊂` `⊖` `⌽` `+/` - - [ ] `mandelbrot.apl` — complex iteration with rank-polymorphic `+ × ⌊` (or real-axis subset) + - [x] `mandelbrot.apl` — complex iteration with rank-polymorphic `+ × ⌊` (or real-axis subset) - [x] `primes.apl` — `(2=+⌿0=A∘.|A)/A←⍳N` sieve - [ ] `n-queens.apl` — backtracking via reduce - [ ] `quicksort.apl` — the classic Roger Hui one-liner @@ -118,6 +118,7 @@ data; format for string templating. _Newest first._ +- 2026-05-07: Phase 6 mandelbrot real-axis — apl-mandelbrot-1d batched z=z²+c with permanent alive-mask; c∈{-2,-1,0,0.25} bounded, c=1→3, c=0.5→5, c=2→2; +9 tests; 296/296 - 2026-05-07: Phase 6 life — Conway via 9-shift toroidal sum + alive-rule (cnt=3 OR alive∧cnt=4); apl-life-step + life.apl source; blinker oscillates, block stable, glider advances; +7 tests; 287/287 - 2026-05-07: Phase 6 primes — sieve via outer-product residue + reduce-first + compress; apl-compress added; lib/apl/tests/programs/primes.apl source; +11 tests; 280/280 - 2026-05-07: Phase 5 conformance.sh + scoreboard.{json,md} — per-suite runner; current snapshot 269/269; **Phase 5 complete**