All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.sandia.cognition.math.matrix.mtj.AbstractMTJMatrix Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * File:                AbstractMTJMatrix.java
 * Authors:             Kevin R. Dixon
 * Company:             Sandia National Laboratories
 * Project:             Cognitive Foundry
 *
 * Copyright February 21, 2006, Sandia Corporation.  Under the terms of Contract
 * DE-AC04-94AL85000, there is a non-exclusive license for use of this work by
 * or on behalf of the U.S. Government. Export of this program may require a
 * license from the United States Government. See CopyrightHistory.txt for
 * complete details.
 *
 */

package gov.sandia.cognition.math.matrix.mtj;

import gov.sandia.cognition.annotation.CodeReview;
import gov.sandia.cognition.annotation.CodeReviewResponse;
import gov.sandia.cognition.annotation.PublicationReference;
import gov.sandia.cognition.annotation.PublicationType;
import gov.sandia.cognition.annotation.SoftwareLicenseType;
import gov.sandia.cognition.annotation.SoftwareReference;
import gov.sandia.cognition.math.matrix.mtj.decomposition.SingularValueDecompositionMTJ;
import gov.sandia.cognition.math.matrix.mtj.decomposition.EigenDecompositionRightMTJ;
import gov.sandia.cognition.math.ComplexNumber;
import gov.sandia.cognition.math.matrix.AbstractMatrix;
import gov.sandia.cognition.math.matrix.DimensionalityMismatchException;
import gov.sandia.cognition.math.matrix.Matrix;
import gov.sandia.cognition.math.matrix.MatrixEntry;
import gov.sandia.cognition.math.matrix.decomposition.SingularValueDecomposition;
import gov.sandia.cognition.math.matrix.TwoMatrixEntry;
import gov.sandia.cognition.math.matrix.Vector;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import no.uib.cipr.matrix.MatrixSingularException;

/**
 * Relies on internal MTJ matrix to do some of the heavy lifting, without
 * assuming that the underlying matrix is Dense or Sparse
 *
 * @author Kevin R. Dixon
 * @since  1.0
 *
 */
@CodeReview(
    reviewer="Jonathan McClain",
    date="2006-05-18",
    changesNeeded=true,
    comments="Comments marked throughout the file with / / / on first column.",
    response=@CodeReviewResponse(
        respondent="Kevin R. Dixon",
        date="2006-05-18",
        moreChangesNeeded=false,
        comments={
            "Added an assertion to dotTimesEquals",
            "Commented the private classes so that they're up to snuff."
        }
    )
)
@PublicationReference(
    author="Bjorn-Ove Heimsund",
    title="Matrix Toolkits for Java (MTJ)",
    type=PublicationType.WebPage,
    year=2006,
    url="http://ressim.berlios.de/",
    notes="All subclasses essentially wrap one of MTJ's matrix classes."
)
@SoftwareReference(
    name="Matrix Toolkits for Java (MTJ)",
    version="0.9.6",
    url="http://ressim.berlios.de/",
    license=SoftwareLicenseType.LGPL,
    licenseVersion="2.1",
    licenseURL="http://ressim.berlios.de/")
public abstract class AbstractMTJMatrix
    extends AbstractMatrix
{ 

    /**
     * Internal matrix that does the heavy lifting from the MTJ package
     */
    private transient no.uib.cipr.matrix.Matrix internalMatrix;

    /**
     * Creates a new instance of AbstractMTJMatrix
     * @param internalMatrix MTJ-based matrix to store inside of this
     */
    protected AbstractMTJMatrix(
        no.uib.cipr.matrix.Matrix internalMatrix)
    {
        this.setInternalMatrix(internalMatrix);
    }

    @Override
    public AbstractMTJMatrix clone()
    {
        AbstractMTJMatrix clone = (AbstractMTJMatrix) super.clone();
        clone.setInternalMatrix( this.getInternalMatrix().copy() );
        return clone;
    }

    /**
     * Gets the internal MTJ matrix that this class is wrapping.
     * 
     * @return Internal MTJ matrix.
     */
    public no.uib.cipr.matrix.Matrix getInternalMatrix()
    {
        return this.internalMatrix;
    }

    /**
     * Setter for internalMatrix
     * @param internalMatrix internal MTJ-based matrix
     */
    protected void setInternalMatrix(
        no.uib.cipr.matrix.Matrix internalMatrix)
    {
        if( internalMatrix == null )
        {
            throw new IllegalArgumentException(
                "internalMatrix cannot be null!" );
        }
        this.internalMatrix = internalMatrix;
    }

    public int getNumRows()
    {
        return this.internalMatrix.numRows();
    }

    public int getNumColumns()
    {
        return this.internalMatrix.numColumns();
    }
    
    @Override
    public double get(
        final int rowIndex,
        final int columnIndex)
    {
        return this.internalMatrix.get(rowIndex, columnIndex);
    }

    @Override
    public void set(
        final int rowIndex,
        final int columnIndex,
        final double value)
    {
        this.internalMatrix.set(rowIndex, columnIndex, value);
    }

    public double getElement(
        int rowIndex,
        int columnIndex)
    {
        return this.internalMatrix.get(rowIndex, columnIndex);
    }

    public void setElement(
        int rowIndex,
        int columnIndex,
        double value)
    {
        this.internalMatrix.set(rowIndex, columnIndex, value);
    }

    public ComplexNumber logDeterminant()
    {

        if (this.isSquare() == false)
        {
            throw new IllegalArgumentException("Matrix must be square");
        }

        DenseMatrix decompositionMatrix;


        if (this instanceof DenseMatrix)
        {
            decompositionMatrix = (DenseMatrix) this;
        }
        else
        {
            decompositionMatrix = new DenseMatrix(this);
        }

        boolean useEVD = false;
        if (useEVD)
        {
            EigenDecompositionRightMTJ evd = EigenDecompositionRightMTJ.create(
                decompositionMatrix);
            return evd.getLogDeterminant();
        }
        else
        {

            // Note: It's generally faster to use LU decomposition for
            // determinants, but QR can be more stable in some circumstances
            no.uib.cipr.matrix.UpperTriangDenseMatrix triangularMatrix;
            boolean useQR = false;
            int sign;
            if (useQR)
            {
                no.uib.cipr.matrix.QR decomposition =
                    no.uib.cipr.matrix.QR.factorize(this.internalMatrix);
                triangularMatrix = decomposition.getR();
                sign = 1;
            }
            else
            {
                no.uib.cipr.matrix.DenseLU decomposition =
                    no.uib.cipr.matrix.DenseLU.factorize(this.internalMatrix);
                triangularMatrix = decomposition.getU();

                sign = 1;
                for (int i = 0; i < decomposition.getPivots().length; i++)
                {
                    if (decomposition.getPivots()[i] != (i + 1))
                    {
                        sign = -sign;
                    }
                }

            }

            int M = triangularMatrix.numRows();
            int N = triangularMatrix.numColumns();
            int maxIndex = (M < N) ? M : N;

            //Both LU and QR return upper triangular matrices and the "L" and
            // "Q" matrices have a determinant of one, so the real action is
            // in the "U" and "R" matrices, which are both upper (right)
            // triangular.  As we remember from our algebra courses, the
            // eigenvalues of a triangular matrix are the diagonal elements
            // themselves.  We also remember that the product of the eigenvalues
            // is the determinant.
            // 
            // The diagonal elements (for either LU or QR) will be REAL, but
            // they may be negative.  The logarithm of a negative number is
            // the logarithm of the absolute value of the number, with an
            // imaginary part of PI.  The exponential is all that matters, so
            // the log-determinant is equivalent, MODULO PI (3.14...), so
            // we just toggle this sign bit.
            double logsum = 0.0;
            for (int i = 0; i < maxIndex; i++)
            {
                double eigenvalue = triangularMatrix.get(i, i);
                if (eigenvalue < 0.0)
                {
                    sign = -sign;
                    logsum += Math.log(-eigenvalue);
                }
                else
                {
                    logsum += Math.log(eigenvalue);
                }

            }

            return new ComplexNumber(logsum, (sign < 0) ? Math.PI : 0.0);
        }

    }

    /**
     * Determines if the matrices are effectively equal to each other
     * 
     * 
     * @return true if effectively equal, false otherwise
     * @param matrix Matrix to compare this to, must be same size as
     *          this
     * @param effectiveZero tolerance to determine element-wise equality
     */
    public boolean equals(
        final AbstractMTJMatrix matrix,
        double effectiveZero)
    {
        // Create a new iterator that iterates through the UNION of nonzero
        // entries between the two given matrices.  Then all we have to do
        // is see if any of these entries are move than "effectiveZero" apart
        MatrixUnionIteratorMTJ iterator =
            new MatrixUnionIteratorMTJ(this, matrix);

        while (iterator.hasNext())
        {
            TwoMatrixEntry entry = iterator.next();
            double diff = entry.getFirstValue() - entry.getSecondValue();
            if (Math.abs(diff) > effectiveZero)
            {
                return false;
            }
        }

        return true;

    }

    public void plusEquals(
        final Matrix matrix)
    {
        this.plusEquals((AbstractMTJMatrix) matrix);
    }

    /**
     * Inline addition of this and matrix, modifies
     * the elements of this
     * 
     * 
     * @param matrix Must have same dimensions this
     */
    public void plusEquals(
        final AbstractMTJMatrix matrix)
    {
        // MTJ does the dimension checking, so I'm not going to duplicate that
        // with an additional check
        this.internalMatrix.add(matrix.internalMatrix);
    }

    public void minusEquals(
        final Matrix matrix)
    {
        this.minusEquals((AbstractMTJMatrix) matrix);
    }

    /**
     * Subtracts the elements of matrix from the elements of 
     * this, modifies the elements of this
     * 
     * 
     * @param matrix Must have same dimensions this
     */
    public void minusEquals(
        final AbstractMTJMatrix matrix)
    {
        // MTJ does the dimension checking, so I'm not going to duplicate that
        // with an additional check
        double additionScaleFactor = -1.0;
        this.internalMatrix.add(additionScaleFactor,
            matrix.internalMatrix);
    }

    public AbstractMTJMatrix times(
        final Matrix matrix)
    {
        return this.times((AbstractMTJMatrix) matrix);
    }

    /**
     * Matrix multiplication of this and matrix,
     * operates like the "*" operator in Matlab
     * 
     * 
     * @return Matrix multiplication of this and
     * matrix, will this.getNumRows() rows and
     * matrix.getNumColumns() columns
     * @param matrix this.getNumColumns()==matrix.getNumRows()
     */
    public abstract AbstractMTJMatrix times(
        final AbstractMTJMatrix matrix);

    public void dotTimesEquals(
        final Matrix matrix)
    {
        this.dotTimesEquals((AbstractMTJMatrix) matrix);
    }

    /**
     * Inline element-wise multiplication of the elements in this
     * and matrix, modifies the elements of this
     * 
     * @param matrix Must have same dimensions this
     */
    public void dotTimesEquals(
        final AbstractMTJMatrix matrix)
    {
        // make sure the matrices are the same dimension
        this.assertSameDimensions(matrix);

        // Iterators loop over all values, or all nonzero, values in "this".
        // The resulting matrix doesn't need to element-wise multiply the
        // zero values, since the result would be zero anyway.
        for (MatrixEntry e : this)
        {
            double otherValue = matrix.getElement(
                e.getRowIndex(), e.getColumnIndex());

            e.setValue(e.getValue() * otherValue);
        }
    }

    public void scaleEquals(
        double scaleFactor)
    {
        // Iterators loop over all values, or all nonzero, values in "this".
        // The resulting matrix doesn't need to element-wise multiply the
        // zero values, since the result would be zero anyway.
        for (MatrixEntry e : this)
        {
            e.setValue(e.getValue() * scaleFactor);
        }
    }

    @Override
    public void scaledPlusEquals(
        final double scaleFactor,
        final Matrix other)
    {
        this.scaledPlusEquals(scaleFactor, (AbstractMTJMatrix) other);
    }

    /**
     * Adds to this vector the scaled version of the other given vector.
     *
     * @param   scaleFactor
     *      The scale factor to use.
     * @param   other
     *      The other vector to scale and then add to this vector.
     */
    public void scaledPlusEquals(
        final double scaleFactor,
        final AbstractMTJMatrix other)
    {
        this.internalMatrix.add(scaleFactor, other.internalMatrix);
    }

    /**
     * Subtracts from this matrix the scaled version of the other given matrix.
     *
     * @param   scaleFactor
     *      The scale factor to use.
     * @param   other
     *      The other matrix to scale and then subtract from this matrix.
     */
    public void scaledMinusEquals(
        final double scaleFactor,
        final AbstractMTJMatrix other)
    {
        this.scaledPlusEquals(-scaleFactor, other);
    }

    public Iterator iterator()
    {
        return new AbstractMTJMatrixIterator();
    }

    public boolean isSquare()
    {
        return this.getNumRows() == this.getNumColumns();
    }

    public AbstractMTJVector times(
        final Vector vector)
    {
        return this.times((AbstractMTJVector) vector);
    }

    /**
     * Returns the column vector from the equation
     * return = this * vector 
     *
     *
     * @param vector
     *          Vector by which to post-multiply this, must have the
     *          same number of rows as this
     * @return Vector with the same dimensionality as the number of rows as
     * this and vector
     */
    abstract public AbstractMTJVector times(
        final AbstractMTJVector vector);

    public void identity()
    {
        int N;
        this.internalMatrix.zero();
        if (this.getNumRows() < this.getNumColumns())
        {
            N = this.getNumRows();
        }
        else
        {
            N = this.getNumColumns();
        }

        for (int i = 0; i < N; i++)
        {
            this.setElement(i, i, 1.0);
        }
    }

    public Matrix solve(
        final Matrix B)
    {
        return this.solve((AbstractMTJMatrix) B);
    }

    public Vector solve(
        final Vector b)
    {
        return this.solve((AbstractMTJVector) b);
    }

    /**
     * Solves for "X" in the equation this * X = B, where X is a DenseMatrix,
     * "this" and "B" will be converted to a DenseMatrix (if not already)
     * @param B AbstractMTJMatrix, will be converted to a DenseMatrix
     * @return DenseMatrix of "X" in this * X = B
     */
    final public Matrix solve(
        AbstractMTJMatrix B)
    {
        DenseMatrix X =
            new DenseMatrix(this.getNumColumns(), B.getNumColumns());

        DenseMatrix Bdense;
        if (B instanceof DenseMatrix)
        {
            Bdense = (DenseMatrix) B;
        }
        else
        {
            Bdense = new DenseMatrix(B);
        }

        DenseMatrix Adense;
        if (this instanceof DenseMatrix)
        {
            Adense = (DenseMatrix) this;
        }
        else
        {
            Adense = new DenseMatrix(this);
        }

        boolean usePseudoInverse = false;
        try
        {
            Adense.solveInto(Bdense, X);
            usePseudoInverse = false;
        }
        catch ( MatrixSingularException e )
        {
            Logger.getLogger(AbstractMTJMatrix.class.getName()).log(Level.WARNING,
                "AbstractMTJMatrix.solve(): Matrix is singular.");
            usePseudoInverse = true;
        }
        
        // Sometimes LAPACK will return NaNs or infs as the solutions, but MTJ
        // won't throw the exception, so we need to check for this.
        // If we detect this, then we'll use a pseudoinverse
        if(!usePseudoInverse)        
        {
            for( int i = 0; i < X.getNumRows(); i++ )
            {
                for (int j = 0; j < X.getNumColumns(); j++ )
                {
                    double v = X.getElement(i, j);
                    if( Double.isNaN(v) || Double.isInfinite(v) )
                    {
                        Logger.getLogger(AbstractMTJMatrix.class.getName()).log(Level.WARNING,
                            "AbstractMTJMatrix.solve(): Solver produced invalid results.");
                        usePseudoInverse = true;
                        break;
                    }
                }
                if( usePseudoInverse )
                {
                    break;
                }
            }
        }
        
        if( usePseudoInverse )
        {
            // The original LU solver produced a sucky answer, so let's use
            // the absurdly expensive SVD least-squares solution
            return Adense.pseudoInverse().times(Bdense);
        }
        
        return X;
    }

    /**
     * Solves for "x" in the equation: this*x = b
     *
     * @param b must satisfy this.getNumColumns() == b.getDimensionality()
     * @return x Vector with dimensions (this.getNumRows())
     */
    public Vector solve(
        final AbstractMTJVector b)
    {
        DenseVector x = new DenseVector(this.getNumColumns());
        DenseVector bdense;
        if (b instanceof DenseVector)
        {
            bdense = (DenseVector) b;
        }
        else
        {
            bdense = new DenseVector(b);
        }

        DenseMatrix Adense;
        if (this instanceof DenseMatrix)
        {
            Adense = (DenseMatrix) this;
        }
        else
        {
            Adense = new DenseMatrix(this);
        }

        boolean usePseudoInverse = false;
        try
        {
            Adense.solveInto(bdense, x);
            usePseudoInverse = false;
        }
        catch ( MatrixSingularException e )
        {
            Logger.getLogger(AbstractMTJMatrix.class.getName()).log(Level.WARNING,
                "AbstractMTJMatrix.solve(): Matrix is singular.");
            usePseudoInverse = true;
        }
        
        // Sometimes LAPACK will return NaNs or infs as the solutions, but MTJ
        // won't throw the exception, so we need to check for this.
        // If we detect this, then we'll use a pseudoinverse
        if(!usePseudoInverse)        
        {
            for( int i = 0; i < x.getDimensionality(); i++ )
            {
                double v = x.getElement(i);
                if( Double.isNaN(v) || Double.isInfinite(v) )
                {
                    Logger.getLogger(AbstractMTJMatrix.class.getName()).log(Level.WARNING,
                        "AbstractMTJMatrix.solve(): Solver produced invalid results.");
                    usePseudoInverse = true;
                    break;
                }
            }
        }
        
        if( usePseudoInverse )
        {
            // The original LU solver produced a sucky answer, so let's use
            // the absurdly expensive SVD least-squares solution
            return Adense.pseudoInverse().times(b);
        }
        
        return x;
    }

    public Matrix inverse()
    {

        if (this.isSquare() == false)
        {
            throw new UnsupportedOperationException(
                "Can only invert square matrices.");
        }

        DenseMatrix I = DenseMatrixFactoryMTJ.INSTANCE.createIdentity(
            this.getNumRows(), this.getNumColumns());

        Matrix AI = this.solve(I);

        return AI;

    }

    public double normFrobenius()
    {
        return this.internalMatrix.norm(
            no.uib.cipr.matrix.Matrix.Norm.Frobenius);
    }

    @Override
    public double normFrobeniusSquared()
    {
// TODO: It would be nice to have this without the square root directly rather than squaring the normFrobenius.
        final double normFrobenius = this.normFrobenius();
        return normFrobenius * normFrobenius;
    }
    
    public boolean isSymmetric(
        double effectiveZero)
    {

        if (this.isSquare() == false)
        {
            throw new DimensionalityMismatchException(
                this.getNumRows(), this.getNumColumns());
        }

        double difference;

        // Loop over each row, but start the column one to the right of the
        // diagonal, which is row+1
        for (int i = 0; i < this.getNumRows(); i++)
        {
            for (int j = i + 1; j < this.getNumColumns(); j++)
            {
                // If we find any transposed entry which has an absolute
                // difference greater than the effectiveZero, then we
                // know that the matrix isn't symmetric
                difference = this.getElement(i, j) - this.getElement(j, i);
                if (Math.abs(difference) > effectiveZero)
                {
                    return false;
                }
            }
        }

        /*
         * We didn't find any transposed entries that are different,
         * so the matrix is symmetric
         */
        return true;
    }

    @Override
    public void zero()
    {
        this.internalMatrix.zero();
    }

    public int rank(
        double effectiveZero)
    {

        DenseMatrix svdDude;

        if (this instanceof DenseMatrix)
        {
            svdDude = (DenseMatrix) this;
        }
        else
        {
            svdDude = new DenseMatrix(this);
        }

        SingularValueDecomposition svd =
            SingularValueDecompositionMTJ.create(svdDude);

        return svd.effectiveRank(effectiveZero);

    }

    /**
     * Internal method for accessing MTJ's general transpose routine
     *
     * @param destinationMatrix
     *          matrix into which to store the result
     */
    protected void transposeInto(
        AbstractMTJMatrix destinationMatrix)
    {
        this.internalMatrix.transpose(
            destinationMatrix.internalMatrix);
    }

    /**
     * Internal routine for storing a submatrix into and AbstractMTJMatrix.
     * Gets the embedded submatrix inside of the Matrix, specified by the
     * inclusive, zero-based indices such that the result matrix will have size
     * (maxRow-minRow+1) x (maxColum-minCcolumn+1)
     * 
     * 
     * @param minRow Zero-based index into the rows of the Matrix, must be less than
     *          or equal to maxRow
     * @param maxRow Zero-based index into the rows of the Matrix, must be greater
     *          than or equal to minRow
     * @param minColumn Zero-based index into the rows of the Matrix, must be less than
     *          or equal to maxColumn
     * @param maxColumn Zero-based index into the rows of the Matrix, must be greater
     *          than or equal to minColumn
     * @param destinationMatrix
     *          the destination submatrix of dimension
     *          (maxRow-minRow+1)x(maxColumn-minColumn+1)
     *
     */
    protected void getSubMatrixInto(
        int minRow,
        int maxRow,
        int minColumn,
        int maxColumn,
        AbstractMTJMatrix destinationMatrix)
    {

        if ((destinationMatrix.getNumRows() != (maxRow - minRow + 1)) ||
            (destinationMatrix.getNumColumns() != (maxColumn - minColumn + 1)))
        {
            throw new DimensionalityMismatchException(
                "Submatrix is incorrect size.");
        }

        for (int i = minRow; i <= maxRow; i++)
        {
            for (int j = minColumn; j <= maxColumn; j++)
            {
                destinationMatrix.setElement(
                    i - minRow, j - minColumn, this.getElement(i, j));
            }
        }

    }

    /**
     * Internal function call that stores the result of a matrix multiply
     * into the given destinationMatrix.
     *
     * @param multiplicationMatrix
     *          matrix to postmultiply this by
     * @param destinationMatrix
     *          matrix to store the matrix multiplication result, must have
     *          rows == this.getNumRows and columns ==
     *          multiplicationMatrix.getNumColumns
     */
    protected void timesInto(
        final AbstractMTJMatrix multiplicationMatrix,
        AbstractMTJMatrix destinationMatrix)
    {
        int M = this.getNumRows();
        int N = multiplicationMatrix.getNumColumns();
        if ((M != destinationMatrix.getNumRows()) ||
            (N != destinationMatrix.getNumColumns()))
        {
            throw new DimensionalityMismatchException(
                "Multiplication dimensions do not agree.");
        }

        this.internalMatrix.mult(
            multiplicationMatrix.internalMatrix,
            destinationMatrix.internalMatrix);
    }

    /**
     * Internal function call that stores the result of a matrix multiply
     * into the given destinationVector.
     *
     * @param multiplicationVector
     *          vector to postmultiply this by
     * @param destinationVector
     *          vector to store the matrix multiplication result, must have
     *          dimensionality == this.getNumRows
     */
    protected void timesInto(
        final AbstractMTJVector multiplicationVector,
        AbstractMTJVector destinationVector)
    {
        int M = this.getNumRows();
        if (M != destinationVector.getDimensionality())
        {
            throw new DimensionalityMismatchException(
                M, destinationVector.getDimensionality());
        }

        this.internalMatrix.mult(
            multiplicationVector.getInternalVector(),
            destinationVector.getInternalVector());
    }

    @Override
    public void increment(
        final int row,
        final int column,
        final double value)
    {
        this.internalMatrix.add(row, column, value);
    }
    
    public void convertFromVector(
        Vector parameters)
    {

        int M = this.getNumRows();
        int N = this.getNumColumns();

        if ((M * N) != parameters.getDimensionality())
        {
            throw new IllegalArgumentException(
                "Elements in this does not equal elements in parameters");
        }

        int index = 0;
        for (int j = 0; j < N; j++)
        {
            for (int i = 0; i < M; i++)
            {
                this.setElement(i, j, parameters.getElement(index));
                index++;
            }
        }

    }

    public DenseVector convertToVector()
    {

        int M = this.getNumRows();
        int N = this.getNumColumns();

        DenseVector parameters = new DenseVector(M * N);

        int index = 0;
        for (int j = 0; j < N; j++)
        {
            for (int i = 0; i < M; i++)
            {
                parameters.setElement(index, this.getElement(i, j));
                index++;
            }
        }

        return parameters;
    }

    /**
     * Private iterator class for MTJ-based matrices, contains backpointer to
     * the AbstractMTJMatrix so that the entry can modify the underlying
     * elements
     */
    class AbstractMTJMatrixIterator
        implements Iterator
    {

        /**
         * Internal MTJ-based iterator that does the heavy lifting
         */
        private Iterator internalIterator;

        /**
         * MatrixEntry for this matrix
         */
        private MatrixEntry entry = null;

        /**
         * Creates a new instance of the iterator, with a back pointer to the matrix
         */
        public AbstractMTJMatrixIterator()
        {
            setInternalIterator(
                AbstractMTJMatrix.this.internalMatrix.iterator());
            setEntry(new AbstractMTJMatrixEntry());
        }

        /**
         * Getter for internalIterator
         * @return Internal MTJ-based iterator that does the heavy lifting
         */
        protected Iterator getInternalIterator()
        {
            return this.internalIterator;
        }

        /**
         * Setter for internalIterator
         * @param internalIterator Internal MTJ-based iterator that does the heavy lifting
         */
        protected void setInternalIterator(
            Iterator internalIterator)
        {
            this.internalIterator = internalIterator;
        }

        /**
         * {@inheritDoc}
         * @return {@inheritDoc}
         */
        public boolean hasNext()
        {
            return this.getInternalIterator().hasNext();
        }

        /**
         * {@inheritDoc}
         * @return {@inheritDoc}
         */
        public MatrixEntry next()
        {
            no.uib.cipr.matrix.MatrixEntry internalNext =
                getInternalIterator().next();

            getEntry().setRowIndex(internalNext.row());
            getEntry().setColumnIndex(internalNext.column());
            return getEntry();
        }

        /**
         * {@inheritDoc}
         */
        public void remove()
        {
            getInternalIterator().remove();
        }

        /**
         * Getter for entry
         * @return MatrixEntry for this matrix
         */
        public MatrixEntry getEntry()
        {
            return this.entry;
        }

        /**
         * Setter for entry
         * @param entry MatrixEntry for this matrix
         */
        public void setEntry(
            MatrixEntry entry)
        {
            this.entry = entry;
        }

    }

    /**
     * MatrixEntry implementation for MTJ-based matrices,
     * with a backpointer to the underlying AbstractMTJMatrix
     */
    class AbstractMTJMatrixEntry
        implements MatrixEntry
    {

        /**
         * Current row index for the entry
         */
        private int rowIndex;

        /**
         * Current column index for the entry
         */
        private int columnIndex;

        /**
         * Creates a new matrix entry with zeros for indices
         */
        AbstractMTJMatrixEntry()
        {
            this(0, 0);
        }

        /**
         * Creates a new matrix entry at the specified indices
         * @param rowIndex zero-based row index for the entry
         * @param columnIndex zero-based column index for the entry
         */
        AbstractMTJMatrixEntry(
            int rowIndex,
            int columnIndex)
        {
            this.setRowIndex(rowIndex);
            this.setColumnIndex(columnIndex);
        }

        /**
         * {@inheritDoc}
         * @return {@inheritDoc}
         */
        public double getValue()
        {
            return AbstractMTJMatrix.this.getElement(
                this.getRowIndex(), this.getColumnIndex());
        }

        /**
         * {@inheritDoc}
         * @param value {@inheritDoc}
         */
        public void setValue(
            double value)
        {
            AbstractMTJMatrix.this.setElement(
                this.getRowIndex(), this.getColumnIndex(), value);
        }

        /**
         * {@inheritDoc}
         * @return {@inheritDoc}
         */
        public int getRowIndex()
        {
            return this.rowIndex;
        }

        /**
         * {@inheritDoc}
         * @param rowIndex {@inheritDoc}
         */
        public void setRowIndex(
            int rowIndex)
        {
            this.rowIndex = rowIndex;
        }

        /**
         * {@inheritDoc}
         * @return {@inheritDoc}
         */
        public int getColumnIndex()
        {
            return this.columnIndex;
        }

        /**
         * {@inheritDoc}
         * @param columnIndex {@inheritDoc}
         */
        public void setColumnIndex(
            int columnIndex)
        {
            this.columnIndex = columnIndex;
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy