Skip to content

Instantly share code, notes, and snippets.

@mschauer
Last active January 13, 2016 19:41
Show Gist options
  • Save mschauer/b04e000e9d0963e40058 to your computer and use it in GitHub Desktop.
Save mschauer/b04e000e9d0963e40058 to your computer and use it in GitHub Desktop.
Bracket calculus

Bracket calculus

This formalizes a bit an indexing calculus with brackets which incorporates array construction, array comprehension, slicing and mapping of arrays. Note: ... is the splatting operator and is etcetera.

Guiding principles

1.[…] or T[…] is a shape preserving array construction operator (T a type)

2.f[…] respective A[…] is a map or array indexing operator (A an array, f a function)

Both follow the same fundamental rule

shape([expr]) == shape(expr)

and

shape(A[expr]) == shape(expr)

For f[A] the shape of the result is determined by shape(f[A]) == (shape(f(A[1]...)...,shape(A)...) similar as in mapslices.

Indexing and construction differ mainly with respect to the meaning of , as delimiter.

Within bracket expressions ;, ..., : are operations constructing shaped objects as detailed below, almost agreeing with julias meaning.

Shape

For arrays A,

shape(A) == size(A).

Further,

shape(A[a,b,…]) == (shape(a)...,shape(b)...,…)

so A[1,1] will be a singleton with shape(1,1)= ()

Arrays

An array Aprovides a singleton indexing method A[i,j, …] in this gist written with parenthesis A(i,j, …) for clarity and is not much different from a function f(i,j,…), except that is has shape(A) = size(A).

Array literals

Array construction is done with commas

[A,B,…]

optionally by giving the type of the array elements

T[A,B,…] 

Taking the shape preserving principle strictly would entail A == [A], so only [A,] would denote the array containing A, but of course we define

[A] = [A,]

as exception to the shape preserving principle.

Array literals with ;

The concatenating delimiter ; has formally the property

shape([A1; A2; ; An ]) = (r[1] + r[2] + ... + r[n], s, t)

if shape(Ai) = (r[i], s, t). This has nice consequences later when introducing matrix literals.

Especially for A = [a; b; c], M=[m; n; o]

[A;M] = [a; b; c; m; o]

and

[A;] = A

Array comprehension

Comprehensions

[expr(i) for i in I]
[expr(i,j) for i in I, j in J]
T[expr(i) for i in I]
T[expr(i,j) for i in I, j in J]
f[expr(i) for i in I]
f[expr(i,j) for i in I, j in J]
A[expr(i) for i in I]
A[expr(i,j) for i in I, j in J]

are all defined by the properties

[expr(i) for i in I][k] = expr(I[k])]
[expr(i,j) for i in I, j in J] == expr(I[k],J[l])
f[expr(i) for i in I][k] == f(expr(I[k]))
f[expr(i,j) for i in I, j in J][k,l] = f(expr(I[k],J[l]))
A[expr(i,j) for i in I, j in J][k,l] = A[expr(I[k],J[l])]
etc.

Concatenation and splatting in comprehensions

It is natural to extend the meaning of ; to comprehensions

[expr(i); for i in I][k] == [expr(I[1]); … ; expr(I[n])]

Indexing of arrays

For arrays A and indices i,j

A[i,j] == A[(i,j)...] == A((i,j)...) == A(i,j)
shape(A[i,j]) == shape((i,j)...) == (,)

In general

A[I] == A[idx for idx in I] == [A[idx] for idx in I] == [A(idx...) for idx in I]

The current convention A[(i,j)] == A[i,j] implies the last equality. This obeys shape(A[I]) == shape(I).

As tuples are opened, for an array of tuples I

A[I] = [A[ids...] for ids in I

Shaped objects separated by commas in indexing correspond to forming of a product with A[I,J][ids] == A[I,J][ids..] == A(ids...) for tuple ids.

A[I,J] == A[idx for idx in product(I,J)] ==  A[(i...,j...) for i in I, j in J]

The colon can be formally seen as 1:size(A,i) with shape (size(A,i),).

Hence

A[:] == A[i for i in 1:size(A,1)] = A[(i,)... for i in 1:size(A,1)]
A[:,:] == A[(a,i,c) for i in 1:size(A,1), j in 1:size(A,2)]
A[a,:,c] == A[(a,i,c) for i in 1:size(A,2)]
etc.

and of functions

The same formula hold for functions f

f[I,J] == f[idx for idx in product(I,J)] ==  f[(i...,j...) for i in I, j in J]

The interpretation of tuples as elements from the array case is extended according to f[(i,j)] == f(i,j)

Array construction with [A,B,C] and T[A,B,C] is not covered by this product calculus with comma as delimiter.

Space as delimiter and diagonals

It turns out that derived from the julia definition of [v w] we obtain a concept related to the diagonal of the product A[I,J]. A good name for the concept would be splicing, it is almost the same as the zip operation. We define

A[J K][i] == A(J[i]...,K[i]...)
A[J K][i,j] == A(J[i,j]...,K[i,j]...) 

etc. and obtain especially for vectors

[v w z][i,:] = [v[i], w[i], z[i]]

One can think of v w as vector of tuples without parentheses, because shape([v w]) == (shape(v)..., 2) and shape shape(A[v w]) == shape(v) or as vector of tuples taking into account that [] opens tuples.

Note that both space separation and ;-separation can be extended conformal with the array concatenation notation

[A1 B1 C1; A2 B2 C2; A3 B2 C3]

if [A B] == [a b v w x] for A == [a b] and B == [v w] from the vector of tuple without parentheses interpretation.

Also, a notation for the diagonal emerges

A[: :] = diag(A) 

Syntax mapping with over

If A and B have shape, the only sane convention is that [f(A,B)] is the array containing f(A,B) but we might set

[expr(:,:) over [A B]][i,j] = expr(A[i,j], B[i,j])
[expr(:,:) over [A,B]][i,j,k,l] = expr(A[i,j], B[k,l])

and obtain a nice array map notation. [Should tuples be automatically opened here expr(A[i,j]..., B[i,j]...)?]

Deficient indexing

Whether for an d-dimensional array A = A[:, :, …, :] the expression A[i, …, k] is understood as A[i, …, k, :, …, :] as in C or as in Julia and Matlab or as A[:, …, :,i, …, k,] seems to be independent of this calculus.

Edit: I clarified a bit how arrays of tuples behave as indices and that [ ] opens tuples, but the question how arrays of tuples are injected into functions and array indicies will have to be revisited.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment