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

mikera.matrixx.Matrixx Maven / Gradle / Ivy

Go to download

Fast double-precision vector and matrix maths library for Java, supporting N-dimensional numeric arrays.

There is a newer version: 0.67.0
Show newest version
package mikera.matrixx;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import mikera.arrayz.INDArray;
import mikera.indexz.Index;
import mikera.matrixx.impl.ADiagonalMatrix;
import mikera.matrixx.impl.AStridedMatrix;
import mikera.matrixx.impl.ColumnMatrix;
import mikera.matrixx.impl.DenseColumnMatrix;
import mikera.matrixx.impl.DiagonalMatrix;
import mikera.matrixx.impl.IdentityMatrix;
import mikera.matrixx.impl.ScalarMatrix;
import mikera.matrixx.impl.SparseColumnMatrix;
import mikera.matrixx.impl.SparseRowMatrix;
import mikera.matrixx.impl.StridedMatrix;
import mikera.matrixx.impl.StridedRowMatrix;
import mikera.matrixx.impl.ZeroMatrix;
import mikera.vectorz.AVector;
import mikera.vectorz.Tools;
import mikera.vectorz.Vector3;
import mikera.vectorz.Vectorz;
import mikera.vectorz.impl.SparseIndexedVector;
import mikera.vectorz.util.ErrorMessages;
import mikera.vectorz.util.VectorzException;
import us.bpsm.edn.parser.Parseable;
import us.bpsm.edn.parser.Parser;
import us.bpsm.edn.parser.Parsers;

/**
 * Static method class for matrices
 * 
 * @author Mike
 */
public class Matrixx {

	private static final long SPARSE_ELEMENT_THRESHOLD = 100000;
	
	private final static Random rand=new Random();

	/**
	 * Creates an identity matrix
	 */
	public static AMatrix createIdentityMatrix(int dimensions) {
		return createImmutableIdentityMatrix(dimensions);
	}

	/**
	 * Creates a sparse, immutable identity matrix. This is the most efficient format for identity matrices
	 */
	public static IdentityMatrix createImmutableIdentityMatrix(int dimensions) {
		return IdentityMatrix.create(dimensions);
	}
	
	/**
	 * Creates a fully mutable identity matrix
	 */
	public static AMatrix createMutableIdentityMatrix(int dimensions) {
		AMatrix m = newMatrix(dimensions, dimensions);
		for (int i = 0; i < dimensions; i++) {
			m.unsafeSet(i, i, 1.0);
		}
		return m;
	}
	
	/**
	 * Coerce an object to a matrix format, on a best effort basis.
	 * 
	 * Can handle:
	 * - Existing matrices
	 * - Vectors (will be broadcast to a n x 1 column matrix)
	 * - Scalars (will be broadcast to a 1 x 1 matrix)
	 */
	public static AMatrix toMatrix(INDArray a) {
		if (a instanceof AMatrix) return (AMatrix)a;
		int dims=a.dimensionality();
		if (dims==0) return Matrix.wrap(1, 1, new double[]{a.get()});
		
		if (dims==1) {
			if (a instanceof AVector) return ColumnMatrix.wrap(((AVector)a).clone());
			return ColumnMatrix.wrap(a.toVector());
		}
		if (dims==2) return Matrix.create(a);
		throw new UnsupportedOperationException("Can't convert to matrix: "
				+ a.getClass() + " with shape " +a.getShape());
	}

	/**
	 * Coerce an object to a matrix format, on a best effort basis.
	 * 
	 * Can handle:
	 * - INDArray instances of suitable shape (will be broadcast to a matrix if needed)
	 * - Iterable objects representing a list of rows
	 * 
	 */
	@SuppressWarnings("unchecked")
	public static AMatrix toMatrix(Object o) {
		if (o instanceof INDArray) {
			return toMatrix((INDArray)o);
		} else if (o instanceof Iterable) {
			List al = Tools.toList((Iterable) o);
			return createFromVectors(al);
		}
		throw new UnsupportedOperationException("Can't convert to matrix: "
				+ o.getClass());
	}

	/**
	 * Creates a sparse matrix from the given matrix, ignoring zeros
	 */
	public static AMatrix createSparse(AMatrix m) {
		int rc=m.rowCount();
		int cc=m.columnCount();
		if ((rc==0)||(cc==0)) return ZeroMatrix.create(rc, cc);
		return SparseRowMatrix.create(m);
	}
	
	/**
	 * Creates a sparse matrix of the given size, initially zero-filled. Uses row-based storage by default
	 */
	public static SparseRowMatrix createSparse(int rowCount, int columnCount) {
		return SparseRowMatrix.create(rowCount,columnCount);
	}
	
	/**
	 * Creates a sparse matrix from the given matrix, ignoring zeros. Uses row-based storage by default
	 */
	public static SparseRowMatrix createSparseRows(Iterable rows) {
		Iterator rowIterator=rows.iterator();
		return createSparseRows(rowIterator);
	}
	
	/**
	 * Creates a sparse matrix from the given iterator. Each vector in the iterator will be copied to
	 * a row in the new sparse matrix 
	 */
	public static ArrayList createSparseArray(Iterator vecIterator) {
		AVector v0=vecIterator.next();
		int len = v0.length();
		ArrayList vecList = new ArrayList();
		vecList.add(v0);
		while (vecIterator.hasNext()) {
			AVector v = vecIterator.next();
            if ((v == null) || (v.isZero()))
                v = Vectorz.createZeroVector(len);
            else
			    v = v.sparseClone();
			vecList.add(v.sparseClone());
		}
        return vecList;
	}

	public static SparseRowMatrix createSparseRows(Iterator rowIterator) {
		return SparseRowMatrix.wrap(createSparseArray(rowIterator));
    }
	
	public static SparseColumnMatrix createSparseColumns(Iterator colIterator) {
		return SparseColumnMatrix.wrap(createSparseArray(colIterator));
    }
	
	/**
	 * Create a sparse array, given an Index of column positions and AVector of corresponding values for each row in the sparse array
	 * Performs a defensive copy of underlying data.
	 */
	public static AMatrix createSparse(int columnCount, Index[] indexes,
			AVector[] weights) {
		int rowCount = indexes.length;
		if (rowCount != weights.length)
			throw new IllegalArgumentException("Length of indexes array must match length of weights array");
		SparseRowMatrix sm=SparseRowMatrix.create(rowCount, columnCount);
		for (int i = 0; i < rowCount; i++) {
			sm.replaceRow(i, SparseIndexedVector.wrap(columnCount, indexes[i].clone(), weights[i].toDoubleArray()));
		}
		return sm;
	}

	/**
	 * Creates a SparseColumnMatrix from the given matrix, ignoring zeros
	 */
	public static SparseColumnMatrix createSparseColumns(AMatrix m) {
		int cc = m.columnCount();
		AVector[] cols = new AVector[cc];
		for (int i = 0; i < cc; i++) {
			cols[i] = Vectorz.createSparse(m.getColumn(i));
		}
		return SparseColumnMatrix.wrap(cols);
	}
	
	/**
	 * Creates a SparseRowMatrix matrix from the given matrix, ignoring zeros
	 */
	public static AMatrix createSparseRows(AMatrix m) {
		if (m.rowCount()==0) return ZeroMatrix.create(0, m.columnCount());
		return SparseRowMatrix.create(m);
	}
	
	/**
	 * Creates a SparseRowMatrix matrix from the given INDArray, ignoring zeros
	 */
	public static SparseRowMatrix createSparseRows(INDArray a) {
		if (!(a.dimensionality()==2)) throw new IllegalArgumentException(ErrorMessages.incompatibleShape(a));
		int rc=a.getShape(0);
		int cc=a.getShape(1);
		SparseRowMatrix m=SparseRowMatrix.create(rc,cc);
		for (int i=0; iSPARSE_ELEMENT_THRESHOLD) return createSparse(rows,columns);
		return Matrix.create(rows, columns);
	}

	/**
	 * Creates a new matrix using the elements in the specified vector.
	 * Truncates or zero-pads the data as required to fill the new matrix
	 * @param data
	 * @param rows
	 * @param columns
	 * @return
	 */
	public static Matrix createFromVector(AVector source, int rows, int columns) {
		int length=source.length();
		Matrix m = Matrix.create(rows, columns);
		int n=Math.min(rows*columns, length);
		source.copyTo(0, m.data, 0, n);
		return m;
	}
	
	/**
	 * Computes the inverse of a matrix. Returns null if the matrix is singular.
	 * 
	 * Throws an Exception is the matrix is not square
	 * @param m
	 * @return
	 */
	public AMatrix createInverse(AMatrix m) {
		return m.inverse();
	}

	/**
	 * Creates a zero-filled matrix with the specified number of dimensions for both rows and columns
	 * @param dimensions
	 * @return
	 */
	private static Matrix createSquareMatrix(int dimensions) {
		return Matrix.create(dimensions, dimensions);
	}
	
