
io.jenetics.lattices.matrix.DoubleMatrix2d 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;
import static java.util.Objects.requireNonNull;
import static io.jenetics.lattices.NumericalContext.ZERO_EPSILON;
import java.util.function.DoubleUnaryOperator;
import io.jenetics.lattices.NumericalContext;
import io.jenetics.lattices.array.DenseDoubleArray;
import io.jenetics.lattices.array.DoubleArray;
import io.jenetics.lattices.grid.DoubleGrid2d;
import io.jenetics.lattices.grid.Extent1d;
import io.jenetics.lattices.grid.Extent2d;
import io.jenetics.lattices.grid.Factory2d;
import io.jenetics.lattices.grid.Loop2d;
import io.jenetics.lattices.grid.Range2d;
import io.jenetics.lattices.grid.StrideOrder2d;
import io.jenetics.lattices.grid.Structure1d;
import io.jenetics.lattices.grid.Structure2d;
/**
* Generic class for 2-d matrices holding {@code double} elements. Instances
* of this class are usually created via a factory.
* {@code
* final DoubleMatrix2d matrix5x10 = DoubleMatrix2d.DENSE.create(5, 10);
* }
*
* @see #DENSE
*
* @author Franz Wilhelmstötter
* @since 3.0
* @version 3.0
*/
public class DoubleMatrix2d
extends DoubleGrid2d
implements Matrix2d
{
/**
* Factory for creating dense 2-d double matrices.
*/
public static final Factory2d DENSE = struct ->
new DoubleMatrix2d(
struct,
DenseDoubleArray.ofSize(struct.extent().size())
);
/**
* Create a new 2-d matrix with the given {@code structure} and element
* {@code array}.
*
* @param structure the matrix structure
* @param array the element array
*/
public DoubleMatrix2d(final Structure2d structure, final DoubleArray array) {
super(structure, array);
}
/**
* Create a new matrix view from the given {@code grid}.
*
* @param grid the data grid
*/
public DoubleMatrix2d(final DoubleGrid2d grid) {
this(grid.structure(), grid.array());
}
@Override
public Factory2d factory() {
return struct -> new DoubleMatrix2d(
struct,
array.like(struct.extent().size())
);
}
@Override
public DoubleMatrix2d view(final Structure2d structure) {
return new DoubleMatrix2d(structure, array);
}
@Override
public DoubleMatrix2d copy(final Range2d range) {
// Fast copy, if applicable.
if (range.row() == 0 &&
range.col() == 0 &&
range.height() == rows() &&
range.width() == cols() &&
structure.order().equals(new StrideOrder2d(new Extent2d(range))))
{
return new DoubleMatrix2d(structure, array.copy());
} else {
final var struct = structure.copy(range);
final var elems = array.like(range.size());
final var loop = new Loop2d.RowMajor(struct.extent());
loop.forEach((r, c) ->
elems.set(
struct.order().index(r, c),
get(r + range.row(), c + range.col())
)
);
return new DoubleMatrix2d(struct, elems);
}
}
@Override
public DoubleMatrix2d transpose() {
return new DoubleMatrix2d(structure.transpose(), array);
}
/* *************************************************************************
* Matrix view methods.
* ************************************************************************/
/**
* Constructs and returns a view representing the rows of the given
* column. The returned view is backed by this matrix, so changes in the
* returned view are reflected in this matrix, and vice-versa.
*
* @param index the column index.
* @return a new column view.
* @throws IndexOutOfBoundsException if {@code index < 0 || index >= cols()}
* @throws UnsupportedOperationException if the {@link #order()} function
* is not an instance of {@link StrideOrder2d}
*/
public DoubleMatrix1d colAt(final int index) {
return new DoubleMatrix1d(structure.colAt(index), array);
}
/**
* Constructs and returns a view representing the columns of the
* given row. The returned view is backed by this matrix, so changes in the
* returned view are reflected in this matrix, and vice-versa.
*
* @param index the row index.
* @return a new row view.
* @throws IndexOutOfBoundsException if {@code index < 0 || index >= rows()}
* @throws UnsupportedOperationException if the {@link #order()} function
* is not an instance of {@link StrideOrder2d}
*/
public DoubleMatrix1d rowAt(final int index) {
return new DoubleMatrix1d(structure.rowAt(index), array);
}
/* *************************************************************************
* Matrix algebra methods.
* ************************************************************************/
/**
* Linear algebraic matrix-vector multiplication
*
* z = alpha * A * y + beta*z
* z[i] = alpha*Sum(A[i, j] * y[j]) + beta*z[i],
* i = 0..A.rows() - 1, j = 0..y.size() - 1
* where
* A == this
*
*
* @implNote
* Matrix shape conformance is checked after potential
* transpositions.
*
* @param y the source vector.
* @param z the vector where results are to be stored. Set this parameter to
* {@code null} to indicate that a new result vector should be
* constructed.
* @return z, or a newly created result matrix
* @throws IllegalArgumentException if {@code A.cols() != y.size() ||
* A.rows() > z.size())}.
*/
public DoubleMatrix1d mult(
final DoubleMatrix1d y,
final DoubleMatrix1d z,
final double alpha,
final double beta,
final boolean transposeA
) {
if (transposeA) {
return view(structure().transpose())
.mult(y, z, alpha, beta, false);
}
if (z == null) {
final var struct = new Structure1d(new Extent1d(rows()));
final var elems = array.like(struct.extent().size());
return mult(y, new DoubleMatrix1d(struct, elems), alpha, beta, false);
}
if (cols() != y.size() || rows() > z.size()) {
throw new IllegalArgumentException(
"Incompatible args: " + extent() + ", " + y.extent() + ", " + z.extent()
);
}
for (int i = rows(); --i >= 0; ) {
double s = 0;
for (int j = cols(); --j >= 0;) {
s = Math.fma(get(i, j), y.get(j), s);
}
z.set(i, Math.fma(alpha, s, beta*z.get(i)));
}
return z;
}
/**
* Linear algebraic matrix-vector multiplication; {@code z = A * y};
* Equivalent to {@code return A.mult(y, z, 1, 0);}
*
* @see #mult(DoubleMatrix1d, DoubleMatrix1d, double, double, boolean)
*
* @param y the source vector.
* @param z the vector where results are to be stored. Set this parameter to
* {@code null} to indicate that a new result vector should be
* constructed.
* @return z, or a newly created result matrix
*/
public DoubleMatrix1d mult(final DoubleMatrix1d y, final DoubleMatrix1d z) {
return mult(y, z, 1, (z == null ? 1 : 0), false);
}
/**
* Linear algebraic matrix-matrix multiplication:
*
* C = alpha * A x B + beta*C
* C[i, j] = alpha*Sum(A[i, k] * B[k, j]) + beta*C[i, j], k = 0..n-1
*
* Matrix shapes:
*
* A(m x n), B(n x p), C(m x p)
*
*
* @implNote
* Matrix shape conformance is checked after potential
* transpositions.
*
* @param B the second source matrix.
* @param C the matrix where results are to be stored. Set this parameter to
* {@code null} to indicate that a new result matrix should be
* constructed.
* @return C, or a newly created result matrix
* @throws IllegalArgumentException if {@code B.rows() != A.cols()} or
* {@code C.rows() != A.rows() || C.cols() != B.cols()} or
* {@code A == C || B == C}
*/
public DoubleMatrix2d mult(
final DoubleMatrix2d B,
final DoubleMatrix2d C,
final double alpha,
final double beta,
final boolean transposeA,
final boolean transposeB
) {
requireNonNull(B);
if (transposeA) {
return view(structure().transpose())
.mult(B, C, alpha, beta, false, transposeB);
}
if (transposeB) {
return mult(
B.view(B.structure().transpose()), C,
alpha, beta, false, false
);
}
if (C == null) {
return mult(B, like(rows(), B.cols()), alpha, beta, false, false);
}
final int m = rows();
final int n = cols();
final int p = B.cols();
if (B.rows() != n) {
throw new IllegalArgumentException(
"2-d matrix inner dimensions must be equal:" +
extent() + ", " + B.extent()
);
}
if (C.rows() != m || C.cols() != p) {
throw new IllegalArgumentException(
"Incompatible result matrix: " +
extent() + ", " + B.extent() + ", " + C.extent()
);
}
if (this == C || B == C) {
throw new IllegalArgumentException(
"Matrices A, B or C must not be identical."
);
}
for (int j = p; --j >= 0;) {
for (int i = m; --i >= 0;) {
double s = 0;
for (int k = n; --k >= 0;) {
s = Math.fma(get(i, k), B.get(k, j), s);
}
C.set(i, j, Math.fma(alpha, s, beta*C.get(i, j)));
}
}
return C;
}
/**
* Linear algebraic matrix-matrix multiplication {@code C = A x B}, which is
* equivalent to {@code A.mult(B, C, 1, 0, false, false)}.
*
* @see #mult(DoubleMatrix2d, DoubleMatrix2d, double, double, boolean, boolean)
*
* @param B the second source matrix.
* @param C the matrix where results are to be stored. Set this parameter to
* {@code null} to indicate that a new result matrix should be
* constructed.
* @return C, or a newly created result matrix.
* @throws IllegalArgumentException if {@code B.rows() != A.cols()} or
* {@code C.rows() != A.rows() || C.cols() != B.cols()} or
* {@code A == C || B == C}
*/
public DoubleMatrix2d mult(final DoubleMatrix2d B, final DoubleMatrix2d C) {
return mult(B, C, 1, (C == null ? 1 : 0), false, false);
}
/**
* Return the sum of all cells: {@code Sum(x[i, j])}.
*
* @return the sum of all cells
*/
public double sum() {
if (size() == 0) {
return 0;
} else {
return reduce(Double::sum, DoubleUnaryOperator.identity());
}
}
@Override
public boolean equals(final Object object) {
return object == this ||
object instanceof DoubleMatrix2d matrix &&
NumericalContext.with(ZERO_EPSILON, () -> equals(matrix));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy