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&lt;?&gt;},
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 &otimes; 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}