blob: 8fcefdb147f9fc1e753a3448518b86f8f5e0d144 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class factor all services common to matrices.
* <p>This class is the base class of all matrix implementations, it
* is also the base class of the {@link SquareMatrix} class which adds
* methods specific to square matrices.</p>
* <p>This class both handles the storage of matrix elements and
* implements the classical operations on matrices (addition,
* substraction, multiplication, transposition). It relies on two
* protected methods ({@link #getRangeForRow} and {@link
* #getRangeForColumn}) to get tight loop bounds for matrices with
* known structures full of zeros. These methods should be
* implemented by derived classes to provide information about their
* specific shape to the general algorithms implemented by this
* abstract class.</p>
* @version $Id$
* @author L. Maisonobe
*/
public abstract class Matrix
implements Serializable {
/** Simple constructor.
* Build a matrix with null elements.
* @param rows number of rows of the matrix
* @param columns number of columns of the matrix
*/
protected Matrix(int rows, int columns) {
// sanity check
if (rows <= 0 || columns <= 0) {
throw new IllegalArgumentException("cannot build a matrix"
+ " with negative or null dimension");
}
this.rows = rows;
this.columns = columns;
data = new double[rows * columns];
for (int i = 0; i < data.length; ++i) {
data[i] = 0.0;
}
}
/** Simple constructor.
* Build a matrix with specified elements.
* @param rows number of rows of the matrix
* @param columns number of columns of the matrix
* @param data table of the matrix elements (stored row after row)
*/
public Matrix(int rows, int columns, double[] data) {
// sanity check
if (rows <= 0 || columns <= 0) {
throw new IllegalArgumentException("cannot build a matrix"
+ " with negative or null dimension");
}
this.rows = rows;
this.columns = columns;
this.data = (data == null) ? null : (double[]) data.clone();
}
/** Copy constructor.
* @param m matrix to copy
*/
protected Matrix(Matrix m) {
rows = m.rows;
columns = m.columns;
data = new double[rows * columns];
System.arraycopy(m.data, 0, data, 0, m.data.length);
}
/** Polymorphic copy operator.
* This method build a new object of the same type of the
* instance. It is somewhat similar to the {@link Object#clone}
* method, except that it has public access, it doesn't throw any
* specific exception and it returns a Matrix.
*@see Object#clone
*/
public abstract Matrix duplicate();
/** Get the number of rows of the matrix.
* @return number of rows
* @see #getColumns
*/
public int getRows() {
return rows;
}
/** Get the number of columns of the matrix.
* @return number of columns
* @see #getRows
*/
public int getColumns() {
return columns;
}
/** Get a matrix element.
* @param i row index, from 0 to rows - 1
* @param j column index, from 0 to cols - 1
* @return value of the element
* @exception ArrayIndexOutOfBoundsException if the indices are wrong
* @see #setElement
*/
public double getElement(int i, int j) {
if (i < 0 || i >= rows || j < 0 || j >= columns) {
throw new IllegalArgumentException("cannot get element ("
+ i + ", " + j + ") from a "
+ rows + 'x' + columns
+ " matrix");
}
return data[i * columns + j];
}
/** Set a matrix element.
* @param i row index, from 0 to rows - 1
* @param j column index, from 0 to cols - 1
* @param value value of the element
* @exception ArrayIndexOutOfBoundsException if the indices are wrong
* @see #getElement
*/
public void setElement(int i, int j, double value) {
if (i < 0 || i >= rows || j < 0 || j >= columns) {
throw new IllegalArgumentException("cannot set element ("
+ i + ", " + j + ") in a "
+ rows + 'x' + columns
+ " matrix");
}
data[i * columns + j] = value;
}
/** Add a matrix to the instance.
* This method adds a matrix to the instance. It returns a new
* matrix and does not modify the instance.
* @param m matrix to add
* @return a new matrix containing the result
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public Matrix add(Matrix m) {
// validity check
if ((rows != m.rows) || (columns != m.columns)) {
throw new IllegalArgumentException("cannot add a "
+ m.rows + 'x' + m.columns
+ " matrix to a "
+ rows + 'x' + columns
+ " matrix");
}
double[] resultData = new double[rows * columns];
int resultIndex = 0;
int lowerElements = 0;
int upperElements = 0;
// external loop through the rows
for (int i = 0; i < rows; ++i) {
// compute the indices of the internal loop
NonNullRange r = NonNullRange.reunion(getRangeForRow(i),
m.getRangeForRow(i));
// assign the zeros before the non null range
int j = 0;
while (j < r.begin) {
resultData[resultIndex] = 0.0;
++resultIndex;
++j;
}
// compute the possibly non null elements
while (j < r.end) {
// compute the current element
resultData[resultIndex] = data[resultIndex] + m.data[resultIndex];
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
++resultIndex;
++j;
}
// assign the zeros after the non null range
while (j < columns) {
resultData[resultIndex++] = 0.0;
++resultIndex;
++j;
}
}
return MatrixFactory.buildMatrix(rows, columns, resultData,
lowerElements, upperElements);
}
/** Substract a matrix from the instance.
* This method substracts a matrix from the instance. It returns a new
* matrix and does not modify the instance.
* @param m matrix to substract
* @return a new matrix containing the result
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public Matrix sub(Matrix m) {
// validity check
if ((rows != m.rows) || (columns != m.columns)) {
throw new IllegalArgumentException("cannot substract a "
+ m.rows + 'x' + m.columns
+ " matrix from a "
+ rows + 'x' + columns
+ " matrix");
}
double[] resultData = new double[rows * columns];
int resultIndex = 0;
int lowerElements = 0;
int upperElements = 0;
// external loop through the rows
for (int i = 0; i < rows; ++i) {
// compute the indices of the internal loop
NonNullRange r = NonNullRange.reunion(getRangeForRow(i),
m.getRangeForRow(i));
// assign the zeros before the non null range
int j = 0;
while (j < r.begin) {
resultData[resultIndex] = 0.0;
++resultIndex;
++j;
}
// compute the possibly non null elements
while (j < r.end) {
// compute the current element
resultData[resultIndex] = data[resultIndex] - m.data[resultIndex];
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
++resultIndex;
++j;
}
// assign the zeros after the non null range
while (j < columns) {
resultData[resultIndex++] = 0.0;
++resultIndex;
++j;
}
}
return MatrixFactory.buildMatrix(rows, columns, resultData,
lowerElements, upperElements);
}
/** Multiply the instance by a matrix.
* This method multiplies the instance by a matrix. It returns a new
* matrix and does not modify the instance.
* @param m matrix by which to multiply
* @return a new matrix containing the result
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public Matrix mul(Matrix m) {
// validity check
if (columns != m.rows) {
throw new IllegalArgumentException("cannot multiply a "
+ rows + 'x' + columns
+ " matrix by a "
+ m.rows + 'x' + m.columns
+ " matrix");
}
double[] resultData = new double[rows * m.columns];
int resultIndex = 0;
int lowerElements = 0;
int upperElements = 0;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < m.columns; ++j) {
double value = 0.0;
// compute the tighter possible indices of the internal loop
NonNullRange r = NonNullRange.intersection(getRangeForRow(i),
m.getRangeForColumn(j));
if (r.begin < r.end) {
int k = r.begin;
int idx = i * columns + k;
int midx = k * m.columns + j;
while (k++ < r.end) {
value += data[idx++] * m.data[midx];
midx += m.columns;
}
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
}
// store the element value
resultData[resultIndex++] = value;
}
}
return MatrixFactory.buildMatrix(rows, m.columns, resultData,
lowerElements, upperElements);
}
/** Multiply the instance by a scalar.
* This method multiplies the instance by a scalar. It returns a new
* matrix and does not modify the instance.
* @param a scalar by which to multiply
* @return a new matrix containing the result
* @see #selfMul(double)
*/
public Matrix mul(double a) {
Matrix copy = duplicate();
copy.selfMul(a);
return copy;
}
/** Multiply the instance by a scalar.
* This method multiplies the instance by a scalar.
* It does modify the instance.
* @param a scalar by which to multiply
* @see #mul(double)
*/
public void selfMul(double a) {
for (int i = 0; i < rows; ++i) {
NonNullRange r = getRangeForRow(i);
for (int j = r.begin, index = i * columns + r.begin; j < r.end; ++j) {
data[index++] *= a;
}
}
}
/** Compute the transpose of the instance.
* This method transposes the instance. It returns a new
* matrix and does not modify the instance.
* @return a new matrix containing the result
*/
public Matrix getTranspose() {
double[] resultData = new double[columns * rows];
int resultIndex = 0;
int upperElements = 0;
int lowerElements = 0;
for (int i = 0; i < columns; ++i) {
// compute the indices of the internal loop
NonNullRange range = getRangeForColumn(i);
int j = 0;
int index = i;
// assign the zeros before the non null range
while (j < range.begin) {
resultData[resultIndex++] = 0.0;
index += columns;
++j;
}
// compute the possibly non null elements
while (j < range.end) {
resultData[resultIndex] = data[index];
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
index += columns;
++resultIndex;
++j;
}
// assign the zeros after the non null range
while (j < rows) {
resultData[resultIndex] = 0.0;
++resultIndex;
++j;
}
}
return MatrixFactory.buildMatrix(columns, rows, resultData,
lowerElements, upperElements);
}
/** Set a range to the non null part covered by a row.
* @param i index of the row
* @return range of non nul elements in the specified row
* @see #getRangeForColumn
*/
protected abstract NonNullRange getRangeForRow(int i);
/** Set a range to the non null part covered by a column.
* @param j index of the column
* @return range of non nul elements in the specified column
* @see #getRangeForRow
*/
protected abstract NonNullRange getRangeForColumn(int j);
public String toString() {
String separator = System.getProperty("line.separator");
StringBuffer buf = new StringBuffer();
for (int index = 0; index < rows * columns; ++index) {
if (index > 0) {
if (index % columns == 0) {
buf.append(separator);
} else {
buf.append(' ');
}
}
buf.append(Double.toString(data[index]));
}
return buf.toString();
}
/** number of rows of the matrix. */
protected final int rows;
/** number of columns of the matrix. */
protected final int columns;
/** array of the matrix elements.
* the elements are stored in a one dimensional array, row after row
*/
protected final double[] data;
}