diff --git a/lib/apl/runtime.sx b/lib/apl/runtime.sx index e26dcf1f..8d26c5a9 100644 --- a/lib/apl/runtime.sx +++ b/lib/apl/runtime.sx @@ -996,3 +996,23 @@ (get a :ravel) (get b :ravel))) (error "length error: shape mismatch")))))) + +(define + apl-outer + (fn + (f a b) + (let + ((a-shape (get a :shape)) + (b-shape (get b :shape)) + (a-ravel (get a :ravel)) + (b-ravel (get b :ravel))) + (make-array + (append a-shape b-shape) + (flatten + (map + (fn + (x) + (map + (fn (y) (disclose (f (apl-scalar x) (apl-scalar y)))) + b-ravel)) + a-ravel)))))) diff --git a/lib/apl/tests/operators.sx b/lib/apl/tests/operators.sx index 9c17911b..fbe07e1a 100644 --- a/lib/apl/tests/operators.sx +++ b/lib/apl/tests/operators.sx @@ -244,4 +244,95 @@ apl-mul (make-array (list 2 2) (list 1 2 3 4)) (make-array (list 2 2) (list 5 6 7 8)))) - (list 5 12 21 32)) \ No newline at end of file + (list 5 12 21 32)) + +(apl-test + "outer product mult table values" + (rv + (apl-outer + apl-mul + (make-array (list 3) (list 1 2 3)) + (make-array (list 3) (list 1 2 3)))) + (list 1 2 3 2 4 6 3 6 9)) + +(apl-test + "outer product mult table shape" + (sh + (apl-outer + apl-mul + (make-array (list 3) (list 1 2 3)) + (make-array (list 3) (list 1 2 3)))) + (list 3 3)) + +(apl-test + "outer product add table values" + (rv + (apl-outer + apl-add + (make-array (list 2) (list 1 2)) + (make-array (list 3) (list 10 20 30)))) + (list 11 21 31 12 22 32)) + +(apl-test + "outer product add table shape" + (sh + (apl-outer + apl-add + (make-array (list 2) (list 1 2)) + (make-array (list 3) (list 10 20 30)))) + (list 2 3)) + +(apl-test + "outer product scalar+vector shape" + (sh + (apl-outer apl-mul (apl-scalar 5) (make-array (list 3) (list 1 2 3)))) + (list 3)) + +(apl-test + "outer product scalar+vector values" + (rv + (apl-outer apl-mul (apl-scalar 5) (make-array (list 3) (list 1 2 3)))) + (list 5 10 15)) + +(apl-test + "outer product vector+scalar shape" + (sh + (apl-outer apl-mul (make-array (list 3) (list 1 2 3)) (apl-scalar 10))) + (list 3)) + +(apl-test + "outer product scalar+scalar" + (rv (apl-outer apl-mul (apl-scalar 6) (apl-scalar 7))) + (list 42)) + +(apl-test + "outer product scalar+scalar shape" + (sh (apl-outer apl-mul (apl-scalar 6) (apl-scalar 7))) + (list)) + +(apl-test + "outer product equality identity matrix values" + (rv + (apl-outer + apl-eq + (make-array (list 3) (list 1 2 3)) + (make-array (list 3) (list 1 2 3)))) + (list 1 0 0 0 1 0 0 0 1)) + +(apl-test + "outer product matrix+vector rank doubling shape" + (sh + (apl-outer + apl-add + (make-array (list 2 2) (list 1 2 3 4)) + (make-array (list 3) (list 10 20 30)))) + (list 2 2 3)) + +(apl-test + "outer product matrix+vector rank doubling values" + (rv + (apl-outer + apl-add + (make-array (list 2 2) (list 1 2 3 4)) + (make-array (list 3) (list 10 20 30)))) + (list 11 21 31 12 22 32 13 23 33 14 24 34)) \ No newline at end of file diff --git a/plans/apl-on-sx.md b/plans/apl-on-sx.md index 1140acef..291be996 100644 --- a/plans/apl-on-sx.md +++ b/plans/apl-on-sx.md @@ -76,7 +76,7 @@ Core mapping: - [x] Reduce `f/` (last axis), `f⌿` (first axis) — including `∧/`, `∨/`, `+/`, `×/`, `⌈/`, `⌊/` - [x] Scan `f\`, `f⍀` - [x] Each `f¨` — applies `f` to each scalar/element -- [ ] Outer product `∘.f` — `1 2 3 ∘.× 1 2 3` ↦ multiplication table +- [x] Outer product `∘.f` — `1 2 3 ∘.× 1 2 3` ↦ multiplication table - [ ] Inner product `f.g` — `+.×` is matrix multiply - [ ] Commute `f⍨` — `f⍨ x` ↔ `x f x`, `x f⍨ y` ↔ `y f x` - [ ] Compose `f∘g` — applies `g` first then `f` @@ -118,6 +118,7 @@ data; format for string templating. _Newest first._ +- 2026-05-06: Phase 4 step 4 — outer product ∘.f (apl-outer); rank-doubling result shape = a-shape++b-shape; 151/151 tests - 2026-05-06: Phase 4 step 3 — each f¨ (monadic apl-each + dyadic apl-each-dyadic); scalar broadcast both sides; 139/139 tests - 2026-05-06: Phase 4 step 2 — scan f\ (last axis) + f⍀ (first axis); apl-scan/apl-scan-first; 125/125 tests - 2026-05-06: Phase 4 step 1 — reduce f/ (last axis) + f⌿ (first axis); apl-reduce/apl-reduce-first; 110/110 tests