All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ojalgo.matrix.decomposition.LDLDecomposition Maven / Gradle / Ivy

Go to download

oj! Algorithms - ojAlgo - is Open Source Java code that has to do with mathematics, linear algebra and optimisation.

There is a newer version: 55.0.1
Show newest version
/*
 * Copyright 1997-2024 Optimatika
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.ojalgo.matrix.decomposition;

import static org.ojalgo.function.constant.PrimitiveMath.MACHINE_SMALLEST;
import static org.ojalgo.function.constant.PrimitiveMath.ZERO;

import org.ojalgo.RecoverableCondition;
import org.ojalgo.array.BasicArray;
import org.ojalgo.function.BinaryFunction;
import org.ojalgo.function.aggregator.Aggregator;
import org.ojalgo.function.aggregator.AggregatorFunction;
import org.ojalgo.function.constant.PrimitiveMath;
import org.ojalgo.matrix.store.GenericStore;
import org.ojalgo.matrix.store.MatrixStore;
import org.ojalgo.matrix.store.PhysicalStore;
import org.ojalgo.matrix.store.R064Store;
import org.ojalgo.scalar.ComplexNumber;
import org.ojalgo.scalar.Quadruple;
import org.ojalgo.scalar.Quaternion;
import org.ojalgo.scalar.RationalNumber;
import org.ojalgo.structure.Access2D;
import org.ojalgo.structure.Access2D.Collectable;
import org.ojalgo.structure.Structure2D;
import org.ojalgo.type.NumberDefinition;
import org.ojalgo.type.context.NumberContext;

abstract class LDLDecomposition> extends InPlaceDecomposition implements LDL {

    static final class C128 extends LDLDecomposition {

        C128() {
            super(GenericStore.C128);
        }

    }

    static final class H256 extends LDLDecomposition {

        H256() {
            super(GenericStore.H256);
        }

    }

    static final class Q128 extends LDLDecomposition {

        Q128() {
            super(GenericStore.Q128);
        }

    }

    static final class R064 extends LDLDecomposition {

        R064() {
            super(R064Store.FACTORY);
        }

    }

    static final class R128 extends LDLDecomposition {

        R128() {
            super(GenericStore.R128);
        }

    }

    private final Pivot myPivot = new Pivot();
    private double myThreshold = Double.NaN;

    protected LDLDecomposition(final PhysicalStore.Factory> factory) {
        super(factory);
    }

    public final void btran(final PhysicalStore arg) {

        int[] order = myPivot.getOrder();

        if (myPivot.isModified()) {
            arg.rows(order).copy().supplyTo(arg);
        }

        DecompositionStore body = this.getInPlace();

        arg.substituteForwards(body, true, false, false);

        BinaryFunction divide = this.function().divide();
        for (int i = 0; i < order.length; i++) {
            arg.modifyRow(i, divide.by(body.get(i, i)));
        }

        arg.substituteBackwards(body, true, true, false);

        if (myPivot.isModified()) {
            arg.rows(myPivot.reverseOrder()).copy().supplyTo(arg);
        }
    }

    public N calculateDeterminant(final Access2D matrix) {
        this.decompose(this.wrap(matrix));
        return this.getDeterminant();
    }

    public int countSignificant(final double threshold) {

        DecompositionStore internal = this.getInPlace();

        int significant = 0;
        for (int ij = 0, limit = this.getMinDim(); ij < limit; ij++) {
            if (Math.abs(internal.doubleValue(ij, ij)) > threshold) {
                significant++;
            }
        }

        return significant;
    }

    public boolean decompose(final Access2D.Collectable> matrix) {
        return this.doDecompose(matrix, true);
    }

    public boolean decomposeWithoutPivoting(final Collectable> matrix) {
        return this.doDecompose(matrix, false);
    }

    public MatrixStore getD() {
        return this.getInPlace().diagonal();
    }

    public N getDeterminant() {

        AggregatorFunction aggregator = this.aggregator().product();

        this.getInPlace().visitDiagonal(aggregator);

        if (myPivot.signum() == -1) {
            return aggregator.toScalar().negate().get();
        }
        return aggregator.get();
    }

    @Override
    public MatrixStore getInverse(final PhysicalStore preallocated) {

        int[] order = myPivot.getOrder();
        boolean modified = myPivot.isModified();

        if (modified) {
            preallocated.fillAll(this.scalar().zero().get());
            for (int i = 0; i < order.length; i++) {
                preallocated.set(i, order[i], PrimitiveMath.ONE);
            }
        }

        DecompositionStore body = this.getInPlace();

        preallocated.substituteForwards(body, true, false, !modified);

        BinaryFunction divide = this.function().divide();
        for (int i = 0; i < order.length; i++) {
            preallocated.modifyRow(i, 0, divide.by(body.doubleValue(i, i)));
        }

        preallocated.substituteBackwards(body, true, true, false);

        return preallocated.rows(myPivot.reverseOrder());
    }

    public MatrixStore getL() {
        DecompositionStore tmpInPlace = this.getInPlace();
        MatrixStore tmpBuilder = tmpInPlace;
        return tmpBuilder.triangular(false, true);
    }

    public int[] getPivotOrder() {
        return myPivot.getOrder();
    }

    public double getRankThreshold() {

        N largest = this.getInPlace().aggregateDiagonal(Aggregator.LARGEST);
        double epsilon = this.getDimensionalEpsilon();

        return epsilon * Math.max(MACHINE_SMALLEST, NumberDefinition.doubleValue(largest));
    }

    public int[] getReversePivotOrder() {
        return myPivot.reverseOrder();
    }

    public MatrixStore getSolution(final Collectable> rhs) {
        return this.getSolution(rhs, this.preallocate(this.getInPlace(), rhs));
    }

    @Override
    public MatrixStore getSolution(final Collectable> rhs, final PhysicalStore preallocated) {

        int[] order = myPivot.getOrder();

        preallocated.fillMatching(this.collect(rhs).rows(order));

        DecompositionStore body = this.getInPlace();

        preallocated.substituteForwards(body, true, false, false);

        BinaryFunction divide = this.function().divide();
        for (int i = 0; i < order.length; i++) {
            preallocated.modifyRow(i, divide.by(body.get(i, i)));
        }

        preallocated.substituteBackwards(body, true, true, false);

        return preallocated.rows(myPivot.reverseOrder());
    }

    public MatrixStore invert(final Access2D original) throws RecoverableCondition {

        this.decompose(this.wrap(original));

        if (this.isSolvable()) {
            return this.getInverse();
        }
        throw RecoverableCondition.newMatrixNotInvertible();
    }

    public MatrixStore invert(final Access2D original, final PhysicalStore preallocated) throws RecoverableCondition {

        this.decompose(this.wrap(original));

        if (this.isSolvable()) {
            return this.getInverse(preallocated);
        }
        throw RecoverableCondition.newMatrixNotInvertible();
    }

    public boolean isPivoted() {
        return myPivot.isModified();
    }

    @Override
    public boolean isSolvable() {
        return super.isSolvable();
    }

    public PhysicalStore preallocate(final Structure2D template) {
        long tmpCountRows = template.countRows();
        return this.allocate(tmpCountRows, tmpCountRows);
    }

    public PhysicalStore preallocate(final Structure2D templateBody, final Structure2D templateRHS) {
        return this.allocate(templateRHS.countRows(), templateRHS.countColumns());
    }

    public MatrixStore solve(final Access2D body, final Access2D rhs) throws RecoverableCondition {

        this.decompose(this.wrap(body));

        if (this.isSolvable()) {
            return this.getSolution(this.wrap(rhs));
        }
        throw RecoverableCondition.newEquationSystemNotSolvable();
    }

    public MatrixStore solve(final Access2D body, final Access2D rhs, final PhysicalStore preallocated) throws RecoverableCondition {

        this.decompose(this.wrap(body));

        if (this.isSolvable()) {
            return this.getSolution(this.wrap(rhs), preallocated);
        }
        throw RecoverableCondition.newEquationSystemNotSolvable();
    }

    private boolean doDecompose(final Access2D.Collectable> matrix, final boolean pivoting) {

        this.reset();

        DecompositionStore store = this.setInPlace(matrix);

        int dim = this.getMinDim();

        myPivot.reset(dim);

        BasicArray multipliers = this.makeArray(dim);

        // Main loop - along the diagonal
        for (int ij = 0; ij < dim; ij++) {

            if (pivoting) {
                // Find next pivot row
                int pivotRow = store.indexOfLargestOnDiagonal(ij, ij);
                // Pivot?
                if (pivotRow != ij) {
                    store.exchangeHermitian(pivotRow, ij);
                    myPivot.change(pivotRow, ij);
                }
            }

            double storeDiagVal = store.doubleValue(ij, ij);

            if (Double.isFinite(myThreshold) && myThreshold > ZERO) {

                // double maxColVal = ZERO;
                // for (int i = ij + 1; i < dim; i++) {
                //     maxColVal = Math.max(maxColVal, Math.abs(store.doubleValue(i, ij)));
                // }
                // maxColVal *= myThreshold;
                // maxColVal *= maxColVal;

                double candidate = Math.max(Math.abs(storeDiagVal), myThreshold);

                if (candidate > storeDiagVal) {
                    storeDiagVal = candidate;
                    store.set(ij, ij, storeDiagVal);
                }
            }

            // Do the calculations...
            if (NumberContext.compare(storeDiagVal, PrimitiveMath.ZERO) != 0) {

                // Calculate multipliers and copy to local column
                // Current column, below the diagonal
                store.divideAndCopyColumn(ij, ij, multipliers);

                // Apply transformations to everything below and to the right of the pivot element
                store.applyLDL(ij, multipliers);

            } else {

                store.set(ij, ij, ZERO);
            }

        }

        return this.computed(true);
    }

    @Override
    protected boolean checkSolvability() {
        return this.isSquare() && this.isFullRank();
    }

    void setThreshold(final N threshold) {
        myThreshold = NumberDefinition.doubleValue(threshold);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy