Introduction
Apple is an experimental compiler with a typed frontend inhabited by
two demons.
Installation
The REPL and typechecker are available on the release page .
Reference Card
In the REPL, type \l.
vanessa@MacBookAir apple % arepl
> \l
Λ scan √ sqrt
⋉ max ⋊ min
⍳ integer range ⌊, ⌈ floor, ceiling
e: exp ⨳ {m,n} convolve
\~ successive application \`n infix
_. log ' map
` zip `{i,j∘[k,l]} rank
𝒻 range (real) 𝜋 pi
⋮
Use :ty for more:
> :ty (⊲)
a → Vec i a → Vec (i + 1) a
:help inside the REPL:
> :help
:help, :h Show this help
:ty <expression> Display the type of an expression
:ann <expression> Annotate with types
:bench, :b <expression> Benchmark an expression
:list List all names that are in scope
⋮
Editor Integration
There is a vim plugin
and a VSCode
extension .
The file extension is .🍎 or .🍏.
Capabilities
Range (Integer)
To generate an integer range use ..
(..)
: int → int → Vec #n int
.. is a binary operator taking a start value and end
value as arguments, viz.
> 1..9
Vec 9 [1, 2, 3, 4, 5, 6, 7, 8, 9]
Iota
⍳ generates an integer range beginning with 0.
> ⍳
⍳
: int(n) → Vec (n + 1) int
> ⍳9
Vec 10 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Real Range
For a range of real numbers, use frange or
𝒻.
> :ty frange
float → float → int → Vec #n float
frange takes a start value, an end value, and the number
of steps.
> frange 0 9 10
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
Map
' maps over an array.
> (*2)'⍳ 0 9
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
Functions can be curried .
Zip
Pick the greater value among two vectors:
> (⋉)`⟨0,_1,3.0⟩ ⟨_3,1,3⟩
Vec 3 [0.0, 1.0, 3.0]
Fold
/ folds over an array.
> (+)/⍳ 1 100
5050
Scan
> (+)Λ 1..3
Vec 3 [1, 3, 6]
Array Literals
Array literals are delineated by ⟨…⟩.
> ⟨_1,0::int⟩
[-1, 0]
> ⟨⟨0,1⟩,⟨_1,0::int⟩⟩
[ [0, 1]
, [-1, 0] ]
Compact Numeric Literals
𝔸21
will be interpreted as
⟨2,1⟩
This only works for single-digit numbers, but cosmically justified
numbers tend
to be small .
Sort
⍋ sorts a numeric array.
> ⍋ (rand. _20 20 :: Vec 13 int)
Vec 13 [-17, -13, -10, -9, -7, -7, -7, -5, 2, 3, 4, 12, 17]
Axis
ᶥ (postfix) returns a vector of indices with the same
length as the leading axis of its argument.
> :ty [xᶥ]
Vec i a → Vec i int
> frange _10 0 11
Vec 11 [-10.0, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0]
> (frange _10 0 11)ᶥ
Vec 11 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Reverse
~ reverses an array.
> ~(⍳9)
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Reverse applied to a higher-dimensional array reverses elements
(sub-arrays) along the first dimension.
> ⟨⟨0,1⟩,⟨1,0::int⟩,⟨2,4⟩⟩
[ [0, 1]
, [1, 0]
, [2, 4] ]
> ~⟨⟨0,1⟩,⟨1,0::int⟩,⟨2,4⟩⟩
[ [2, 4]
, [1, 0]
, [0, 1] ]
> ~'⟨⟨0,1⟩,⟨1,0::int⟩,⟨2,4⟩⟩
[ [1, 0]
, [0, 1]
, [4, 2] ]
Outer Product
The outer product ⊗ creates a table by applying some
function.
> :ty \f.\x.\y. x f⊗ y
(a → b → c) → Arr sh0 a → Arr sh1 b → Arr (sh0 ⧺ sh1) c
> (frange 0 9 10) (*)⊗ (frange 0 9 10)
[ [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
, [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
, [0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0]
, [0.0, 3.0, 6.0, 9.0, 12.0, 15.0, 18.0, 21.0, 24.0, 27.0]
, [0.0, 4.0, 8.0, 12.0, 16.0, 20.0, 24.0, 28.0, 32.0, 36.0]
, [0.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0]
, [0.0, 6.0, 12.0, 18.0, 24.0, 30.0, 36.0, 42.0, 48.0, 54.0]
, [0.0, 7.0, 14.0, 21.0, 28.0, 35.0, 42.0, 49.0, 56.0, 63.0]
, [0.0, 8.0, 16.0, 24.0, 32.0, 40.0, 48.0, 56.0, 64.0, 72.0]
, [0.0, 9.0, 18.0, 27.0, 36.0, 45.0, 54.0, 63.0, 72.0, 81.0] ]
> (frange 0 4 5) [(x,y)]⊗ (frange 0 4 5)
[ [(0.0*0.0), (0.0*1.0), (0.0*2.0), (0.0*3.0), (0.0*4.0)]
, [(1.0*0.0), (1.0*1.0), (1.0*2.0), (1.0*3.0), (1.0*4.0)]
, [(2.0*0.0), (2.0*1.0), (2.0*2.0), (2.0*3.0), (2.0*4.0)]
, [(3.0*0.0), (3.0*1.0), (3.0*2.0), (3.0*3.0), (3.0*4.0)]
, [(4.0*0.0), (4.0*1.0), (4.0*2.0), (4.0*3.0), (4.0*4.0)] ]
Reshape
♭ flattens an array to a vector, ♯ adds a
dimension.
> :ty λA. ♭ (A::Arr (28×28×1) a)
Arr (28 × 28 × 1) a → Vec 784 a
> ♯ (⍳9)
Arr (1×10) [ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ]
Successive Application
> :ty (\~)
(a → a → b) → Arr (i + 1 `Cons` sh) a → Arr (i `Cons` sh) b
[(-)\~ x] gives successive differences.
> (-)\~ ((^2)'(frange 0 9 10))
[1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0]
Rotate
> (⊖)
(⊖) : int → Vec i a → Vec i a
> 2 ⊖ ⍳9
[2, 3, 4, 5, 6, 7, 8, 9, 0, 1]
> _2 ⊖ ⍳9
[8, 9, 0, 1, 2, 3, 4, 5, 6, 7]
> 2 ⊖ ⟨⟨1,2⟩,⟨3,4⟩,⟨5,6.0⟩⟩
Arr (3×2) [ [5.0, 6.0]
, [1.0, 2.0]
, [3.0, 4.0] ]
Cycle
cyc. or ⊙ (infix) concatenates an array
with itself a specified number of times.
> cyc. ⟨0::int,1⟩ 4
Vec 8 [0, 1, 0, 1, 0, 1, 0, 1]
> ⟨0::int,1⟩⊙4
Vec 8 [0, 1, 0, 1, 0, 1, 0, 1]
Repeat
> ⟨1.0,0⟩〃3
Arr (3×2) [ [1.0, 0.0]
, [1.0, 0.0]
, [1.0, 0.0] ]
Transpose
⍉ or |:
> ⍉ ⟨⟨1.0,3⟩,⟨4,4⟩,⟨2,_2⟩⟩
[ [1.0, 4.0, 2.0]
, [3.0, 4.0, -2.0] ]
> ⟨⟨1.0,3⟩,⟨4,4⟩,⟨2,_2⟩⟩
[ [1.0, 3.0]
, [4.0, 4.0]
, [2.0, -2.0] ]
Unfold
> gen. (1::int) (*2) 10
Vec 10 [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
Infix
Moving average:
> [(+)/x%ℝ(:x)]\` 7 (frange 0 9 10)
[3.0, 4.0, 5.0, 6.0]
> [x]\`4 (frange 0 5 6)
[ [0.0, 1.0, 2.0, 3.0]
, [1.0, 2.0, 3.0, 4.0]
, [2.0, 3.0, 4.0, 5.0] ]
Bind
> {i←2::int;i*i}
4
Bind, preventing inlining:
> {i⟜2::int;i*i}
4
One can see that 2 is stored in a register by inspecting
the generated assembly:
> :asm {i←2::int;i*i}
mov x0, #0x4
ret
> :asm {i⟜2::int;i*i}
mov x0, #0x2
mul x0, x0, x0
ret
Polymorphic Bind
> {sum ⇐ [(+)/x]; sum (⍳ 9)+⌊(sum(frange 0 9 10))}
90
> {sum ← [(+)/x]; sum (⍳ 9)+⌊(sum(frange 0 9 10))}
1:42: could not unify 'float' with 'int' in expression '𝒻 0 9 10'
Strong Induction
𝓕 is like gen. but with access to all
previously computed values.
> :ty 𝓕
Vec m a → (Vec k a → a) → int(n) → Vec (m + n) a
Fibonacci sequence:
> 𝓕 ⟨1::int,1⟩ [}.x+}.(}:x)] 10
Vec 11 [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Unfold With State
> :ty ug.
(b → (b * a)) → b → int(n) → Vec n a
Rank
Rank `{i,j∘[k,l]} or `{i,j[k,l]} lifts a
function to operate on i, j-cells, optionally specifying axes k,l.
Iteration is bottom-up; by contrast map ' cuts across the
leading dimension.
To make a scalar function apply to arrays, re-rank
> :ty ((*)`{0,0})
(IsNum c) :=> Arr sh c → Arr sh c → Arr sh c
Sigmoid on an arbitrary-dimension array:
> ⟨⟨0,1,2⟩,⟨3,4,5::int⟩⟩
Arr (2×3) [ [0, 1, 2]
, [3, 4, 5] ]
> ((+)/)`{1} ⟨⟨0,1,2⟩,⟨3,4,5::int⟩⟩
Vec 3 [3, 5, 7]
> ((+)/)`{1∘[2]} ⟨⟨0,1,2⟩,⟨3,4,5::int⟩⟩
Vec 2 [3, 12]
This may be confusing; Apple’s rank feature was poorly designed.
Take 0-cells (scalars) from the first array and 1-cells from the
second.
> (⊲)`{0,1[2]} ⟨0::int,1⟩ ⟨⟨2,3⟩,⟨4,5⟩⟩
Arr (2×3) [ [0, 2, 3]
, [1, 4, 5] ]
> ⍉ ((2 ⊖)`{1} ⟨⟨1,2⟩,⟨3,4⟩,⟨5,6.0⟩⟩)
Arr (3×2) [ [5.0, 6.0]
, [1.0, 2.0]
, [3.0, 4.0] ]
> :ty [♭`{3∘[2,3,4]} (x :: Arr (60000 × 28 × 28 × 1) float)]
Arr (60000 × 28 × 28 × 1) float → Arr (60000 × 784) float
Sum Axes
> :store a ⟨⟨⟨0::int,1,2,3⟩,⟨4,5,6,7⟩,⟨8,9,10,11⟩⟩,⟨⟨12,13,14,15⟩,⟨16,17,18,19⟩,⟨20,21,22,23⟩⟩⟩
> ((+)/)`{1} a
Arr (3×4) [ [12, 14, 16, 18]
, [20, 22, 24, 26]
, [28, 30, 32, 34] ]
> ((+)/)`{1∘[2]} a
Arr (2×4) [ [12, 15, 18, 21]
, [48, 51, 54, 57] ]
> ((+)/)`{1∘[3]} a
Arr (2×3) [ [6, 22, 38]
, [54, 70, 86] ]
> [(+)/* 0 x]`{2} a
Vec 4 [60, 66, 72, 78]
> [(+)/* 0 x]`{2∘[1,3]} a
Vec 3 [60, 92, 124]
> [(+)/* 0 x]`{2∘[2,3]} a
Vec 2 [66, 210]
For comparison:
> [(+)/* 0 (x::M int)]'a
Vec 2 [66, 210]
> (+)/* 0 a
276
Dot Product
x⋅y is shorthand for (+)/(*)`x y.
Random Numbers
𝔯 or rand.
> 𝔯 0 1 :: float
0.582699021207219
> 𝔯 0 1 :: Arr (3 × 3) float
Arr (3×3) [ [0.9827522631010304, 0.30678513061418045, 0.6008551364891055]
, [0.608715653358658, 0.2124387982011875, 0.8858951305876062]
, [0.30465710174579286, 0.15185986406856955, 0.33766190287353126] ]
Tuples
Use ->n to access the nth element of a
tuple, viz.
> (1.0,#t,4::int)->1
1.0
> (1.0,#t,4::int)->2
#t
> (1.0,#t,4::int)->3
4
Convolve
Convolve (⨳ {m,n}) is like infix
for higher-rank windows. One can also write (# {m,n}).
> [(⋉)/x] ⨳ {3} ⟨_1,0,4,_2,3,3,1,1,0,_5.0⟩
Vec 8 [4.0, 4.0, 4.0, 3.0, 3.0, 3.0, 1.0, 1.0]
> [(⋉)/x] \`3 ⟨_1,0,4,_2,3,3,1,1,0,_5.0⟩
Vec 8 [4.0, 4.0, 4.0, 3.0, 3.0, 3.0, 1.0, 1.0]
> ([(+)/* 0 (x::Arr (2×3) float)%ℝ(:x)] # {2,3}) ⟨⟨1.0,2,0,0⟩,⟨_1,_2,3,4⟩,⟨5,6,3,1⟩,⟨3,1,1,3⟩⟩
Arr (3×2) [ [0.5, 1.1666666666666665]
, [2.333333333333333, 2.5]
, [3.1666666666666665, 2.5] ]
(⨳ {m∘k,n∘l}) specifies the skip between windows/blocks,
viz.
> ([(⋉)/* _1e100 (x::M float)] ⨳ {2∘2,2∘2}) ⟨⟨20.0,200,_5,23⟩,⟨_13,134,119,100⟩,⟨120,32,49,25⟩,⟨_120,12,9,23⟩⟩
Arr (2×2) [ [200.0, 119.0]
, [120.0, 49.0] ]
> ([(⋉)/* _1e100 (x::M float)] ⨳ {2,2∘2}) ⟨⟨20.0,200,_5,23⟩,⟨_13,134,119,100⟩,⟨120,32,49,25⟩,⟨_120,12,9,23⟩⟩
Arr (3×2) [ [200.0, 119.0]
, [134.0, 119.0]
, [120.0, 49.0] ]
Combinators
S, K
> :ty 𝐒
(a → b → c) → (a → b) → a → c
> :ty 𝐊
b → a → b
Fork
Haskell’s on ,
Curry’s Φ
combinator ):
> :ty (⑂)
(b → b → c) → (a → b) → a → a → c
One can define the logarithm in any base from the natural logarithm
_., viz.
> ([y%x]⑂_.) 2 8
3.0
REPL Functionality
Load
> :yank chisqcdf math/chisqcdf.🍎
> chisqcdf 10 28
0.9982004955179669
math/chisqcdf.🍎 is a file that contains an expression
of type float → float→ float.
Benchmark
> :bench frange 0 999 1000
benchmarking...
time 1.423 μs (1.417 μs .. 1.427 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 1.426 μs (1.422 μs .. 1.429 μs)
std dev 11.94 ns (9.819 ns .. 14.22 ns)
QuickCheck
Apple can generate shape-correct test cases for property testing. For
instance,
> :qc \x. [(+)/(*)`x y] x x >= 0.0
Passed, 100.
tests that the dot product of a vector with itself is
nonnegative.
Exotic Syntax
Coronis
Instead of
One can write
Using the typographical
coronis .
Matrix Dimensions
One can specify matrix dimensions in a type signature with unicode
subscript digits separated by a comma.
is equivalent to
𝔯 0 1 :: Arr (12 × 12 ) float
Identifiers
Identifiers may be latin or greek characters, or a single character
from the mathematical greek or mathematical latin unicode block,
optionally followed by some subscript alphanumeric characters.
∂ may appear at the beginning of multi-character
identifiers or on its own. The single-character lambda λ is
reserved. ∫, 𝛻, and ∇ are also
identifiers but may not be followed by a subscript.
Thus pxs, aₙ, sn₁,
𝐶, φs, ∂f, 𝜉, and
𝜌₀ are valid identifiers but 𝜉s,
𝜉∂, and f∂ are parsed as application of one
identifier to another.
Vulgar Fractions
Unicode vulgar fractions are considered float literals:
> ⅚
0.8333333333333334
One can use ⅟ for the reciprocal, viz.
> [⅟(1+e:(_x))]
λx. 1.0 % (1 + 2.718281828459045 ** _ x) : float → float
Time
In the REPL, one can use ⏱ in place of
:bench, i.e.
> ⏱ [(+)/x%ℝ(:x)]\`7 (𝒻 0 999 1000)
Type Shorthands
𝟘 can be used in place of int, viz.
> 17*3::𝟘
51
> 𝔯 1 6::𝟘
5
> 𝔯 1 6::Vec 6 𝟘
Vec 6 [6, 2, 1, 5, 5, 3]
𝞈 is shorthand for float.
> 𝔯 0 1::𝞈
0.8404853694114252
𝟙 is shorthand for Vec n, n an
index variable. 𝟚 is a synonym for M .
Examples
Cross Product
The cross product of two vectors \((a_1,a_2,a_3)\) , \((b_1,b_2,b_3) \in \mathbb{R}^3\) is the
vector \(\in\mathbb{R}^3\)
\[(a_2b_3-a_3b2,
a_3b1-a_1b3,a_1b_2-a_2b_1)\]
In Apple, we can write:
λa.λb. (-)`((*)`(1 ⊖a) (_1 ⊖b)) ((*)`(_1 ⊖a) (1 ⊖b))
This uses zips and rotations (no indices are mentioned); it is a new
perspective on the problem.
Linear Regression
λxs.λys.
{
Σ ← [(+)/x];
n ⟜ ℝ(:xs);
xbar ⟜ (Σ xs) % n; ybar ⟜ (Σ ys) % n;
xy ⟜ Σ ((*)`xs ys);
x2 ⟜ Σ ((^2 )'xs);
denom ⟜ (x2-n*(xbar^2 ));
a ← ((ybar*x2)-(xbar*xy))%denom;
b ← (xy-(n*xbar*ybar))%denom;
(a,b)
}
Note the ⟜ to prevent expressions from being
inlined.
Kullback-Leibler Divergence
λp.λq. (+)/([x*_.(x%y)]`p q)
Numerically Stable Geometric
Mean
λxs.
⸎ avg ← [{n ⟜ ℝ(:xs); ((+)/xs)%n}]
; e: (avg (_.'xs))
Array Strides
(Column-major order)
Train Neural Network
λwh.λwo.λbh.λbo.
{ X ⟜ ⟨⟨0 ,0 ⟩,⟨0 ,1 ⟩,⟨1 ,0 ⟩,⟨1 ,1 ⟩⟩;
Y ← ⟨0 ,1 ,1 ,0 ⟩;
sigmoid ← [1 %(1 +e: (_x))];
sDdx ← [x*(1 -x)];
sum ⇐ [(+)/x];
ho ⟜ sigmoid`{0 } ([(+)`bh x]'(X%.wh));
prediction ⟜ (sigmoid ∴ (+bo))'(ho%:wo);
l1E ← (-)`Y prediction;
l1Δ ⟜ (*)`(sDdx'prediction) l1E;
he ← l1Δ (*)⊗ wo;
hΔ ⟜ (*)`{0 ,0 } (sDdx`{0 } ho) he;
wha ← (+)`{0 ,0 } wh ((|:X)%.hΔ);
woa ← (+)`wo ((|:ho)%:l1Δ);
bha ← [(+)/ₒ x y]`{0 ,1 } bh hΔ;
boa ← bo + sum l1Δ;
(wha,woa,bha,boa)
}
This is equivalent to the Python :
import numpy as np
def sigmoid (x):
return 1 / (1 + np.exp(- x))
def sigmoid_derivative(x):
return x * (1 - x)
inputs = np.array([[0 ,0 ],[0 ,1 ],[1 ,0 ],[1 ,1 ]])
expected_output = np.array([[0 ],[1 ],[1 ],[0 ]])
hidden_layer_activation = np.dot(inputs,hidden_weights)
hidden_layer_activation += hidden_bias
hidden_layer_output = sigmoid(hidden_layer_activation)
output_layer_activation = np.dot(hidden_layer_output,output_weights)
output_layer_activation += output_bias
predicted_output = sigmoid(output_layer_activation)
#Backpropagation
error = expected_output - predicted_output
d_predicted_output = error * sigmoid_derivative(predicted_output)
error_hidden_layer = d_predicted_output.dot(output_weights.T)
d_hidden_layer = error_hidden_layer * sigmoid_derivative(hidden_layer_output)
#Updating Weights and Biases
output_weights += hidden_layer_output.T.dot(d_predicted_output)
output_bias += np.sum (d_predicted_output,axis= 0 ,keepdims= True )
hidden_weights += inputs.T.dot(d_hidden_layer)
hidden_bias += np.sum (d_hidden_layer,axis= 0 ,keepdims= True )
Permutations
Inverse
We will convert
a permutation represented as a vector of integers into a Boolean
matrix ; then the transpose of this will be the inverse permutation
(as a Boolean matrix).
To convert a permutation to its Boolean matrix representation:
> [x (=)⊗ xᶥ] 𝔸021
Arr (3×3) [ [#t, #f, #f]
, [#f, #f, #t]
, [#f, #t, #f] ]
To convert back:
> ([x]@.)'([x (=)⊗ xᶥ] 𝔸021)
Vec 3 [0, 2, 1]
Putting it all together to get the inverse:
> ([x]@.)'(⍉([x (=)⊗ xᶥ] 𝔸120))
Vec 3 [2, 0, 1]
This uses @. (index-of), showing an array style that is
not appreciated in functional programming.
Reduced Representation
Hui, Iverson,
and McDonnell give the lovely enumeration of permutations in reduced
form:
λn.
{
fact ← [(*)/ₒ 1 (1 ..x)];
antibase ← λk.λbs. (->1 )'({: ((λqr.λb. {s ⟜ qr->2 ; (s|b, s/.b)}) Λₒ (0 ,k) bs));
bs ⟜ 1 ..n;
(λk. ~(antibase k bs))'⍳ (fact n-1 )
}
> r 3
Arr (6×3) [ [0, 0, 0]
, [0, 1, 0]
, [1, 0, 0]
, [1, 1, 0]
, [2, 0, 0]
, [2, 1, 0] ]
antibase
can be used to convert seconds to hours-minutes-seconds:
> {antibase ← λk.λbs. (->1)'({: ((λqr.λb. {s ⟜ qr->2; (s|b, s/.b)}) Λₒ (0,k) bs)); antibase 86399 ⟨24,60,60⟩}
Vec 3 [23, 59, 59]
If a polygon has vertices at \(x_n\) , \(y_n\) , then its area is given by
\[A=\frac{1}{2}\Biggl|(x_1y_2+x_2y_3+\cdots+x_ny_1)-(y_1x_2+y_2x_3+\cdots+y_nx_1)\Biggr|\]
λxs.λys.
{ sum ⇐ [(+)/x]
; 0 .5 *abs.(sum((*)`xs (1 ⊖ys)) - sum((*)`(1 ⊖xs) ys))
}
Note the array style: ⊖, ` (zip), and fold are enough to
eschew pointful definitions.
Polynomials
Evaluation
λp.λx. ~p⋅gen. 1 (*x) (𝓉p)
To compute
\(\displaystyle
\frac{p(x)}{x-a}\)
for \(p\) a polynomial:
λp.λa. {:((λs.λc. (a*s+c)) Λₒ 0 p)
Base-n representation
λa.λn. {log ← λb.λx. _.x%_.b;N ⟜ ⌊(log (ℝn) (ℝa))+1 ;~(ug. (λs. (s/.n, s|n)) a N)}
Covariance Matrix
Given a \(K \times N\) matrix of
\(N\) obervations on \(K\) variables, we can compute the sample
covariance matrix thusly:
λxs.
{
𝜇 ← [⸎n⟜ ℝ(:x); (+)/x%n]; rs ← 𝜇'xs;
nd ⟜ [(-x)'y]`{0 ,1 ∘[2 ]} rs xs;
N ⟜ ℝ(:({.xs))-1 ;
nd [x⋅y%N]⊗ nd
}
The array style gives a new take on the problem.
Array
To take all but the last 6 elements:
[{.\`7 x]
To drop the first 6 elements:
[}.\`7 x]
Take the first 7 elements:
> {. ([x] \`7 ⍳ 9)
Vec 7 [0, 1, 2, 3, 4, 5, 6]
Take the last 7 elements:
> }. ([x] \`7 ⍳ 9)
Vec 7 [3, 4, 5, 6, 7, 8, 9]
Chunks
> [x] # {2∘2} (frange 0 9 10)
Arr (5×2) [ [0.0, 1.0]
, [2.0, 3.0]
, [4.0, 5.0]
, [6.0, 7.0]
, [8.0, 9.0] ]
Matrix-Vector Multiplication
λA.λx. (x⋅)`{1 ∘[2 ]} (A::Arr (i × j) float )
Filter
[{m⟜(⋉)/x::Vec n float ; (=m)@.x}]
λxs.
{ digitSum ← [?x>10 ,.x-9 ,.x]
; t ← (+)/ [digitSum (x*y)]`(~(}:xs)) (}: (cyc. ⟨2 ,1 ::int ⟩ 8 ))
; 10 -t|10 =}.xs
}
Note zipping with cyc. ⟨2,1::int⟩ 8 to get alternating
2, 1, … factors.
Combinatorics
Catalan Numbers
The Catalan numbers satisfy
\[C_n=\sum_{i=0}^{n-1}C_iC_{n-i-1}\]
where \(C_0=1,C_1=1,C_2=2\) .
We can compute them in Apple with:
{ Σ ← λl.λu.λf.(+)/(f'l..u)
; 𝓕 ⟨1 ::int ,1 ,2 ⟩ (λC. {n⟜ 𝓉C; Σ 0 (n-1 ) (λi. (C˙i*C˙(n-i-1 )))})
}
The use of “strong induction” provides a new take on the problem
where Python uses memoization.
A000081
The number of unlabeled rooted trees with at most \(n\) nodes (this appears in chemistry
(counting alkanes) ).
{ sum ⇐ [(+)/x]; Σ ⇐ λl.λu.λf. (+)/(f'l..u)
; divisors ← λn. (λk. (n|k=0 ))§1 ..n
; 𝓕 𝔸01 (λas. {n⟜ 𝓉as; Σ 1 (n-1 ) (λj.sum ((λd. d*as˙d)'(divisors j))*as˙(n-j))/.(n-1 )})
}
Cf. Reinhard Zumkeller’s Haskell
solution by sharing/laziness.
Elliptic Fourier Series
From Kuhl
and Giardnia , the coefficients are given by:
\[ a_n = \frac{T}{2n^2\pi^2}\sum_{p=1}^K
\frac{\Delta x_p}{\Delta t_p}\left(\cos\frac{2n\pi t_p}{T} -
\cos\frac{2n\pi t_{p-1}}{T}\right) \]
\[ b_n = \frac{T}{2n^2\pi^2}\sum_{p=1}^K
\frac{\Delta x_p}{\Delta t_p}\left(\sin\frac{2n\pi t_p}{T} -
\sin\frac{2n\pi t_{p-1}}{T}\right) \]
\[ c_n = \frac{T}{2n^2\pi^2}\sum_{p=1}^K
\frac{\Delta y_p}{\Delta t_p}\left(\cos\frac{2n\pi t_p}{T} -
\cos\frac{2n\pi t_{p-1}}{T}\right) \]
\[ d_n = \frac{T}{2n^2\pi^2}\sum_{p=1}^K
\frac{\Delta y_p}{\Delta t_p}\left(\sin\frac{2n\pi t_p}{T} -
\sin\frac{2n\pi t_{p-1}}{T}\right) \]
The offsets are given by:
\[ A_0 = \frac{1}{T} \sum_{p=1}^K
\left[\frac{\Delta x_p}{2\Delta t_p} (t_p^2-t_{p-1}^2) + \xi_p
(t_p-t_{p-1}^2)\right]\]
\[ C_0 = \frac{1}{T} \sum_{p=1}^K
\left[\frac{\Delta y_p}{2\Delta t_p} (t_p^2-t_{p-1}^2) + \delta_p
(t_p-t_{p-1}^2)\right]\]
where
\[ \xi_p = \sum_j^{p-1} \Delta x_j -
\frac{\Delta x_p}{\Delta t_p} \sum_j^{p-1}\Delta t_j \]
\[ \delta_p = \sum_j^{p-1} \Delta y_j -
\frac{\Delta y_p}{\Delta t_p} \sum_j^{p-1}\Delta t_j \]
\[ \xi_0,~\delta_0 = 0 \]
In Apple we can generate the first N coefficients
alongside the offsets with:
λxs.λys.λN.
{ sum ← [(+)/x]
; tieSelf ← [({.x)⊳x]; Δ ← [(-)\~(tieSelf x)]
; dxs ⟜ Δ xs; dys ⟜ Δ ys
; dts ⟜ [√(x^2 +y^2 )]`dxs dys
; dxss ⟜ ((%)`dxs dts); dyss ⟜ ((%)`dys dts)
; pxs ← (+)Λ dxs; pys ← (+)Λ dys; pts ⟜ (+)Λₒ 0 dts; T ⟜}. pts
; coeffs ← λn.
{ n ⟜ ℝn; k ⟜ 2 *n*𝜋%T
; c ⟜ T%(2 *n^2 *𝜋^2 )
; cosDiffs ⟜ (-)\~([cos.(k*x)]'pts)
; sinDiffs ⟜ (-)\~([sin.(k*x)]'pts)
; aₙ ← c*sum ((*)`dxss cosDiffs)
; cₙ ← c*sum ((*)`dyss cosDiffs)
; bₙ ← c*sum ((*)`dxss sinDiffs)
; dₙ ← c*sum ((*)`dyss sinDiffs)
; (aₙ,bₙ,cₙ,dₙ)
}
; dtss ⟜ (-)\~((^2 )'pts)
; ppts ⟜ {: pts
; 𝜉 ← (-)`pxs ((*)`((%)`dxs dts) ppts)
; 𝛿 ← (-)`pys ((*)`((%)`dys dts) ppts)
; A ← (0 .5 *sum ((*)`((%)`dxs dts) dtss) + sum ((*)`𝜉 dts))%T
; C ← (0 .5 *sum ((*)`((%)`dys dts) dtss) + sum ((*)`𝛿 dts))%T
; (coeffs'(1 ..N),A,C)
}
Note the array style, e.g. (-)\~ to define successive
differences on an array rather than definining pointfully.
Geography
Let 𝜆₀, 𝜑₀ be the coördinates of the origin, 𝜑₁, 𝜑₂ standard
parallels, φs and lambdas the longitudes and
latitudes, respectively.
\𝜆₀.\𝜑₀.\𝜑₁.\𝜑₂.\φs.\lambdas.
{
𝑛 ⟜ (sin. 𝜑₁+sin.𝜑₂)%2 ;
𝐶 ⟜ (cos. 𝜑₁)^2 +2 *𝑛*sin. 𝜑₁;
𝜌₀ ⟜ √(𝐶-2 *𝑛*sin. 𝜑₀)%𝑛;
albers ← \𝜑.\𝜆.
{
𝜃 ⟜ 𝑛*(𝜆-𝜆₀);
𝜌 ⟜ √(𝐶-2 *𝑛*sin. 𝜑)%𝑛;
(𝜌*sin. 𝜃, 𝜌₀-𝜌*cos. 𝜃)
};
albers`φs lambdas
}
Number Theory
Primality Check
λn.¬((∨)/ₒ #f ([(n|x)=0]'2..(⌊(√(ℝn)))))
Radical
Compute the radical of an integer \(n\) , \(\displaystyle \prod_{p|n} p\)
λn.
{ ni ⟜ ⌊(√(ℝn))
; isPrime ← λn.¬((∨)/ₒ #f ([n|x=0 ]'2 ..(⌊(√(ℝn))))); pf ⇐ (isPrime #.)
; pps ⟜ (λk. (n|k=0 )) #. 2 ..ni
; ?ni^2 =n
,.((*)/ₒ 1 (pf (pps⧺(n/.)'}:?pps)))
,.((*)/ₒ 1 (pf (n ⊲ pps⧺((n/.)'pps))))
}
This shows the awkwardness of an array style.
Python Extension Module
Sample Normal Distribution
To generate normally distributed random values, we can use
\[ X = \sqrt{-2 \log B_1} \cos (2\pi B_2)
\]
where \(B_1\) , \(B_2\) are uniformly distributed \(\in [0,1)\) .
Debugging randomness is fraught, so we turn to Python’s visualization
libraries.
Out[4]:
(array([ 14., 47., 126., 206., 289., 170., 96., 42., 9., 1.]),
array([-2.93368273, -2.26350525, -1.59332778, -0.9231503 , -0.25297283,
0.41720465, 1.08738213, 1.7575596 , 2.42773708, 3.09791456,
3.76809203]),
<BarContainer object of 10 artists>)