| Title: | Ricci Calculus |
|---|---|
| Description: | Provides a compact 'R' interface for performing tensor calculations. This is achieved by allowing (upper and lower) index labeling of arrays and making use of Ricci calculus conventions to implicitly trigger contractions and diagonal subsetting. Explicit tensor operations, such as addition, subtraction and multiplication of tensors via the standard operators, raising and lowering indices, taking symmetric or antisymmetric tensor parts, as well as the Kronecker product are available. Common tensors like the Kronecker delta, Levi Civita epsilon, certain metric tensors, the Christoffel symbols, the Riemann as well as Ricci tensors are provided. The covariant derivative of tensor fields with respect to any metric tensor can be evaluated. An effort was made to provide the user with useful error messages. |
| Authors: | Lukas Schneiderbauer [aut, cre, cph] (ORCID: <https://orcid.org/0000-0002-0975-6803>) |
| Maintainer: | Lukas Schneiderbauer <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.1.1.9000 |
| Built: | 2026-06-02 07:29:54 UTC |
| Source: | https://github.com/lschneiderbauer/ricci |
This function creates a index slot label specification. Any R symbol
can serve as a label. .() is typically used in conjunction with
%_%.
.(...).(...)
... |
Index labels separated by commas optionally prefixed by "+" and "-" to indicate the index position (upper and lower respectively). If no prefix is provided, a lower index ("-") is assumed. This argument uses non-standard evaluation: any R symbol that is not a reserved keyword can be used. |
A named list of two character vectors representing the index label names and index position.
# three lower index slots .(i, j, k) # one lower and upper index .(i, +j)# three lower index slots .(i, j, k) # one lower and upper index .(i, +j)
Converts a tensor() to an array() by stripping the index labels.
An index label order
needs to be provided so that the array's dim() order is well defined.
as_a(x, ...)as_a(x, ...)
x |
A labeled array ("tensor" object) created by |
... |
Index labels separated by commas optionally prefixed by "+" and "-"
to indicate the index position (upper and lower respectively).
If no prefix is provided, a lower index ("-") is assumed.
This argument uses non-standard evaluation: any R symbol
that is not a reserved keyword can be used.
The specification needs to match all the labels occurring in |
A usual array() without attached labels. The dimension order is
determined by ....
The same functionality is implemented in as.array.tensor()
but with standard evaluation.
array(1:8, dim = c(2, 2, 2)) %_% .(i, +i, k) |> as_a(k)array(1:8, dim = c(2, 2, 2)) %_% .(i, +i, k) |> as_a(k)
Converts a tensor() to an array() by stripping the index labels.
An index label order
needs to be provided so that the array's dim() order is well defined.
## S3 method for class 'tensor' as.array( x, index_order = NULL, ..., arg = "index_order", call = rlang::caller_env() )## S3 method for class 'tensor' as.array( x, index_order = NULL, ..., arg = "index_order", call = rlang::caller_env() )
x |
A labeled array ("tensor" object) created by |
index_order |
An index specification created with |
... |
Not used. |
arg, call
|
Used for error handling. Can be ignored by the user. |
The tensor components as usual array() object without any index labels
attached.
array(1:8, dim = c(2, 2, 2)) %_% .(i, +i, k) |> as.array(.(k))array(1:8, dim = c(2, 2, 2)) %_% .(i, +i, k) |> as.array(.(k))
Takes the antisymmetric tensor part of a tensor x with respect to the
specified indices ....
An error is thrown if the specified indices do not exist.
asym(x, ...)asym(x, ...)
x |
|
... |
Any number of index expressions. The indices need to occur
in |
A modified tensor object.
Wikipedia: Ricci calculus - Symmetric and antisymmetric parts
Other tensor operations:
Ops.tensor(),
kron(),
l(),
r(),
subst(),
sym()
a <- array(1:4, dim = c(2, 2)) a %_% .(i, j) |> asym(i, j)a <- array(1:4, dim = c(2, 2)) a %_% .(i, j) |> asym(i, j)
Evaluates a symbolic array at a particular point in parameter space. Partial evaluation is not allowed, all variables/symbols need to be accounted for. The result is a numeric array.
at(x, vars) ## S3 method for class 'array' at(x, vars) ## S3 method for class 'tensor' at(x, vars)at(x, vars) ## S3 method for class 'array' at(x, vars) ## S3 method for class 'tensor' at(x, vars)
x |
|
vars |
A named vector with parameter-value assignments. Each named entry represents a substitution of a symbol with the given value. |
A numeric array() or tensor().
g_sph(3) |> at(c(ph1 = 0, ph2 = 0))g_sph(3) |> at(c(ph1 = 0, ph2 = 0))
Provides the Christoffel symbols of the first kind with
respect to the Levi Civita connection for a given metric tensor.
christoffel(g)christoffel(g)
g |
A covariant metric tensor, a "metric_field" object. See |
The Christoffel symbols are a rank 3 array of numbers.
Returns the Christoffel symbols of the first kind
as rank 3 array().
Wikipedia: Christoffel symbols
Other geometric tensors:
ricci(),
ricci_sc(),
riemann()
christoffel(g_eucl_sph(3))christoffel(g_eucl_sph(3))
Calculates the (symbolic) covariant derivative
with respect
to the Levi Civita connection of any (symbolic) tensor field.
The result is a new tensor of one rank higher than
the original tensor rank.
covd(x, i, g, act_on = NULL)covd(x, i, g, act_on = NULL)
x |
A labeled tensor object, created by |
i |
An index slot label specification created with |
g |
A covariant metric tensor, a "metric_field" object. See |
act_on |
An optional index slot label specification created with |
Note that symbolic derivatives are not always completely trustworthy.
They usually ignore subtle issues like undefined expressions at certain
points. The example from below is telling:
The symbolic derivative
evaluates to zero identically, although strictly speaking the derivative
is not defined at .
The covariant derivative: a new labeled array with one or more additional
indices (depending on i).
Wikipedia: Covariant Derivative
options(ricci.auto_simplify = TRUE) # gradient of "sin(sqrt(x1^2+x2^2+x3^2))" in 3-dimensional euclidean space covd("sin(x1)", .(k), g = g_eucl_cart(3)) # laplace operator covd("sin(x1)", .(-k, +k), g = g_eucl_cart(3)) covd("1/r", .(-k, +k), g = g_eucl_sph(3))options(ricci.auto_simplify = TRUE) # gradient of "sin(sqrt(x1^2+x2^2+x3^2))" in 3-dimensional euclidean space covd("sin(x1)", .(k), g = g_eucl_cart(3)) # laplace operator covd("sin(x1)", .(-k, +k), g = g_eucl_cart(3)) covd("1/r", .(-k, +k), g = g_eucl_sph(3))
Provides a labeled generalized Kronecker delta. In the special case of two labels this represents simply the identity matrix. The Kronecker delta always has an even number of indices. Note that the first half of the tensor labels need to be lowered, while the second half needs upper indices. Otherwise an error is thrown.
d(n)d(n)
n |
The dimension. |
A function that expects index labels (see .()) and returns a
labeled tensor. The underlying data will differs depending on
the number of labels provided.
Underlying implementation: calculus::delta()
Wikipedia: Generalized Kronecker delta
Other tensor symbols:
e()
d(3)(i, +j) d(3)(i, j, +k, +l)d(3)(i, +j) d(3)(i, j, +k, +l)
Provides a labeled Levi-Civita epsilon (pseudo) tensor. The indices are required to be lowered. Otherwise an error is thrown.
e(...)e(...)
... |
Index labels separated by commas optionally prefixed by "+" and "-" to indicate the index position (upper and lower respectively). If no prefix is provided, a lower index ("-") is assumed. This argument uses non-standard evaluation: any R symbol that is not a reserved keyword can be used. |
A labeled tensor object. The underlying data will differs depending on the number of labels provided.
Underlying implementation: calculus::epsilon()
Wikipedia: Levi-Civita symbol
Other tensor symbols:
d()
e(i, j) e(i, j, k)e(i, j) e(i, j, k)
Adds an expectation function that can be used with the testthat package. Compares two tensors and determines whether they are equal or not.
expect_tensor_equal(object, expected, ...)expect_tensor_equal(object, expected, ...)
object, expected
|
Computation and value to compare it to. Both arguments supports limited unquoting to make it easier to generate readable failures within a function or for loop. See quasi_label for more details. |
... |
Arguments passed on to
|
The actual value invisibly.
Provides the Euclidean metric tensor of .
g_eucl_cart() returns a numeric (constant) tensor in Cartesian coordinates,
while g_eucl_sph() returns a symbolic tensor field in generalized spherical
coordinates .
g_eucl_cart(n, coords = paste0("x", 1:n)) g_eucl_sph(n, coords = c("r", paste0("ph", 1:(n - 1))))g_eucl_cart(n, coords = paste0("x", 1:n)) g_eucl_sph(n, coords = c("r", paste0("ph", 1:(n - 1))))
n |
The dimension of the metric tensor. |
coords |
A character vector of coordinate names. The length needs to match the tensor dimensions. |
As usual, spherical coordinates are degenerate at and , so be
careful around those points.
The covariant metric tensor as array imputed with coordinate names.
Wikipedia: Euclidean metric tensor
Other metric tensors:
g_mink_cart(),
g_sph(),
g_ss(),
metric_field()
g_eucl_cart(3) g_eucl_cart(3) %_% .(+i, +j) g_eucl_sph(3) g_eucl_sph(3) %_% .(+i, +j)g_eucl_cart(3) g_eucl_cart(3) %_% .(+i, +j) g_eucl_sph(3) g_eucl_sph(3) %_% .(+i, +j)
g_mink_cart() provides the covariant metric tensor in n dimensions in
Cartesian coordinates with signature .
g_mink_sph() provides the same tensor where the spatial part uses spherical
coordinates.
g_mink_cart(n, coords = paste0("x", 1:n - 1)) g_mink_sph(n, coords = c("t", "r", paste0("ph", 1:(n - 2))))g_mink_cart(n, coords = paste0("x", 1:n - 1)) g_mink_sph(n, coords = c("t", "r", paste0("ph", 1:(n - 2))))
n |
The dimension of the metric tensor. |
coords |
A character vector of coordinate names. The length needs to match the tensor dimensions. |
The covariant metric tensor as array imputed with coordinate names.
Wikipedia Minkowski metric tensor
Other metric tensors:
g_eucl_cart(),
g_sph(),
g_ss(),
metric_field()
g_mink_cart(4) g_mink_cart(4) %_% .(+i, +j) g_mink_sph(4) g_mink_sph(4) %_% .(+i, +j)g_mink_cart(4) g_mink_cart(4) %_% .(+i, +j) g_mink_sph(4) g_mink_sph(4) %_% .(+i, +j)
Provides the metric tensor of the sphere with radius 1.
g_sph() returns a symbolic tensor field in generalized spherical
coordinates .
g_sph(n, coords = paste0("ph", 1:n))g_sph(n, coords = paste0("ph", 1:n))
n |
The dimension of the metric tensor. |
coords |
A character vector of coordinate names. The length needs to match the tensor dimensions. |
As usual, spherical coordinates are degenerate at , so be
careful around those points.
The covariant metric tensor as array imputed with coordinate names.
Wikipedia: Sphere
Other metric tensors:
g_eucl_cart(),
g_mink_cart(),
g_ss(),
metric_field()
g_sph(3) g_sph(3) %_% .(+i, +j)g_sph(3) g_sph(3) %_% .(+i, +j)
Provides the metric tensor of the Einstein equation's Schwarzschild solution
in Schwarzschild coordinates where the Schwarzschild radius is set to 1.
g_ss(n, coords = c("t", "r", paste0("ph", 1:(n - 2))))g_ss(n, coords = c("t", "r", paste0("ph", 1:(n - 2))))
n |
The dimension of the metric tensor. |
coords |
A character vector of coordinate names. The length needs to match the tensor dimensions. |
Note that Schwarzschild coordinates become singular at the Schwarzschild
radius (event horizon) and at .
The covariant metric tensor as array imputed with coordinate names.
Wikipedia: Schwarzschild metric
Other metric tensors:
g_eucl_cart(),
g_mink_cart(),
g_sph(),
metric_field()
g_ss(4) g_ss(4) %_% .(+i, +j)g_ss(4) g_ss(4) %_% .(+i, +j)
A Kronecker product is simply a tensor product whose underlying vector space basis is relabeled. In the present context this is realized by combining multiple index labels into one. The associated dimension to the new label is then simply the product of the dimensions associated to the old index labels respectively.
kron(x, ...)kron(x, ...)
x |
|
... |
Any number of expressions (separated by a comma) of the form
|
A modified tensor object.
Wikipedia: Kronecker Product
Other tensor operations:
Ops.tensor(),
asym(),
l(),
r(),
subst(),
sym()
a <- array(1:8, dim = c(2, 2, 2)) a %_% .(i, j, k) |> kron(.(i, j) -> l)a <- array(1:8, dim = c(2, 2, 2)) a %_% .(i, j, k) |> kron(.(i, j) -> l)
l() lowers specified tensor indices using a covariant metric tensor
provided in g.
Note that the order of indices is not preserved due to performance reasons.
An error is thrown if the specified indices do not exist or are not in the
correct position.
l(x, ..., g = NULL)l(x, ..., g = NULL)
x |
|
... |
Any number of index expressions. The indices need to occur
in |
g |
A covariant metric tensor, a "metric_field" object. See |
A modified tensor object.
Other tensor operations:
Ops.tensor(),
asym(),
kron(),
r(),
subst(),
sym()
Metric tensors are an essential ingredient of (Pseudo-) Riemannian
manifolds and define distance relations between points.
They are used to define geometric tensors such as e.g. the Ricci curvature
ricci(), and a metric connection, i.e. a covariant derivative.
They are also essential for raising and lowering indices of tensor fields
correctly when using non-flat coordinates.
metric_field(metric, metric_inv, coords)metric_field(metric, metric_inv, coords)
metric |
A |
metric_inv |
A |
coords |
A character vector of |
An object of class c("metric_field", "array") that represents the
components of a metric tensor on a (Pseudo-) Riemannian manifold in a
certain coordinate system specified by coords.
Wikipedia: Metric tensor
Other metric tensors:
g_eucl_cart(),
g_mink_cart(),
g_sph(),
g_ss()
Once a labeled array (tensor) has been defined, tensor arithmetic operations
can be carried out with the usual +, -, *, /, and == symbols.
## S3 method for class 'tensor' Ops(e1, e2)## S3 method for class 'tensor' Ops(e1, e2)
e1, e2
|
Labeled arrays created with %_%. |
A resulting labeled array in case of +, -, *, /.
TRUE or FALSE in case of ==.
Addition and subtraction requires the two tensors to have an equal index structure, i.e. the index names their position and the dimensions associated to the index names have to agree. The index order does not matter, the operation will match dimensions by index name.
Tensor multiplication takes into account implicit Ricci calculus rules depending on index placement.
Equal-named and opposite-positioned dimensions are contracted.
Equal-named and equal-positioned dimensions are subsetted.
The result is an outer product for distinct index names.
Division performs element-wise division. If the second argument is a scalar, each element is simply divided by the scalar. Similar to addition and subtraction, division requires the two tensors to have an equal index structure, i.e. the index names their position and the dimensions associated to the index names have to agree.
A tensor is equal to a tensor if
and only if the index structure agrees and all components are equal.
Other tensor operations:
asym(),
kron(),
l(),
r(),
subst(),
sym()
a <- array(1:4, c(2, 2)) b <- array(3 + 1:4, c(2, 2)) # addition a %_% .(i, j) + b %_% .(j, i) # multiplication a %_% .(i, j) * b %_% .(+i, k) # division a %_% .(i, j) / 10 # equality check a %_% .(i, j) == a %_% .(i, j) a %_% .(i, j) == a %_% .(j, i) a %_% .(i, j) == b %_% .(i, j) # this will err because index structure does not agree try(a %_% .(i, j) == a %_% .(k, j))a <- array(1:4, c(2, 2)) b <- array(3 + 1:4, c(2, 2)) # addition a %_% .(i, j) + b %_% .(j, i) # multiplication a %_% .(i, j) * b %_% .(+i, k) # division a %_% .(i, j) / 10 # equality check a %_% .(i, j) == a %_% .(i, j) a %_% .(i, j) == a %_% .(j, i) a %_% .(i, j) == b %_% .(i, j) # this will err because index structure does not agree try(a %_% .(i, j) == a %_% .(k, j))
r() raises specified tensor indices using a covariant metric tensor
provided in g.
Note that the order of indices is not preserved due to performance reasons.
An error is thrown if the specified indices do not exist or are not in the
correct position.
r(x, ..., g = NULL)r(x, ..., g = NULL)
x |
|
... |
Any number of index expressions. The indices need to occur
in |
g |
A covariant metric tensor, a "metric_field" object. See |
A modified tensor object.
Other tensor operations:
Ops.tensor(),
asym(),
kron(),
l(),
subst(),
sym()
Provides the covariant Ricci curvature tensor .
ricci(g)ricci(g)
g |
A covariant metric tensor, a "metric_field" object. See |
Returns the covariant Ricci curvature tensor
as rank 2 array().
Wikipedia: Ricci curvature tensor
Other geometric tensors:
christoffel(),
ricci_sc(),
riemann()
ricci(g_eucl_sph(3))ricci(g_eucl_sph(3))
Provides the Ricci scalar .
ricci_sc(g)ricci_sc(g)
g |
A covariant metric tensor, a "metric_field" object. See |
Returns the Ricci scalar as single number/expression.
Wikipedia: Ricci scalar
Other geometric tensors:
christoffel(),
ricci(),
riemann()
ricci_sc(g_eucl_sph(3))ricci_sc(g_eucl_sph(3))
Provides the covariant Riemann curvature tensor .
riemann(g)riemann(g)
g |
A covariant metric tensor, a "metric_field" object. See |
Returns the covariant Riemann curvature tensor
as rank 4 array().
Wikipedia: Riemann curvature tensor
Other geometric tensors:
christoffel(),
ricci(),
ricci_sc()
riemann(g_eucl_sph(3))riemann(g_eucl_sph(3))
Attempts to simplify expressions in an array or tensor.
Non-array objects are coerced to arrays with as.array().
simplify(x)simplify(x)
x |
A character |
Instead of using an explicit call to simplify() you also have the option
to enable automatic simplification via option(ricci.auto_simplify = TRUE).
Note however that this comes at a significant performance cost.
This operation requires the Ryacas package.
A character array() or tensor() of the same form, potentially
with simplified expressions.
simplify("x + y - x")simplify("x + y - x")
Substitutes tensor labels with other labels. This is simply a renaming procedure. The operation might trigger implicit diagonal subsetting. An error is thrown if the specified indices do not exist.
subst(x, ...)subst(x, ...)
x |
|
... |
Any number of expressions (separated by a comma) of the form
|
A modified tensor object.
Other tensor operations:
Ops.tensor(),
asym(),
kron(),
l(),
r(),
sym()
Takes the symmetric tensor part of a tensor x with respect to the
specified indices ....
An error is thrown if the specified indices do not exist.
sym(x, ...)sym(x, ...)
x |
|
... |
Any number of index expressions. The indices need to occur
in |
A modified tensor object.
Wikipedia: Ricci calculus - Symmetric and antisymmetric parts
Other tensor operations:
Ops.tensor(),
asym(),
kron(),
l(),
r(),
subst()
a <- array(1:4, dim = c(2, 2)) a %_% .(i, j) |> sym(i, j)a <- array(1:4, dim = c(2, 2)) a %_% .(i, j) |> sym(i, j)
Creates a labeled array (tensor) from an array. %_% and tensor() serve
the same purpose, but typically usage of %_% is preferred due to
brevity.
tensor() is exported to provide a standard-evaluation interface
as well which might be useful under some circumstances.
tensor(a, index_names, index_positions, call = NULL) a %_% itensor(a, index_names, index_positions, call = NULL) a %_% i
a |
An array or any object that can be coerced to an array via
|
index_names |
A character vector of index names / labels. |
index_positions |
A character vector of index positions with two allowed
values "+" and "-", for "upper" and "lower" position respectively.
The length of |
call |
For internal use only. |
i |
An index slot label specification created with |
A labeled tensor object of class "tensor", an array()
with attached dimension labels. Note that the index structure
of the resulting tensor does not necessarily have to match i. In case
implicit calculations are already triggered (e.g. contractions)
the index structure reflects the resulting tensor.
a <- array(1:4, dim = c(2, 2)) a %_% .(i, j)a <- array(1:4, dim = c(2, 2)) a %_% .(i, j)