/* $Revision$ $Author$ $Date$ * * Copyright (C) 2001-2007 Stephan Michels <stephan@vern.chem.tu-berlin.de> * * 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. * All we ask is that proper credit is given for our work, which includes * - but is not limited to - adding the above copyright notice to the beginning * of your source code files, and to any copyright notice that you may distribute * with programs based on this work. * * 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.math; import java.text.DecimalFormat; /** * This class contains a matrix. * * @author Stephan Michels <stephan@vern.chem.tu-berlin.de> * @cdk.githash * @cdk.created 2001-06-07 * @cdk.module qm */ public class Matrix { // Attention! Variables are unprotected /** the content of this matrix **/ public double[][] matrix; /** the number of rows of this matrix */ public int rows; /** the number of columns of this matrix */ public int columns; /** * Creates a new Matrix. */ public Matrix(int rows, int columns) { this.rows = rows; this.columns = columns; matrix = new double[rows][columns]; } /** * Creates a Matrix with content of an array. */ public Matrix(double[][] array) { rows = array.length; int i,j; columns = array[0].length; for(i=1; i<rows; i++) columns = Math.min(columns,array[i].length); matrix = new double[rows][columns]; for(i=0; i<rows; i++) for(j=0; j<columns; j++) matrix[i][j] = array[i][j]; } /** * Returns the number of rows. */ public int getRows() { return rows; } /** * Returns the number of columns. */ public int getColumns() { return columns; } /** * Creates a Vector with the content of a row from this Matrix. */ public Vector getVectorFromRow(int index) { double[] row = new double[columns]; for(int i=0; i<columns; i++) row[i] = matrix[index][i]; return new Vector(row); } /** * Creates a Vector with the content of a column from this Matrix. */ public Vector getVectorFromColumn(int index) { double[] column = new double[rows]; for(int i=0; i<rows; i++) column[i] = matrix[i][index]; return new Vector(column); } /** * Creates a Vector with the content of the diagonal elements from this Matrix. */ public Vector getVectorFromDiagonal() { int size = Math.min(rows, columns); Vector result = new Vector(size); for(int i=0; i<rows; i++) result.vector[i] = matrix[i][i]; return result; } /** * Adds two matrices. */ public Matrix add(Matrix b) { if ((b==null) || (rows!=b.rows) || (columns!=b.columns)) return null; int i, j; Matrix result = new Matrix(rows, columns); for(i=0; i<rows; i++) for(j=0; j<columns; j++) result.matrix[i][j] = matrix[i][j]+b.matrix[i][j]; return result; } /** * Subtracts from two matrices. */ public Matrix sub(Matrix b) { if ((b==null) || (rows!=b.rows) || (columns!=b.columns)) return null; int i, j; Matrix result = new Matrix(rows, columns); for(i=0; i<rows; i++) for(j=0; j<columns; j++) result.matrix[i][j] = matrix[i][j]-b.matrix[i][j]; return result; } /** * Multiplies this Matrix with another one. */ public Matrix mul(Matrix b) { if ((b==null) || (columns!=b.rows)) return null; Matrix result = new Matrix(rows, b.columns); int i,j,k; double sum; for(i=0; i<rows; i++) for(k=0; k<b.columns; k++) { sum = 0; for(j=0; j<columns; j++) sum += matrix[i][j]*b.matrix[j][k]; result.matrix[i][k] = sum; } return result; } /** * Multiplies a Vector with this Matrix. */ public Vector mul(Vector a) { if ((a==null) || (columns!=a.size)) return null; Vector result = new Vector(rows); int i,j; double sum; for(i=0; i<rows; i++) { sum = 0; for(j=0; j<columns; j++) sum += matrix[i][j]*a.vector[j]; result.vector[i] = sum; } return result; } /** * Multiplies a scalar with this Matrix. */ public Matrix mul(double a) { Matrix result = new Matrix(rows, columns); int i,j; for(i=0; i<rows; i++) for(j=0; j<columns; j++) result.matrix[i][j] = matrix[i][j]*a; return result; } /** * Copies a matrix. */ public Matrix duplicate() { Matrix result = new Matrix(rows, columns); int i,j; for(i=0; i<rows; i++) for(j=0; j<columns; j++) result.matrix[i][j] = matrix[i][j]; return result; } /** * Transposes a matrix. */ public Matrix transpose() { Matrix result = new Matrix(columns, rows); int i,j; for(i=0; i<rows; i++) for(j=0; j<columns; j++) result.matrix[j][i] = matrix[i][j]; return result; } /** * Similar transformation * Ut * M * U */ public Matrix similar(Matrix U) { Matrix result = new Matrix(U.columns, U.columns); double sum, innersum; for(int i=0; i<U.columns; i++) for(int j=0; j<U.columns; j++) { sum = 0d; for(int k=0; k<U.columns; k++) { innersum = 0d; for(int l=0; l<U.columns; l++) innersum += matrix[k][l]*U.matrix[l][j]; sum += U.matrix[k][i]*innersum; } result.matrix[i][j] = sum; } return result; } public double contraction() { int i,j; double result = 0d; for(i=0; i<rows; i++) for(j=0; j<columns; j++) result += matrix[i][j]; return result; } /** * Return a matrix as a String. */ public String toString() { if ((rows<=0) || (columns<=0)) return "[]"; int i,j; DecimalFormat format = new DecimalFormat("00.0000"); format.setPositivePrefix("+"); StringBuffer str = new StringBuffer(); for(i=0; i<(rows-1); i++) { for(j=0; j<(columns-1); j++) if (Math.round(matrix[i][j]*10000)!=0) str.append(format.format(matrix[i][j])+" "); else str.append("-------- "); if (Math.round(matrix[i][columns-1]*10000)!=0) str.append(format.format(matrix[i][columns-1])+"\n"); else str.append("--------\n"); } for(j=0; j<(columns-1); j++) if (Math.round(matrix[rows-1][j]*10000)!=0) str.append(format.format(matrix[rows-1][j])+" "); else str.append("-------- "); if (Math.round(matrix[rows-1][columns-1]*10000)!=0) str.append(format.format(matrix[rows-1][columns-1])); else str.append("-------- "); return str.toString(); } /** * Diagonalize this matrix with the Jacobi algorithm. * * @param nrot Count of max. rotations * @return Matrix m, with m^t * this * m = diagonal * * @cdk.keyword Jacobi algorithm * @cdk.keyword diagonalization */ public Matrix diagonalize(int nrot) { Matrix m = duplicate(); if (m.rows!=m.columns) { System.err.println("Matrix.diagonal: Sizes mismatched"); return null; } int n = m.rows; int j,iq,ip,i; double tresh,theta,tau,t,sm,s,h,g,c; double[] b,z; Matrix v = new Matrix(columns,columns); Vector d = new Vector(columns); b = new double[n+1]; z = new double[n+1]; for (ip=0;ip<n;ip++) { for (iq=0;iq<n;iq++) v.matrix[ip][iq]=0.0; v.matrix[ip][ip]=1.0; } for (ip=0;ip<n;ip++) { b[ip]=d.vector[ip]=m.matrix[ip][ip]; z[ip]=0.0; } nrot=0; for (i=1;i<=50;i++) { sm=0.0; for (ip=0;ip<n-1;ip++) { for (iq=ip+1;iq<n;iq++) sm += Math.abs(m.matrix[ip][iq]); } // Ready ?? if (sm == 0.0) return v; if (i < 4) tresh=0.2*sm/(n*n); else tresh=0.0; for (ip=0;ip<n-1;ip++) { for (iq=ip+1;iq<n;iq++) { g=100.0*Math.abs(m.matrix[ip][iq]); if ((i > 4) && (Math.abs(d.vector[ip])+g == Math.abs(d.vector[ip])) && (Math.abs(d.vector[iq])+g == Math.abs(d.vector[iq]))) m.matrix[ip][iq]=0.0; else if (Math.abs(m.matrix[ip][iq]) > tresh) { h = d.vector[iq]-d.vector[ip]; if (Math.abs(h)+g == Math.abs(h)) t = (m.matrix[ip][iq])/h; else { theta = 0.5*h/(m.matrix[ip][iq]); t = 1.0/(Math.abs(theta)+Math.sqrt(1.0+theta*theta)); if (theta < 0.0) t = -t; } c = 1.0/Math.sqrt(1+t*t); s = t*c; tau = s/(1.0+c); h = t*m.matrix[ip][iq]; z[ip] -= h; z[iq] += h; d.vector[ip] -= h; d.vector[iq] += h; m.matrix[ip][iq]=0.0; // Case of rotaions 1<=j<p for (j=0;j<ip;j++) { g=m.matrix[j][ip]; h=m.matrix[j][iq]; m.matrix[j][ip]=g-s*(h+g*tau); m.matrix[j][iq]=h+s*(g-h*tau); } // Case of rotaions p<j<q for (j=ip+1;j<iq;j++) { g=m.matrix[ip][j]; h=m.matrix[j][iq]; m.matrix[ip][j]=g-s*(h+g*tau); m.matrix[j][iq]=h+s*(g-h*tau); } // Case of rotaions q<j<=n for (j=iq+1;j<n;j++) { g=m.matrix[ip][j]; h=m.matrix[iq][j]; m.matrix[ip][j]=g-s*(h+g*tau); m.matrix[iq][j]=h+s*(g-h*tau); } for (j=0;j<n;j++) { g=v.matrix[j][ip]; h=v.matrix[j][iq]; v.matrix[j][ip]=g-s*(h+g*tau); v.matrix[j][iq]=h+s*(g-h*tau); } ++nrot; } } } for (ip=0;ip<n;ip++) { b[ip] += z[ip]; d.vector[ip]=b[ip]; z[ip]=0.0; } } System.out.println("Too many iterations in routine JACOBI"); return v; } /** * Solves a linear equation system with Gauss elimination. * * @cdk.keyword Gauss elimination */ public static Vector elimination(Matrix matrix, Vector vector) { int i,j,k,ipvt; int n = vector.size; int[] pivot = new int[n]; double c, temp; //double[] x = new double[n]; Vector result = new Vector(n); Matrix a = matrix.duplicate(); Vector b = vector.duplicate(); for(j=0; j<(n-1); j++) { c = Math.abs(a.matrix[j][j]); pivot[j] = j; ipvt = j; for(i=j+1; i<n; i++) if (Math.abs(a.matrix[i][j])>c) { c = Math.abs(a.matrix[i][j]); ipvt = i; } // Exchanges rows when necessary if (pivot[j]!=ipvt) { pivot[j] = ipvt; pivot[ipvt] = j; for(k=0; k<n; k++) { temp = a.matrix[j][k]; a.matrix[j][k] = a.matrix[pivot[j]][k]; a.matrix[pivot[j]][k] = temp; } temp = b.vector[j]; b.vector[j] = b.vector[pivot[j]]; b.vector[pivot[j]] = temp; } // Store multipliers for(i=j+1; i<n; i++) a.matrix[i][j] = a.matrix[i][j] / a.matrix[j][j]; // Give elements below the diagonal a zero value for(i=j+1; i<n; i++) { for(k=j+1; k<n; k++) a.matrix[i][k] = a.matrix[i][k] - a.matrix[i][j]*a.matrix[j][k]; b.vector[i] = b.vector[i] - a.matrix[i][j]*b.vector[j]; a.matrix[i][j] = 0; // Not necessary } } // Rueckwaertseinsetzen (which is?) result.vector[n-1] = b.vector[n-1]/a.matrix[n-1][n-1]; for(j=n-2; j>=0; j--) { result.vector[j] = b.vector[j]; for(k=n-1; k>j; k--) result.vector[j] = result.vector[j] - result.vector[k]*a.matrix[j][k]; result.vector[j] = result.vector[j]/a.matrix[j][j]; } return result; } /** * Orthonormalize the vectors of this matrix by Gram-Schmidt. * * @cdk.keyword orthonormalization * @cdk.keyword Gram-Schmidt algorithm */ public Matrix orthonormalize(Matrix S) { int p,q,k,i,j; double innersum; double length; //Matrix scr = S.mul(this); Matrix result = duplicate(); for(p=0; p<columns; p++) // Loops over all vectors { for(i=0; i<rows; i++) result.matrix[i][p] = matrix[i][p]; for(k=0; k<p; k++) // Substracts the previous vector { // First the calculation of the product <phi_p|phi_k>=length length = 0; for(i=0; i<rows; i++) // Loops over all vectors { innersum = 0; for(j=0; j<rows; j++) { innersum += result.matrix[j][p]*S.matrix[i][j]; } length += result.matrix[i][k]*innersum; } // Then the substraction of phi_k*length for(q=0; q<rows; q++) result.matrix[q][p] -= result.matrix[q][k]*length; } // Calculates the integral for normalization length = 0; for(i=0; i<rows; i++) for(j=0; j<rows; j++) length += result.matrix[i][p]*result.matrix[j][p]*S.matrix[i][j]; length = Math.sqrt(length); // Normalizes the vector if (length!=0d) for(q=0; q<rows; q++) result.matrix[q][p] /= length; else System.out.println("Warning(orthonormalize):"+(p+1)+". Vector has length null"); } return result; } /** * Normalizes the vectors of this matrix. */ public Matrix normalize(Matrix S) { int p,q,i,j; double length; Matrix result = duplicate(); for(p=0; p<columns; p++) // Loops over all vectors { // Calculates the normalization factor length = 0; for(i=0; i<rows; i++) for(j=0; j<rows; j++) length += result.matrix[i][p]*result.matrix[j][p]*S.matrix[i][j]; length = Math.sqrt(length); // Normalizes the vector if (length!=0d) for(q=0; q<rows; q++) result.matrix[q][p] /= length; else System.out.println("Warning(orthonormalize):"+(p+1)+". Vector has length null"); } return result; } }