	/**
	 * Extracts a lower triangular matrix from a matrix 
	 */
	public static AMatrix extractLowerTriangular(AMatrix a) {
		int rc=a.rowCount();
		if (rc>a.columnCount()) throw new IllegalArgumentException("Too few columns in matrix");
		AMatrix r=Matrixx.newMatrix(rc,rc);
		for (int i=0; ia.rowCount()) throw new IllegalArgumentException("Too few rows in matrix");
		AMatrix r=Matrixx.newMatrix(cc,cc);
		for (int i=0; i rows) {
		int rc = rows.size();
		AVector firstRow = Vectorz.create(rows.get(0));
		int cc = firstRow.length();

		Matrix m = Matrix.create(rc, cc);
		m.setRow(0, firstRow);

		for (int i = 1; i < rc; i++) {
			m.setRow(i, Vectorz.create(rows.get(i)));
		}
		return m;
	}

	/**
	 * Creates a mutable copy of a matrix
	 */
	public static AMatrix create(IMatrix m) {
		int rows = m.rowCount();
		int columns = m.columnCount();
		AMatrix result = newMatrix(rows, columns);
		result.set(m);
		return result;
	}

	/**
	 * Fills a matrix with uniform random numbers
	 * @param m
	 */
	public static void fillRandomValues(AMatrix m) {
		int rows = m.rowCount();
		int columns = m.columnCount();
		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < columns; j++) {
				m.unsafeSet(i, j, rand.nextDouble());
			}
		}
	}
	
	/**
	 * Fills a matrix with uniform random numbers, using the specified Random instance
	 * @param m
	 */
	public static void fillRandomValues(AMatrix m, Random rand) {
		int rows = m.rowCount();
		int columns = m.columnCount();
		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < columns; j++) {
				m.unsafeSet(i, j, rand.nextDouble());
			}
		}
	}

	/** 
	 * Create a matrix using as array of vectors which represent the data for each row
	 * @param data
	 * @return
	 */
	public static AMatrix createFromVectors(INDArray... data) {
		int rc = data.length;
		int cc = (rc == 0) ? 0 : data[0].sliceCount();
		AMatrix m = newMatrix(rc, cc);
		for (int i = 0; i < rc; i++) {
			m.setRow(i, data[i].asVector());
		}
		return m;
	}

	/** 
	 * Create a matrix using a list of vectors as the data for each row
	 * @param data
	 * @return
	 */
	public static AMatrix createFromVectors(List data) {
		int rc = data.size();
		int cc = (rc == 0) ? 0 : data.get(0).sliceCount();
		AMatrix m = newMatrix(rc, cc);
		for (int i = 0; i < rc; i++) {
			m.setRow(i,data.get(i).asVector());
		}
		return m;
	}

	// ====================================
	// Edn formatting and parsing functions

	private static Parser.Config getMatrixParserConfig() {
		return Parsers.defaultConfiguration();
	}

	/**
	 * Parse a matrix in edn format
	 * 
	 * @param ednString
	 * @return
	 */
	public static AMatrix parse(String ednString) {
		Parser p = Parsers.newParser(getMatrixParserConfig());
		Parseable ps = Parsers.newParseable(ednString);
		@SuppressWarnings("unchecked")
		List> data = (List>) p.nextValue(ps);
		int rc = data.size();
		int cc = (rc == 0) ? 0 : data.get(0).size();
		AMatrix m = newMatrix(rc, cc);
		for (int i = 0; i < rc; i++) {
			List row=data.get(i);
			for (int j = 0; j < cc; j++) {
				m.unsafeSet(i, j, Tools.toDouble(row.get(j)));
			}
		}
		return m;
	}

	/**
	 * Creates a matrix using the given objects to generate individual rows
	 * @param vs
	 * @return
	 */
	public static AMatrix create(Object... vs) {
		return create(Arrays.asList(vs));
	}

	/**
	 * Creates a matrix using the given double[][] data
	 * @param data
	 * @return
	 */
	public static Matrix create(double[][] data) {
		return Matrix.create(data);
	}

	/**
	 * Wraps double[] data in a strided matrix of the most efficient available type.
	 * 
	 * @param array
	 * @param arrayOffset
	 * @param reverse
	 * @param reverse2
	 * @return
	 */
	public static AStridedMatrix wrapStrided(double[] data, int rows, int cols, int offset, int rowStride, int colStride) {
		if ((offset==0)&&(data.length==rows*cols)) {
			if ((rows<=1)||(cols<=1)||((cols==rowStride)&&(colStride==1))) {
				return Matrix.wrap(rows, cols, data);
			} 
			if ((rows==colStride)&&(rowStride==1)) {
				return DenseColumnMatrix.wrap(rows, cols, data);
			} 
		}
		if (colStride==1) {
			return StridedRowMatrix.wrap(data, rows, cols, offset, rowStride);			
		}
		return StridedMatrix.wrap(data, rows, cols, offset, rowStride, colStride);
	}

	/**
	 * Creates a sparse matrix using the given arrays as slices
	 * @param slices An iterable object containing n length m vectors to use as matrix rows 
	 * @return
	 */
	public static AMatrix createSparse(Iterable slices) {
		INDArray slice1=slices.iterator().next();
		int cc=slice1.sliceCount();
		ArrayList al=new ArrayList();
		for (INDArray a:slices) {
			if ((a.dimensionality()!=1)||(a.sliceCount()!=cc)) throw new IllegalArgumentException(ErrorMessages.incompatibleShape(a)); 
			al.add(a.sparse().asVector());
		}
		return SparseRowMatrix.create(al);
	}

	/**
	 * Creates a sparse matrix using the given objects as slices
	 * @param slices
	 * @return
	 */
	public static AMatrix createSparse(Object... slices) {
		int rc=slices.length;
		AVector[] vs=new AVector[rc];
		AVector s0=Vectorz.createSparse(slices[0]);
		int cc=s0.length();
		vs[0]=s0;
		for (int i=1; i