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.Iterator; 012import java.util.List; 013 014import javolution.context.ConcurrentContext; 015import javolution.context.ObjectFactory; 016import javolution.lang.MathLib; 017import javolution.util.FastTable; 018 019import org.jscience.mathematics.number.Float64; 020 021/** 022 * <p> This class represents an optimized {@link Matrix matrix} implementation 023 * for {@link Float64 64 bits floating-point} numbers.</p> 024 * 025 * <p> Instances of this class can be created from {@link Float64Vector}, 026 * either as rows or columns if the matrix is transposed. For example:[code] 027 * Float64Vector<Rational> column0 = Float64Vector.valueOf(...); 028 * Float64Vector<Rational> column1 = Float64Vector.valueOf(...); 029 * Float64Matrix<Rational> M = Float64Matrix.valueOf(column0, column1).transpose(); 030 * [/code]</p> 031 * 032 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 033 * @version 3.3, January 2, 2007 034 */ 035public final class Float64Matrix extends Matrix<Float64> { 036 037 /** 038 * Holds the number of columns n. 039 */ 040 int _n;; 041 042 /** 043 * Indicates if this matrix is transposed (the rows are then the columns). 044 */ 045 boolean _transposed; 046 047 /** 048 * Holds this matrix rows (or columns when transposed). 049 */ 050 final FastTable<Float64Vector> _rows = new FastTable<Float64Vector>(); 051 052 /** 053 * Returns a dense matrix from a 2-dimensional array of <code>double</code> 054 * values. The first dimension being the row and the second being the 055 * column. 056 * 057 * @param values the array of <code>double</code> values. 058 * @return the matrix having the specified elements. 059 * @throws DimensionException if rows have different length. 060 * @see Float64Vector 061 */ 062 public static Float64Matrix valueOf(double[][] values) { 063 int m = values.length; 064 int n = values[0].length; 065 Float64Matrix M = Float64Matrix.newInstance(n, false); 066 for (int i = 0; i < m; i++) { 067 Float64Vector row = Float64Vector.valueOf(values[i]); 068 if (row.getDimension() != n) 069 throw new DimensionException(); 070 M._rows.add(row); 071 } 072 return M; 073 } 074 075 /** 076 * Returns a complex matrix holding the specified row vectors 077 * (column vectors if {@link #transpose transposed}). 078 * 079 * @param rows the row vectors. 080 * @return the matrix having the specified rows. 081 * @throws DimensionException if the rows do not have the same dimension. 082 */ 083 public static Float64Matrix valueOf(Float64Vector... rows) { 084 final int n = rows[0].getDimension(); 085 Float64Matrix M = Float64Matrix.newInstance(n, false); 086 for (int i = 0, m = rows.length; i < m; i++) { 087 Float64Vector rowi = rows[i]; 088 if (rowi.getDimension() != n) 089 throw new DimensionException( 090 "All vectors must have the same dimension."); 091 M._rows.add(rowi); 092 } 093 return M; 094 } 095 096 /** 097 * Returns a complex matrix holding the row vectors from the specified 098 * collection (column vectors if {@link #transpose transposed}). 099 * 100 * @param rows the list of row vectors. 101 * @return the matrix having the specified rows. 102 * @throws DimensionException if the rows do not have the same dimension. 103 */ 104 public static Float64Matrix valueOf(List<Float64Vector> rows) { 105 final int n = rows.get(0).getDimension(); 106 Float64Matrix M = Float64Matrix.newInstance(n, false); 107 Iterator<Float64Vector> iterator = rows.iterator(); 108 for (int i = 0, m = rows.size(); i < m; i++) { 109 Float64Vector rowi = iterator.next(); 110 if (rowi.getDimension() != n) 111 throw new DimensionException( 112 "All vectors must have the same dimension."); 113 M._rows.add(rowi); 114 } 115 return M; 116 } 117 118 /** 119 * Returns a complex matrix equivalent to the specified matrix. 120 * 121 * @param that the matrix to convert. 122 * @return <code>that</code> or a complex matrix holding the same elements 123 * as the specified matrix. 124 */ 125 public static Float64Matrix valueOf(Matrix<Float64> that) { 126 if (that instanceof Float64Matrix) 127 return (Float64Matrix) that; 128 int n = that.getNumberOfColumns(); 129 int m = that.getNumberOfRows(); 130 Float64Matrix M = Float64Matrix.newInstance(n, false); 131 for (int i = 0; i < m; i++) { 132 Float64Vector rowi = Float64Vector.valueOf(that.getRow(i)); 133 M._rows.add(rowi); 134 } 135 return M; 136 } 137 138 @Override 139 public int getNumberOfRows() { 140 return _transposed ? _n : _rows.size(); 141 } 142 143 @Override 144 public int getNumberOfColumns() { 145 return _transposed ? _rows.size() : _n; 146 } 147 148 @Override 149 public Float64 get(int i, int j) { 150 return _transposed ? _rows.get(j).get(i) : _rows.get(i).get(j); 151 } 152 153 @Override 154 public Float64Vector getRow(int i) { 155 if (!_transposed) 156 return _rows.get(i); 157 // Else transposed. 158 int n = _rows.size(); 159 int m = _n; 160 if ((i < 0) || (i >= m)) 161 throw new DimensionException(); 162 Float64Vector V = Float64Vector.newInstance(n); 163 for (int j = 0; j < n; j++) { 164 V.set(j, _rows.get(j).get(i).doubleValue()); 165 } 166 return V; 167 } 168 169 @Override 170 public Float64Vector getColumn(int j) { 171 if (_transposed) 172 return _rows.get(j); 173 int m = _rows.size(); 174 if ((j < 0) || (j >= _n)) 175 throw new DimensionException(); 176 Float64Vector V = Float64Vector.newInstance(m); 177 for (int i = 0; i < m; i++) { 178 V.set(i, _rows.get(i).get(j).doubleValue()); 179 } 180 return V; 181 } 182 183 @Override 184 public Float64Vector getDiagonal() { 185 int m = this.getNumberOfRows(); 186 int n = this.getNumberOfColumns(); 187 int dimension = MathLib.min(m, n); 188 Float64Vector V = Float64Vector.newInstance(dimension); 189 for (int i = 0; i < dimension; i++) { 190 V.set(i, this.get(i, i).doubleValue()); 191 } 192 return V; 193 } 194 195 @Override 196 public Float64Matrix opposite() { 197 Float64Matrix M = Float64Matrix.newInstance(_n, _transposed); 198 for (int i = 0, p = _rows.size(); i < p; i++) { 199 M._rows.add(_rows.get(i).opposite()); 200 } 201 return M; 202 } 203 204 @Override 205 public Float64Matrix plus(Matrix<Float64> that) { 206 if (this.getNumberOfRows() != that.getNumberOfRows()) 207 throw new DimensionException(); 208 Float64Matrix M = Float64Matrix.newInstance(_n, _transposed); 209 for (int i = 0, p = _rows.size(); i < p; i++) { 210 M._rows.add(_rows.get(i).plus( 211 _transposed ? that.getColumn(i) : that.getRow(i))); 212 } 213 return M; 214 } 215 216 @Override 217 public Float64Matrix minus(Matrix<Float64> that) { // Returns more specialized type. 218 return this.plus(that.opposite()); 219 } 220 221 @Override 222 public Float64Matrix times(Float64 k) { 223 Float64Matrix M = Float64Matrix.newInstance(_n, _transposed); 224 for (int i = 0, p = _rows.size(); i < p; i++) { 225 M._rows.add(_rows.get(i).times(k)); 226 } 227 return M; 228 } 229 230 @Override 231 public Float64Vector times(Vector<Float64> v) { 232 if (v.getDimension() != this.getNumberOfColumns()) 233 throw new DimensionException(); 234 final int m = this.getNumberOfRows(); 235 Float64Vector V = Float64Vector.newInstance(m); 236 for (int i = 0; i < m; i++) { 237 V.set(i, this.getRow(i).times(v).doubleValue()); 238 } 239 return V; 240 } 241 242 @Override 243 public Float64Matrix times(Matrix<Float64> that) { 244 final int n = this.getNumberOfColumns(); 245 final int m = this.getNumberOfRows(); 246 final int p = that.getNumberOfColumns(); 247 if (that.getNumberOfRows() != n) 248 throw new DimensionException(); 249 // Creates a mxp matrix in transposed form (p columns vectors of size m) 250 Float64Matrix M = Float64Matrix.newInstance(m, true); // Transposed. 251 M._rows.setSize(p); 252 Multiply multiply = Multiply.valueOf(this, that, 0, p, M._rows); 253 multiply.run(); 254 Multiply.recycle(multiply); 255 return M; 256 } 257 258 // Logic to multiply two matrices. 259 private static class Multiply implements Runnable { 260 private static final ObjectFactory<Multiply> FACTORY = new ObjectFactory<Multiply>() { 261 262 @Override 263 protected Multiply create() { 264 return new Multiply(); 265 } 266 }; 267 268 private Float64Matrix _left; 269 270 private Matrix<Float64> _right; 271 272 private int _rightColumnStart; 273 274 private int _rightColumnEnd; 275 276 private FastTable<Float64Vector> _columnsResult; 277 278 static Multiply valueOf(Float64Matrix left, Matrix<Float64> right, 279 int rightColumnStart, int rightColumnEnd, 280 FastTable<Float64Vector> columnsResult) { 281 Multiply multiply = Multiply.FACTORY.object(); 282 multiply._left = left; 283 multiply._right = right; 284 multiply._rightColumnStart = rightColumnStart; 285 multiply._rightColumnEnd = rightColumnEnd; 286 multiply._columnsResult = columnsResult; 287 return multiply; 288 } 289 290 static void recycle(Multiply multiply) { 291 multiply._left = null; 292 multiply._right = null; 293 multiply._columnsResult = null; 294 Multiply.FACTORY.recycle(multiply); 295 } 296 297 public void run() { 298 if (_rightColumnEnd - _rightColumnStart < 32) { // Direct calculation. 299 FastTable<Float64Vector> rows = _left.getRows(); 300 final int m = rows.size(); 301 for (int j = _rightColumnStart; j < _rightColumnEnd; j++) { 302 Vector<Float64> thatColj = _right.getColumn(j); 303 Float64Vector column = Float64Vector.newInstance(m); 304 _columnsResult.set(j, column); 305 for (int i = 0; i < m; i++) { 306 column.set(i, rows.get(i).times(thatColj) 307 .doubleValue()); 308 } 309 } 310 } else { // Concurrent/Recursive calculation. 311 int halfIndex = (_rightColumnStart + _rightColumnEnd) >> 1; 312 Multiply firstHalf = Multiply.valueOf(_left, _right, 313 _rightColumnStart, halfIndex, _columnsResult); 314 Multiply secondHalf = Multiply.valueOf(_left, _right, 315 halfIndex, _rightColumnEnd, _columnsResult); 316 ConcurrentContext.enter(); 317 try { 318 ConcurrentContext.execute(firstHalf); 319 ConcurrentContext.execute(secondHalf); 320 } finally { 321 ConcurrentContext.exit(); 322 } 323 Multiply.recycle(firstHalf); 324 Multiply.recycle(secondHalf); 325 } 326 } 327 } 328 329 private FastTable<Float64Vector> getRows() { 330 if (!_transposed) 331 return _rows; 332 FastTable<Float64Vector> rows = FastTable.newInstance(); 333 for (int i = 0; i < _n; i++) { 334 rows.add(this.getRow(i)); 335 } 336 return rows; 337 } 338 339 @Override 340 public Float64Matrix inverse() { 341 if (!isSquare()) 342 throw new DimensionException("Matrix not square"); 343 return Float64Matrix.valueOf(LUDecomposition.valueOf(this).inverse()); 344 } 345 346 @Override 347 public Float64 determinant() { 348 return LUDecomposition.valueOf(this).determinant(); 349 } 350 351 @Override 352 public Float64Matrix transpose() { 353 Float64Matrix M = Float64Matrix.newInstance(_n, !_transposed); 354 M._rows.addAll(this._rows); 355 return M; 356 } 357 358 @Override 359 public Float64 cofactor(int i, int j) { 360 if (_transposed) { 361 int k = i; 362 i = j; 363 j = k; // Swaps i,j 364 } 365 int m = _rows.size(); 366 Float64Matrix M = Float64Matrix.newInstance(m - 1, _transposed); 367 for (int k1 = 0; k1 < m; k1++) { 368 if (k1 == i) 369 continue; 370 Float64Vector row = _rows.get(k1); 371 Float64Vector V = Float64Vector.newInstance(_n - 1); 372 M._rows.add(V); 373 for (int k2 = 0, k = 0; k2 < _n; k2++) { 374 if (k2 == j) 375 continue; 376 V.set(k++, row.get(k2).doubleValue()); 377 } 378 } 379 return M.determinant(); 380 } 381 382 @Override 383 public Float64Matrix adjoint() { 384 Float64Matrix M = Float64Matrix.newInstance(_n, _transposed); 385 int m = _rows.size(); 386 for (int i = 0; i < m; i++) { 387 Float64Vector row = Float64Vector.newInstance(_n); 388 M._rows.add(row); 389 for (int j = 0; j < _n; j++) { 390 Float64 cofactor = _transposed ? cofactor(j, i) 391 : cofactor(i, j); 392 row.set(j, ((i + j) % 2 == 0) ? cofactor.doubleValue() 393 : cofactor.opposite().doubleValue()); 394 } 395 } 396 return M.transpose(); 397 } 398 399 @Override 400 public Float64Matrix tensor(Matrix<Float64> that) { 401 return Float64Matrix.valueOf(DenseMatrix.valueOf(this).tensor(that)); 402 } 403 404 @Override 405 public Float64Vector vectorization() { 406 return Float64Vector.valueOf(DenseMatrix.valueOf(this).vectorization()); 407 } 408 409 @Override 410 public Float64Matrix copy() { 411 Float64Matrix M = Float64Matrix.newInstance(_n, _transposed); 412 for (Float64Vector row : _rows) { 413 M._rows.add(row.copy()); 414 } 415 return M; 416 } 417 418 /////////////////////// 419 // Factory creation. // 420 /////////////////////// 421 422 static Float64Matrix newInstance(int n, boolean transposed) { 423 Float64Matrix M = FACTORY.object(); 424 M._n = n; 425 M._transposed = transposed; 426 return M; 427 } 428 429 private static ObjectFactory<Float64Matrix> FACTORY = new ObjectFactory<Float64Matrix>() { 430 @Override 431 protected Float64Matrix create() { 432 return new Float64Matrix(); 433 } 434 435 @Override 436 protected void cleanup(Float64Matrix matrix) { 437 matrix._rows.reset(); 438 } 439 }; 440 441 private Float64Matrix() { 442 } 443 444 private static final long serialVersionUID = 1L; 445 446}