001/* 002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences. 003 * Copyright (C) 2006 - JScience (http://jscience.org/) 004 * All rights reserved. 005 * 006 * Permission to use, copy, modify, and distribute this software is 007 * freely granted, provided that this notice is preserved. 008 */ 009package org.jscience.mathematics.vector; 010 011import java.util.Comparator; 012 013import javolution.context.StackContext; 014import javolution.lang.MathLib; 015import javolution.lang.Realtime; 016import javolution.lang.ValueType; 017import javolution.text.Text; 018import javolution.text.TextBuilder; 019import javolution.xml.XMLFormat; 020import javolution.xml.stream.XMLStreamException; 021 022import org.jscience.mathematics.structure.Field; 023import org.jscience.mathematics.structure.Ring; 024import org.jscience.mathematics.structure.VectorSpace; 025 026/** 027 * <p> This class represents a rectangular table of elements of a ring-like 028 * algebraic structure.</p> 029 * 030 * <p> Instances of this class can be used to resolve system of linear equations 031 * involving <i>any kind</i> of {@link Field Field} elements 032 * (e.g. {@link org.jscience.mathematics.number.Real Real}, 033 * {@link org.jscience.mathematics.number.Complex Complex}, 034 * {@link org.jscience.physics.amount.Amount Amount<?>}, 035 * {@link org.jscience.mathematics.function.Function Function}, etc). 036 * For example:[code] 037 * // Creates a dense matrix (2x2) of Rational numbers. 038 * DenseMatrix<Rational> M = DenseMatrix.valueOf( 039 * { Rational.valueOf(23, 45), Rational.valueOf(33, 75) }, 040 * { Rational.valueOf(15, 31), Rational.valueOf(-20, 45)}); 041 * 042 * // Creates a sparse matrix (16x2) of Real numbers. 043 * SparseMatrix<Real> M = SparseMatrix.valueOf( 044 * SparseVector.valueOf(16, Real.ZERO, 0, Real.valueOf(5)), 045 * SparseVector.valueOf(16, Real.ZERO, 15, Real.valueOf(-3))); 046 * 047 * // Creates a floating-point (64 bits) matrix (3x2). 048 * Float64Matrix M = Float64Matrix.valueOf( 049 * {{ 1.0, 2.0, 3.0}, { 4.0, 5.0, 6.0}}); 050 * 051 * // Creates a complex single column matrix (1x2). 052 * ComplexMatrix M = ComplexMatrix.valueOf( 053 * {{ Complex.valueOf(1.0, 2.0), Complex.valueOf(4.0, 5.0)}}).transpose(); 054 * 055 * // Creates an identity matrix (2x2) for modulo integer. 056 * SparseMatrix<ModuloInteger> IDENTITY = SparseMatrix.valueOf( 057 * DenseVector.valueOf(ModuloInteger.ONE, ModuloInteger.ONE), ModuloInteger.ZERO); 058 * [/code]</p> 059 * 060 * <p> Non-commutative field multiplication is supported. Invertible square 061 * matrices may form a non-commutative field (also called a division 062 * ring). In which case this class may be used to resolve system of linear 063 * equations with matrix coefficients.</p> 064 * 065 * <p> Implementation Note: Matrices may use {@link 066 * javolution.context.StackContext StackContext} and {@link 067 * javolution.context.ConcurrentContext ConcurrentContext} in order to 068 * minimize heap allocation and accelerate calculations on multi-core 069 * systems.</p> 070 * 071 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 072 * @version 3.3, December 24, 2006 073 * @see <a href="http://en.wikipedia.org/wiki/Matrix_%28mathematics%29"> 074 * Wikipedia: Matrix (mathematics)</a> 075 */ 076public abstract class Matrix<F extends Field<F>> 077 implements VectorSpace<Matrix<F>, F>, Ring<Matrix<F>>, ValueType, Realtime { 078 079 /** 080 * Holds the default XML representation for matrices. For example:[code] 081 * <DenseMatrix rows="2" columns="2"> 082 * <Complex real="1.0" imaginary="0.0" /> 083 * <Complex real="0.0" imaginary="1.0" /> 084 * <Complex real="0.0" imaginary="0.4" /> 085 * <Complex real="-5.0" imaginary="-1.0" /> 086 * </DenseMatrix>[/code] 087 */ 088 @SuppressWarnings("unchecked") 089 protected static final XMLFormat<Matrix> XML = new XMLFormat<Matrix>( 090 Matrix.class) { 091 092 @Override 093 public void read(InputElement xml, Matrix M) throws XMLStreamException { 094 // Nothing to do. 095 } 096 097 @Override 098 public void write(Matrix M, OutputElement xml) 099 throws XMLStreamException { 100 final int m = M.getNumberOfRows(); 101 final int n = M.getNumberOfColumns(); 102 xml.setAttribute("rows", m); 103 xml.setAttribute("columns", n); 104 for (int i = 0; i < m; i++) { 105 for (int j = 0; j < n; j++) { 106 xml.add(M.get(i, j)); 107 } 108 } 109 } 110 }; 111 112 /** 113 * Default constructor (for sub-classes). 114 */ 115 protected Matrix() { 116 } 117 118 /** 119 * Returns the number of rows <code>m</code> for this matrix. 120 * 121 * @return m, the number of rows. 122 */ 123 public abstract int getNumberOfRows(); 124 125 /** 126 * Returns the number of columns <code>n</code> for this matrix. 127 * 128 * @return n, the number of columns. 129 */ 130 public abstract int getNumberOfColumns(); 131 132 /** 133 * Returns a single element from this matrix. 134 * 135 * @param i the row index (range [0..m[). 136 * @param j the column index (range [0..n[). 137 * @return the element read at [i,j]. 138 * @throws IndexOutOfBoundsException <code> 139 * ((i < 0) || (i >= m)) || ((j < 0) || (j >= n))</code> 140 */ 141 public abstract F get(int i, int j); 142 143 /** 144 * Returns the row identified by the specified index in this matrix. 145 * 146 * @param i the row index (range [0..m[). 147 * @return the vector holding the specified row. 148 * @throws IndexOutOfBoundsException <code>(i < 0) || (i >= m)</code> 149 */ 150 public abstract Vector<F> getRow(int i); 151 152 /** 153 * Returns the column identified by the specified index in this matrix. 154 * 155 * @param j the column index (range [0..n[). 156 * @return the vector holding the specified column. 157 * @throws IndexOutOfBoundsException <code>(j < 0) || (j >= n)</code> 158 */ 159 public abstract Vector<F> getColumn(int j); 160 161 /** 162 * Returns the diagonal vector. 163 * 164 * @return the vector holding the diagonal elements. 165 */ 166 public abstract Vector<F> getDiagonal(); 167 168 /** 169 * Returns the negation of this matrix. 170 * 171 * @return <code>-this</code>. 172 */ 173 public abstract Matrix<F> opposite(); 174 175 /** 176 * Returns the sum of this matrix with the one specified. 177 * 178 * @param that the matrix to be added. 179 * @return <code>this + that</code>. 180 * @throws DimensionException matrices's dimensions are different. 181 */ 182 public abstract Matrix<F> plus(Matrix<F> that); 183 184 /** 185 * Returns the difference between this matrix and the one specified. 186 * 187 * @param that the matrix to be subtracted. 188 * @return <code>this - that</code>. 189 * @throws DimensionException matrices's dimensions are different. 190 */ 191 public Matrix<F> minus(Matrix<F> that) { 192 return this.plus(that.opposite()); 193 } 194 195 /** 196 * Returns the product of this matrix by the specified factor. 197 * 198 * @param k the coefficient multiplier. 199 * @return <code>this · k</code> 200 */ 201 public abstract Matrix<F> times(F k); 202 203 /** 204 * Returns the product of this matrix by the specified vector. 205 * 206 * @param v the vector. 207 * @return <code>this · v</code> 208 * @throws DimensionException if <code> 209 * v.getDimension() != this.getNumberOfColumns()<code> 210 */ 211 public abstract Vector<F> times(Vector<F> v); 212 213 /** 214 * Returns the product of this matrix with the one specified. 215 * 216 * @param that the matrix multiplier. 217 * @return <code>this · that</code>. 218 * @throws DimensionException if <code> 219 * this.getNumberOfColumns() != that.getNumberOfRows()</code>. 220 */ 221 public abstract Matrix<F> times(Matrix<F> that); 222 223 /** 224 * Returns the inverse of this matrix (must be square). 225 * 226 * @return <code>1 / this</code> 227 * @throws DimensionException if this matrix is not square. 228 */ 229 public abstract Matrix<F> inverse(); 230 231 /** 232 * Returns this matrix divided by the one specified. 233 * 234 * @param that the matrix divisor. 235 * @return <code>this / that</code>. 236 * @throws DimensionException if that matrix is not square or dimensions 237 * do not match. 238 */ 239 public Matrix<F> divide(Matrix<F> that) { 240 return this.times(that.inverse()); 241 } 242 243 /** 244 * Returns the inverse or pseudo-inverse if this matrix if not square. 245 * 246 * <p> Note: To resolve the equation <code>A * X = B</code>, 247 * it is usually faster to calculate <code>A.lu().solve(B)</code> 248 * rather than <code>A.inverse().times(B)</code>.</p> 249 * 250 * @return the inverse or pseudo-inverse of this matrix. 251 */ 252 public Matrix<F> pseudoInverse() { 253 if (isSquare()) 254 return this.inverse(); 255 Matrix<F> thisTranspose = this.transpose(); 256 return (thisTranspose.times(this)).inverse().times(thisTranspose); 257 } 258 259 /** 260 * Returns the determinant of this matrix. 261 * 262 * @return this matrix determinant. 263 * @throws DimensionException if this matrix is not square. 264 */ 265 public abstract F determinant(); 266 267 /** 268 * Returns the transpose of this matrix. 269 * 270 * @return <code>A'</code>. 271 */ 272 public abstract Matrix<F> transpose(); 273 274 /** 275 * Returns the cofactor of an element in this matrix. It is the value 276 * obtained by evaluating the determinant formed by the elements not in 277 * that particular row or column. 278 * 279 * @param i the row index. 280 * @param j the column index. 281 * @return the cofactor of <code>THIS[i,j]</code>. 282 * @throws DimensionException matrix is not square or its dimension 283 * is less than 2. 284 */ 285 public abstract F cofactor(int i, int j); 286 287 /** 288 * Returns the adjoint of this matrix. It is obtained by replacing each 289 * element in this matrix with its cofactor and applying a + or - sign 290 * according (-1)**(i+j), and then finding the transpose of the resulting 291 * matrix. 292 * 293 * @return the adjoint of this matrix. 294 * @throws DimensionException if this matrix is not square or if 295 * its dimension is less than 2. 296 */ 297 public abstract Matrix<F> adjoint(); 298 299 /** 300 * Indicates if this matrix is square. 301 * 302 * @return <code>getNumberOfRows() == getNumberOfColumns()</code> 303 */ 304 public boolean isSquare() { 305 return getNumberOfRows() == getNumberOfColumns(); 306 } 307 308 /** 309 * Solves this matrix for the specified vector (returns <code>x</code> 310 * such as <code>this · x = y</code>). 311 * 312 * @param y the vector for which the solution is calculated. 313 * @return <code>x</code> such as <code>this · x = y</code> 314 * @throws DimensionException if that matrix is not square or dimensions 315 * do not match. 316 */ 317 public Vector<F> solve(Vector<F> y) { 318 DenseMatrix<F> M = DenseMatrix.newInstance(y.getDimension(), true); 319 M._rows.add(DenseVector.valueOf(y)); 320 return solve(M).getColumn(0); 321 } 322 323 /** 324 * Solves this matrix for the specified matrix (returns <code>x</code> 325 * such as <code>this · x = y</code>). 326 * 327 * @param y the matrix for which the solution is calculated. 328 * @return <code>x</code> such as <code>this · x = y</code> 329 * @throws DimensionException if that matrix is not square or dimensions 330 * do not match. 331 */ 332 public Matrix<F> solve(Matrix<F> y) { 333 return LUDecomposition.valueOf(this).solve(y); // Default implementation. 334 } 335 336 /** 337 * Returns this matrix raised at the specified exponent. 338 * 339 * @param exp the exponent. 340 * @return <code>this<sup>exp</sup></code> 341 * @throws DimensionException if this matrix is not square. 342 */ 343 public Matrix<F> pow(int exp) { 344 if (exp > 0) { 345 StackContext.enter(); 346 try { 347 Matrix<F> pow2 = this; 348 Matrix<F> result = null; 349 while (exp >= 1) { // Iteration. 350 if ((exp & 1) == 1) { 351 result = (result == null) ? pow2 : result.times(pow2); 352 } 353 pow2 = pow2.times(pow2); 354 exp >>>= 1; 355 } 356 return StackContext.outerCopy(result); 357 } finally { 358 StackContext.exit(); 359 } 360 } else if (exp == 0) { 361 return this.times(this.inverse()); // Identity. 362 } else { 363 return this.pow(-exp).inverse(); 364 } 365 } 366 367 /** 368 * Returns the trace of this matrix. 369 * 370 * @return the sum of the diagonal elements. 371 */ 372 public F trace() { 373 F sum = this.get(0, 0); 374 for (int i = MathLib.min(getNumberOfColumns(), getNumberOfRows()); --i > 0;) { 375 sum = sum.plus(get(i, i)); 376 } 377 return sum; 378 } 379 380 /** 381 * Returns the linear algebraic matrix tensor product of this matrix 382 * and another (Kronecker product). The default implementation returns 383 * a {@link DenseMatrix}. 384 * 385 * @param that the second matrix. 386 * @return <code>this ⊗ that</code> 387 * @see <a href="http://en.wikipedia.org/wiki/Kronecker_product"> 388 * Wikipedia: Kronecker Product</a> 389 */ 390 public abstract Matrix<F> tensor(Matrix<F> that); 391 392 /** 393 * Returns the vectorization of this matrix. The vectorization of 394 * a matrix is the column vector obtain by stacking the columns of the 395 * matrix on top of one another. The default implementation returns 396 * a {@link DenseVector}. 397 * 398 * @return the vectorization of this matrix. 399 * @see <a href="http://en.wikipedia.org/wiki/Vectorization_%28mathematics%29"> 400 * Wikipedia: Vectorization.</a> 401 */ 402 public abstract Vector<F> vectorization(); 403 404 /** 405 * Returns the text representation of this matrix. 406 * 407 * @return the text representation of this matrix. 408 */ 409 public Text toText() { 410 final int m = this.getNumberOfRows(); 411 final int n = this.getNumberOfColumns(); 412 TextBuilder tmp = TextBuilder.newInstance(); 413 tmp.append('{'); 414 for (int i = 0; i < m; i++) { 415 tmp.append('{'); 416 for (int j = 0; j < n; j++) { 417 tmp.append(get(i, j)); 418 if (j != n - 1) { 419 tmp.append(", "); 420 } 421 } 422 tmp.append("}"); 423 if (i != m - 1) { 424 tmp.append(",\n"); 425 } 426 } 427 tmp.append("}"); 428 Text txt = tmp.toText(); 429 TextBuilder.recycle(tmp); 430 return txt; 431 } 432 433 /** 434 * Returns the text representation of this matrix as a 435 * <code>java.lang.String</code>. 436 * 437 * @return <code>toText().toString()</code> 438 */ 439 public final String toString() { 440 return toText().toString(); 441 } 442 443 /** 444 * Indicates if this matrix can be considered equals to the one 445 * specified using the specified comparator when testing for 446 * element equality. The specified comparator may allow for some 447 * tolerance in the difference between the matrix elements. 448 * 449 * @param that the matrix to compare for equality. 450 * @param cmp the comparator to use when testing for element equality. 451 * @return <code>true</code> if this matrix and the specified matrix are 452 * both matrices with equal elements according to the specified 453 * comparator; <code>false</code> otherwise. 454 */ 455 public boolean equals(Matrix<F> that, Comparator<F> cmp) { 456 if (this == that) 457 return true; 458 final int m = this.getNumberOfRows(); 459 final int n = this.getNumberOfColumns(); 460 if ((that.getNumberOfRows() != m) || (that.getNumberOfColumns() != n)) 461 return false; 462 for (int i = m; --i >= 0;) { 463 for (int j = n; --j >= 0;) { 464 if (cmp.compare(this.get(i, j), that.get(i, j)) != 0) 465 return false; 466 } 467 } 468 return true; 469 } 470 471 /** 472 * Indicates if this matrix is strictly equal to the object specified. 473 * 474 * @param that the object to compare for equality. 475 * @return <code>true</code> if this matrix and the specified object are 476 * both matrices with equal elements; <code>false</code> otherwise. 477 * @see #equals(Matrix, Comparator) 478 */ 479 public boolean equals(Object that) { 480 if (this == that) 481 return true; 482 if (!(that instanceof Matrix)) 483 return false; 484 final int m = this.getNumberOfRows(); 485 final int n = this.getNumberOfColumns(); 486 Matrix<?> M = (Matrix<?>) that; 487 if ((M.getNumberOfRows() != m) || (M.getNumberOfColumns() != n)) 488 return false; 489 for (int i = m; --i >= 0;) { 490 for (int j = n; --j >= 0;) { 491 if (!this.get(i, j).equals(M.get(i, j))) 492 return false; 493 } 494 } 495 return true; 496 } 497 498 /** 499 * Returns a hash code value for this matrix. 500 * Equals objects have equal hash codes. 501 * 502 * @return this matrix hash code value. 503 * @see #equals 504 */ 505 public int hashCode() { 506 final int m = this.getNumberOfRows(); 507 final int n = this.getNumberOfColumns(); 508 int code = 0; 509 for (int i = m; --i >= 0;) { 510 for (int j = n; --j >= 0;) { 511 code += get(i, j).hashCode(); 512 } 513 } 514 return code; 515 } 516 517 /** 518 * Returns a copy of this matrix 519 * {@link javolution.context.AllocatorContext allocated} 520 * by the calling thread (possibly on the stack). 521 * 522 * @return an identical and independant copy of this matrix. 523 */ 524 public abstract Matrix<F> copy(); 525 526 527}