/*
 * Redberry: symbolic tensor computations.
 *
 * Copyright (c) 2010-2015:
 *   Stanislav Poslavsky   
 *   Bolotin Dmitriy       
 *
 * 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 .
 */
package cc.redberry.core.transformations.substitutions;

import cc.redberry.core.number.Complex;
import cc.redberry.core.tensor.*;
import cc.redberry.core.transformations.Transformation;
import cc.redberry.core.utils.TensorUtils;

import static cc.redberry.core.utils.TensorUtils.shareSimpleTensors;

/**
 * Substitution.
 *
 * @author Dmitry Bolotin
 * @author Stanislav Poslavsky
 * @since 1.0
 */
public final class SubstitutionTransformation implements Transformation {
    private final PrimitiveSubstitution[] primitiveSubstitutions;
    private final boolean applyIfModified;

    private SubstitutionTransformation(PrimitiveSubstitution[] primitiveSubstitutions, boolean applyIfModified) {
        this.primitiveSubstitutions = primitiveSubstitutions;
        this.applyIfModified = applyIfModified;
    }

    /**
     * Creates a set of substitutions.
     *
     * @param expressions     an array of the expressions
     * @param applyIfModified if false, then if some substitution was
     *                        applied to some tensor in tree, then others
     *                        will be skipped in current subtree.
     */
    public SubstitutionTransformation(Expression[] expressions, boolean applyIfModified) {
        this.applyIfModified = applyIfModified;
        primitiveSubstitutions = new PrimitiveSubstitution[expressions.length];
        for (int i = expressions.length - 1; i >= 0; --i)
            primitiveSubstitutions[i] = createPrimitiveSubstitution(expressions[i].get(0), expressions[i].get(1));
    }

    /**
     * Creates a substitution.
     *
     * @param expression expression
     */
    public SubstitutionTransformation(Expression expression) {
        this(expression.get(0), expression.get(1));
    }

    /**
     * Creates a set of substitutions.
     *
     * @param expressions an array of the expressions
     */
    public SubstitutionTransformation(Expression... expressions) {
        this(expressions, expressions.length == 1 ?
                !shareSimpleTensors(expressions[0].get(0), expressions[0].get(1)) : false);
    }

    /**
     * Creates a substitution.
     *
     * @param from            from tensor
     * @param to              to tensor
     * @param applyIfModified if false, then if some substitution was
     *                        applied to some tensor in tree, then others
     *                        will be skipped in current subtree.
     * @throws IllegalArgumentException if {@code from} free indices are not equal
     *                                  to {@code to} free indices
     */
    public SubstitutionTransformation(Tensor from, Tensor to, boolean applyIfModified) {
        checkConsistence(from, to);
        primitiveSubstitutions = new PrimitiveSubstitution[1];
        primitiveSubstitutions[0] = createPrimitiveSubstitution(from, to);
        this.applyIfModified = applyIfModified;
    }


    /**
     * Creates a set of substitutions.
     *
     * @param from an array of from tensors
     * @param to   an array of to tensors
     * @throws IllegalArgumentException if {@code from.length != to.length}
     * @throws IllegalArgumentException if for there is a pair for which
     *                                  {@code from[i]} free indices equal
     *                                  to {@code to[i]} free indices
     */
    public SubstitutionTransformation(Tensor[] from, Tensor[] to) {
        this(from, to, from.length == 1 ? !shareSimpleTensors(from[0], to[0]) : false);
    }


    /**
     * Creates a substitution
     *
     * @param from from tensor
     * @param to   to tensor
     * @throws IllegalArgumentException if {@code from} free indices are not equal
     *                                  to {@code to} free indices
     */
    public SubstitutionTransformation(Tensor from, Tensor to) {
        this(from, to, !shareSimpleTensors(from, to));
    }

