
org.numenta.nupic.util.SparseMatrix Maven / Gradle / Ivy
/* ---------------------------------------------------------------------
* Numenta Platform for Intelligent Computing (NuPIC)
* Copyright (C) 2014, Numenta, Inc. Unless you have an agreement
* with Numenta, Inc., for a separate license for this software code, the
* following terms and conditions apply:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses.
*
* http://numenta.org/licenses/
* ---------------------------------------------------------------------
*/
package org.numenta.nupic.util;
import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import java.lang.reflect.Array;
import java.util.Arrays;
/**
* Allows storage of array data in sparse form, meaning that the indexes
* of the data stored are maintained while empty indexes are not. This allows
* savings in memory and computational efficiency because iterative algorithms
* need only query indexes containing valid data. The dimensions of matrix defined
* at construction time and immutable - matrix fixed size data structure.
*
* @author David Ray
*
* @param
*/
public abstract class SparseMatrix {
protected final int[] dimensionMultiples;
protected final int[] dimensions;
protected final int numDimensions;
protected final boolean isColumnMajor;
public SparseMatrix(int[] dimensions) {
this(dimensions, false);
}
/**
* Constructs a new {@code SparseMatrix} object to be configured with specified
* dimensions and major ordering.
*
* @param dimensions the dimensions of this sparse array
* @param useColumnMajorOrdering flag indicating whether to use column ordering or
* row major ordering. if false (the default), then row
* major ordering will be used. If true, then column major
* ordering will be used.
*/
public SparseMatrix(int[] dimensions, boolean useColumnMajorOrdering) {
this.dimensions = dimensions;
this.numDimensions = dimensions.length;
this.dimensionMultiples = initDimensionMultiples(
useColumnMajorOrdering ? reverse(dimensions) : dimensions);
isColumnMajor = useColumnMajorOrdering;
}
/**
* Returns the utility array which holds the multiples used
* to calculate indexes.
*
* @return the utility multiples array.
*/
public int[] getDimensionMultiples() {
return dimensionMultiples;
}
/**
* Returns the array describing the dimensionality of the configured array.
* @return the array describing the dimensionality of the configured array.
*/
public int[] getDimensions() {
return dimensions;
}
/**
* Returns the configured number of dimensions.
*
* @return the configured number of dimensions.
*/
public int getNumDimensions() {
return numDimensions;
}
/**
* Sets the object to occupy the specified index.
*
* @param index the index the object will occupy
* @param object the object to be indexed.
*
* @return this {@code SparseMatrix} implementation
*/
protected > S set(int index, T object) { return null; }
/**
* Sets the object to occupy the specified index.
*
* @param index the index the object will occupy
* @param value the value to be indexed.
*
* @return this {@code SparseMatrix} implementation
*/
protected > S set(int index, int value) { return null; }
/**
* Sets the object to occupy the specified index.
*
* @param index the index the object will occupy
* @param value the value to be indexed.
*
* @return this {@code SparseMatrix} implementation
*/
protected > S set(int index, double value) { return null; }
/**
* Sets the specified object to be indexed at the index
* computed from the specified coordinates.
* @param object the object to be indexed.
* @param coordinates the row major coordinates [outer --> ,...,..., inner]
*
* @return this {@code SparseMatrix} implementation
*/
protected > S set(int[] coordinates, T object) { return null; }
/**
* Sets the specified object to be indexed at the index
* computed from the specified coordinates.
* @param value the value to be indexed.
* @param coordinates the row major coordinates [outer --> ,...,..., inner]
*
* @return this {@code SparseMatrix} implementation
*/
protected > S set(int value, int... coordinates) { return null; }
/**
* Sets the specified object to be indexed at the index
* computed from the specified coordinates.
* @param value the value to be indexed.
* @param coordinates the row major coordinates [outer --> ,...,..., inner]
*
* @return this {@code SparseMatrix} implementation
*/
protected > S set(double value, int... coordinates) { return null; }
/**
* Returns the T at the specified index.
*
* @param index the index of the T to return
* @return the T at the specified index.
*/
protected T getObject(int index) { return null; }
/**
* Returns the T at the specified index.
*
* @param index the index of the T to return
* @return the T at the specified index.
*/
protected int getIntValue(int index) { return -1; }
/**
* Returns the T at the specified index.
*
* @param index the index of the T to return
* @return the T at the specified index.
*/
protected double getDoubleValue(int index) { return -1.0; }
/**
* Returns an outer array of T values.
* @return
*/
protected abstract V values();
/**
* Returns the T at the index computed from the specified coordinates
* @param coordinates the coordinates from which to retrieve the indexed object
* @return the indexed object
*/
protected T get(int... coordinates) { return null; }
/**
* Returns the int value at the index computed from the specified coordinates
* @param coordinates the coordinates from which to retrieve the indexed object
* @return the indexed object
*/
protected int getIntValue(int... coordinates) { return -1; }
/**
* Returns the double value at the index computed from the specified coordinates
* @param coordinates the coordinates from which to retrieve the indexed object
* @return the indexed object
*/
protected double getDoubleValue(int... coordinates) { return -1.0; }
/**
* Returns a sorted array of occupied indexes.
* @return a sorted array of occupied indexes.
*/
public int[] getSparseIndices() {
return null;
}
/**
* Returns an array of all the flat indexes that can be
* computed from the current configuration.
* @return
*/
public int[] get1DIndexes() {
TIntList results = new TIntArrayList(getMaxIndex() + 1);
visit(dimensions, 0, new int[numDimensions], results);
return results.toArray();
}
/**
* Recursively loops through the matrix dimensions to fill the results
* array with flattened computed array indexes.
*
* @param bounds
* @param currentDimension
* @param p
* @param results
*/
private void visit(int[] bounds, int currentDimension, int[] p, TIntList results) {
for (int i = 0; i < bounds[currentDimension]; i++) {
p[currentDimension] = i;
if (currentDimension == p.length - 1) {
results.add(computeIndex(p));
}
else visit(bounds, currentDimension + 1, p, results);
}
}
/**
* Returns the maximum accessible flat index.
* @return the maximum accessible flat index.
*/
public int getMaxIndex() {
return dimensions[0] * Math.max(1, dimensionMultiples[0]) - 1;
}
/**
* Uses the specified {@link TypeFactory} to return an array
* filled with the specified object type, according this {@code SparseMatrix}'s
* configured dimensions
*
* @param factory a factory to make a specific type
* @return the dense array
*/
@SuppressWarnings("unchecked")
public T[] asDense(TypeFactory factory) {
T[] retVal = (T[])Array.newInstance(factory.typeClass(), dimensions);
fill(factory, 0, dimensions, dimensions[0], retVal);
return retVal;
}
/**
* Uses reflection to create and fill a dynamically created multidimensional array.
*
* @param f the {@link TypeFactory}
* @param dimensionIndex the current index into this class's configured dimensions array
* *NOT* the dimensions used as this method's argument
* @param dimensions the array specifying remaining dimensions to create
* @param count the current dimensional size
* @param arr the array to fill
* @return a dynamically created multidimensional array
*/
@SuppressWarnings("unchecked")
protected Object[] fill(TypeFactory f, int dimensionIndex, int[] dimensions, int count, Object[] arr) {
if(dimensions.length == 1) {
for(int i = 0;i < count;i++) {
arr[i] = f.make(this.dimensions);
}
return arr;
}else{
for(int i = 0;i < count;i++) {
int[] inner = copyInnerArray(dimensions);
T[] r = (T[])Array.newInstance(f.typeClass(), inner);
arr[i] = fill(f, dimensionIndex + 1, inner, this.dimensions[dimensionIndex + 1], r);
}
return arr;
}
}
/**
* Utility method to shrink a single dimension array by one index.
* @param array the array to shrink
* @return
*/
protected int[] copyInnerArray(int[] array) {
if(array.length == 1) return array;
int[] retVal = new int[array.length - 1];
System.arraycopy(array, 1, retVal, 0, array.length - 1);
return retVal;
}
/**
* Reverses the specified array.
* @param input
* @return
*/
public static int[] reverse(int[] input) {
int[] retVal = new int[input.length];
for(int i = input.length - 1, j = 0;i >= 0;i--, j++) {
retVal[j] = input[i];
}
return retVal;
}
/**
* Initializes internal helper array which is used for multidimensional
* index computation.
*
* @param dimensions
* @return
*/
protected int[] initDimensionMultiples(int[] dimensions) {
int holder = 1;
int len = dimensions.length;
int[] dimensionMultiples = new int[numDimensions];
for(int i = 0;i < len;i++) {
holder *= (i == 0 ? 1 : dimensions[len - i]);
dimensionMultiples[len - 1 - i] = holder;
}
return dimensionMultiples;
}
/**
* Assumes row-major ordering. For a 3 dimensional array, the
* indexing expected is [depth, height, width] or [slice, row, column].
*
* @param coordinates
* @return
*/
public int computeIndex(int[] coordinates) {
return computeIndex(coordinates, true);
}
/**
* Assumes row-major ordering. For a 3 dimensional array, the
* indexing expected is [depth, height, width] or [slice, row, column].
*
* @param coordinates
* @param doCheck won't validate bounds if false
* @return
*/
public int computeIndex(int[] coordinates, boolean doCheck) {
if(doCheck) checkDims(coordinates);
int[] localMults = isColumnMajor ? reverse(dimensionMultiples) : dimensionMultiples;
int base = 0;
for(int i = 0;i < coordinates.length;i++) {
base += (localMults[i] * coordinates[i]);
}
return base;
}
/**
* Returns an integer array representing the coordinates of the specified index
* in terms of the configuration of this {@code SparseMatrix}.
*
* @param index the flat index to be returned as coordinates
* @return
*/
public int[] computeCoordinates(int index) {
int[] returnVal = new int[numDimensions];
int base = index;
for(int i = 0;i < dimensionMultiples.length;i++) {
int quotient = base / dimensionMultiples[i];
base %= dimensionMultiples[i];
returnVal[i] = quotient;
}
return isColumnMajor ? reverse(returnVal) : returnVal;
}
/**
* Checks the indexes specified to see whether they are within the
* configured bounds and size parameters of this array configuration.
*
* @param index the array dimensions to check
*/
protected void checkDims(int[] index) {
if(index.length != numDimensions) {
throw new IllegalArgumentException("Specified coordinates exceed the configured array dimensions " +
"input dimensions: " + index.length + " > number of configured dimensions: " + numDimensions);
}
for(int i = 0;i < index.length - 1;i++) {
if(index[i] >= dimensions[i]) {
throw new IllegalArgumentException("Specified coordinates exceed the configured array dimensions " +
print1DArray(index) + " > " + print1DArray(dimensions));
}
}
}
/**
* Prints the specified array to a returned String.
*
* @param aObject the array object to print.
* @return the array in string form suitable for display.
*/
public static String print1DArray(Object aObject) {
if (aObject.getClass().isArray()) {
if (aObject instanceof Object[]) // can we cast to Object[]
return Arrays.toString((Object[]) aObject);
else { // we can't cast to Object[] - case of primitive arrays
int length = Array.getLength(aObject);
Object[] objArr = new Object[length];
for (int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy