/* $RCSfile$
* $Author$
* $Date$
* $Revision$
*
* Copyright (C) 1997-2007 The Chemistry Development Kit (CDK) project
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
package org.openscience.cdk.graph.invariant;
import org.openscience.cdk.graph.invariant.exception.BadMatrixFormatException;
import org.openscience.cdk.graph.invariant.exception.IndexOutOfBoundsException;
import org.openscience.cdk.graph.invariant.exception.MatrixNotInvertibleException;
/**
* This class is intended to provide the user an efficient way of implementing matrix of double number and
* using normal operations (linear operations, addition, substraction, multiplication, inversion, concatenation)
* on them. The internal representation of a matrix is an array of array of double objects. For the moment,
* double class is the best way I have developped to perform exact operation on numbers; however, for
* irdoubles, normal operations on float and doubles have to be performed, with the well-known risks of error
* this implies. This class also provides a way of representing matrix as arrays of String for output use.
*
* <P>Please note that although in most books matrix elements' indexes take values between [1..n] I chose not
* to disturb Java language way of calling indexes; so the indexes used here take values between [0..n-1] instead.
*
* @author Jean-Sebastien Senecal
* @cdk.githash
* @version 1.0
* @cdk.created 1999-05-20
*/
public class GIMatrix {
private double[][] array; // the matrix itself as an array of doubles
private int m, n; // matrix's params (m=no of line, n=no of columns)
/**
* Class constructor. Uses an array of integers to create a new Matrix object. Note that integers
* will be converted to double objects so mathematical operations may be properly performed and
* provide exact solutions. The given array should be properly instantiated as a matrix i.e. it
* must contain a fixed number of lines and columns, otherwise an exception will be thrown.
* Array must be at leat 1x1.
* @param array an array of integer (first index is the line, second is the column)
* @exception BadMatrixFormatException in case the given array is unproper to construct a matrix
*/
public GIMatrix(int[][] array) {
double[][] temp = new double[array.length][];
for (int i = 0; i < array.length; i++) {
temp[i] = new double[array[i].length]; // line by line ...
for (int j = 0; j < array[i].length; j++)
temp[i][j] = array[i][j]; // converts ints to doubles
}
// verifyMatrixFormat(temp);
this.array = temp;
m = temp.length;
n = temp[0].length;
} // constructor Matrix(int[][])
/**
* Class constructor. Uses an array of doubles to create a new Matrix object. The given array should
* be properly instantiated as a matrix i.e. it must contain a fixed number of lines and columns,
* otherwise an exception will be thrown. Array must be at leat 1x1.
*
* @param array an array of double objects (first index is the line, second is the column)
* @exception BadMatrixFormatException in case the given array is unproper to construct a matrix
*/
public GIMatrix(double[][] array) throws BadMatrixFormatException {
verifyMatrixFormat(array);
double[][] temp = new double[array.length][];
for (int i = 0; i < array.length; i++) {
temp[i] = new double[array[i].length]; // line by line ...
for (int j = 0; j < array[i].length; j++)
temp[i][j] = array[i][j];
}
this.array = temp;
m = array.length;
n = array[0].length;
} // constructor Matrix(double[][])
/**
* Class constructor. Creates a new Matrix object with fixed dimensions. The matrix is
* initialised to the "zero" matrix.
*
* @param line number of lines
* @param col number of columns
*/
public GIMatrix(int line, int col) {
array = new double[line][col];
for (int i = 0; i < line; i++)
for (int j = 0; j < col; j++)
array[i][j] = 0.0;
m = line;
n = col;
} // constructor Matrix(int,int)
/**
* Class constructor. Copies an already existing Matrix object in a new Matrix object.
* @param matrix a Matrix object
*/
public GIMatrix(GIMatrix matrix) {
double[][] temp = new double[matrix.height()][];
for (int i = 0; i < matrix.height(); i++) {
temp[i] = new double[matrix.width()]; // line by line ...
for (int j = 0; j < matrix.width(); j++) {
try { temp[i][j] = matrix.getValueAt(i,j); }
catch (IndexOutOfBoundsException e) {} // never happens
}
}
this.array = temp;
m = array.length;
n = array[0].length;
} // constructor Matrix(Matrix)
/**
* Class constructor. Creates a new Matrix object using a table of matrices (an array of Matrix objects).
* The given array should be properly instantiated i.e. it must contain a fixed number of lines and columns,
* otherwise an exception will be thrown.
* @param table an array of matrices
* @exception BadMatrixFormatException if the table is not properly instantiated
*/
public GIMatrix(GIMatrix[][] table) throws BadMatrixFormatException {
verifyTableFormat(table);
m = n = 0;
for (int i = 0; i < table.length; i++) m += table[i][0].height();
for (int j = 0; j < table[0].length; j++) n += table[0][j].width();
double[][] temp = new double[m][n];
int k = 0; // counters for matrices
for (int i = 0; i < m; i++) {
temp[i] = new double[n]; // line by line ...
if (i == table[k][0].height()) k++; // last line of matrix reached
int h = 0;
for (int j = 0; j < n; j++) {
if (j == table[k][h].width()) h++; // last column of matrix reached
try {
GIMatrix tempMatrix = table[k][h];
temp[i][j] = tempMatrix.getValueAt(i - k*tempMatrix.height(),j - h*tempMatrix.width());
}
catch (IndexOutOfBoundsException e) {} // never happens
}
}
this.array = temp;
} // constructor Matrix(Matrix)
/**
* Returns the number of lines of the matrix.
* @return the height of the matrix
*/
public int height() { return m; } // method heigth()
/**
* Returns the number of columns of the matrix.
* @return the width of the matrix
*/
public int width() { return n; } // method width()
/**
* Returns the internal representation of the matrix, that is an array of double objects.
* @return an array of double equivalent to the matrix
*/
public double[][] getArrayValue() { return array; } // method getArrayValue()
/**
* Resets the value of the matrix to the given array of double numbers
* @param array an array of double objects (first index is the line, second is the column)
* @exception BadMatrixFormatException in case the given array is unproper to construct a matrix
*/
public void setArrayValue(double[][] array) throws BadMatrixFormatException {
verifyMatrixFormat(array);
this.array = array;
} // method setArrayValue(double[][])
/**
* Returns the value of the given element.
* @param i the line number
* @param j the column number
* @return the double at the given index in the Matrix
* @exception IndexOutOfBoundsException if the given index is out of the matrix's range
*/
public double getValueAt(int i, int j) throws IndexOutOfBoundsException {
if ( (i < 0)||(i >= m)||(j < 0)||(j >= n) ) throw new IndexOutOfBoundsException();
return array[i][j];
} // method getValueAt(int,int)
/**
* Sets the value of the element at the given index.
* @param i the line number
* @param j the column number
* @param element the double to place at the given index in the Matrix
* @exception IndexOutOfBoundsException if the given index is out of the matrix's range
*/
public void setValueAt(int i, int j, double element) throws IndexOutOfBoundsException {
if ( (i < 0)||(i >= m)||(j < 0)||(j >= n) ) throw new IndexOutOfBoundsException();
array[i][j] = element;
} // method setValueAt(int,int,double)
/**
* Returns the line-matrix at the given line index
* @param i the line number
* @return the specified line as a Matrix object
* @exception IndexOutOfBoundsException if the given index is out of the matrix's range
*/
public GIMatrix getLine(int i) throws IndexOutOfBoundsException {
if ( (i < 0)||(i >= m) ) throw new IndexOutOfBoundsException();
double[][] line = new double[1][n];
for (int k = 0; k < n; k++) line[0][k] = array[i][k];
try { return new GIMatrix(line); } // format is always OK anyway ...
catch (BadMatrixFormatException e) { return null; }
} // method getLine(int)
/**
* Returns the column-matrix at the given line index
* @param j the column number
* @return the specified column as a Matrix object
* @exception IndexOutOfBoundsException if the given index is out of the matrix's range
*/
public GIMatrix getColumn(int j) throws IndexOutOfBoundsException {
if ( (j < 0)||(j >= n) ) throw new IndexOutOfBoundsException();
double[][] column = new double[m][1];
for (int k = 0; k < m; k++) column[k][0] = array[k][j];
try { return new GIMatrix(column); } // format is always OK anyway ...
catch (BadMatrixFormatException e) { return null; }
} // method getColumn(int)
/**
* Sets the line of the matrix at the specified index to a new value.
* @param i the line number
* @param line the line to be placed at the specified index
* @exception IndexOutOfBoundsException if the given index is out of the matrix's range
* @exception BadMatrixFormatException in case the given Matrix is unproper to replace a line of this Matrix
*/
public void setLine(int i, GIMatrix line) throws IndexOutOfBoundsException, BadMatrixFormatException {
if ( (i < 0)||(i >= m) ) throw new IndexOutOfBoundsException();
if ((line.height() != 1) || (line.width() != n)) throw new BadMatrixFormatException();
for (int k = 0; k < n; k++) array[i][k] = line.getValueAt(0,k);
} // method setLine(int,Matrix)
/**
* Sets the column of the matrix at the specified index to a new value.
* @param j the column number
* @param column the colums to be placed at the specified index
* @exception IndexOutOfBoundsException if the given index is out of the matrix's range
* @exception BadMatrixFormatException in case the given Matrix is unproper to replace a column of this Matrix
*/
public void setColumn(int j, GIMatrix column) throws IndexOutOfBoundsException, BadMatrixFormatException {
if ( (j < 0)||(j >= n) ) throw new IndexOutOfBoundsException();
if ((column.height() != m) || (column.width() != 1)) throw new BadMatrixFormatException();
for (int k = 0; k < m; k++) array[k][j] = column.getValueAt(k,0);
} // method setColumn(int,Matrix)
/**
* Returns the identity matrix.
* @param n the matrix's dimension (identity matrix is a square matrix)
* @return the identity matrix of format nxn
*/
public static GIMatrix identity(int n) {
double[][] identity = new double[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) identity[i][j] = 0.0;
identity[i][i] = 1.0;
for (int j = i+1; j < n; j++) identity[i][j] = 0.0;
}
try { return new GIMatrix(identity); } // format is always OK anyway ...
catch (BadMatrixFormatException e) { return null; }
} // method identity(int)
/**
* Returns a null matrix (with zeros everywhere) of given dimensions.
* @param m number of lines
* @param n number of columns
* @return the zero (null) matrix of format mxn
*/
public static GIMatrix zero(int m, int n) {
double[][] zero = new double[m][n];
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
zero[i][j] = 0.0;
try { return new GIMatrix(zero); } // format is always OK anyway ...
catch (BadMatrixFormatException e) { return null; }
} // method zero(int,int)
/**
* Verifies if two given matrix are equal or not. The matrix must be of the same size and dimensions,
* otherwise an exception will be thrown.
* @param matrix the Matrix object to be compared to
* @return true if both matrix are equal element to element
* @exception BadMatrixFormatException if the given matrix doesn't have the same dimensions as this one
*/
public boolean equals(GIMatrix matrix) throws BadMatrixFormatException {
if ( (height() != matrix.height())||(width() != matrix.width()) )
throw new BadMatrixFormatException();
double[][] temp = matrix.getArrayValue();
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
if (!(array[i][j]==temp[i][j])) return false;
return true;
} // method equals(Matrix)
/**
* Verifies if the matrix is square, that is if it has an equal number of lines and columns.
* @return true if this matrix is square
*/
public boolean isSquare() { return (m == n); } // method isSquare()
/**
* Verifies if the matrix is symmetric, that is if the matrix is equal to it's transpose.
* @return true if the matrix is symmetric
* @exception BadMatrixFormatException if the matrix is not square
*/
public boolean isSymmetric() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
// the loop looks in the lower half of the matrix to find non-symetric elements
for (int i = 1; i < m; i++) // starts at index 1 because index (0,0) always symmetric
for (int j = 0; j < i; j++)
if (!(array[i][j]==array[j][i])) return false;
return true; // the matrix has passed the test
} //method isSymmetric()
// NOT OVER, LOOK MORE CAREFULLY FOR DEFINITION
/**
* Verifies if the matrix is antisymmetric, that is if the matrix is equal to the opposite of
* it's transpose.
* @return true if the matrix is antisymmetric
* @exception BadMatrixFormatException if the matrix is not square
*/
public boolean isAntisymmetric() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
// the loop looks in the lower half of the matrix to find non-antisymetric elements
for (int i = 0; i < m; i++) // not as isSymmetric() loop
for (int j = 0; j <= i; j++)
if (!(array[i][j]==-array[j][i])) return false;
return true; // the matrix has passed the test
} // method isAntisymmetric()
/**
* Verifies if the matrix is triangular superior or not. A triangular superior matrix has
* zero (0) values everywhere under it's diagonal.
* @return true if the matrix is triangular superior
* @exception BadMatrixFormatException if the matrix is not square
*/
public boolean isTriangularSuperior() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
// the loop looks in the lower half of the matrix to find non-null elements
for (int i = 1; i < m; i++) // starts at index 1 because index (0,0) is on the diagonal
for (int j = 0; j < i; j++)
if (!(array[i][j] == 0.0)) return false;
return true; // the matrix has passed the test
} // method isTriangularSuperior
/**
* Verifies if the matrix is triangular inferior or not. A triangular inferior matrix has
* zero (0) values everywhere upper it's diagonal.
* @return true if the matrix is triangular inferior
* @exception BadMatrixFormatException if the matrix is not square
*/
public boolean isTriangularInferior() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
// the loop looks in the upper half of the matrix to find non-null elements
for (int i = 1; i < m; i++) // starts at index 1 because index (0,0) is on the diagonal
for (int j = i; j < n; j++)
if (!(array[i][j] == 0.0)) return false;
return true; // the matrix has passed the test
} // method isTriangularInferior()
/**
* Verifies whether or not the matrix is diagonal. A diagonal matrix only has elements on its diagonal
* and zeros (0) at every other index. The matrix must be square.
* @return true if the matrix is diagonal
* @exception BadMatrixFormatException if the matrix is not square
*/
public boolean isDiagonal() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
// the loop looks both halves of the matrix to find non-null elements
for (int i = 1; i < m; i++) // starts at index 1 because index (0,0) must not be checked
for (int j = 0; j < i; j++)
if ( (!(array[i][j] == 0.0))||(!(array[j][i]== 0.0)) )
return false; // not null
return true;
} // method isDiagonal
/**
* Verifies if the matrix is invertible or not by asking for its determinant.
* @return true if the matrix is invertible
* @exception BadMatrixFormatException if the matrix is not square
*/
public boolean isInvertible() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
return (!(determinant() == 0)); // det != 0
} // method isInvertible()
/**
* Returns the transpose of this matrix. The transpose of a matrix A = {a(i,j)} is the matrix B = {b(i,j)}
* such that b(i,j) = a(j,i) for every i,j i.e. it is the symetrical reflexion of the matrix along its
* diagonal. The matrix must be square to use this method, otherwise an exception will be thrown.
* @return the matrix's transpose as a Matrix object
* @exception BadMatrixFormatException if the matrix is not square
*/
public GIMatrix inverse() throws MatrixNotInvertibleException {
try {
if (!isInvertible()) throw new MatrixNotInvertibleException();
} catch (BadMatrixFormatException e) {
throw new MatrixNotInvertibleException();
}
GIMatrix I = identity(n); // Creates an identity matrix of same dimensions
GIMatrix table;
try {
GIMatrix[][] temp = {{this,I}};
table = new GIMatrix(temp);
} catch (BadMatrixFormatException e) { return null; } // never happens
table = table.GaussJordan(); // linear reduction method applied
double[][] inv = new double[m][n];
for (int i = 0; i < m; i++) // extracts inverse matrix
for (int j = n; j < 2*n; j++) {
try { inv[i][j-n] = table.getValueAt(i,j); }
catch (IndexOutOfBoundsException e) { return null; } // never happens
}
try { return new GIMatrix(inv); }
catch (BadMatrixFormatException e) { return null; } // never happens...
} // method inverse()
/**
* Gauss-Jordan algorithm. Returns the reduced-echeloned matrix of this matrix. The
* algorithm has not yet been optimised but since it is quite simple, it should not be
* a serious problem.
* @return the reduced matrix
*/
public GIMatrix GaussJordan() {
GIMatrix tempMatrix= new GIMatrix(this);
try {
int i = 0;
int j = 0;
int k = 0;
boolean end = false;
while ( (i < m) && (!end) ) {
boolean allZero = true; // true if all elements under line i are null (zero)
while (j < n) { // determination of the pivot
for (k = i; k < m; k++) {
if (!(tempMatrix.getValueAt(k,j)== 0.0)) { // if an element != 0
allZero = false;
break;
} }
if (allZero) j++;
else break;
}
if (j == n) end = true;
else {
if (k != i) tempMatrix = tempMatrix.invertLine(i,k);
if (!(tempMatrix.getValueAt(i,j)== 1.0)) // if element != 1
tempMatrix = // A = L(i)(1/a(i,j))(A)
tempMatrix.multiplyLine(i,1/tempMatrix.getValueAt(i,j));
for (int q = 0; q < m; q++)
if (q != i) // A = L(q,i)(-a(q,j))(A)
tempMatrix = tempMatrix.addLine(q,i,-tempMatrix.getValueAt(q,j));
}
i++;
}
// normally here, r = i-1
return tempMatrix;
} catch (IndexOutOfBoundsException e) { return null; } // never happens... well I hope ;)
/*
From: LEROUX, P. Algebre lineaire: une approche matricielle.
Modulo Editeur, 1983. p. 75. (In French)
*/
} // method GaussJordan()
/**
* Returns the transpose of this matrix. The transpose of a matrix A = {a(i,j)} is the matrix B = {b(i,j)}
* such that b(i,j) = a(j,i) for every i,j i.e. it is the symetrical reflexion of the matrix along its
* diagonal. The matrix must be square to use this method, otherwise an exception will be thrown.
* @return the matrix's transpose as a Matrix object
* @exception BadMatrixFormatException if the matrix is not square
*/
public GIMatrix transpose() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
double[][] transpose = new double[array.length][array[0].length];
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
transpose[i][j] = array[j][i];
return new GIMatrix(transpose);
} // method transpose()
/**
* Returns a matrix containing all of the diagonal elements of this matrix and zero (0) everywhere
* else. This matrix is called the diagonal of the matrix.
* @return the diagonal of the matrix
* @exception BadMatrixFormatException if the matrix is not square
*/
public GIMatrix diagonal() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
double[][] diagonal = new double[array.length][array[0].length];;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++) {
if (i == j) diagonal[i][j] = array[i][j];
else diagonal[i][j] = 0;
}
return new GIMatrix(diagonal);
} // method diagonal()
/**
* Returns the resulting matrix of an elementary linear operation that consists of multiplying a
* single line of the matrix by a constant.
* @param i the line number
* @param c the double constant that multiplies the line
* @return the resulting Matrix object of the linear operation
* @exception IndexOutOfBoundsException if the given index is out of the matrix's range
*/
public GIMatrix multiplyLine(int i, double c) throws IndexOutOfBoundsException {
if ( (i < 0)||(i >= m) ) throw new IndexOutOfBoundsException();
double[][] temp = array;
for (int k = 0; k < n; k++) temp[i][k] = c*temp[i][k]; // mutliply every member of the line by c
try { return new GIMatrix(temp); } // format is always OK anyway ...
catch (BadMatrixFormatException e) { return null; }
} // method multiplyLine(int,int)
/**
* Returns the resulting matrix of an elementary linear operation that consists of inverting two lines.
* @param i the first line number
* @param j the second line number
* @return the resulting Matrix object of the linear operation
* @exception IndexOutOfBoundsException if the given index is out of the matrix's range
*/
public GIMatrix invertLine(int i, int j) throws IndexOutOfBoundsException {
if ( (i < 0)||(i >= m)||(j < 0)||(j >= m) ) throw new IndexOutOfBoundsException();
double[][] temp = array;
double[] tempLine = temp[j]; // temporary line
temp[j] = temp[i];
temp[i] = tempLine;
try { return new GIMatrix(temp); } // format is always OK anyway ...
catch (BadMatrixFormatException e) { return null; }
} // method invertLine(int,int)
/**
* Returns the resulting matrix of an elementary linear operation that consists of adding one line,
* multiplied by some constant factor, to another line.
* @param i the first line number
* @param j the second line number (to be added to the first)
* @param c the double constant that multiplies the first line
* @return the resulting Matrix object of the linear operation
* @exception IndexOutOfBoundsException if the given index is out of the matrix's range
*/
public GIMatrix addLine(int i, int j, double c) throws IndexOutOfBoundsException{
if ( (i < 0)||(i >= m)||(j < 0)||(j >= m) ) throw new IndexOutOfBoundsException();
double[][] temp = array;
for (int k = 0; k < n; k++)
temp[i][k] = temp[i][k]+c*temp[j][k]; // add multiplied element of i to element of j
try { return new GIMatrix(temp); } // format is always OK anyway ...
catch (BadMatrixFormatException e) { return null; }
} // method addLine(int,int,double)
/**
* Addition from two matrices.
*/
public GIMatrix add(GIMatrix b)
{
if ((b==null) || (m!=b.m) || (n!=b.n))
return null;
int i, j;
GIMatrix result = new GIMatrix(m,n);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
result.array[i][j] = array[i][j]+b.array[i][j];
return result;
}
/**
* Returns the result of the scalar multiplication of the matrix, that is the multiplication of every
* of its elements by a given number.
* @param c the constant by which the matrix is multiplied
* @return the resulting matrix of the scalar multiplication
*/
public GIMatrix multiply(double c) {
double[][] temp = array;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
temp[i][j] = c*temp[i][j];
try { return new GIMatrix(temp); } // format is always OK anyway ...
catch (BadMatrixFormatException e) { return null; }
} // method multiply(double)
/**
* Returns the result of the matricial multiplication of this matrix by another one. The matrix passed
* as parameter <i>follows</i> this matrix in the multiplication, so for an example if the dimension of
* the actual matrix is mxn, the dimension of the second one should be nxp in order for the multiplication
* to be performed (otherwise an exception will be thrown) and the resulting matrix will have dimension mxp.
* @param matrix the matrix following this one in the matricial multiplication
* @return the resulting matrix of the matricial multiplication
* @exception BadMatrixFormatException if the matrix passed in arguments has wrong dimensions
*/
public GIMatrix multiply(GIMatrix matrix) throws BadMatrixFormatException {
if (n != matrix.height()) throw new BadMatrixFormatException(); // unsuitable dimensions
int p = matrix.width();
double[][] temp = new double[m][p];
double[][] multiplied = matrix.getArrayValue();
for (int i = 0; i < m; i++) // line index of the first matrix
for (int k = 0; k < p; k++) { // column index of the second matrix
temp[i][k] = array[i][0]*multiplied[0][k]; // first multiplication
for (int j = 1; j < n; j++) // sum of multiplications
temp[i][k] = temp[i][k]+array[i][j]*multiplied[j][k];
}
return new GIMatrix(temp);
} // method multiply(Matrix)
/**
* Returns the determinant of this matrix. The matrix must be
* square in order to use this method, otherwise an exception will be thrown.
* <i>Warning: this algorithm is very unefficient and takes too much time to compute
* with large matrices.</i>
* @return the determinant of the matrix
* @exception BadMatrixFormatException if the matrix is not square
*/
public double determinant() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
return det(array); // use of recursive method
} // method determinant()
// Method used for recursive determinant algorithm. Supposes the given array is square.
private double det(double[][] mat) {
if (mat.length == 1) return mat[0][0];
double temp = mat[0][0]*det(M(mat,0,0)); // (-1)^(0+0)*m[0][0]*det(M(i,j)) ... first assignation
for (int k = 1; k < mat.length; k++)
temp = temp+(det(M(mat,0,k))*((k % 2 == 0)?mat[0][k]:-mat[0][k]));
// Note: ((0+k)%2 == 0)?1:-1 is equivalent to (-1)^(0+k)
return temp;
} // method det(double[][])
// Returns the minor of the array (supposed square) i.e. the array least its i-th line
// and j-th column
private double[][] M(double[][] mat, int i, int j) {
double[][] temp = new double[mat.length-1][mat[0].length-1]; // "void minor"
for (int k = 0; k < i; k++) {
for (int h = 0; h < j; h++)
temp[k][h] = mat[k][h];
for (int h = j+1; h < mat[0].length; h++)
temp[k][h-1] = mat[k][h];
}
for (int k = i+1; k < mat.length; k++) {
for (int h = 0; h < j; h++)
temp[k-1][h] = mat[k][h];
for (int h = j+1; h < mat[0].length; h++)
temp[k-1][h-1] = mat[k][h];
}
return temp;
} // method M(double[][],int,int)
/**
* Returns the trace of this matrix, that is the sum of the elements of its diagonal. The matrix must be
* square in order to use this method, otherwise an exception will be thrown.
* @return the trace of the matrix
* @exception BadMatrixFormatException if the matrix is not square
*/
public double trace() throws BadMatrixFormatException {
if (m != n) throw new BadMatrixFormatException();
double trace = array[0][0];
for (int i = 1; i < m; i++) trace = trace+array[i][i];
return trace;
} // method trace()
// /**
// * Returns the matrix as a String.
// * The double numbers are printed using the form "p/q" or "a b/c" depending on the value of the boolean,
// * and use a minimal number of spaces as specified. It is the user's responsibility to grant a number
// * of spaces wide enough to get proper alignment.
// * @see tatien.toolbox.double#toString
// * @param simple must be true to get simple expression, false otherwise as specified
// * @param spaces number of spaces
// */
// public String print(boolean simple, int spaces) {
// String print = "";
// for (int i = 0; i < m; i++) {
// print = print + array[i][0].toString(simple,spaces);
// for (int j = 1; j < n; j++) {
// print = print + array[i][j].toString(simple,spaces);
// }
// print = print + "\n";
// }
// return print;
// } // method print()
// Verifies if the matrix is of good format when calling a constructor or setArrayValue
private void verifyMatrixFormat(double[][] testedMatrix) throws BadMatrixFormatException {
if ( (testedMatrix.length == 0)||(testedMatrix[0].length == 0) ) throw new BadMatrixFormatException();
int noOfColumns = testedMatrix[0].length;
for (int i = 1; i < testedMatrix.length; i++)
if (testedMatrix[i].length != noOfColumns) throw new BadMatrixFormatException();
} // method verifyMatrixFormat(double[][])
// In the case of the implementation of a table i.e. an array of matrices, verifies if the table is proper.
private void verifyTableFormat(GIMatrix[][] testedTable) throws BadMatrixFormatException {
if ( (testedTable.length == 0)||(testedTable[0].length == 0) ) throw new BadMatrixFormatException();
int noOfColumns = testedTable[0].length;
int currentHeigth, currentWidth;
for (int i = 0; i < testedTable.length; i++) { // verifies correspondence of m's (heigth)
if (testedTable[i].length != noOfColumns) throw new BadMatrixFormatException();
currentHeigth = testedTable[i][0].height();
for (int j = 1; j < testedTable[0].length; j++)
if (testedTable[i][j].height() != currentHeigth) throw new BadMatrixFormatException();
}
for (int j = 0; j < testedTable[0].length; j++) { // verifies correspondence of n's (width)
currentWidth = testedTable[0][j].width();
for (int i = 1; i < testedTable.length; i++)
if (testedTable[i][j].width() != currentWidth) throw new BadMatrixFormatException();
}
} // method verifyTableFormat(Matrix[][])
} // class Matrix