/*
* JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
* Copyright (C) 2006 - JScience (http://jscience.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package org.jscience.mathematics.vector;
import java.util.Comparator;
import javolution.context.StackContext;
import javolution.lang.MathLib;
import javolution.lang.Realtime;
import javolution.lang.ValueType;
import javolution.text.Text;
import javolution.text.TextBuilder;
import javolution.xml.XMLFormat;
import javolution.xml.stream.XMLStreamException;
import org.jscience.mathematics.structure.Field;
import org.jscience.mathematics.structure.Ring;
import org.jscience.mathematics.structure.VectorSpace;
/**
* This class represents a rectangular table of elements of a ring-like
* algebraic structure.
*
* Instances of this class can be used to resolve system of linear equations
* involving any kind of {@link Field Field} elements
* (e.g. {@link org.jscience.mathematics.number.Real Real},
* {@link org.jscience.mathematics.number.Complex Complex},
* {@link org.jscience.physics.amount.Amount Amount<?>},
* {@link org.jscience.mathematics.function.Function Function}, etc).
* For example:[code]
* // Creates a dense matrix (2x2) of Rational numbers.
* DenseMatrix M = DenseMatrix.valueOf(
* { Rational.valueOf(23, 45), Rational.valueOf(33, 75) },
* { Rational.valueOf(15, 31), Rational.valueOf(-20, 45)});
*
* // Creates a sparse matrix (16x2) of Real numbers.
* SparseMatrix M = SparseMatrix.valueOf(
* SparseVector.valueOf(16, Real.ZERO, 0, Real.valueOf(5)),
* SparseVector.valueOf(16, Real.ZERO, 15, Real.valueOf(-3)));
*
* // Creates a floating-point (64 bits) matrix (3x2).
* Float64Matrix M = Float64Matrix.valueOf(
* {{ 1.0, 2.0, 3.0}, { 4.0, 5.0, 6.0}});
*
* // Creates a complex single column matrix (1x2).
* ComplexMatrix M = ComplexMatrix.valueOf(
* {{ Complex.valueOf(1.0, 2.0), Complex.valueOf(4.0, 5.0)}}).transpose();
*
* // Creates an identity matrix (2x2) for modulo integer.
* SparseMatrix IDENTITY = SparseMatrix.valueOf(
* DenseVector.valueOf(ModuloInteger.ONE, ModuloInteger.ONE), ModuloInteger.ZERO);
* [/code]
*
* Non-commutative field multiplication is supported. Invertible square
* matrices may form a non-commutative field (also called a division
* ring). In which case this class may be used to resolve system of linear
* equations with matrix coefficients.
*
* Implementation Note: Matrices may use {@link
* javolution.context.StackContext StackContext} and {@link
* javolution.context.ConcurrentContext ConcurrentContext} in order to
* minimize heap allocation and accelerate calculations on multi-core
* systems.
*
* @author Jean-Marie Dautelle
* @version 3.3, December 24, 2006
* @see
* Wikipedia: Matrix (mathematics)
*/
public abstract class Matrix>
implements VectorSpace, F>, Ring>, ValueType, Realtime {
/**
* Holds the default XML representation for matrices. For example:[code]
*
*
*
*
*
* [/code]
*/
@SuppressWarnings("unchecked")
protected static final XMLFormat XML = new XMLFormat(
Matrix.class) {
@Override
public void read(InputElement xml, Matrix M) throws XMLStreamException {
// Nothing to do.
}
@Override
public void write(Matrix M, OutputElement xml)
throws XMLStreamException {
final int m = M.getNumberOfRows();
final int n = M.getNumberOfColumns();
xml.setAttribute("rows", m);
xml.setAttribute("columns", n);
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
xml.add(M.get(i, j));
}
}
}
};
/**
* Default constructor (for sub-classes).
*/
protected Matrix() {
}
/**
* Returns the number of rows m for this matrix.
*
* @return m, the number of rows.
*/
public abstract int getNumberOfRows();
/**
* Returns the number of columns n for this matrix.
*
* @return n, the number of columns.
*/
public abstract int getNumberOfColumns();
/**
* Returns a single element from this matrix.
*
* @param i the row index (range [0..m[).
* @param j the column index (range [0..n[).
* @return the element read at [i,j].
* @throws IndexOutOfBoundsException
* ((i < 0) || (i >= m)) || ((j < 0) || (j >= n))
*/
public abstract F get(int i, int j);
/**
* Returns the row identified by the specified index in this matrix.
*
* @param i the row index (range [0..m[).
* @return the vector holding the specified row.
* @throws IndexOutOfBoundsException (i < 0) || (i >= m)
*/
public abstract Vector getRow(int i);
/**
* Returns the column identified by the specified index in this matrix.
*
* @param j the column index (range [0..n[).
* @return the vector holding the specified column.
* @throws IndexOutOfBoundsException (j < 0) || (j >= n)
*/
public abstract Vector getColumn(int j);
/**
* Returns the diagonal vector.
*
* @return the vector holding the diagonal elements.
*/
public abstract Vector getDiagonal();
/**
* Returns the negation of this matrix.
*
* @return -this.
*/
public abstract Matrix opposite();
/**
* Returns the sum of this matrix with the one specified.
*
* @param that the matrix to be added.
* @return this + that.
* @throws DimensionException matrices's dimensions are different.
*/
public abstract Matrix plus(Matrix that);
/**
* Returns the difference between this matrix and the one specified.
*
* @param that the matrix to be subtracted.
* @return this - that.
* @throws DimensionException matrices's dimensions are different.
*/
public Matrix minus(Matrix that) {
return this.plus(that.opposite());
}
/**
* Returns the product of this matrix by the specified factor.
*
* @param k the coefficient multiplier.
* @return this · k
*/
public abstract Matrix times(F k);
/**
* Returns the product of this matrix by the specified vector.
*
* @param v the vector.
* @return this · v
* @throws DimensionException if
* v.getDimension() != this.getNumberOfColumns()
*/
public abstract Vector times(Vector v);
/**
* Returns the product of this matrix with the one specified.
*
* @param that the matrix multiplier.
* @return this · that.
* @throws DimensionException if
* this.getNumberOfColumns() != that.getNumberOfRows().
*/
public abstract Matrix times(Matrix that);
/**
* Returns the inverse of this matrix (must be square).
*
* @return 1 / this
* @throws DimensionException if this matrix is not square.
*/
public abstract Matrix inverse();
/**
* Returns this matrix divided by the one specified.
*
* @param that the matrix divisor.
* @return this / that.
* @throws DimensionException if that matrix is not square or dimensions
* do not match.
*/
public Matrix divide(Matrix that) {
return this.times(that.inverse());
}
/**
* Returns the inverse or pseudo-inverse if this matrix if not square.
*
* Note: To resolve the equation A * X = B,
* it is usually faster to calculate A.lu().solve(B)
* rather than A.inverse().times(B).
*
* @return the inverse or pseudo-inverse of this matrix.
*/
public Matrix pseudoInverse() {
if (isSquare())
return this.inverse();
Matrix thisTranspose = this.transpose();
return (thisTranspose.times(this)).inverse().times(thisTranspose);
}
/**
* Returns the determinant of this matrix.
*
* @return this matrix determinant.
* @throws DimensionException if this matrix is not square.
*/
public abstract F determinant();
/**
* Returns the transpose of this matrix.
*
* @return A'.
*/
public abstract Matrix transpose();
/**
* Returns the cofactor of an element in this matrix. It is the value
* obtained by evaluating the determinant formed by the elements not in
* that particular row or column.
*
* @param i the row index.
* @param j the column index.
* @return the cofactor of THIS[i,j].
* @throws DimensionException matrix is not square or its dimension
* is less than 2.
*/
public abstract F cofactor(int i, int j);
/**
* Returns the adjoint of this matrix. It is obtained by replacing each
* element in this matrix with its cofactor and applying a + or - sign
* according (-1)**(i+j), and then finding the transpose of the resulting
* matrix.
*
* @return the adjoint of this matrix.
* @throws DimensionException if this matrix is not square or if
* its dimension is less than 2.
*/
public abstract Matrix adjoint();
/**
* Indicates if this matrix is square.
*
* @return getNumberOfRows() == getNumberOfColumns()
*/
public boolean isSquare() {
return getNumberOfRows() == getNumberOfColumns();
}
/**
* Solves this matrix for the specified vector (returns x
* such as this · x = y).
*
* @param y the vector for which the solution is calculated.
* @return x such as this · x = y
* @throws DimensionException if that matrix is not square or dimensions
* do not match.
*/
public Vector solve(Vector y) {
DenseMatrix M = DenseMatrix.newInstance(y.getDimension(), true);
M._rows.add(DenseVector.valueOf(y));
return solve(M).getColumn(0);
}
/**
* Solves this matrix for the specified matrix (returns x
* such as this · x = y).
*
* @param y the matrix for which the solution is calculated.
* @return x such as this · x = y
* @throws DimensionException if that matrix is not square or dimensions
* do not match.
*/
public Matrix solve(Matrix y) {
return LUDecomposition.valueOf(this).solve(y); // Default implementation.
}
/**
* Returns this matrix raised at the specified exponent.
*
* @param exp the exponent.
* @return thisexp
* @throws DimensionException if this matrix is not square.
*/
public Matrix pow(int exp) {
if (exp > 0) {
StackContext.enter();
try {
Matrix pow2 = this;
Matrix result = null;
while (exp >= 1) { // Iteration.
if ((exp & 1) == 1) {
result = (result == null) ? pow2 : result.times(pow2);
}
pow2 = pow2.times(pow2);
exp >>>= 1;
}
return StackContext.outerCopy(result);
} finally {
StackContext.exit();
}
} else if (exp == 0) {
return this.times(this.inverse()); // Identity.
} else {
return this.pow(-exp).inverse();
}
}
/**
* Returns the trace of this matrix.
*
* @return the sum of the diagonal elements.
*/
public F trace() {
F sum = this.get(0, 0);
for (int i = MathLib.min(getNumberOfColumns(), getNumberOfRows()); --i > 0;) {
sum = sum.plus(get(i, i));
}
return sum;
}
/**
* Returns the linear algebraic matrix tensor product of this matrix
* and another (Kronecker product). The default implementation returns
* a {@link DenseMatrix}.
*
* @param that the second matrix.
* @return this ⊗ that
* @see
* Wikipedia: Kronecker Product
*/
public abstract Matrix tensor(Matrix that);
/**
* Returns the vectorization of this matrix. The vectorization of
* a matrix is the column vector obtain by stacking the columns of the
* matrix on top of one another. The default implementation returns
* a {@link DenseVector}.
*
* @return the vectorization of this matrix.
* @see
* Wikipedia: Vectorization.
*/
public abstract Vector vectorization();
/**
* Returns the text representation of this matrix.
*
* @return the text representation of this matrix.
*/
public Text toText() {
final int m = this.getNumberOfRows();
final int n = this.getNumberOfColumns();
TextBuilder tmp = TextBuilder.newInstance();
tmp.append('{');
for (int i = 0; i < m; i++) {
tmp.append('{');
for (int j = 0; j < n; j++) {
tmp.append(get(i, j));
if (j != n - 1) {
tmp.append(", ");
}
}
tmp.append("}");
if (i != m - 1) {
tmp.append(",\n");
}
}
tmp.append("}");
Text txt = tmp.toText();
TextBuilder.recycle(tmp);
return txt;
}
/**
* Returns the text representation of this matrix as a
* java.lang.String.
*
* @return toText().toString()
*/
public final String toString() {
return toText().toString();
}
/**
* Indicates if this matrix can be considered equals to the one
* specified using the specified comparator when testing for
* element equality. The specified comparator may allow for some
* tolerance in the difference between the matrix elements.
*
* @param that the matrix to compare for equality.
* @param cmp the comparator to use when testing for element equality.
* @return true if this matrix and the specified matrix are
* both matrices with equal elements according to the specified
* comparator; false otherwise.
*/
public boolean equals(Matrix that, Comparator cmp) {
if (this == that)
return true;
final int m = this.getNumberOfRows();
final int n = this.getNumberOfColumns();
if ((that.getNumberOfRows() != m) || (that.getNumberOfColumns() != n))
return false;
for (int i = m; --i >= 0;) {
for (int j = n; --j >= 0;) {
if (cmp.compare(this.get(i, j), that.get(i, j)) != 0)
return false;
}
}
return true;
}
/**
* Indicates if this matrix is strictly equal to the object specified.
*
* @param that the object to compare for equality.
* @return true if this matrix and the specified object are
* both matrices with equal elements; false otherwise.
* @see #equals(Matrix, Comparator)
*/
public boolean equals(Object that) {
if (this == that)
return true;
if (!(that instanceof Matrix))
return false;
final int m = this.getNumberOfRows();
final int n = this.getNumberOfColumns();
Matrix> M = (Matrix>) that;
if ((M.getNumberOfRows() != m) || (M.getNumberOfColumns() != n))
return false;
for (int i = m; --i >= 0;) {
for (int j = n; --j >= 0;) {
if (!this.get(i, j).equals(M.get(i, j)))
return false;
}
}
return true;
}
/**
* Returns a hash code value for this matrix.
* Equals objects have equal hash codes.
*
* @return this matrix hash code value.
* @see #equals
*/
public int hashCode() {
final int m = this.getNumberOfRows();
final int n = this.getNumberOfColumns();
int code = 0;
for (int i = m; --i >= 0;) {
for (int j = n; --j >= 0;) {
code += get(i, j).hashCode();
}
}
return code;
}
/**
* Returns a copy of this matrix
* {@link javolution.context.AllocatorContext allocated}
* by the calling thread (possibly on the stack).
*
* @return an identical and independant copy of this matrix.
*/
public abstract Matrix copy();
}
Ads help maintain this website.