
org.ejml.sparse.csc.CommonOps_FSCC Maven / Gradle / Ivy
Show all versions of ejml-fsparse Show documentation
/*
* Copyright (c) 2022, Peter Abeles. All Rights Reserved.
*
* This file is part of Efficient Java Matrix Library (EJML).
*
* 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.
*/
package org.ejml.sparse.csc;
import javax.annotation.Generated;
import org.ejml.MatrixDimensionException;
import org.ejml.data.FGrowArray;
import org.ejml.data.FMatrixRMaj;
import org.ejml.data.FMatrixSparseCSC;
import org.ejml.data.IGrowArray;
import org.ejml.dense.row.CommonOps_FDRM;
import org.ejml.interfaces.decomposition.LUSparseDecomposition_F32;
import org.ejml.interfaces.linsol.LinearSolverSparse;
import org.ejml.masks.Mask;
import org.ejml.ops.FOperatorBinary;
import org.ejml.ops.FOperatorBinaryIdx;
import org.ejml.ops.FOperatorUnary;
import org.ejml.ops.IPredicateBinary;
import org.ejml.sparse.FillReducing;
import org.ejml.sparse.csc.factory.DecompositionFactory_FSCC;
import org.ejml.sparse.csc.factory.LinearSolverFactory_FSCC;
import org.ejml.sparse.csc.misc.ImplCommonOps_FSCC;
import org.ejml.sparse.csc.mult.ImplMultiplication_FSCC;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import static org.ejml.UtilEjml.*;
/**
* Most common operations on {@link FMatrixSparseCSC}. For example, addition, matrix multiplication, ... etc
*
* @author Peter Abeles
*/
@Generated("org.ejml.sparse.csc.CommonOps_DSCC")
public class CommonOps_FSCC {
private CommonOps_FSCC(){}
/**
* Checks to see if row indicies are sorted into ascending order. O(N)
*
* @return true if sorted and false if not
*/
public static boolean checkIndicesSorted( FMatrixSparseCSC A ) {
for (int j = 0; j < A.numCols; j++) {
int idx0 = A.col_idx[j];
int idx1 = A.col_idx[j + 1];
if (idx0 != idx1 && A.nz_rows[idx0] >= A.numRows)
return false;
for (int i = idx0 + 1; i < idx1; i++) {
int row = A.nz_rows[i];
if (A.nz_rows[i - 1] >= row)
return false;
if (row >= A.numRows)
return false;
}
}
return true;
}
public static boolean checkStructure( FMatrixSparseCSC A ) {
if (A.col_idx.length < A.numCols + 1)
return false;
if (A.col_idx[A.numCols] != A.nz_length)
return false;
if (A.nz_rows.length < A.nz_length)
return false;
if (A.nz_values.length < A.nz_length)
return false;
if (A.col_idx[0] != 0)
return false;
for (int i = 0; i < A.numCols; i++) {
if (A.col_idx[i] > A.col_idx[i + 1]) {
return false;
}
if (A.col_idx[i + 1] - A.col_idx[i] > A.numRows)
return false;
}
if (!checkSortedFlag(A))
return false;
if (checkDuplicateElements(A))
return false;
return true;
}
public static boolean checkSortedFlag( FMatrixSparseCSC A ) {
if (A.indicesSorted)
return checkIndicesSorted(A);
return true;
}
/**
* Checks for duplicate elements. A is sorted
*
* @param A Matrix to be tested.
* @return true if duplicates or false if false duplicates
*/
public static boolean checkDuplicateElements( FMatrixSparseCSC A ) {
A = A.copy(); // create a copy so that it doesn't modify A
A.sortIndices(null);
return !checkSortedFlag(A);
}
/**
* Perform matrix transpose
*
* @param A Input matrix. Not modified
* @param A_t Storage for transpose of 'a'. Must be correct shape. data length might be adjusted.
* @param gw (Optional) Storage for internal workspace. Can be null.
* @return The transposed matrix
*/
public static FMatrixSparseCSC transpose( FMatrixSparseCSC A, @Nullable FMatrixSparseCSC A_t, @Nullable IGrowArray gw ) {
A_t = reshapeOrDeclare(A_t, A.numCols, A.numRows, A.nz_length);
ImplCommonOps_FSCC.transpose(A, A_t, gw);
return A_t;
}
public static FMatrixSparseCSC mult( FMatrixSparseCSC A, FMatrixSparseCSC B,
@Nullable FMatrixSparseCSC outputC ) {
return mult(A, B, outputC, null, null);
}
/**
* Performs matrix multiplication. C = A*B
*
* @param A (Input) Matrix. Not modified.
* @param B (Input) Matrix. Not modified.
* @param outputC (Output) Storage for results. Data length is increased if insufficient.
* @param gw (Optional) Storage for internal workspace. Can be null.
* @param gx (Optional) Storage for internal workspace. Can be null.
*/
public static FMatrixSparseCSC mult( FMatrixSparseCSC A, FMatrixSparseCSC B,
@Nullable FMatrixSparseCSC outputC,
@Nullable IGrowArray gw, @Nullable FGrowArray gx ) {
if (A.numCols != B.numRows)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
outputC = reshapeOrDeclare(outputC, A, A.numRows, B.numCols);
ImplMultiplication_FSCC.mult(A, B, outputC, gw, gx);
return outputC;
}
/**
* Performs matrix multiplication. C = A*B
*
* @param A Matrix
* @param B Dense Matrix
* @param outputC Dense Matrix
*/
public static FMatrixRMaj mult( FMatrixSparseCSC A, FMatrixRMaj B, @Nullable FMatrixRMaj outputC ) {
if (A.numCols != B.numRows)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
outputC = reshapeOrDeclare(outputC, A.numRows, B.numCols);
ImplMultiplication_FSCC.mult(A, B, outputC);
return outputC;
}
/**
* C = C + A*B
*/
public static void multAdd( FMatrixSparseCSC A, FMatrixRMaj B, FMatrixRMaj outputC ) {
if (A.numCols != B.numRows)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
if (A.numRows != outputC.numRows || B.numCols != outputC.numCols)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B, outputC));
ImplMultiplication_FSCC.multAdd(A, B, outputC);
}
/**
* Performs matrix multiplication. C = AT*B
*
* @param A Matrix
* @param B Dense Matrix
* @param outputC Dense Matrix
*/
public static FMatrixRMaj multTransA( FMatrixSparseCSC A, FMatrixRMaj B, @Nullable FMatrixRMaj outputC,
@Nullable FGrowArray work ) {
if (A.numRows != B.numRows)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
if (work == null)
work = new FGrowArray();
outputC = reshapeOrDeclare(outputC, A.numCols, B.numCols);
ImplMultiplication_FSCC.multTransA(A, B, outputC, work);
return outputC;
}
/**
* C = C + AT*B
*/
public static void multAddTransA( FMatrixSparseCSC A, FMatrixRMaj B, FMatrixRMaj outputC,
@Nullable FGrowArray work ) {
if (A.numRows != B.numRows)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
if (A.numCols != outputC.numRows || B.numCols != outputC.numCols)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B, outputC));
if (work == null)
work = new FGrowArray();
ImplMultiplication_FSCC.multAddTransA(A, B, outputC, work);
}
/**
* Performs matrix multiplication. C = A*BT
*
* @param A Matrix
* @param B Dense Matrix
* @param outputC Dense Matrix
*/
public static FMatrixRMaj multTransB( FMatrixSparseCSC A, FMatrixRMaj B, @Nullable FMatrixRMaj outputC,
@Nullable FGrowArray work ) {
if (A.numCols != B.numCols)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
outputC = reshapeOrDeclare(outputC, A.numRows, B.numRows);
if (work == null)
work = new FGrowArray();
ImplMultiplication_FSCC.multTransB(A, B, outputC, work);
return outputC;
}
/**
* C = C + A*BT
*/
public static void multAddTransB( FMatrixSparseCSC A, FMatrixRMaj B, FMatrixRMaj outputC,
@Nullable FGrowArray work ) {
if (A.numCols != B.numCols)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
if (A.numRows != outputC.numRows || B.numRows != outputC.numCols)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B, outputC));
if (work == null)
work = new FGrowArray();
ImplMultiplication_FSCC.multAddTransB(A, B, outputC, work);
}
/**
* Performs matrix multiplication. C = AT*BT
*
* @param A Matrix
* @param B Dense Matrix
* @param outputC Dense Matrix
*/
public static FMatrixRMaj multTransAB( FMatrixSparseCSC A, FMatrixRMaj B, FMatrixRMaj outputC ) {
if (A.numRows != B.numCols)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
outputC = reshapeOrDeclare(outputC, A.numCols, B.numRows);
ImplMultiplication_FSCC.multTransAB(A, B, outputC);
return outputC;
}
/**
* C = C + AT*BT
*/
public static void multAddTransAB( FMatrixSparseCSC A, FMatrixRMaj B, FMatrixRMaj outputC ) {
if (A.numRows != B.numCols)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
if (A.numCols != outputC.numRows || B.numRows != outputC.numCols)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B, outputC));
ImplMultiplication_FSCC.multAddTransAB(A, B, outputC);
}
/**
* Given a symmetric matrix, which is represented by a lower triangular matrix, convert it back into
* a full symmetric matrix
*
* @param A (Input) Lower triangular matrix
* @param outputB (Output) Symmetric matrix.
* @param gw (Optional) Workspace. Can be null.
*/
public static void symmLowerToFull( FMatrixSparseCSC A, FMatrixSparseCSC outputB, @Nullable IGrowArray gw ) {
ImplCommonOps_FSCC.symmLowerToFull(A, outputB, gw);
}
/**
* Performs matrix addition:
* C = αA + βB
*
* @param alpha scalar value multiplied against A
* @param A Matrix
* @param beta scalar value multiplied against B
* @param B Matrix
* @param outputC Output matrix.
* @param gw (Optional) Storage for internal workspace. Can be null.
* @param gx (Optional) Storage for internal workspace. Can be null.
*/
public static FMatrixSparseCSC add( float alpha, FMatrixSparseCSC A, float beta, FMatrixSparseCSC B,
@Nullable FMatrixSparseCSC outputC,
@Nullable IGrowArray gw, @Nullable FGrowArray gx ) {
if (A.numRows != B.numRows || A.numCols != B.numCols)
throw new MatrixDimensionException("Inconsistent matrix shapes. " + stringShapes(A, B));
outputC = reshapeOrDeclare(outputC, A, A.numRows, A.numCols);
ImplCommonOps_FSCC.add(alpha, A, beta, B, outputC, gw, gx);
return outputC;
}
public static FMatrixSparseCSC identity( int length ) {
return identity(length, length);
}
public static FMatrixSparseCSC identity( int numRows, int numCols ) {
int min = Math.min(numRows, numCols);
FMatrixSparseCSC A = new FMatrixSparseCSC(numRows, numCols, min);
setIdentity(A);
return A;
}
public static void setIdentity( FMatrixSparseCSC A ) {
int min = Math.min(A.numRows, A.numCols);
A.growMaxLength(min, false);
A.nz_length = min;
Arrays.fill(A.nz_values, 0, min, 1);
for (int i = 1; i <= min; i++) {
A.col_idx[i] = i;
A.nz_rows[i - 1] = i - 1;
}
for (int i = min + 1; i <= A.numCols; i++) {
A.col_idx[i] = min;
}
}
/**
* B = scalar*A. A and B can be the same instance.
*
* @param scalar (Input) Scalar value
* @param A (Input) Matrix. Not modified.
* @param outputB (Output) Matrix. Modified.
*/
public static void scale( float scalar, FMatrixSparseCSC A, FMatrixSparseCSC outputB ) {
if (A != outputB) {
outputB.copyStructure(A);
for (int i = 0; i < A.nz_length; i++) {
outputB.nz_values[i] = A.nz_values[i]*scalar;
}
} else {
for (int i = 0; i < A.nz_length; i++) {
outputB.nz_values[i] *= scalar;
}
}
}
/**
* B = A/scalar. A and B can be the same instance.
*
* @param scalar (Input) Scalar value
* @param A (Input) Matrix. Not modified.
* @param outputB (Output) Matrix. Modified.
*/
public static void divide( FMatrixSparseCSC A, float scalar, FMatrixSparseCSC outputB ) {
if (A != outputB) {
outputB.copyStructure(A);
for (int i = 0; i < A.nz_length; i++) {
outputB.nz_values[i] = A.nz_values[i]/scalar;
}
} else {
for (int i = 0; i < A.nz_length; i++) {
A.nz_values[i] /= scalar;
}
}
}
/**
* B = scalar/A. A and B can be the same instance. Only non-zero values are affected
*
* @param A (Input) Matrix. Not modified.
* @param scalar (Input) Scalar value
* @param outputB (Output) Matrix. Modified.
*/
public static void divide( float scalar, FMatrixSparseCSC A, FMatrixSparseCSC outputB ) {
if (A != outputB) {
outputB.copyStructure(A);
}
for (int i = 0; i < A.nz_length; i++) {
outputB.nz_values[i] = scalar/A.nz_values[i];
}
}
/**
* B = -A. Changes the sign of elements in A and stores it in B. A and B can be the same instance.
*
* @param A (Input) Matrix. Not modified.
* @param outputB (Output) Matrix. Modified.
*/
public static void changeSign( FMatrixSparseCSC A, FMatrixSparseCSC outputB ) {
if (A != outputB) {
outputB.copyStructure(A);
}
for (int i = 0; i < A.nz_length; i++) {
outputB.nz_values[i] = -A.nz_values[i];
}
}
/**
* Returns the value of the element with the smallest abs()
*
* @param A (Input) Matrix. Not modified.
* @return scalar
*/
public static float elementMinAbs( FMatrixSparseCSC A ) {
if (A.nz_length == 0)
return 0;
float min = A.isFull() ? Math.abs(A.nz_values[0]) : 0;
for (int i = 0; i < A.nz_length; i++) {
float val = Math.abs(A.nz_values[i]);
if (val < min) {
min = val;
}
}
return min;
}
/**
* Returns the value of the element with the largest abs()
*
* @param A (Input) Matrix. Not modified.
* @return scalar
*/
public static float elementMaxAbs( FMatrixSparseCSC A ) {
if (A.nz_length == 0)
return 0;
float max = A.isFull() ? Math.abs(A.nz_values[0]) : 0;
for (int i = 0; i < A.nz_length; i++) {
float val = Math.abs(A.nz_values[i]);
if (val > max) {
max = val;
}
}
return max;
}
/**
* Returns the value of the element with the minimum value
*
* @param A (Input) Matrix. Not modified.
* @return scalar
*/
public static float elementMin( FMatrixSparseCSC A ) {
if (A.nz_length == 0)
return 0;
// if every element is assigned a value then the first element can be a minimum.
// Otherwise zero needs to be considered
float min = A.isFull() ? A.nz_values[0] : 0;
for (int i = 0; i < A.nz_length; i++) {
float val = A.nz_values[i];
if (val < min) {
min = val;
}
}
return min;
}
/**
* Returns the value of the element with the largest value
*
* @param A (Input) Matrix. Not modified.
* @return scalar
*/
public static float elementMax( FMatrixSparseCSC A ) {
if (A.nz_length == 0)
return 0;
// if every element is assigned a value then the first element can be a max.
// Otherwise zero needs to be considered
float max = A.isFull() ? A.nz_values[0] : 0;
for (int i = 0; i < A.nz_length; i++) {
float val = A.nz_values[i];
if (val > max) {
max = val;
}
}
return max;
}
/**
* Sum of all elements
*
* @param A (Input) Matrix. Not modified.
* @return scalar
*/
public static float elementSum( FMatrixSparseCSC A ) {
if (A.nz_length == 0)
return 0;
float sum = 0;
for (int i = 0; i < A.nz_length; i++) {
sum += A.nz_values[i];
}
return sum;
}
/**
* Performs an element-wise multiplication.
* output[i,j] = A[i,j]*B[i,j]
* All matrices must have the same shape.
*
* @param A (Input) Matrix.
* @param B (Input) Matrix
* @param output (Output) Matrix. data array is grown to min(A.nz_length,B.nz_length), resulting a in a large speed boost.
* @param gw (Optional) Storage for internal workspace. Can be null.
* @param gx (Optional) Storage for internal workspace. Can be null.
*/
public static FMatrixSparseCSC elementMult( FMatrixSparseCSC A, FMatrixSparseCSC B, @Nullable FMatrixSparseCSC output,
@Nullable IGrowArray gw, @Nullable FGrowArray gx ) {
if (A.numCols != B.numCols || A.numRows != B.numRows)
throw new MatrixDimensionException("All inputs must have the same number of rows and columns. " + stringShapes(A, B));
output = reshapeOrDeclare(output, A, A.numRows, A.numCols);
ImplCommonOps_FSCC.elementMult(A, B, output, gw, gx);
return output;
}
/**
* Finds the maximum abs in each column of A and stores it into values
*
* @param A (Input) Matrix
* @param outputB (Output) storage for column max abs
*/
public static void maxAbsCols( FMatrixSparseCSC A, @Nullable FMatrixRMaj outputB ) {
outputB = reshapeOrDeclare(outputB, 1, A.numCols);
for (int i = 0; i < A.numCols; i++) {
int idx0 = A.col_idx[i];
int idx1 = A.col_idx[i + 1];
float maxabs = 0;
for (int j = idx0; j < idx1; j++) {
float v = Math.abs(A.nz_values[j]);
if (v > maxabs)
maxabs = v;
}
outputB.data[i] = maxabs;
}
}
/**
* Multiply all elements of column 'i' by value[i]. A[:,i] *= values[i].
* Equivalent to A = A*diag(values)
*
* @param A (Input/Output) Matrix. Modified.
* @param values (Input) multiplication factor for each column
* @param offset (Input) first index in values to start at
*/
public static void multColumns( FMatrixSparseCSC A, float[] values, int offset ) {
if (values.length + offset < A.numCols)
throw new IllegalArgumentException("Array is too small. " + values.length + " < " + A.numCols);
for (int i = 0; i < A.numCols; i++) {
int idx0 = A.col_idx[i];
int idx1 = A.col_idx[i + 1];
float v = values[offset + i];
for (int j = idx0; j < idx1; j++) {
A.nz_values[j] *= v;
}
}
}
/**
* Divides all elements of column 'i' by values[i]. A[:,i] /= values[i].
* Equivalent to A = A*inv(diag(values))
*
* @param A (Input/Output) Matrix. Modified.
* @param values (Input) multiplication factor for each column
* @param offset (Input) first index in values to start at
*/
public static void divideColumns( FMatrixSparseCSC A, float[] values, int offset ) {
if (values.length + offset < A.numCols)
throw new IllegalArgumentException("Array is too small. " + values.length + " < " + A.numCols);
for (int i = 0; i < A.numCols; i++) {
int idx0 = A.col_idx[i];
int idx1 = A.col_idx[i + 1];
float v = values[offset + i];
for (int j = idx0; j < idx1; j++) {
A.nz_values[j] /= v;
}
}
}
/**
* Equivalent to multiplying a matrix B by two diagonal matrices.
* B = A*B*C, where A=diag(a) and C=diag(c).
*
* @param diagA Array of length offsteA + B.numRows
* @param offsetA First index in A
* @param B Rectangular matrix
* @param diagC Array of length indexC + B.numCols
* @param offsetC First index in C
*/
public static void multRowsCols( float[] diagA, int offsetA,
FMatrixSparseCSC B,
float[] diagC, int offsetC ) {
if (diagA.length + offsetA < B.numRows)
throw new IllegalArgumentException("diagA is too small.");
if (diagC.length + offsetC < B.numCols)
throw new IllegalArgumentException("diagA is too small.");
for (int i = 0; i < B.numCols; i++) {
int idx0 = B.col_idx[i];
int idx1 = B.col_idx[i + 1];
float c = diagC[offsetC + i];
for (int j = idx0; j < idx1; j++) {
B.nz_values[j] *= c*diagA[offsetA + B.nz_rows[j]];
}
}
}
/**
* Equivalent to multiplying a matrix B by the inverse of two diagonal matrices.
* B = inv(A)*B*inv(C), where A=diag(a) and C=diag(c).
*
* @param diagA Array of length offsteA + B.numRows
* @param offsetA First index in A
* @param B Rectangular matrix
* @param diagC Array of length indexC + B.numCols
* @param offsetC First index in C
*/
public static void divideRowsCols( float[] diagA, int offsetA,
FMatrixSparseCSC B,
float[] diagC, int offsetC ) {
if (diagA.length + offsetA < B.numRows)
throw new IllegalArgumentException("diagA is too small.");
if (diagC.length + offsetC < B.numCols)
throw new IllegalArgumentException("diagA is too small.");
for (int i = 0; i < B.numCols; i++) {
int idx0 = B.col_idx[i];
int idx1 = B.col_idx[i + 1];
float c = diagC[offsetC + i];
for (int j = idx0; j < idx1; j++) {
B.nz_values[j] /= c*diagA[offsetA + B.nz_rows[j]];
}
}
}
/**
* Returns a diagonal matrix with the specified diagonal elements.
*
* @param values values of diagonal elements
* @return A diagonal matrix
*/
public static FMatrixSparseCSC diag( float... values ) {
int N = values.length;
return diag(new FMatrixSparseCSC(N, N, N), values, 0, N);
}
/**
* Creates a diagonal matrix from an array. Elements in the array can be offset.
*
* @param A (Optional) Storage for diagonal matrix. If null a new one will be declared.
* @param values First index in the diagonal matirx
* @param length Length of the diagonal matrix
* @param offset First index in values
* @return The diagonal matrix
*/
public static FMatrixSparseCSC diag( @Nullable FMatrixSparseCSC A,
float[] values, int offset, int length ) {
int N = length;
if (A == null)
A = new FMatrixSparseCSC(N, N, N);
else
A.reshape(N, N, N);
A.nz_length = N;
for (int i = 0; i < N; i++) {
A.col_idx[i + 1] = i + 1;
A.nz_rows[i] = i;
A.nz_values[i] = values[offset + i];
}
return A;
}
/**
*
* Extracts the diagonal elements 'src' write it to the 'dst' vector. 'dst'
* can either be a row or column vector.
*
*
* @param A Matrix whose diagonal elements are being extracted. Not modified.
* @param outputB A vector the results will be written into. Modified.
*/
public static void extractDiag( FMatrixSparseCSC A, FMatrixSparseCSC outputB ) {
int N = Math.min(A.numRows, A.numCols);
if (!MatrixFeatures_FSCC.isVector(outputB)) {
outputB.reshape(N, 1, N);
} else if (outputB.numRows*outputB.numCols != N) {
outputB.reshape(N, 1, N);
} else {
outputB.growMaxLength(N, false);
}
outputB.nz_length = N;
outputB.indicesSorted = true;
if (outputB.numRows != 1) {
outputB.col_idx[0] = 0;
outputB.col_idx[1] = N;
for (int i = 0; i < N; i++) {
outputB.nz_values[i] = A.unsafe_get(i, i);
outputB.nz_rows[i] = i;
}
} else {
outputB.col_idx[0] = 0;
for (int i = 0; i < N; i++) {
outputB.nz_values[i] = A.unsafe_get(i, i);
outputB.nz_rows[i] = 0;
outputB.col_idx[i + 1] = i + 1;
}
}
}
/**
*
* Extracts the diagonal elements 'src' write it to the 'dst' vector. 'dst'
* can either be a row or column vector.
*
*
* @param A Matrix whose diagonal elements are being extracted. Not modified.
* @param outputB A vector the results will be written into. Modified.
*/
public static void extractDiag( FMatrixSparseCSC A, FMatrixRMaj outputB ) {
int N = Math.min(A.numRows, A.numCols);
if (outputB.getNumElements() != N || !(outputB.numRows == 1 || outputB.numCols == 1)) {
outputB.reshape(N, 1);
}
for (int i = 0; i < N; i++) {
outputB.data[i] = A.unsafe_get(i, i);
}
}
/**
* Converts the permutation vector into a matrix. B = P*A. B[p[i],:] = A[i,:]
*
* @param p (Input) Permutation vector
* @param inverse (Input) If it is the inverse. B[i,:] = A[p[i],:)
* @param P (Output) Permutation matrix
*/
public static FMatrixSparseCSC permutationMatrix( int[] p, boolean inverse, int N,
@Nullable FMatrixSparseCSC P ) {
if (P == null)
P = new FMatrixSparseCSC(N, N, N);
else
P.reshape(N, N, N);
P.indicesSorted = true;
P.nz_length = N;
// each column should have one element inside of it
if (!inverse) {
for (int i = 0; i < N; i++) {
P.col_idx[i + 1] = i + 1;
P.nz_rows[p[i]] = i;
P.nz_values[i] = 1;
}
} else {
for (int i = 0; i < N; i++) {
P.col_idx[i + 1] = i + 1;
P.nz_rows[i] = p[i];
P.nz_values[i] = 1;
}
}
return P;
}
/**
* Converts the permutation matrix into a vector
*
* @param P (Input) Permutation matrix
* @param vector (Output) Permutation vector
*/
public static void permutationVector( FMatrixSparseCSC P, int[] vector ) {
if (P.numCols != P.numRows) {
throw new MatrixDimensionException("Expected a square matrix");
} else if (P.nz_length != P.numCols) {
throw new IllegalArgumentException("Expected N non-zero elements in permutation matrix");
} else if (vector.length < P.numCols) {
throw new IllegalArgumentException("vector is too short");
}
int M = P.numCols;
for (int i = 0; i < M; i++) {
if (P.col_idx[i + 1] != i + 1)
throw new IllegalArgumentException("Unexpected number of elements in a column");
vector[P.nz_rows[i]] = i;
}
}
/**
* Computes the inverse permutation vector
*
* @param original Original permutation vector
* @param inverse It's inverse
*/
public static void permutationInverse( int[] original, int[] inverse, int length ) {
for (int i = 0; i < length; i++) {
inverse[original[i]] = i;
}
}
public static int[] permutationInverse( int[] original, int length ) {
int[] inverse = new int[length];
permutationInverse(original, inverse, length);
return inverse;
}
/**
* Applies the row permutation specified by the vector to the input matrix and save the results
* in the output matrix. output[perm[j],:] = input[j,:]
*
* @param permInv (Input) Inverse permutation vector. Specifies new order of the rows.
* @param input (Input) Matrix which is to be permuted
* @param output (Output) Matrix which has the permutation stored in it. Is reshaped.
*/
public static void permuteRowInv( int[] permInv, FMatrixSparseCSC input, FMatrixSparseCSC output ) {
if (input.numRows > permInv.length)
throw new IllegalArgumentException("permutation vector must have at least as many elements as input has rows");
output.reshape(input.numRows, input.numCols, input.nz_length);
output.nz_length = input.nz_length;
output.indicesSorted = false;
System.arraycopy(input.nz_values, 0, output.nz_values, 0, input.nz_length);
System.arraycopy(input.col_idx, 0, output.col_idx, 0, input.numCols + 1);
int idx0 = 0;
for (int i = 0; i < input.numCols; i++) {
int idx1 = output.col_idx[i + 1];
for (int j = idx0; j < idx1; j++) {
output.nz_rows[j] = permInv[input.nz_rows[j]];
}
idx0 = idx1;
}
}
/**
* Applies the forward column and inverse row permutation specified by the two vector to the input matrix
* and save the results in the output matrix. output[permRow[j],permCol[i]] = input[j,i]
*
* @param permRowInv (Input) Inverse row permutation vector. Null is the same as passing in identity.
* @param input (Input) Matrix which is to be permuted
* @param permCol (Input) Column permutation vector. Null is the same as passing in identity.
* @param output (Output) Matrix which has the permutation stored in it. Is reshaped.
*/
public static void permute( @Nullable int[] permRowInv, FMatrixSparseCSC input, @Nullable int[] permCol,
FMatrixSparseCSC output ) {
if (permRowInv != null && input.numRows > permRowInv.length)
throw new IllegalArgumentException("rowInv permutation vector must have at least as many elements as input has columns");
if (permCol != null && input.numCols > permCol.length)
throw new IllegalArgumentException("permCol permutation vector must have at least as many elements as input has rows");
output.reshape(input.numRows, input.numCols, input.nz_length);
output.indicesSorted = false;
output.nz_length = input.nz_length;
int N = input.numCols;
// traverse through in order for the output columns
int outputNZ = 0;
for (int i = 0; i < N; i++) {
int inputCol = permCol != null ? permCol[i] : i; // column of input to source from
int inputNZ = input.col_idx[inputCol];
int total = input.col_idx[inputCol + 1] - inputNZ; // total nz in this column
output.col_idx[i + 1] = output.col_idx[i] + total;
for (int j = 0; j < total; j++) {
int row = input.nz_rows[inputNZ];
output.nz_rows[outputNZ] = permRowInv != null ? permRowInv[row] : row;
output.nz_values[outputNZ++] = input.nz_values[inputNZ++];
}
}
}
/**
* Permutes a vector. output[i] = input[perm[i]]
*
* @param perm (Input) permutation vector
* @param input (Input) Vector which is to be permuted
* @param output (Output) Where the permuted vector is stored.
* @param N Number of elements in the vector.
*/
public static void permute( int[] perm, float[] input, float[] output, int N ) {
for (int k = 0; k < N; k++) {
output[k] = input[perm[k]];
}
}
/**
* Permutes a vector in the inverse. output[perm[k]] = input[k]
*
* @param perm (Input) permutation vector
* @param input (Input) Vector which is to be permuted
* @param output (Output) Where the permuted vector is stored.
* @param N Number of elements in the vector.
*/
public static void permuteInv( int[] perm, float[] input, float[] output, int N ) {
for (int k = 0; k < N; k++) {
output[perm[k]] = input[k];
}
}
/**
* Applies the permutation to upper triangular symmetric matrices. Typically a symmetric matrix only stores the
* upper triangular part, so normal permutation will have undesirable results, e.g. the zeros will get mixed
* in and will no longer be symmetric. This algorithm will handle the implicit lower triangular and construct
* new upper triangular matrix.
*
*
See page cs_symperm() on Page 22 of "Direct Methods for Sparse Linear Systems"
*
* @param input (Input) Upper triangular symmetric matrix which is to be permuted.
* Entries below the diagonal are ignored.
* @param permInv (Input) Inverse permutation vector. Specifies new order of the rows and columns.
* @param output (Output) Upper triangular symmetric matrix which has the permutation stored in it. Reshaped.
* @param gw (Optional) Storage for internal workspace. Can be null.
*/
public static void permuteSymmetric( FMatrixSparseCSC input, int[] permInv, FMatrixSparseCSC output,
@Nullable IGrowArray gw ) {
if (input.numRows != input.numCols)
throw new MatrixDimensionException("Input must be a square matrix. " + stringShapes(input, output));
if (input.numRows != permInv.length)
throw new MatrixDimensionException("Number of column in input must match length of permInv");
int N = input.numCols;
int[] w = adjustClear(gw, N); // histogram with column counts
output.reshape(N, N, 0);
output.indicesSorted = false;
output.col_idx[0] = 0;
// determine column counts for output
for (int j = 0; j < N; j++) {
int j2 = permInv[j];
int idx0 = input.col_idx[j];
int idx1 = input.col_idx[j + 1];
for (int p = idx0; p < idx1; p++) {
int i = input.nz_rows[p];
if (i > j) // ignore the lower triangular portion
continue;
int i2 = permInv[i];
w[i2 > j2 ? i2 : j2]++;
}
}
// update structure of output
output.histogramToStructure(w);
System.arraycopy(output.col_idx, 0, w, 0, output.numCols);
for (int j = 0; j < N; j++) {
// column j of Input is row j2 of Output
int j2 = permInv[j];
int idx0 = input.col_idx[j];
int idx1 = input.col_idx[j + 1];
for (int p = idx0; p < idx1; p++) {
int i = input.nz_rows[p];
if (i > j) // ignore the lower triangular portion
continue;
int i2 = permInv[i];
// row i of Input is row i2 of Output
int q = w[i2 > j2 ? i2 : j2]++;
output.nz_rows[q] = i2 < j2 ? i2 : j2;
output.nz_values[q] = input.nz_values[p];
}
}
}
/**
* Concats two matrices along their rows (vertical).
*
* @param top Matrix on the top
* @param bottom Matrix on the bototm
* @param out (Output) (Optional) Storage for combined matrix. Resized.
* @return Combination of the two matrices
*/
public static FMatrixSparseCSC concatRows( FMatrixSparseCSC top, FMatrixSparseCSC bottom,
@Nullable FMatrixSparseCSC out ) {
if (top.numCols != bottom.numCols)
throw new MatrixDimensionException("Number of columns must match. " + stringShapes(top, bottom));
if (out == null)
out = new FMatrixSparseCSC(0, 0, 0);
out.reshape(top.numRows + bottom.numRows, top.numCols, top.nz_length + bottom.nz_length);
out.nz_length = top.nz_length + bottom.nz_length;
int index = 0;
for (int i = 0; i < top.numCols; i++) {
int top0 = top.col_idx[i];
int top1 = top.col_idx[i + 1];
int bot0 = bottom.col_idx[i];
int bot1 = bottom.col_idx[i + 1];
int out0 = out.col_idx[i];
int out1 = out0 + top1 - top0 + bot1 - bot0;
out.col_idx[i + 1] = out1;
for (int j = top0; j < top1; j++, index++) {
out.nz_values[index] = top.nz_values[j];
out.nz_rows[index] = top.nz_rows[j];
}
for (int j = bot0; j < bot1; j++, index++) {
out.nz_values[index] = bottom.nz_values[j];
out.nz_rows[index] = top.numRows + bottom.nz_rows[j];
}
}
out.indicesSorted = false;
return out;
}
/**
* Concats two matrices along their columns (horizontal).
*
* @param left Matrix on the left
* @param right Matrix on the right
* @param out (Output) (Optional) Storage for combined matrix. Resized.
* @return Combination of the two matrices
*/
public static FMatrixSparseCSC concatColumns( FMatrixSparseCSC left, FMatrixSparseCSC right,
@Nullable FMatrixSparseCSC out ) {
if (left.numRows != right.numRows)
throw new MatrixDimensionException("Number of rows must match. " + stringShapes(left, right));
if (out == null)
out = new FMatrixSparseCSC(0, 0, 0);
out.reshape(left.numRows, left.numCols + right.numCols, left.nz_length + right.nz_length);
out.nz_length = left.nz_length + right.nz_length;
System.arraycopy(left.col_idx, 0, out.col_idx, 0, left.numCols + 1);
System.arraycopy(left.nz_rows, 0, out.nz_rows, 0, left.nz_length);
System.arraycopy(left.nz_values, 0, out.nz_values, 0, left.nz_length);
int index = left.nz_length;
for (int i = 0; i < right.numCols; i++) {
int r0 = right.col_idx[i];
int r1 = right.col_idx[i + 1];
out.col_idx[left.numCols + i] = index;
out.col_idx[left.numCols + i + 1] = index + (r1 - r0);
for (int j = r0; j < r1; j++, index++) {
out.nz_rows[index] = right.nz_rows[j];
out.nz_values[index] = right.nz_values[j];
}
}
out.indicesSorted = left.indicesSorted && right.indicesSorted;
return out;
}
/**
* Extracts a column from A and stores it into out.
*
* @param A (Input) Source matrix. not modified.
* @param column The column in A
* @param out (Output, Optional) Storage for column vector
* @return The column of A.
*/
public static FMatrixSparseCSC extractColumn( FMatrixSparseCSC A, int column, @Nullable FMatrixSparseCSC out ) {
if (out == null)
out = new FMatrixSparseCSC(1, 1, 1);
int idx0 = A.col_idx[column];
int idx1 = A.col_idx[column + 1];
out.reshape(A.numRows, 1, idx1 - idx0);
out.nz_length = idx1 - idx0;
out.col_idx[0] = 0;
out.col_idx[1] = out.nz_length;
System.arraycopy(A.nz_values, idx0, out.nz_values, 0, out.nz_length);
System.arraycopy(A.nz_rows, idx0, out.nz_rows, 0, out.nz_length);
return out;
}
/**
* Creates a submatrix by extracting the specified rows from A. rows = {row0 %le; i %le; row1}.
*
* @param A (Input) matrix
* @param row0 First row. Inclusive
* @param row1 Last row+1.
* @param out (Output, Option) Storage for output matrix
* @return The submatrix
*/
public static FMatrixSparseCSC extractRows( FMatrixSparseCSC A, int row0, int row1,
@Nullable FMatrixSparseCSC out ) {
if (out == null)
out = new FMatrixSparseCSC(1, 1, 1);
out.reshape(row1 - row0, A.numCols, A.nz_length);
// out.col_idx[0] = 0;
// out.nz_length = 0;
for (int col = 0; col < A.numCols; col++) {
int idx0 = A.col_idx[col];
int idx1 = A.col_idx[col + 1];
for (int i = idx0; i < idx1; i++) {
int row = A.nz_rows[i];
if (row >= row0 && row < row1) {
out.nz_values[out.nz_length] = A.nz_values[i];
out.nz_rows[out.nz_length++] = row - row0;
}
}
out.col_idx[col + 1] = out.nz_length;
}
return out;
}
/**
*
* Extracts a submatrix from 'src' and inserts it in a submatrix in 'dst'.
*
*
* si-y0 , j-x0 = oij for all y0 ≤ i < y1 and x0 ≤ j < x1
*
* where 'sij' is an element in the submatrix and 'oij' is an element in the
* original matrix.
*
*
* WARNING: This is a very slow operation for sparse matrices. The current implementation is simple but
* involves excessive memory copying.
*
* @param src The original matrix which is to be copied. Not modified.
* @param srcX0 Start column.
* @param srcX1 Stop column+1.
* @param srcY0 Start row.
* @param srcY1 Stop row+1.
* @param dst Where the submatrix are stored. Modified.
* @param dstY0 Start row in dst.
* @param dstX0 start column in dst.
*/
public static void extract( FMatrixSparseCSC src, int srcY0, int srcY1, int srcX0, int srcX1,
FMatrixSparseCSC dst, int dstY0, int dstX0 ) {
if (srcY1 < srcY0 || srcY0 < 0 || srcY1 > src.getNumRows())
throw new MatrixDimensionException("srcY1 < srcY0 || srcY0 < 0 || srcY1 > src.numRows. " + stringShapes(src, dst));
if (srcX1 < srcX0 || srcX0 < 0 || srcX1 > src.getNumCols())
throw new MatrixDimensionException("srcX1 < srcX0 || srcX0 < 0 || srcX1 > src.numCols. " + stringShapes(src, dst));
int w = srcX1 - srcX0;
int h = srcY1 - srcY0;
if (dstY0 + h > dst.getNumRows())
throw new IllegalArgumentException("dst is too small in rows. " + dst.getNumRows() + " < " + (dstY0 + h));
if (dstX0 + w > dst.getNumCols())
throw new IllegalArgumentException("dst is too small in columns. " + dst.getNumCols() + " < " + (dstX0 + w));
zero(dst, dstY0, dstY0 + h, dstX0, dstX0 + w);
// NOTE: One possible optimization would be to determine the non-zero pattern in dst after the change is
// applied, modify it's structure, then copy the values in. That way you aren't shifting memory constantly.
//
// NOTE: Another optimization would be to sort the src so that it doesn't need to go through every row
for (int colSrc = srcX0; colSrc < srcX1; colSrc++) {
int idxS0 = src.col_idx[colSrc];
int idxS1 = src.col_idx[colSrc + 1];
for (int i = idxS0; i < idxS1; i++) {
int row = src.nz_rows[i];
if (row >= srcY0 && row < srcY1) {
dst.set(row - srcY0 + dstY0, colSrc - srcX0 + dstX0, src.nz_values[i]);
}
}
}
}
/**
* Select entries from A and save them in C.
* A more generic but probably also slower version of `extract`
*
* Can be used to select f.i. the lower or upper triangle of a matrix
*
* Simplified version of: GxB_Select
*
* @param A (Input) Matrix. Not modified.
* @param selector Function to decide whether an entry gets selected
* @param output (Optional/Output) Matrix to use for the output. Can be the same as A
* @return Matrix storing the selected entries of A
*/
public static FMatrixSparseCSC select( FMatrixSparseCSC A, IPredicateBinary selector, @Nullable FMatrixSparseCSC output) {
if (output != A) {
output = reshapeOrDeclare(output, A);
}
ImplCommonOps_FSCC.select(A, output, selector);
return output;
}
/**
*
* Sets every element in the matrix to the specified value. This can require a very large amount of
* memory and might exceed the maximum array size
*
* Aij = value
*
*
* @param A A matrix whose elements are about to be set. Modified.
* @param value The value each element will have.
*/
public static void fill( FMatrixSparseCSC A, float value ) {
int N = A.numCols*A.numRows;
A.growMaxLength(N, false);
A.col_idx[0] = 0;
for (int col = 0; col < A.numCols; col++) {
int idx0 = A.col_idx[col];
int idx1 = A.col_idx[col + 1] = idx0 + A.numRows;
for (int i = idx0; i < idx1; i++) {
A.nz_rows[i] = i - idx0;
A.nz_values[i] = value;
}
}
A.nz_length = N;
A.indicesSorted = true;
}
/**
*
* Computes the sum of each column in the input matrix and returns the results in a vector:
*
* bj = sum(i=1:m ; aij)
*
*
* @param input Input matrix
* @param output Optional storage for output. Reshaped into a row vector. Modified.
* @return Vector containing the sum of each column
*/
public static FMatrixRMaj sumCols( FMatrixSparseCSC input, @Nullable FMatrixRMaj output ) {
if (output == null) {
output = new FMatrixRMaj(1, input.numCols);
} else {
output.reshape(1, input.numCols);
}
for (int col = 0; col < input.numCols; col++) {
int idx0 = input.col_idx[col];
int idx1 = input.col_idx[col + 1];
float sum = 0;
for (int i = idx0; i < idx1; i++) {
sum += input.nz_values[i];
}
output.data[col] = sum;
}
return output;
}
/**
*
* Computes the minimum of each column in the input matrix and returns the results in a vector:
*
* bj = min(i=1:m ; aij)
*
*
* @param input Input matrix
* @param output Optional storage for output. Reshaped into a row vector. Modified.
* @return Vector containing the minimums of each column
*/
public static FMatrixRMaj minCols( FMatrixSparseCSC input, @Nullable FMatrixRMaj output ) {
output = reshapeOrDeclare(output, 1, input.numCols);
for (int col = 0; col < input.numCols; col++) {
int idx0 = input.col_idx[col];
int idx1 = input.col_idx[col + 1];
// take in account the zeros
float min = idx1 - idx0 == input.numRows ? Float.MAX_VALUE : 0;
for (int i = idx0; i < idx1; i++) {
float v = input.nz_values[i];
if (min > v) {
min = v;
}
}
output.data[col] = min;
}
return output;
}
/**
*
* Computes the maximums of each column in the input matrix and returns the results in a vector:
*
* bj = max(i=1:m ; aij)
*
*
* @param input Input matrix
* @param output Optional storage for output. Reshaped into a row vector. Modified.
* @return Vector containing the maximums of each column
*/
public static FMatrixRMaj maxCols( FMatrixSparseCSC input, @Nullable FMatrixRMaj output ) {
output = reshapeOrDeclare(output, 1, input.numCols);
for (int col = 0; col < input.numCols; col++) {
int idx0 = input.col_idx[col];
int idx1 = input.col_idx[col + 1];
// take in account the zeros
float max = idx1 - idx0 == input.numRows ? -Float.MAX_VALUE : 0;
for (int i = idx0; i < idx1; i++) {
float v = input.nz_values[i];
if (max < v) {
max = v;
}
}
output.data[col] = max;
}
return output;
}
/**
*
* Computes the sum of each row in the input matrix and returns the results in a vector:
*
* bj = sum(i=1:n ; aji)
*
*
* @param input Input matrix
* @param output Optional storage for output. Reshaped into a column vector. Modified.
* @return Vector containing the sum of each row
*/
public static FMatrixRMaj sumRows( FMatrixSparseCSC input, @Nullable FMatrixRMaj output ) {
output = reshapeOrDeclare(output, input.numRows, 1);
Arrays.fill(output.data, 0, input.numRows, 0);
for (int col = 0; col < input.numCols; col++) {
int idx0 = input.col_idx[col];
int idx1 = input.col_idx[col + 1];
for (int i = idx0; i < idx1; i++) {
output.data[input.nz_rows[i]] += input.nz_values[i];
}
}
return output;
}
/**
*
* Computes the minimum of each row in the input matrix and returns the results in a vector:
*
* bj = min(i=1:n ; aji)
*
*
* @param input Input matrix
* @param output Optional storage for output. Reshaped into a column vector. Modified.
* @param gw work space
* @return Vector containing the minimum of each row
*/
public static FMatrixRMaj minRows( FMatrixSparseCSC input, @Nullable FMatrixRMaj output, @Nullable IGrowArray gw ) {
output = reshapeOrDeclare(output, input.numRows, 1);
int[] w = adjust(gw, input.numRows, input.numRows);
Arrays.fill(output.data, 0, input.numRows, Float.MAX_VALUE);
for (int col = 0; col < input.numCols; col++) {
int idx0 = input.col_idx[col];
int idx1 = input.col_idx[col + 1];
for (int i = idx0; i < idx1; i++) {
int row = input.nz_rows[i];
float v = input.nz_values[i];
if (output.data[row] > v) {
output.data[row] = v;
}
w[row]++;
}
}
for (int row = 0; row < input.numRows; row++) {
// consider the zeros now if a row wasn't filled in all the way
if (w[row] != input.numCols) {
if (output.data[row] > 0) {
output.data[row] = 0;
}
}
}
return output;
}
/**
*
* Computes the maximum of each row in the input matrix and returns the results in a vector:
*
* bj = max(i=1:n ; aji)
*
*
* @param input Input matrix
* @param output Optional storage for output. Reshaped into a column vector. Modified.
* @param gw work space
* @return Vector containing the maximum of each row
*/
public static FMatrixRMaj maxRows( FMatrixSparseCSC input, @Nullable FMatrixRMaj output, @Nullable IGrowArray gw ) {
output = reshapeOrDeclare(output, input.numRows, 1);
int[] w = adjust(gw, input.numRows, input.numRows);
Arrays.fill(output.data, 0, input.numRows, -Float.MAX_VALUE);
for (int col = 0; col < input.numCols; col++) {
int idx0 = input.col_idx[col];
int idx1 = input.col_idx[col + 1];
for (int i = idx0; i < idx1; i++) {
int row = input.nz_rows[i];
float v = input.nz_values[i];
if (output.data[row] < v) {
output.data[row] = v;
}
w[row]++;
}
}
for (int row = 0; row < input.numRows; row++) {
// consider the zeros now if a row wasn't filled in all the way
if (w[row] != input.numCols) {
if (output.data[row] < 0) {
output.data[row] = 0;
}
}
}
return output;
}
/**
* Zeros an inner rectangle inside the matrix.
*
* @param A Matrix that is to be modified.
* @param row0 Start row.
* @param row1 Stop row+1.
* @param col0 Start column.
* @param col1 Stop column+1.
*/
public static void zero( FMatrixSparseCSC A, int row0, int row1, int col0, int col1 ) {
for (int col = col1 - 1; col >= col0; col--) {
int numRemoved = 0;
int idx0 = A.col_idx[col], idx1 = A.col_idx[col + 1];
for (int i = idx0; i < idx1; i++) {
int row = A.nz_rows[i];
// if sorted a faster technique could be used
if (row >= row0 && row < row1) {
numRemoved++;
} else if (numRemoved > 0) {
A.nz_rows[i - numRemoved] = row;
A.nz_values[i - numRemoved] = A.nz_values[i];
}
}
if (numRemoved > 0) {
// this could be done more intelligently. Each time a column is adjusted all the columns are adjusted
// after it. Maybe accumulate the changes in each column and do it in one pass? Need an array to store
// those results though
for (int i = idx1; i < A.nz_length; i++) {
A.nz_rows[i - numRemoved] = A.nz_rows[i];
A.nz_values[i - numRemoved] = A.nz_values[i];
}
A.nz_length -= numRemoved;
for (int i = col + 1; i <= A.numCols; i++) {
A.col_idx[i] -= numRemoved;
}
}
}
}
/**
* Computes the inner product of two column vectors taken from the input matrices.
*
* dot = A(:,colA)'*B(:,colB)
*
* @param A Matrix
* @param colA Column in A
* @param B Matrix
* @param colB Column in B
* @return Dot product
*/
public static float dotInnerColumns( FMatrixSparseCSC A, int colA, FMatrixSparseCSC B, int colB,
@Nullable IGrowArray gw, @Nullable FGrowArray gx ) {
return ImplMultiplication_FSCC.dotInnerColumns(A, colA, B, colB, gw, gx);
}
/**
*
* Solves for x in the following equation:
*
* A*x = b
*
*
*
* If the system could not be solved then false is returned. If it returns true
* that just means the algorithm finished operating, but the results could still be bad
* because 'A' is singular or nearly singular.
*
*
*
* If repeat calls to solve are being made then one should consider using {@link LinearSolverFactory_FSCC}
* instead.
*
*
*
* It is ok for 'b' and 'x' to be the same matrix.
*
*
* @param a (Input) A matrix that is m by n. Not modified.
* @param b (Input) A matrix that is n by k. Not modified.
* @param x (Output) A matrix that is m by k. Modified.
* @return true if it could invert the matrix false if it could not.
*/
public static boolean solve( FMatrixSparseCSC a,
FMatrixRMaj b,
FMatrixRMaj x ) {
x.reshape(a.numCols, b.numCols);
LinearSolverSparse solver;
if (a.numRows > a.numCols) {
solver = LinearSolverFactory_FSCC.qr(FillReducing.NONE);// todo specify a filling that makes sense
} else {
solver = LinearSolverFactory_FSCC.lu(FillReducing.NONE);
}
// Ensure that the input isn't modified
if (solver.modifiesA())
a = a.copy();
if (solver.modifiesB())
b = b.copy();
// decompose then solve the matrix
if (!solver.setA(a))
return false;
solver.solve(b, x);
return true;
}
/**
*
* Solves for x in the following equation:
*
* A*x = b
*
*
*
* If the system could not be solved then false is returned. If it returns true
* that just means the algorithm finished operating, but the results could still be bad
* because 'A' is singular or nearly singular.
*
*
*
* If repeat calls to solve are being made then one should consider using {@link LinearSolverFactory_FSCC}
* instead.
*
*
*
* It is ok for 'b' and 'x' to be the same matrix.
*
*
* @param a (Input) A matrix that is m by n. Not modified.
* @param b (Input) A matrix that is n by k. Not modified.
* @param x (Output) A matrix that is m by k. Modified.
* @return true if it could invert the matrix false if it could not.
*/
public static boolean solve( FMatrixSparseCSC a,
FMatrixSparseCSC b,
FMatrixSparseCSC x ) {
x.reshape(a.numCols, b.numCols);
LinearSolverSparse solver;
if (a.numRows > a.numCols) {
solver = LinearSolverFactory_FSCC.qr(FillReducing.NONE);// todo specify a filling that makes sense
} else {
solver = LinearSolverFactory_FSCC.lu(FillReducing.NONE);
}
// Ensure that the input isn't modified
if (solver.modifiesA())
a = a.copy();
if (solver.modifiesB())
b = b.copy();
// decompose then solve the matrix
if (!solver.setA(a))
return false;
solver.solveSparse(b, x);
return true;
}
/**
*
* Performs a matrix inversion operation that does not modify the original
* and stores the results in another matrix. The two matrices must have the
* same dimension.
*
* B = A-1
*
*
*
* If the algorithm could not invert the matrix then false is returned. If it returns true
* that just means the algorithm finished. The results could still be bad
* because the matrix is singular or nearly singular.
*
*
*
* For medium to large matrices there might be a slight performance boost to using
* {@link LinearSolverFactory_FSCC} instead.
*
*
* @param A (Input) The matrix that is to be inverted. Not modified.
* @param inverse (Output) Where the inverse matrix is stored. Modified.
* @return true if it could invert the matrix false if it could not.
*/
public static boolean invert( FMatrixSparseCSC A, FMatrixRMaj inverse ) {
if (A.numRows != A.numCols)
throw new IllegalArgumentException("A must be a square matrix");
inverse.reshape(A.numRows, A.numCols);
LinearSolverSparse solver;
solver = LinearSolverFactory_FSCC.lu(FillReducing.NONE);
// Ensure that the input isn't modified
if (solver.modifiesA())
A = A.copy();
FMatrixRMaj I = CommonOps_FDRM.identity(A.numRows);
// decompose then solve the matrix
if (!solver.setA(A))
return false;
solver.solve(I, inverse);
return true;
}
/**
* Returns the determinant of the matrix. If the inverse of the matrix is also
* needed, then using {@link org.ejml.interfaces.decomposition.LUDecomposition_F32} directly (or any
* similar algorithm) can be more efficient.
*
* @param A The matrix whose determinant is to be computed. Not modified.
* @return The determinant.
*/
public static float det( FMatrixSparseCSC A ) {
LUSparseDecomposition_F32 alg = DecompositionFactory_FSCC.lu(FillReducing.NONE);
if (alg.inputModified()) {
A = A.copy();
}
if (!alg.decompose(A))
return 0.0f;
return alg.computeDeterminant().real;
}
/**
* Copies all elements from input into output which are > tol.
*
* @param input (Input) input matrix. Not modified.
* @param output (Output) Output matrix. Modified and shaped to match input.
* @param tol Tolerance for defining zero
*/
public static void removeZeros( FMatrixSparseCSC input, FMatrixSparseCSC output, float tol ) {
ImplCommonOps_FSCC.removeZeros(input, output, tol);
}
/**
* Removes all elements from the matrix that are > tol. The modification is done in place and no temporary
* storage is declared.
*
* @param A (Input/Output) input matrix. Modified.
* @param tol Tolerance for defining zero
*/
public static void removeZeros( FMatrixSparseCSC A, float tol ) {
ImplCommonOps_FSCC.removeZeros(A, tol);
}
/**
* For each duplicate element in the matrix it will remove the duplicates and replace them with a single element
* that is the sum of all the duplicates. The result will be a valid matrix. This is done inplace and does not
* require the matrix to be initially sorted.
*
* @param A (Input/Output) input matrix. Modified.
* @param work Nullable. Internal workspace array.
*/
public static void duplicatesAdd( FMatrixSparseCSC A, @Nullable IGrowArray work ) {
ImplCommonOps_FSCC.duplicatesAdd(A, work);
}
/**
* Multiply all elements of row 'i' by value[i]. A[i,:] *= values[i]
*
* @param diag (Input) multiplication factors
* @param offset (Input) First index in values
* @param A (Input/Output) Matrix. Modified.
*/
public static void multRows( float[] diag, int offset, FMatrixSparseCSC A ) {
if (diag.length < A.numRows)
throw new IllegalArgumentException("Array is too small. " + diag.length + " < " + A.numCols);
for (int i = 0; i < A.nz_length; i++) {
A.nz_values[i] *= diag[A.nz_rows[i + offset]];
}
}
/**
* Divides all elements of row 'i' by value[i]. A[i,:] /= values[i]
*
* @param diag (Input) division factors
* @param offset (Input) First index in values
* @param A (Input/Output) Matrix. Modified.
*/
public static void divideRows( float[] diag, int offset, FMatrixSparseCSC A ) {
if (diag.length < A.numRows)
throw new IllegalArgumentException("Array is too small. " + diag.length + " < " + A.numCols);
for (int i = 0; i < A.nz_length; i++) {
A.nz_values[i] /= diag[A.nz_rows[i + offset]];
}
}
/**
*
* This computes the trace of the matrix:
*
* trace = ∑i=1:n { aii }
* where n = min(numRows,numCols)
*
*
* @param A (Input) Matrix. Not modified.
*/
public static float trace( FMatrixSparseCSC A ) {
float output = 0;
int o = Math.min(A.numCols, A.numRows);
for (int col = 0; col < o; col++) {
int idx0 = A.col_idx[col];
int idx1 = A.col_idx[col + 1];
for (int i = idx0; i < idx1; i++) {
if (A.nz_rows[i] == col) {
output += A.nz_values[i];
break;
}
}
}
return output;
}
/**
* This applies a given unary function on every value stored in the matrix
*
* B = f(A). A and B can be the same instance.
*
* @param input (Input) input matrix. Not modified
* @param func Unary function accepting a float
* @param output (Output) Matrix. Modified.
* @return The output matrix
*/
public static FMatrixSparseCSC apply( FMatrixSparseCSC input, FOperatorUnary func, @Nullable FMatrixSparseCSC output ) {
if (output == null) {
output = input.createLike();
}
if (input != output) {
output.copyStructure(input);
}
for (int i = 0; i < input.nz_length; i++) {
output.nz_values[i] = func.apply(input.nz_values[i]);
}
return output;
}
public static FMatrixSparseCSC apply( FMatrixSparseCSC input, FOperatorUnary func ) {
return apply(input, func, input);
}
/**
* This applies a given unary function on every nz row,value stored in the matrix
*
* B = f(A). A and B can be the same instance.
*
* @param input (Input) input matrix. Not modified
* @param func Binary function accepting (row-index, value)
* @param output (Output) Matrix. Modified.
* @return The output matrix
*/
public static FMatrixSparseCSC applyRowIdx( FMatrixSparseCSC input, FOperatorBinaryIdx func, @Nullable FMatrixSparseCSC output ) {
if (output == null) {
output = input.createLike();
}
if (input != output) {
output.copyStructure(input);
}
for (int i = 0; i < input.nz_length; i++) {
output.nz_values[i] = func.apply(input.nz_rows[i], input.nz_values[i]);
}
return output;
}
/**
* This applies a given unary function on every non-zero column, value stored in the matrix
*
* B = f(A). A and B can be the same instance.
*
* @param input (Input) input matrix. Not modified
* @param func Binary function accepting (column-index, value)
* @param output (Output) Matrix. Modified.
* @return The output matrix
*/
public static FMatrixSparseCSC applyColumnIdx( FMatrixSparseCSC input, FOperatorBinaryIdx func, @Nullable FMatrixSparseCSC output ) {
if (output == null) {
output = input.createLike();
}
if (input != output) {
output.copyStructure(input);
}
for (int col = 0; col < input.numCols; col++) {
for (int i = input.col_idx[col]; i < input.col_idx[col + 1]; i++) {
output.nz_values[i] = func.apply(col, input.nz_values[i]);
}
}
return output;
}
/**
* This accumulates the matrix values to a scalar value.
*
*
* result = initialValue
* for all (i,j) result = func( result, A[i,j] )
*
*
* @param input (Input) input matrix. Not modified
* @param initValue initial value for accumulator
* @param func Accumulator function defining "+" for accumulator += cellValue
* @return accumulated value
*/
public static float reduceScalar( FMatrixSparseCSC input, float initValue, FOperatorBinary func ) {
float result = initValue;
for (int i = 0; i < input.nz_length; i++) {
result = func.apply(result, input.nz_values[i]);
}
return result;
}
public static float reduceScalar( FMatrixSparseCSC input, FOperatorBinary func ) {
return reduceScalar(input, 0, func);
}
/**
* This accumulates the values per column to a scalar value
*
*
* for each column `j`,
* result = initialValue
* for each row 'i' result = func( result, A[i,j] )
* output[j] = result
*
*
* @param input (Input) input matrix. Not modified
* @param initValue initial value for accumulator
* @param func Accumulator function defining "+" for accumulator += cellValue
* @param output output (Output) Vector, where result can be stored in
* @param mask (Optional) Mask for specifying which entries should be overwritten
* @return a column-vector, where v[i] == values of column i reduced to scalar based on `func`
*/
public static FMatrixRMaj reduceColumnWise( FMatrixSparseCSC input, float initValue, FOperatorBinary func,
@Nullable FMatrixRMaj output, @Nullable Mask mask ) {
if (output == null) {
output = new FMatrixRMaj(1, input.numCols);
} else {
output.reshape(1, input.numCols);
}
for (int col = 0; col < input.numCols; col++) {
int start = input.col_idx[col];
int end = input.col_idx[col + 1];
float acc = initValue;
if (mask == null || mask.isSet(col)) {
for (int i = start; i < end; i++) {
acc = func.apply(acc, input.nz_values[i]);
}
}
output.data[col] = acc;
}
return output;
}
/**
* This accumulates the values per row to a scalar value
*
*
* for each column `j`,
* result = initialValue
* for each row 'i' result = func( result, A[i,j] )
* output[j] = result
*
*
* @param input (Input) input matrix. Not modified
* @param initValue initial value for accumulator
* @param func Accumulator function defining "+" for accumulator += cellValue
* @param output output (Output) Vector, where result can be stored in
* @return a row-vector, where v[i] == values of row i reduced to scalar based on `func`
*/
public static FMatrixRMaj reduceRowWise( FMatrixSparseCSC input, float initValue, FOperatorBinary func,
@Nullable FMatrixRMaj output ) {
if (output == null) {
output = new FMatrixRMaj(1, input.numRows);
} else {
output.reshape(1, input.numCols);
}
Arrays.fill(output.data, initValue);
for (int col = 0; col < input.numCols; col++) {
int start = input.col_idx[col];
int end = input.col_idx[col + 1];
for (int i = start; i < end; i++) {
output.data[input.nz_rows[i]] = func.apply(output.data[input.nz_rows[i]], input.nz_values[i]);
}
}
return output;
}
}