    /**
     * Creates a set of substitutions
     *
     * @param from            an array of from tensors
     * @param to              an array of to tensors
     * @param applyIfModified if false, then if some substitution was
     *                        applied to some tensor in tree, then others
     *                        will be skipped in current subtree.
     * @throws IllegalArgumentException if {@code from.length != to.length}
     * @throws IllegalArgumentException if for there is a pair for which
     *                                  {@code from[i]} free indices are
     *                                  not equal to {@code to[i]} free indices
     */
    public SubstitutionTransformation(Tensor[] from, Tensor[] to, boolean applyIfModified) {
        checkConsistence(from, to);
        primitiveSubstitutions = new PrimitiveSubstitution[from.length];
        for (int i = 0; i < from.length; ++i)
            primitiveSubstitutions[i] = createPrimitiveSubstitution(from[i], to[i]);
        this.applyIfModified = applyIfModified;
    }

    /**
     * Creates a new substitution, which treats all of
     * the inner substitutions as simple substitutions.
     *
     * @return new substitution, which treats all of
     *         the inner substitutions as simple substitutions.
     */
    public SubstitutionTransformation asSimpleSubstitution() {
        SubstitutionTransformation ss = new SubstitutionTransformation(primitiveSubstitutions.clone(), applyIfModified);
        for (int i = primitiveSubstitutions.length - 1; i >= 0; --i)
            ss.primitiveSubstitutions[i] =
                    new PrimitiveSimpleTensorSubstitution(
                            ss.primitiveSubstitutions[i].from,
                            ss.primitiveSubstitutions[i].to);
        return ss;
    }

    private static void checkConsistence(Tensor[] from, Tensor[] to) {
        if (from.length != to.length)
            throw new IllegalArgumentException("from array and to array have different length.");
        for (int i = from.length - 1; i >= 0; --i)
            checkConsistence(from[i], to[i]);
    }

    private static void checkConsistence(Tensor from, Tensor to) {
        if (!TensorUtils.isZeroOrIndeterminate(to))
            if (!from.getIndices().getFree().equalsRegardlessOrder(to.getIndices().getFree()))
                throw new IllegalArgumentException("Tensor from free indices not equal to tensor to free indices: " +
                        from.getIndices().getFree() + "  " + to.getIndices().getFree());
    }

    private static PrimitiveSubstitution createPrimitiveSubstitution(Tensor from, Tensor to) {
        if (from.getClass() == SimpleTensor.class)
            return new PrimitiveSimpleTensorSubstitution(from, to);
        if (from.getClass() == TensorField.class) {
            boolean argumentIsNotSimple = false;
            for (Tensor t : from) {
                if (!(t instanceof SimpleTensor)) {
                    argumentIsNotSimple = true;
                    break;
                }
            }
            if (argumentIsNotSimple)
                return new PrimitiveSimpleTensorSubstitution(from, to);
            return new PrimitiveTensorFieldSubstitution(from, to);
        }
        if (from.getClass() == Product.class) {
            if (from.size() == 2 && from.get(0) instanceof Complex) {
                to = Tensors.divide(to, from.get(0));
                from = from.get(1);
                return createPrimitiveSubstitution(from, to);
            }
            return new PrimitiveProductSubstitution(from, to);
        }
        if (from.getClass() == Sum.class)
            return new PrimitiveSumSubstitution(from, to);
        return new PrimitiveSimpleTensorSubstitution(from, to);
    }

    @Override
    public Tensor transform(Tensor t) {
        SubstitutionIterator iterator = new SubstitutionIterator(t);
        Tensor current;
        boolean supposeIndicesAreAdded;
        while ((current = iterator.next()) != null) {
            if (!applyIfModified && iterator.isCurrentModified())
                continue;
            Tensor old = current;
            supposeIndicesAreAdded = false;
            for (PrimitiveSubstitution primitiveSubstitution : primitiveSubstitutions) {
                current = primitiveSubstitution.newTo(old, iterator);
                if (current != old) {
                    supposeIndicesAreAdded |= primitiveSubstitution.possiblyAddsDummies;
                    if (!applyIfModified)
                        break;
                }
                old = current;
            }
            iterator.set(current, supposeIndicesAreAdded);
        }
        return iterator.result();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append('{');
        PrimitiveSubstitution tr;
        for (int i = 0; ; ++i) {
            tr = primitiveSubstitutions[i];
            builder.append(tr.from).append(" -> ").append(tr.to);
            if (i == primitiveSubstitutions.length - 1)
                break;
            builder.append(',');
        }
        return builder.append('}').toString();
    }
}

 

Ads help maintain this website.