gov.sandia.cognition.math.matrix.AbstractMatrix Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cognitive-foundry Show documentation
Show all versions of cognitive-foundry Show documentation
A single jar with all the Cognitive Foundry components.
/*
* File: AbstractMatrix.java
* Authors: Kevin R. Dixon
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright March 1, 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;
import gov.sandia.cognition.annotation.CodeReview;
import gov.sandia.cognition.annotation.CodeReviewResponse;
import gov.sandia.cognition.annotation.CodeReviews;
import gov.sandia.cognition.math.AbstractRing;
import gov.sandia.cognition.math.RingAccumulator;
import java.util.AbstractList;
import java.util.List;
/**
* Abstract implementation of some low-hanging functions in the Matrix
* interface. It contains default implementations of these functions that make
* extensive use of the setElement method, which it assumes to be a fast
* lookup function.
*
* @author Kevin R. Dixon
* @since 1.0
*/
@CodeReviews(
reviews={
@CodeReview(
reviewer="Kevin R. Dixon",
date="2008-02-26",
changesNeeded=false,
comments={
"Minor changes to formatting and documentation for equals().",
"Otherwise, looks good."
}
),
@CodeReview(
reviewer="Jonathan McClain",
date="2006-05-16",
changesNeeded=true,
comments="A few minor changes.",
response=@CodeReviewResponse(
respondent="Justin Basilico",
date="2006-05-16",
moreChangesNeeded=false,
comments="Added a line to the class documentation that says that setElement is used extensively in the abstract implementation."
)
)
}
)
public abstract class AbstractMatrix
extends AbstractRing
implements Matrix
{
/**
* Creates a new instance of AbstractMatrix.
*/
public AbstractMatrix()
{
super();
}
@Override
public boolean equals(
Object other )
{
if (other == null)
{
return false;
}
else if (this == other)
{
return true;
}
else if (other instanceof Matrix)
{
return this.equals( (Matrix) other, 0.0 );
}
else
{
return false;
}
}
@Override
public boolean equals(
Matrix other,
double effectiveZero)
{
final int M = this.getNumRows();
final int N = this.getNumColumns();
if (M != other.getNumRows() || N != other.getNumColumns())
{
return false;
}
for( int i = 0; i < M; i++ )
{
for( int j = 0; j < N; j++ )
{
double difference = this.getElement(i,j) - other.getElement(i,j);
if( Math.abs(difference) > effectiveZero )
{
return false;
}
}
}
return true;
}
@Override
public int hashCode()
{
final int numRows = this.getNumRows();
final int numColumns = this.getNumColumns();
int result = 7 + numRows * numColumns;
for (int i = 0; i < numRows; i++)
{
for (int j = 0; j < numColumns; j++)
{
final double value = this.getElement(i, j);
// We add a check for zero so that sparse matrices can create
// the same hash-code without needing to handle the zeros.
if (value != 0.0)
{
final long bits = Double.doubleToLongBits(value);
// This is based on how NetBeans auto-generates hash codes
// plus the documentation for Map.Entry.
result = 47 * result + i ^ j ^ (int) (bits ^ (bits >>> 32));
}
}
}
return result;
}
@Override
public boolean isSymmetric()
{
return this.isSymmetric( 0.0 );
}
@Override
public boolean checkSameDimensions(
Matrix otherMatrix )
{
return (this.getNumRows() == otherMatrix.getNumRows()) &&
(this.getNumColumns() == otherMatrix.getNumColumns());
}
@Override
public void assertSameDimensions(
Matrix other )
{
if (this.checkSameDimensions( other ) == false)
{
throw new DimensionalityMismatchException(
"Matrices must have same dimensions: " +
"(" + this.getNumRows() + "x" + this.getNumColumns() + ") !=" +
"(" + other.getNumRows() + "x" + other.getNumColumns() + ")" );
}
}
@Override
public boolean checkMultiplicationDimensions(
Matrix postMultiplicationMatrix )
{
return this.getNumColumns() == postMultiplicationMatrix.getNumRows();
}
@Override
public Matrix dotDivide(
final Matrix other)
{
final Matrix result = this.clone();
result.dotDivideEquals(other);
return result;
}
@Override
public void dotDivideEquals(
final Matrix other)
{
this.assertSameDimensions(other);
// This is a dense loop since there is no sparsity in division.
final int rowCount = this.getNumRows();
final int columnCount = this.getNumColumns();
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < columnCount; j++)
{
this.setElement(i, j,
this.getElement(i, j) / other.getElement(i, j));
}
}
}
@Override
public double trace()
{
if (this.isSquare() == false)
{
throw new DimensionalityMismatchException(
"Matrix must be square for trace()" );
}
// find the min dimension... should be same, but this is more robust
int M = this.getNumRows();
int N = this.getNumColumns();
int D = (M < N) ? M : N;
double diagonal_sum = 0.0;
for (int i = 0; i < D; i++)
{
diagonal_sum += this.getElement( i, i );
}
return diagonal_sum;
}
@Override
public void setSubMatrix(
final int minRow,
final int minColumn,
final Matrix submatrix )
{
final int M = submatrix.getNumRows();
final int N = submatrix.getNumColumns();
for (int i = 0; i < M; i++)
{
final int setRow = minRow+i;
for (int j = 0; j < N; j++)
{
final double v = submatrix.getElement( i, j );
this.setElement( setRow, minColumn + j, v );
}
}
}
@Override
public int rank()
{
return this.rank( 0.0 );
}
@Override
public Matrix pseudoInverse()
{
return this.pseudoInverse( 0.0 );
}
@Override
public void setColumn(
int columnIndex,
Vector columnVector )
{
int M = this.getNumRows();
if (M != columnVector.getDimensionality())
{
throw new DimensionalityMismatchException(
M, columnVector.getDimensionality() );
}
for (int i = 0; i < M; i++)
{
this.setElement( i, columnIndex, columnVector.getElement( i ) );
}
}
@Override
public void setRow(
int rowIndex,
Vector rowVector )
{
int N = this.getNumColumns();
if (N != rowVector.getDimensionality())
{
throw new DimensionalityMismatchException(
N, rowVector.getDimensionality() );
}
for (int j = 0; j < N; j++)
{
this.setElement( rowIndex, j, rowVector.getElement( j ) );
}
}
/**
* Internal function that writes the column onto the destinationVector.
* Gets the specified column from the zero-based index and returns a
* vector that corresponds to that column.
*
* @param columnIndex
* zero-based index into the matrix
* @param destinationVector
* Vector with elements equal to the number of rows in this
*/
protected void getColumnInto(
int columnIndex,
Vector destinationVector )
{
int M = this.getNumRows();
if (M != destinationVector.getDimensionality())
{
throw new DimensionalityMismatchException(
M, destinationVector.getDimensionality() );
}
for (int i = 0; i < M; i++)
{
destinationVector.setElement( i, this.getElement( i, columnIndex ) );
}
}
/**
* Internal function that writes the row onto the destinationVector.
* Gets the specified row from the zero-based index and returns a vector
* that corresponds to that column
*
* @param rowIndex zero-based index into the matrix
* @param destinationVector
* Vector with elements equal to the number of columns in this
*/
protected void getRowInto(
int rowIndex,
Vector destinationVector )
{
int N = this.getNumColumns();
if (N != destinationVector.getDimensionality())
{
throw new DimensionalityMismatchException(
N, destinationVector.getDimensionality() );
}
for (int j = 0; j < N; j++)
{
destinationVector.setElement( j, this.getElement( rowIndex, j ) );
}
}
@Override
public Vector sumOfRows()
{
final RingAccumulator accumulator = new RingAccumulator();
final int numRows = this.getNumRows();
for (int i = 0; i < numRows; i++)
{
accumulator.accumulate(this.getRow(i));
}
return accumulator.getSum();
}
@Override
public Vector sumOfColumns()
{
final RingAccumulator accumulator = new RingAccumulator();
final int numColumns = this.getNumColumns();
for (int j = 0; j < numColumns; j++)
{
accumulator.accumulate(this.getColumn(j));
}
return accumulator.getSum();
}
@Override
public boolean isZero(
final double effectiveZero)
{
for (MatrixEntry e : this)
{
if (Math.abs(e.getValue()) > effectiveZero)
{
return false;
}
}
return true;
}
@Override
public void increment(
final int row,
final int column)
{
this.increment(row, column, 1.0);
}
@Override
public void increment(
final int row,
final int column,
final double value)
{
this.setElement(row, column, this.getElement(row, column) + value);
}
@Override
public void decrement(
final int row,
final int column)
{
this.decrement(row, column, 1.0);
}
@Override
public void decrement(
final int row,
final int column,
final double value)
{
this.increment(row, column, -value);
}
@Override
public double[][] toArray()
{
final int rowCount = this.getNumRows();
final int columnCount = this.getNumColumns();
final double[][] result = new double[rowCount][columnCount];
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < columnCount; j++)
{
result[i][j] = this.getElement(i, j);
}
}
return result;
}
@Override
public List valuesAsList()
{
return new ValuesListView();
}
/**
* Implements a view of this matrix as a {@link List}.
*/
class ValuesListView
extends AbstractList
{
/** The number of rows. */
private final int rowCount;
/** The number of columns. */
private final int columnCount;
/** The size of this list, which is the product of the number of rows
* times the number of columns.
*/
private final int size;
/**
* Creates a new {@link ValuesListView} for this matrix.
*/
public ValuesListView()
{
super();
this.rowCount = getNumRows();
this.columnCount = getNumColumns();
this.size = this.rowCount * this.columnCount;
}
@Override
public Double get(
final int i)
{
final int row = i % this.rowCount;
final int column = i / this.rowCount;
return getElement(row, column);
}
@Override
public Double set(
final int i,
final Double value)
{
final int row = i % this.rowCount;
final int column = i / this.rowCount;
final double previous = getElement(row, column);
setElement(row, column, value);
return previous;
}
@Override
public int size()
{
return this.size;
}
}
}