/* Class Matrix * * Defines a matrix and includes the methods needed * for standard matrix manipulations, e.g. multiplation, * and related procedures, e.g. solution of linear * simultaneous equations * * See class ComplexMatrix and PhasorMatrix for complex matrix arithmetic * * WRITTEN BY: Dr Michael Thomas Flanagan * * DATE: June 2002 * UPDATES: 21 April 2004, 16 February 2006, 31 March 2006, 22 April 2006, * 1 July 2007, 17 July 2007, 18 August 2007, 7 October 2007 * 27 February 2008, 7 April 2008, 5 July 2008, 6-15 September 2008, 7-14 October 2008, * 16 February 2009, 16 June 2009, 15 October 2009, 4-5 November 2009 * 12 January 2010, 19 February 2010, 14 November 2010 * 12 January 2011, 20 January 2011, 14-16 July 2011 * 7 April 2012 * * DOCUMENTATION: * See Michael Thomas Flanagan's Java library on-line web page: * http://www.ee.ucl.ac.uk/~mflanaga/java/Matrix.html * http://www.ee.ucl.ac.uk/~mflanaga/java/ * * Copyright (c) 2002 - 2012 Michael Thomas Flanagan * PERMISSION TO COPY: * * Permission to use, copy and modify this software and its documentation for NON-COMMERCIAL purposes is granted, without fee, * provided that an acknowledgement to the author, Dr Michael Thomas Flanagan at www.ee.ucl.ac.uk/~mflanaga, appears in all copies * and associated documentation or publications. * * Redistributions of the source code of this source code, or parts of the source codes, must retain the above copyright notice, this list of conditions * and the following disclaimer and requires written permission from the Michael Thomas Flanagan: * * Redistribution in binary form of all or parts of this class must reproduce the above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or other materials provided with the distribution and requires written permission from the Michael Thomas Flanagan: * * Dr Michael Thomas Flanagan makes no representations about the suitability or fitness of the software for any or for a particular purpose. * Dr Michael Thomas Flanagan shall not be liable for any damages suffered as a result of using, modifying or distributing this software * or its derivatives. * ***************************************************************************************/ package flanagan.math; import flanagan.analysis.Regression; import flanagan.analysis.RegressionFunction; import flanagan.math.ArrayMaths; import flanagan.math.Conv; import flanagan.analysis.Stat; import java.util.ArrayList; import java.util.Vector; import java.math.BigDecimal; import java.math.BigInteger; public class Matrix{ private int numberOfRows = 0; // number of rows private int numberOfColumns = 0; // number of columns private double[][] matrix = null; // 2-D Matrix as double private String[][] matrixS = null; // 2-D Matrix as String private int entryType = -1; // Entry type // = 0; double // = 1; float // = 2; long // = 3; int // = 4; BigDecimal // = 5; BigInteger // = x; // = y; private boolean numericalCheck = true; // = false if no numerical data entered, e.g. only non-numerical strings entered private double[][] hessenberg = null; // 2-D Hessenberg equivalent private boolean hessenbergDone = false; // = true when Hessenberg matrix calculated private int permutationIndex[] = null; // row permutation index private double rowSwapIndex = 1.0D; // row swap index private double[] eigenValues = null; // eigen values of the matrix private double[][] eigenVector = null; // eigen vectors of the matrix private double[] sortedEigenValues = null; // eigen values of the matrix sorted into descending order private double[][] sortedEigenVector = null; // eigen vectors of the matrix sorted to matching descending eigen value order private int numberOfRotations = 0; // number of rotations in Jacobi transformation private int[] eigenIndices = null; // indices of the eigen values before sorting into descending order private int maximumJacobiIterations = 100; // maximum number of Jacobi iterations private boolean eigenDone = false; // = true when eigen values and vectors calculated private boolean matrixCheck = true; // check on matrix status // true - no problems encountered in LU decomposition // false - attempted a LU decomposition on a singular matrix private boolean supressErrorMessage = false; // true - LU decompostion failure message supressed private double tiny = 1.0e-100; // small number replacing zero in LU decomposition // CONSTRUCTORS // Construct a numberOfRows x numberOfColumns matrix of variables all equal to zero public Matrix(int numberOfRows, int numberOfColumns){ this.numberOfRows = numberOfRows; this.numberOfColumns = numberOfColumns; this.matrix = new double[this.numberOfRows][this.numberOfColumns]; this.matrixS = new String[this.numberOfRows][this.numberOfColumns]; this.permutationIndex = new int[this.numberOfRows]; this.entryType = 0; for(int i=0;i public Matrix(ArrayList[] twoDal){ this.numberOfRows = twoDal.length; ArrayMaths[] twoD = new ArrayMaths[this.numberOfRows]; for(int i=0; i public Matrix(Vector[] twoDv){ this.numberOfRows = twoDv.length; ArrayMaths[] twoD = new ArrayMaths[this.numberOfRows]; for(int i=0; i=this.numberOfRows)throw new IllegalArgumentException("Sub-matrix position is outside the row bounds of this Matrix"); if(j+l-1>=this.numberOfColumns)throw new IllegalArgumentException("Sub-matrix position is outside the column bounds of this Matrix"); int m = 0; int n = 0; for(int p=0; p=this.numberOfRows)throw new IllegalArgumentException("Row index, " + i + ", must be less than the number of rows, " + this.numberOfRows); if(i<0)throw new IllegalArgumentException("Row index, " + i + ", must be zero or positive"); return Conv.copy(this.matrix[i]); } // Return a copy of a column public double[] getColumnCopy(int ii){ if(ii>=this.numberOfColumns)throw new IllegalArgumentException("Column index, " + ii + ", must be less than the number of columns, " + this.numberOfColumns); if(ii<0)throw new IllegalArgumentException("column index, " + ii + ", must be zero or positive"); double[] col = new double[this.numberOfRows]; for(int i=0; ik)throw new IllegalArgumentException("row indices inverted"); if(j>l)throw new IllegalArgumentException("column indices inverted"); if(k>=this.numberOfRows)throw new IllegalArgumentException("Sub-matrix position is outside the row bounds of this Matrix" ); if(l>=this.numberOfColumns)throw new IllegalArgumentException("Sub-matrix position is outside the column bounds of this Matrix" + i + " " +l); int n=k-i+1, m=l-j+1; Matrix subMatrix = new Matrix(n, m); double[][] sarray = subMatrix.getArrayReference(); for(int p=0; p=this.numberOfRows)throw new IllegalArgumentException("The entered row index, " + ii + " must lie between 0 and " + (this.numberOfRows-1) + " inclusive"); if(jj<0 || jj>=this.numberOfColumns)throw new IllegalArgumentException("The entered column index, " + jj + " must lie between 0 and " + (this.numberOfColumns-1) + " inclusive"); int[] rowi = new int[this.numberOfRows - 1]; int[] colj = new int[this.numberOfColumns - 1]; int kk = 0; for(int i=0; i=this.numberOfColumns)testOuter = false; } rowPointer++; if(rowPointer>=this.numberOfRows || !testInner)testOuter = false; } for(int i=0; iMath.abs(max[0])){ maxI = minI; maxJ = minJ; } int[] ret = {maxI, maxJ}; double[] hold1 = this.matrix[0]; this.matrix[0] = this.matrix[maxI]; this.matrix[maxI] = hold1; double hold2 = 0.0; for(int i=0; ij && this.matrix[i][j]!=0.0D)test = false; } } return test; } // Check if a matrix is tridiagonal public boolean isTridiagonal(){ boolean test = true; for(int i=0; i(i+1) && this.matrix[i][j]!=0.0D)test = false; } } return test; } // Check if a matrix is upper Hessenberg public boolean isUpperHessenberg(){ boolean test = true; for(int i=0; i(j+1) && this.matrix[i][j]!=0.0D)test = false; } } return test; } // Check if a matrix is a identity matrix public boolean isIdentity(){ boolean test = true; if(this.numberOfRows==this.numberOfColumns){ for(int i=0; iMath.abs(tolerance))test = false; } } } else{ test = false; } return test; } // Check if a matrix is zero within a given tolerance public boolean isNearlyZero(double tolerance){ boolean test = true; for(int i=0; iMath.abs(tolerance))test = false; } } return test; } // Check if a matrix is unit within a given tolerance public boolean isNearlyUnit(double tolerance){ boolean test = true; for(int i=0; iMath.abs(tolerance))test = false; } } return test; } // Check if a matrix is upper triagonal within a given tolerance public boolean isNearlyUpperTriagonal(double tolerance){ boolean test = true; for(int i=0; iMath.abs(tolerance))test = false; } } return test; } // Check if a matrix is lower triagonal within a given tolerance public boolean isNearlyLowerTriagonal(double tolerance){ boolean test = true; for(int i=0; ij && Math.abs(this.matrix[i][j])>Math.abs(tolerance))test = false; } } return test; } // Check if a matrix is an identy matrix within a given tolerance public boolean isNearlyIdenty(double tolerance){ boolean test = true; if(this.numberOfRows==this.numberOfColumns){ for(int i=0; iMath.abs(tolerance))test = false; for(int j=i+1; jMath.abs(tolerance))test = false; if(Math.abs(this.matrix[j][i])>Math.abs(tolerance))test = false; } } } else{ test = false; } return test; } // Check if a matrix is tridiagonal within a given tolerance public boolean isTridiagonal(double tolerance){ boolean test = true; for(int i=0; iMath.abs(tolerance))test = false; if(j>(i+1) && Math.abs(this.matrix[i][j])>Math.abs(tolerance))test = false; } } return test; } // Check if a matrix is tridiagonal within a given tolerance public boolean isNearlyTridiagonal(double tolerance){ boolean test = true; for(int i=0; iMath.abs(tolerance))test = false; if(j>(i+1) && Math.abs(this.matrix[i][j])>Math.abs(tolerance))test = false; } } return test; } // Check if a matrix is upper Hessenberg within a given tolerance public boolean isNearlyUpperHessenberg(double tolerance){ boolean test = true; for(int i=0; iMath.abs(tolerance))test = false; } } return test; } // Check if a matrix is lower Hessenberg within a given tolerance public boolean isNearlyLowerHessenberg(double tolerance){ boolean test = true; for(int i=0; i(j+1) && Math.abs(this.matrix[i][j])>Math.abs(tolerance))test = false; } } return test; } // Check if a matrix is singular public boolean isSingular(){ boolean test = false; double det = this.determinant(); if(det==0.0)test = true; return test; } // Check if a matrix is singular within a given tolerance public boolean isNearlySingular(double tolerance){ boolean test = false; double det = this.determinant(); if(Math.abs(det)<=Math.abs(tolerance))test = true; return test; } // Check for identical rows // Returns the number of pairs of identical rows followed by the row indices of the identical row pairs public ArrayList identicalRows(){ ArrayList ret = new ArrayList(); int nIdentical = 0; for(int i=0; i identicalColumns(){ ArrayList ret = new ArrayList(); int nIdentical = 0; for(int i=0; i zeroRows(){ ArrayList ret = new ArrayList(); int nZero = 0; for(int i=0; i zeroColumns(){ ArrayList ret = new ArrayList(); int nZero = 0; for(int i=0; i big) big=temp; if (big == 0.0D){ if(!this.supressErrorMessage){ System.out.println("Attempted LU Decomposition of a singular matrix in Matrix.luDecomp()"); System.out.println("NaN matrix returned and matrixCheck set to false"); } this.matrixCheck=false; for(int k=0;k= big) { big=dum; imax=i; } } if (j != imax) { for (int k=0;k=0;i--) { sum=xvec[i]; for (int j=i+1;jthis.numberOfColumns){ // overdetermined equations - least squares used - must be used with care int n = bvec.length; if(this.numberOfRows!=n)throw new IllegalArgumentException("Overdetermined equation solution - vector length is not equal to matrix column length"); Matrix avecT = this.transpose(); double[][] avec = avecT.getArrayCopy(); Regression reg = new Regression(avec, bvec); reg.linearGeneral(); xvec = reg.getCoeff(); } else{ throw new IllegalArgumentException("This class does not handle underdetermined equations"); } } return xvec; } //Supress printing of LU decompostion failure message public void supressErrorMessage(){ this.supressErrorMessage = true; } // HESSENBERG MARTIX // Calculates the Hessenberg equivalant of this matrix public void hessenbergMatrix(){ this.hessenberg = this.getArrayCopy(); double pivot = 0.0D; int pivotIndex = 0; double hold = 0.0D; for(int i = 1; i Math.abs(pivot)){ pivot = this.hessenberg[j][i-1]; pivotIndex = j; } } // row and column interchange if(pivotIndex != i){ for(int j = i-1; j 4 && (Math.abs(this.eigenValues[p]) + scaledOffDiagonal) == Math.abs(this.eigenValues[p]) && (Math.abs(this.eigenValues[q]) + scaledOffDiagonal) == Math.abs(this.eigenValues[q])){ amat[p][q] = 0.0; } else if(Math.abs(amat[p][q]) > threshold){ vectorDifference = this.eigenValues[q] - this.eigenValues[p]; if ((Math.abs(vectorDifference) + scaledOffDiagonal) == Math.abs(vectorDifference)) sOverC = amat[p][q]/vectorDifference; else{ cot2rotationAngle = 0.5*vectorDifference/amat[p][q]; sOverC = 1.0/(Math.abs(cot2rotationAngle) + Math.sqrt(1.0 + cot2rotationAngle*cot2rotationAngle)); if (cot2rotationAngle < 0.0) sOverC = -sOverC; } cElement = 1.0/Math.sqrt(1.0 + sOverC*sOverC); sElement = sOverC*cElement; tanHalfRotationAngle = sElement/(1.0 + cElement); vectorDifference = sOverC*amat[p][q]; holdingVector2[p] -= vectorDifference; holdingVector2[q] += vectorDifference; this.eigenValues[p] -= vectorDifference; this.eigenValues[q] += vectorDifference; amat[p][q] = 0.0; for(int j=0;j<=p-1;j++) rotation(amat, tanHalfRotationAngle, sElement, j, p, j, q); for(int j=p+1;j<=q-1;j++) rotation(amat, tanHalfRotationAngle, sElement, p, j, j, q); for(int j=q+1;j= holdingElement){ holdingElement = this.sortedEigenValues[j]; k = j; } } if (k != i){ this.sortedEigenValues[k] = this.sortedEigenValues[i]; this.sortedEigenValues[i] = holdingElement; for(int j=0; j