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

io.jenetics.lattices.matrix.blas.Cholesky Maven / Gradle / Ivy

/*
 * Java Lattice Library (lattices-3.0.0.ALPHA1).
 * Copyright (c) 2022-2022 Franz Wilhelmstötter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author:
 *    Franz Wilhelmstötter ([email protected])
 */
package io.jenetics.lattices.matrix.blas;

import static java.util.Objects.requireNonNull;
import static io.jenetics.lattices.grid.Grids.checkRectangular;

import io.jenetics.lattices.NumericalContext;
import io.jenetics.lattices.matrix.DoubleMatrix1d;
import io.jenetics.lattices.matrix.DoubleMatrix2d;

/**
 * Store the result of a Cholesky-decomposition.
 *
 * @author Franz Wilhelmstötter
 * @since 3.0
 * @version 3.0
 */
public class Cholesky {

    private final DoubleMatrix2d L;
    private final boolean symmetricPositiveDefinite;

    private final NumericalContext context;

    private Cholesky(
        final DoubleMatrix2d L,
        final boolean symmetricPositiveDefinite,
        final NumericalContext context
    ) {
        this.L = requireNonNull(L);
        this.symmetricPositiveDefinite = symmetricPositiveDefinite;

        this.context = requireNonNull(context);
    }

    /**
     * Returns the triangular factor {@code L}.
     *
     * @return {@code L}
     */
    public DoubleMatrix2d L() {
        return L;
    }

    /**
     * Return {@code true} if the matrix {@code A} is symmetric and positive
     * definite.
     *
     * @return {@code true} if {@code A}is symmetric and positive definite
     *         {@code false} otherwise
     */
    public boolean isSymmetricPositiveDefinite() {
        return symmetricPositiveDefinite;
    }

    /**
     * Solves {@code A*X = B} and returns {@code X}.
     *
     * @param B a atrix with as many rows as {@code A} and any number of columns
     * @return {@code X} so that {@code L*L'*X = B}
     * @throws IllegalArgumentException if {@code B.rows() != A.rows()} or
     *         {@code !isSymmetricPositiveDefinite()}
     */
    public DoubleMatrix2d solve(final DoubleMatrix2d B) {
        final var X = B.copy();

        for (int c = 0; c < B.cols(); ++c) {
            // Solve L*Y = B;
            for (int i = 0; i < L.rows(); i++) {
                double sum = B.get(i, c);
                for (int k = i - 1; k >= 0; k--) {
                    sum = -Math.fma(L.get(i, k), X.get(k, c), -sum);
                }
                X.set(i, c, sum/L.get(i, i));
            }

            // Solve L'*X = Y;
            for (int i = L.rows() - 1; i >= 0; i--) {
                double sum = X.get(i, c);
                for (int k = i + 1; k < L.rows(); k++) {
                    sum = -Math.fma(L.get(k, i), X.get(k, c), -sum);
                }
                X.set(i, c, sum/L.get(i, i));
            }
        }

        return X;
    }

    /**
     * Performs a Cholesky-decomposition of the symmetric and positive
     * definite matrix {@code A}.
     *
     * @param A the matrix to be decomposed
     * @return Structure to access {@code L} and
     *         {@code isSymmetricPositiveDefinite} flag
     * @throws IllegalArgumentException if {@code A.rows() < A.cols()}
     */
    public static Cholesky decompose(final DoubleMatrix2d A) {
        checkRectangular(A);

        final var context = NumericalContext.get();

        final var n = A.rows();
        final var L = A.like(n, n);
        var isSymmetricPositiveDefinite = A.cols() == n;

        final var Lrows = new DoubleMatrix1d[n];
        for (int j = 0; j < A.rows(); j++) {
            Lrows[j] = L.rowAt(j);
        }

        // Main loop.
        for (int j = 0; j < n; j++) {
            double d = 0.0;
            for (int k = 0; k < j; k++) {
                double s = Lrows[k].dotProduct(Lrows[j], 0, k);

                s = (A.get(j, k) - s)/L.get(k, k);
                Lrows[j].set(k, s);
                d = Math.fma(s, s, d);
                isSymmetricPositiveDefinite = isSymmetricPositiveDefinite &&
                    context.equals(A.get(k, j), A.get(j, k));
            }
            d = A.get(j, j) - d;
            isSymmetricPositiveDefinite = isSymmetricPositiveDefinite &&
                context.isGreaterZero(d);
            L.set(j, j, Math.sqrt(Math.max(d, 0.0)));

            for (int k = j + 1; k < n; k++) {
                L.set(j, k, 0.0);
            }
        }

        return new Cholesky(L, isSymmetricPositiveDefinite, context);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy