Skip to contents

The goal of {ricci} is to provide a compact1 R interface for performing tensor calculations. This is achieved by labeling (upper and lower) index slots of R’s array and making use of Ricci calculus conventions to implicitly trigger contractions and diagonal subsetting. Explicit tensor operations, such as addition, subtraction, multiplication of tensors via the standard operators (*, +, -), raising and lowering indices, or the Kronecker product are also available. Common tensors like the Kronecker delta, Levi Civita epsilon, and certain metric tensors are provided.

{ricci} uses the calculus package (Guidotti 2022) behind the scenes to perform calculations and simply provides an alternative interface to a subset of its functionality.

Installation

You can install the development version of ricci from GitHub with:

# install.packages("pak")
pak::pak("lschneiderbauer/ricci")

Examples

The central object is R’s array. Adding index slot labels allows us to perform common tensor operations implicitly. After the desired calculations have been carried out we can remove the labels to obtain an ordinary array.

The following example shows how to express the contraction of two tensors, where one index has to be raised, and subsequent diagonal subsetting. For demonstration purposes we use an arbitrary array of rank 3.

library(ricci)

# the data
a <- array(1:(2^2*3), dim = c(2,2,3))

# create labeled array (tensor)
(a %_% .(i, j, A) * 
  # create a labeled array (tensor) and raise index j
  a %_% .(j, i, A) |> r(j, g = g_mink(2))) |> 
  # * -j and +j dimension are implictely contracted
  # * the i-diagonal is selected
  # the result is a tensor of rank 2
  as_a(i, A) # we unlabel the tensor with index order (i, A)
#>      [,1] [,2] [,3]
#> [1,]    5   17   29
#> [2,]   10   22   34

Below we outline more details on possible individual operations.

Creating a labeled array (tensor)

We can use the array a to create a labeled array (tensor) with lower index labels i, j, and k:

aijk a_{ijk}

a %_% .(i, j, k)
#> <Labeled Array> [2x2x3] .(-i, -j, -k)

By default, indices are assumed to be lower indices. We can use a “+” prefix to create an upper index label.

aijk a_{ij}^{\;\;k}

a %_% .(i, j, +k)
#> <Labeled Array> [2x2x3] .(-i, -j, +k)

Performing calculations

Creating index labels on its own is not very interesting nor helpful. The act of labeling tensor index slots becomes useful when the labels are set such that they trigger implicit calculations, or they are combined with other tensors via multiplication or addition.

Contraction

Repeated index labels with opposite position are implicitly contracted.

bj=aiki b_j=a_{i\;k}^{\;i}

b <- a %_% .(i, +i, k)
b
#> <Labeled Array> [3] .(-k)
#> [1]  5 13 21

# retrieve array
b |> as_a(k)
#> [1]  5 13 21

Diagonal subsetting

Repeated labels on the same position (upper or lower) will trigger diagonal subsetting.

cik=aiik c_{ik}=a_{iik}

c <- a %_% .(i, i, k)
c
#> <Labeled Array> [2x3] .(-i, -k)
#>      [,1] [,2] [,3]
#> [1,]    1    5    9
#> [2,]    4    8   12

# retrieve array
c |> as_a(i, k)
#>      [,1] [,2] [,3]
#> [1,]    1    5    9
#> [2,]    4    8   12

Outer tensor product

The same conventions apply for arbitrary tensor multiplication.

dijklmn=aijkalmn d_{ijklmn}=a_{ijk}a_{lmn}

d <- a %_% .(i, j, k) * a %_% .(l, m, n)

Tensor multiplication w/ contractions

e=aijkaijk e=a_{ijk}a^{ijk}

e <- a %_% .(i, j, k) * a %_% .(+i, +j, +k)
e
#> <Scalar>
#> [1] 650

# convert to number
as.numeric(e)
#> [1] 650

Tensor multiplication w/ contractions and subsetting

fj=aijkajik f_j=a_{ijk}a^{i\;k}_{\;j}

f <- a %_% .(i, j, k) * a %_% .(+i, j, +k)
f
#> <Labeled Array> [2] .(-j)
#> [1] 247 403

# retrieve array
f |> as_a(j)
#> [1] 247 403

Tensor addition

Tensor addition is taking care of correct index slot matching (by index labels), so the position of the index does not matter.

gijk=aijk+ajik g_{ijk} = a_{ijk} + a_{jik}

g <- a %_% .(i, j, k) + a %_% .(j, i, k)
g
#> <Labeled Array> [2x2x3] .(-i, -j, -k)

g |> as_a(i, j, k)
#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    2    5
#> [2,]    5    8
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]   10   13
#> [2,]   13   16
#> 
#> , , 3
#> 
#>      [,1] [,2]
#> [1,]   18   21
#> [2,]   21   24
Guidotti, Emanuele. 2022. “Calculus: High-Dimensional Numerical and Symbolic Calculus in R.” Journal of Statistical Software 104 (5). https://doi.org/10.18637/jss.v104.i05.