a field-theory motivated approach to computer algebra

## epsilon_to_delta

Replace a product of two epsilon tensors with a generalised delta
Replace a product of two epsilon tensors with a generalised delta according to the expression $$\epsilon^{r_1\cdots r_{d}} \epsilon_{s_1\cdots s_{d}} = \frac{1}{\sqrt{|g|}}\varepsilon^{r_1 \cdots r_{d}} \sqrt{|g|}\varepsilon_{s_1\cdots s_{d}} = {\rm sign}(g)\, d!\, \delta^{r_1 \cdots r_{d}}_{s_1\cdots s_{d}}\, ,$$ where ${\rm sign}(g)$ denotes the signature of the metric $g$ used to raise/lower the indices (see EpsilonTensor for conventions on the epsilon tensor). When the indices are not ocurring up/down as in this expression, and the index position is not free, metric objects will be generated instead. Here is an example:
{a,b,c,d}::Indices. {a,b,c,d}::Integer(1..3). \delta{#}::KroneckerDelta. \epsilon_{a b c}::EpsilonTensor(delta=\delta). ex:=\epsilon_{a b c} \epsilon_{a b d};
$$\displaystyle{}\epsilon_{a b c} \epsilon_{a b d}$$
epsilon_to_delta(_);
$$\displaystyle{}2\delta_{c d}$$
Remember that if the result is a generalised delta, you can expand it in terms of normal deltas using expand_delta,
ex:=\epsilon_{a b c} \epsilon_{a d e}; epsilon_to_delta(_); expand_delta(_);
$$\displaystyle{}\epsilon_{a b c} \epsilon_{a d e}$$
$$\displaystyle{}2\delta_{b d c e}$$
$$\displaystyle{}\delta_{b d} \delta_{c e}-\delta_{c d} \delta_{b e}$$
In order for this algorithm to work, you need to make sure that the epsilon symbols in your expression are declared as EpsilonTensor and that these declarations involve a specification of the delta and/or metric symbols.
As you can see from this example, contracted indices inside the generalised delta are automatically eliminated, as the algorithm reduce_gendelta is called automatically; if you do not want this use the optional argument reduce=False.
ex:=\epsilon_{a b c} \epsilon_{a b d}; epsilon_to_delta(_, reduce=False);
$$\displaystyle{}\epsilon_{a b c} \epsilon_{a b d}$$
$$\displaystyle{}6\delta_{a a b b c d}$$
Note that the results typically depend on the signature of the space-time, which can be introduced through the optional metric argument of the EpsilonTensor property. Compare the two examples below:
{a,b,c,d}::Indices. {a,b,c,d}::Integer(1..3). \delta{#}::KroneckerDelta. \epsilon_{a b c}::EpsilonTensor(delta=\delta, metric=g_{a b}). g_{a b}::Metric(signature=-1). ex:=\epsilon_{a b c} \epsilon_{a b c};
$$\displaystyle{}\epsilon_{a b c} \epsilon_{a b c}$$
epsilon_to_delta(_);
$$\displaystyle{}-6$$
g_{a b}::Metric(signature=+1). ex:=\epsilon_{a b c} \epsilon_{a b c}; epsilon_to_delta(_);
$$\displaystyle{}\epsilon_{a b c} \epsilon_{a b c}$$
$$\displaystyle{}6$$
Note that you need to specify the full symbol for the metric, including the indices, whereas the Kronecker delta argument only requires the name without the indices (because a contraction can generate generalised Kronecker delta symbols with any number of indices).