/*
* Redberry: symbolic tensor computations.
*
* Copyright (c) 2010-2014:
* Stanislav Poslavsky <stvlpos@mail.ru>
* Bolotin Dmitriy <bolotin.dmitriy@gmail.com>
*
* This file is part of Redberry.
*
* Redberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Redberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Redberry. If not, see <http://www.gnu.org/licenses/>.
*/
package cc.redberry.physics.feyncalc;
import cc.redberry.core.context.CC;
import cc.redberry.core.context.NameAndStructureOfIndices;
import cc.redberry.core.groups.permutations.Permutation;
import cc.redberry.core.groups.permutations.PermutationGroup;
import cc.redberry.core.groups.permutations.PermutationOneLineInt;
import cc.redberry.core.groups.permutations.Permutations;
import cc.redberry.core.indexmapping.IndexMappings;
import cc.redberry.core.indexmapping.Mapping;
import cc.redberry.core.indexmapping.MappingsPort;
import cc.redberry.core.indices.IndexType;
import cc.redberry.core.indices.IndicesFactory;
import cc.redberry.core.indices.IndicesUtils;
import cc.redberry.core.indices.SimpleIndices;
import cc.redberry.core.number.Complex;
import cc.redberry.core.parser.ParseToken;
import cc.redberry.core.parser.ParseUtils;
import cc.redberry.core.parser.preprocessor.ChangeIndicesTypesAndTensorNames;
import cc.redberry.core.parser.preprocessor.TypesAndNamesTransformer;
import cc.redberry.core.tensor.*;
import cc.redberry.core.tensor.iterator.FromChildToParentIterator;
import cc.redberry.core.transformations.EliminateMetricsTransformation;
import cc.redberry.core.transformations.Transformation;
import cc.redberry.core.transformations.expand.ExpandTransformation;
import cc.redberry.core.utils.IntArray;
import cc.redberry.core.utils.IntArrayList;
import cc.redberry.core.utils.TensorUtils;
import gnu.trove.map.hash.TIntObjectHashMap;
import java.util.*;
import static cc.redberry.core.indices.IndicesUtils.*;
import static cc.redberry.core.tensor.StructureOfContractions.getToTensorIndex;
import static cc.redberry.core.tensor.Tensors.*;
/**
* Simplifies combinations of Levi-Civita tensors.
*
* @author Dmitry Bolotin
* @author Stanislav Poslavsky
*/
public class LeviCivitaSimplifyTransformation implements Transformation {
private static final String defaultLeviCivitaName = "eps";
private final int leviCivita;
private final boolean minkowskiSpace;
private final int numberOfIndices;
private final IndexType typeOfLeviCivitaIndices;
private final ChangeIndicesTypesAndTensorNames tokenTransformer;
/**
* First is Levi-Civita self-contraction and second is d^a_a = numberOfIndices
*/
private final Expression[] leviCivitaSimplifications;
/**
* Creates transformation, which simplifies combinations of Levi-Civita tensors in Euclidean or Minkowski space.
*
* @param leviCivita tensor, which will be considered as Levi-Civita tensor
* @param minkowskiSpace if {@code true}, then Levi-Civita tensor will be considered in Minkowski
* space (so e.g. e_abcd*e^abcd = -24), otherwise in Euclidean space
* (so e.g. e_abcd*e^abcd = +24)
*/
public LeviCivitaSimplifyTransformation(SimpleTensor leviCivita, boolean minkowskiSpace) {
checkLeviCivita(leviCivita);
this.leviCivita = leviCivita.getName();
this.minkowskiSpace = minkowskiSpace;
this.numberOfIndices = leviCivita.getIndices().size();
this.typeOfLeviCivitaIndices = IndicesUtils.getTypeEnum(leviCivita.getIndices().get(0));
final String leviCivitaName = CC.getNameManager().getNameDescriptor(leviCivita.getName()).getName(null);
this.tokenTransformer = new ChangeIndicesTypesAndTensorNames(
new TypesAndNamesTransformer() {
@Override
public IndexType newType(IndexType oldType, NameAndStructureOfIndices old) {
return typeOfLeviCivitaIndices;
}
@Override
public String newName(NameAndStructureOfIndices old) {
return old.getName().equals(defaultLeviCivitaName) ? leviCivitaName : old.getName();
}
}
);
leviCivitaSimplifications = getLeviCivitaSubstitutions();
}
@Override
public Tensor transform(Tensor t) {
FromChildToParentIterator iterator = new FromChildToParentIterator(t);
Tensor c;
while ((c = iterator.next()) != null) {
if (c instanceof SimpleTensor
&& ((SimpleTensor) c).getName() == leviCivita
&& c.getIndices().size() != c.getIndices().getFree().size()) {
iterator.set(Complex.ZERO);
}
if (c instanceof Product)
iterator.set(simplifyProduct(c));
}
return iterator.result();
}
private Tensor simplifyProduct(Tensor product) {
/*
* Simplifying symmetries
*/
ProductContent content = ((Product) product).getContent();
//positions of Levi-Civita tensors in product
IntArrayList epsPositions = new IntArrayList();
int i = 0, sizeOfComponent = content.size();
for (; i < sizeOfComponent; ++i)
if (isLeviCivita(content.get(i), leviCivita))
epsPositions.add(i);
//no Levi-Civita tensors found
if (epsPositions.isEmpty())
return product;
//calculating connected components with Levi-Civita tensors
StructureOfContractions fs = content.getStructureOfContractions();
sizeOfComponent = epsPositions.size();
//tensors, which are contracted with Levi-Civita ( ... eps_abcd * (...tensors...)^abpq ... )
Set<Tensor> epsComponent = new HashSet<>(numberOfIndices);
Tensor temp;
int toIndex, a, b;
for (i = 0; i < sizeOfComponent; ++i) {
//traversing contractions and building single component
for (long contraction : fs.contractions[epsPositions.get(i)]) {
toIndex = getToTensorIndex(contraction);
if (toIndex == -1)
continue;
temp = content.get(toIndex);
if (isLeviCivita(temp, leviCivita))
continue;
epsComponent.add(temp);
}
//all eps indices are free
if (epsComponent.isEmpty())
continue;
//product, which is contracted with Levi-Civita ( ... eps_abcd * (product)^ab ... )
temp = multiply(epsComponent.toArray(new Tensor[epsComponent.size()]));
epsComponent.clear();
//free indices of product, which is contracted with Levi-Civita
int[] indices = temp.getIndices().getFree().getAllIndices().copy();
//nothing to do
if (indices.length == 1)
continue;
//positions of indices of product, which are not contracted with eps
IntArrayList nonPermutableList = new IntArrayList();
//indices of Levi-Civita
int[] epsIndices = content.get(epsPositions.get(i)).getIndices().getFree().getAllIndices().copy();
boolean contract;
for (b = 0; b < indices.length; ++b) {
contract = false;
for (a = 0; a < epsIndices.length; ++a)
if (indices[b] == inverseIndexState(epsIndices[a]))
contract = true;
if (!contract)
nonPermutableList.add(b);
}
int[] nonPermutableArray = nonPermutableList.toArray();
//symmetries of eps indices, which are contracted with other product (also totally antisymmetric)
Map<Permutation, Boolean> symmetries = getEpsilonSymmetries(indices.length);
//symmetries of product, which is contracted with Levi-Civita
MappingsPort port = IndexMappings.createPort(temp, temp);
Mapping mapping;
Permutation sym;
//check for two symmetric indices of product contracted with two antisymmetric indices of eps
while ((mapping = port.take()) != null) {
//symmetry of product indices
sym = TensorUtils.getSymmetryFromMapping(indices, mapping);
//if symmetry mixes indices of product, which are not contracted with eps
if (!checkNonPermutingPositions(sym, nonPermutableArray))
continue;
//bingo!
if (sym.antisymmetry() != symmetries.get(sym))
return Complex.ZERO;
}
}
/*
* Simplifying Levi-Civita self-contractions
*/
if (epsPositions.size() == 1)
return product;
for (Expression exp : leviCivitaSimplifications)
product = exp.transform(product);
//todo expand only Levi-Civita sums
product = EliminateMetricsTransformation.eliminate(ExpandTransformation.expand(product, EliminateMetricsTransformation.ELIMINATE_METRICS));
product = leviCivitaSimplifications[1].transform(product);
return product;
}
private static boolean checkNonPermutingPositions(Permutation permutation, int[] nonPermutablePositions) {
for (int i : nonPermutablePositions)
if (permutation.newIndexOf(i) != i)
return false;
return true;
}
private static boolean isLeviCivita(Tensor tensor, int leviCivitaName) {
return tensor instanceof SimpleTensor && ((SimpleTensor) tensor).getName() == leviCivitaName;
}
private Expression getLeviCivitaSelfContraction() {
ParseToken substitutionToken = cachedLeviCivitaSelfContractions.get(numberOfIndices);
if (substitutionToken == null) {
//the l.h.s. of substitution: eps_{abc...}*eps^{pqr...}
//lower indices of eps ( eps_{...lower...} )
int[] lower = new int[numberOfIndices];
//upper indices of eps ( eps^{...lower...} )
int[] upper = new int[numberOfIndices];
for (int i = 0; i < numberOfIndices; ++i) {
lower[i] = i;
upper[i] = IndicesUtils.inverseIndexState(numberOfIndices + i);
}
//eps_{abc...}
SimpleTensor eps1 = simpleTensor(defaultLeviCivitaName, IndicesFactory.createSimple(null, lower));
//eps^{pqr...}
SimpleTensor eps2 = simpleTensor(defaultLeviCivitaName, IndicesFactory.createSimple(null, upper));
//eps_{abc..}*eps^{pqr...}
Tensor lhs = multiply(eps1, eps2);
//the r.h.s. of substitution: determinant of deltas
//Determinant of Kronecker deltas
Tensor[][] matrix = new Tensor[numberOfIndices][numberOfIndices];
int j;
for (int i = 0; i < numberOfIndices; ++i)
for (j = 0; j < numberOfIndices; ++j)
matrix[i][j] = createKronecker(lower[i], upper[j]);
Tensor rhs = TensorUtils.det(matrix);
// eps_{abc..}*eps^{pqr...} = det
Expression substitution = expression(lhs, rhs);
substitutionToken = ParseUtils.tensor2AST(substitution);
cachedLeviCivitaSelfContractions.put(numberOfIndices, substitutionToken);
}
Expression substitution = (Expression) tokenTransformer.transform(substitutionToken).toTensor();
if (minkowskiSpace & numberOfIndices % 2 == 0)
substitution = expression(substitution.get(0), negate(substitution.get(1)));
return substitution;
}
private Expression[] getLeviCivitaSubstitutions() {
Expression[] substitutions = new Expression[2];
//Levi-Civita self-contraction
substitutions[0] = getLeviCivitaSelfContraction();
//d^a_a = numberOfIndices
substitutions[1] = expression(createKronecker(
setType(typeOfLeviCivitaIndices, 0),
setType(typeOfLeviCivitaIndices, 0x80000000)),
new Complex(numberOfIndices));
return substitutions;
}
private static Map<Permutation, Boolean> getEpsilonSymmetries(int indicesSize) {
Map<Permutation, Boolean> symmetries = cachedLeviCivitaSymmetries.get(indicesSize);
if (symmetries != null)
return symmetries;
symmetries = new HashMap<>();
PermutationGroup lc = PermutationGroup.antisymmetricGroup(indicesSize);
for (Permutation symmetry : lc)
symmetries.put(symmetry.toSymmetry(), symmetry.antisymmetry());
cachedLeviCivitaSymmetries.put(indicesSize, symmetries);
return symmetries;
}
private static TIntObjectHashMap<ParseToken> cachedLeviCivitaSelfContractions = new TIntObjectHashMap<>();
private static TIntObjectHashMap<Map<Permutation, Boolean>> cachedLeviCivitaSymmetries = new TIntObjectHashMap<>();
private static void checkLeviCivita(SimpleTensor LeviCivita) {
SimpleIndices indices = LeviCivita.getIndices();
if (indices.size() <= 1)
throw new IllegalArgumentException("Levi-Civita cannot be a scalar.");
byte type = getType(indices.get(0));
for (int i = 1; i < indices.size(); ++i)
if (type != getType(indices.get(i)))
throw new IllegalArgumentException("Levi-Civita have indices with different types.");
}
}