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

org.ejml.data.DMatrixSparseCSC Maven / Gradle / Ivy

Go to download

A fast and easy to use dense and sparse matrix linear algebra library written in Java.

The newest version!
/*
 * 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.data;

import org.ejml.UtilEjml;
import org.ejml.ops.MatrixIO;
import org.ejml.ops.SortCoupledArray_F64;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.Iterator;

/**
 * 

Compressed Column (CC) sparse matrix format. Only non-zero elements are stored.

*

* Format:
* Row indexes for column j are stored in rol_idx[col_idx[j]] to rol_idx[col_idx[j+1]-1]. The values * for the corresponding elements are stored at data[col_idx[j]] to data[col_idx[j+1]-1].
*
* Row indexes must be specified in chronological order. *

* * @author Peter Abeles */ public class DMatrixSparseCSC implements DMatrixSparse { /** * Storage for non-zero values. Only valid up to length-1. */ public double[] nz_values = UtilEjml.ZERO_LENGTH_F64; /** * Length of data. Number of non-zero values in the matrix */ public int nz_length; /** * Specifies which row a specific non-zero value corresponds to. If they are sorted or not with in each column * is specified by the {@link #indicesSorted} flag. */ public int[] nz_rows = UtilEjml.ZERO_LENGTH_I32; /** * Stores the range of indexes in the non-zero lists that belong to each column. Column 'i' corresponds to * indexes col_idx[i] to col_idx[i+1]-1, inclusive. */ public int[] col_idx; /** * Number of rows in the matrix */ public int numRows; /** * Number of columns in the matrix */ public int numCols; /** * Flag that's used to indicate of the row indices are sorted or not. */ public boolean indicesSorted = false; /** * Constructor with a default arrayLength of zero. * * @param numRows Number of rows * @param numCols Number of columns */ public DMatrixSparseCSC( int numRows, int numCols ) { this(numRows, numCols, 0); } /** * Specifies shape and number of non-zero elements that can be stored. * * @param numRows Number of rows * @param numCols Number of columns * @param arrayLength Initial maximum number of non-zero elements that can be in the matrix */ public DMatrixSparseCSC( int numRows, int numCols, int arrayLength ) { if (numRows < 0 || numCols < 0 || arrayLength < 0) throw new IllegalArgumentException("Rows, columns, and arrayLength must be not be negative"); this.numRows = numRows; this.numCols = numCols; this.nz_length = 0; col_idx = new int[numCols + 1]; growMaxLength(arrayLength, false); } public DMatrixSparseCSC( DMatrixSparseCSC original ) { this(original.numRows, original.numCols, original.nz_length); setTo(original); } @Override public int getNumRows() { return numRows; } @Override public int getNumCols() { return numCols; } @Override public DMatrixSparseCSC copy() { return new DMatrixSparseCSC(this); } @Override public DMatrixSparseCSC createLike() { return new DMatrixSparseCSC(numRows, numCols); } @Override public void setTo( Matrix original ) { DMatrixSparseCSC o = (DMatrixSparseCSC)original; reshape(o.numRows, o.numCols, o.nz_length); this.nz_length = o.nz_length; System.arraycopy(o.nz_values, 0, nz_values, 0, nz_length); System.arraycopy(o.nz_rows, 0, nz_rows, 0, nz_length); System.arraycopy(o.col_idx, 0, col_idx, 0, numCols + 1); this.indicesSorted = o.indicesSorted; } @Override public void print() { MatrixIO.printFancy(System.out, this, MatrixIO.DEFAULT_LENGTH); } @Override public void print( String format ) { MatrixIO.print(System.out, this, format); } @Override public void printNonZero() { String format = "%d %d " + MatrixIO.DEFAULT_FLOAT_FORMAT + "\n"; System.out.println("Type = " + getType().name() + " , rows = " + numRows + " , cols = " + numCols + " , nz_length = " + nz_length); for (int col = 0; col < numCols; col++) { int idx0 = col_idx[col]; int idx1 = col_idx[col + 1]; for (int i = idx0; i < idx1; i++) { int row = nz_rows[i]; double value = nz_values[i]; System.out.printf(format, row, col, value); } } } @Override public boolean isAssigned( int row, int col ) { return nz_index(row, col) >= 0; } @Override public double get( int row, int col ) { if (row < 0 || row >= numRows || col < 0 || col >= numCols) throw new IllegalArgumentException("Outside of matrix bounds"); return unsafe_get(row, col); } @Override public double get( int row, int col, double fallBackValue ) { if (row < 0 || row >= numRows || col < 0 || col >= numCols) throw new IllegalArgumentException("Outside of matrix bounds"); return unsafe_get(row, col, fallBackValue); } @Override public double unsafe_get( int row, int col ) { int index = nz_index(row, col); if (index >= 0) return nz_values[index]; return 0; } @Override public double unsafe_get( int row, int col, double fallBackValue ) { int index = nz_index(row, col); if (index >= 0) return nz_values[index]; return fallBackValue; } /** * Returns the index in nz_rows for the element at (row,col) if it already exists in the matrix. If not then -1 * is returned. * * @param row row coordinate * @param col column coordinate * @return nz_row index or -1 if the element does not exist */ public int nz_index( int row, int col ) { int col0 = col_idx[col]; int col1 = col_idx[col + 1]; if (this.indicesSorted) { return Arrays.binarySearch(nz_rows, col0, col1, row); } else { for (int i = col0; i < col1; i++) { if (nz_rows[i] == row) { return i; } } return -1; } } @Override public void set( int row, int col, double val ) { if (row < 0 || row >= numRows || col < 0 || col >= numCols) throw new IllegalArgumentException("Outside of matrix bounds"); unsafe_set(row, col, val); } @Override public void unsafe_set( int row, int col, double val ) { int index = nz_index(row, col); if (index >= 0) { nz_values[index] = val; } else { int idx0 = col_idx[col]; int idx1 = col_idx[col + 1]; // determine the index the new element should be inserted at. This is done to keep it sorted if // it was already sorted for (index = idx0; index < idx1; index++) { if (row < nz_rows[index]) { break; } } // shift all the col_idx after this point by 1 for (int i = col + 1; i <= numCols; i++) { col_idx[i]++; } // if it's already at the maximum array length grow the arrays if (nz_length >= nz_values.length) growMaxLength(nz_length*2 + 1, true); // shift everything by one for (int i = nz_length; i > index; i--) { nz_rows[i] = nz_rows[i - 1]; nz_values[i] = nz_values[i - 1]; } nz_rows[index] = row; nz_values[index] = val; nz_length++; } } @Override public void remove( int row, int col ) { int index = nz_index(row, col); if (index < 0) // it's not in the nz structure return; // shift all the col_idx after this point by -1 for (int i = col + 1; i <= numCols; i++) { col_idx[i]--; } nz_length--; for (int i = index; i < nz_length; i++) { nz_rows[i] = nz_rows[i + 1]; nz_values[i] = nz_values[i + 1]; } } @Override public void zero() { Arrays.fill(col_idx, 0, numCols + 1, 0); nz_length = 0; indicesSorted = false; // see justification in reshape } @Override public DMatrixSparseCSC create( int numRows, int numCols ) { return new DMatrixSparseCSC(numRows, numCols); } @Override public int getNonZeroLength() { return nz_length; } @Override public void reshape( int numRows, int numCols, int arrayLength ) { if (numRows < 0 || numCols < 0 || arrayLength < 0) throw new IllegalArgumentException("Rows, columns, and arrayLength must be not be negative"); // OK so technically it is sorted, but forgetting to correctly set this flag is a common mistake so // decided to be conservative and mark it as unsorted so that stuff doesn't blow up this.indicesSorted = false; this.numRows = numRows; this.numCols = numCols; growMaxLength(arrayLength, false); this.nz_length = 0; if (numCols + 1 > col_idx.length) { col_idx = new int[numCols + 1]; } else { Arrays.fill(col_idx, 0, numCols + 1, 0); } } @Override public void reshape( int numRows, int numCols ) { reshape(numRows, numCols, 0); } @Override public void shrinkArrays() { if (nz_length < nz_values.length) { double[] tmp_values = new double[nz_length]; int[] tmp_rows = new int[nz_length]; System.arraycopy(this.nz_values, 0, tmp_values, 0, nz_length); System.arraycopy(this.nz_rows, 0, tmp_rows, 0, nz_length); this.nz_values = tmp_values; this.nz_rows = tmp_rows; } } /** * Increases the maximum size of the data array so that it can store sparse data up to 'length'. The class * parameter nz_length is not modified by this function call. * * @param arrayLength Desired maximum length of sparse data * @param preserveValue If true the old values will be copied into the new arrays. If false that step will be skipped. */ public void growMaxLength( int arrayLength, boolean preserveValue ) { if (arrayLength < 0) throw new IllegalArgumentException("Negative array length. Overflow?"); // NOTE: The code below has been (experimentally) commented out. A situation arose where we wanted to exceed // the max physical size, which would then be corrected later on. // see if multiplying numRows*numCols will cause an overflow. If it won't then pick the smaller of the two // if( numRows != 0 && numCols <= Integer.MAX_VALUE / numRows ) { // // save the user from themselves // arrayLength = Math.min(numRows*numCols, arrayLength); // } if (arrayLength > this.nz_values.length) { double[] data = new double[arrayLength]; int[] row_idx = new int[arrayLength]; if (preserveValue) { System.arraycopy(this.nz_values, 0, data, 0, this.nz_length); System.arraycopy(this.nz_rows, 0, row_idx, 0, this.nz_length); } this.nz_values = data; this.nz_rows = row_idx; } } /** * Increases the maximum number of columns in the matrix. * * @param desiredColumns Desired number of columns. * @param preserveValue If the array needs to be expanded should it copy the previous values? */ public void growMaxColumns( int desiredColumns, boolean preserveValue ) { if (col_idx.length < desiredColumns + 1) { int[] c = new int[desiredColumns + 1]; if (preserveValue) System.arraycopy(col_idx, 0, c, 0, col_idx.length); col_idx = c; } } /** * Given the histogram of columns compute the col_idx for the matrix. nz_length is automatically set and * nz_values will grow if needed. * * @param histogram histogram of column values in the sparse matrix. modified, see above. */ public void histogramToStructure( int[] histogram ) { col_idx[0] = 0; int index = 0; for (int i = 1; i <= numCols; i++) { col_idx[i] = index += histogram[i - 1]; } nz_length = index; growMaxLength(nz_length, false); if (col_idx[numCols] != nz_length) throw new RuntimeException("Egads"); } /** * Sorts the row indices in ascending order. * * @param sorter (Optional) Used to sort rows. If null a new instance will be declared internally. */ public void sortIndices( @Nullable SortCoupledArray_F64 sorter ) { if (sorter == null) sorter = new SortCoupledArray_F64(); sorter.quick(col_idx, numCols + 1, nz_rows, nz_values); indicesSorted = true; } /** * Copies the non-zero structure of orig into "this" * * @param orig Matrix who's structure is to be copied */ public void copyStructure( DMatrixSparseCSC orig ) { reshape(orig.numRows, orig.numCols, orig.nz_length); this.nz_length = orig.nz_length; System.arraycopy(orig.col_idx, 0, col_idx, 0, orig.numCols + 1); System.arraycopy(orig.nz_rows, 0, nz_rows, 0, orig.nz_length); } /** * If the indices has been sorted or not * * @return true if sorted or false if not sorted */ public boolean isIndicesSorted() { return indicesSorted; } /** * Returns true if number of non-zero elements is the maximum size * * @return true if no more non-zero elements can be added */ public boolean isFull() { return nz_length == numRows*numCols; } @Override public MatrixType getType() { return MatrixType.DSCC; } @Override public Iterator createCoordinateIterator() { return new Iterator<>() { final CoordinateRealValue coordinate = new CoordinateRealValue(); int nz_index = 0; // the index of the non-zero value and row int column = 0; // which column it's in { incrementColumn(); } @Override public boolean hasNext() { return nz_index < nz_length; } @Override public CoordinateRealValue next() { coordinate.row = nz_rows[nz_index]; coordinate.col = column; coordinate.value = nz_values[nz_index]; nz_index++; incrementColumn(); return coordinate; } private void incrementColumn() { while (column + 1 <= numCols && nz_index >= col_idx[column + 1]) { column++; } } }; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy