/*
* File: MatrixAndVectorExample.java
* Authors: Kevin R. Dixon
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright May 6, 2009, 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 examples;
import gov.sandia.cognition.math.matrix.Matrix;
import gov.sandia.cognition.math.matrix.MatrixFactory;
import gov.sandia.cognition.math.matrix.Vector;
import gov.sandia.cognition.math.matrix.VectorFactory;
import gov.sandia.cognition.math.matrix.decomposition.SingularValueDecomposition;
import gov.sandia.cognition.math.matrix.mtj.SparseMatrix;
import gov.sandia.cognition.math.matrix.mtj.SparseMatrixFactoryMTJ;
import gov.sandia.cognition.math.matrix.mtj.decomposition.SingularValueDecompositionMTJ;
import java.util.Arrays;
/**
* This is an example of how to use the matrix package, which includes
* matrix and vector computation, and matrix decompositions.
*
* @author Kevin R. Dixon
* @since 3.0
*/
public class MatrixAndVectorExample
{
/**
* Entry point for the example.
* @param args
* We don't use any arguments for this example.
*/
public static void main(
String ... args )
{
// The creation of all Matrix and Vector instances is done through
// the "MatrixFactory" and "VectorFactory" subclasses. We provide
// default factories through the getDefault() method.
//
// We do this because the functionality of Matrix and Vector are
// defined as interfaces and are independent of an implementation.
// We do this so that anybody can wrap their favorite
// matrix-computation package. In our case, we like the "Matrix
// Toolkits for Java" (MTJ) package for our computation. However,
// if you like using a specific implementation, then feel free
// to point to your most-loved implemtation-specific Factory.
//
// Please note that our Vector interface is called "Vector", which
// causes a name collision with Java's useless Vector class.
// Since nobody would willfully use Java's Vector class, we figured
// that this wouldn't be a huge problem.
// Both vectors and matrices have fixed dimensions once you create them.
// Here is a three-dimensional Vector with all-zero entries.
Vector x0 = VectorFactory.getDefault().createVector(3);
System.out.println( "x0 = " + x0 );
// Now, let's set the values of the Vector using the setElement method.
// We use C-style zero-based indices in our Vector and Matrix packages.
// Note that Java does not allow operator overload like C++. For
// example, it's common for C++ matrix package to use some operator
// overload so that a matrix-package user can simply type
// x0[1] = 2.0
// This has historically lead to problems that people do not realize
// that they've invoked the "operator[]" function... which has lead
// to all sorts of headaches. Instead, Java requires explicit
// method calls for this type of thing.
x0.setElement(0, 1.0);
x0.setElement(1, 2.0);
x0.setElement(2, 3.0);
System.out.println( "x0: " + x0 );
// If you attempt to set an element beyond the dimensionality of
// the Vector (which, again, is fixed upon construction), then
// you will receive a thrown exception.
try
{
// Since we are using zero-based indices, the only valid indices
// are from [0, 1, ... num-1].
x0.setElement( x0.getDimensionality(),0.0);
}
catch (Exception e)
{
System.out.println( "Threw exception: " + e.getClass().getName() );
}
// We access elements of the Vector the getElement() method with
// C-style zero-based indices.
System.out.println( "x0(0): " + x0.getElement(0) );
System.out.println( "x0(1): " + x0.getElement(1) );
System.out.println( "x0(2): " + x0.getElement(2) );
// Just to reiterate... attempting to access an out-of-bounds index
// will throw an exception, just like in the setElement method above.
try
{
// Since we use zero-based indices, negative indices are
// always illegal.
x0.getElement(-1);
}
catch (Exception e)
{
System.out.println( "Threw exception: " + e.getClass().getName() );
}
// This is an example of a three-dimensional Vector with entries
// [1.0, 2.0, 3.0 ].
Vector x1 = VectorFactory.getDefault().copyValues( 1.0, 2.0, 3.0 );
System.out.println( "x1 = " + x1 );
// Let's create a 3x3 identity matrix. This is a matrix that has
// ones along the diagonal, and zeros elsewhere. The result of
// premultiplying an identity matrix by a vector is the same vector
// (so long as the dimensions of the vector equal the number of
// columns in the matrix):
Matrix identity = MatrixFactory.getDefault().createIdentity(3, 3);
System.out.println( "Identity (3x3):\n" + identity );
Vector y1 = identity.times( x1 );
System.out.println( "y1 = I*x1 = " + y1 );
// Again, an appropriate identify matrix times a vector should
// equal the vector itself:
System.out.println( "y1 == x1: " + y1.equals(x1) );
// Many of the Matrix/Vector operations have inline versions of the
// same operation. This help minimize the amount of memory created.
// This is true partially because both Vector and Matrix inherit from
// the same interface, gov.sandia.cognition.math.Ring.
// Let's scale the above equation.
final double scaleFactor = 10.0;
Vector y1scaled1 = identity.scale( scaleFactor ).times( x1 );
System.out.println( "y1scaled1 = (10*I)*x1: " + y1scaled1 );
// Of course, there are many ways to get the same result!
Vector y1scaled2 = identity.times( x1 ).scale(scaleFactor);
System.out.println( "y1scaled1 = I*(x1*10): " + y1scaled2 );
// These should be equal, because scaling is associative.
System.out.println( "y1scaled1 == y1scaled2: " + y1scaled1.equals(y1scaled2) );
// Let's keep a copies of the original values of "identity" because
// we're about to apply inline operators to it.
Matrix identityClone = identity.clone();
// This method modifies the Identity matrix inline and then go through
// the times() method. However, this scales 9 values on the
// scaleEquals() method, so there may be a more efficient way to do it.
identity.scaleEquals(scaleFactor);
Vector y1scaled3 = identity.times(x1);
System.out.println( "y1scaled1 == y1scaled3: " + y1scaled1.equals(y1scaled3) );
// I believe this will be the most efficient way to get the result,
// because we're only scaling three values and we're not creating
// an additional Vector for the non-inlined scale() method.
x1.scaleEquals(scaleFactor);
Vector y1scaled4 = identityClone.times( x1 );
System.out.println( "y1scaled1 == y1scaled4: " + y1scaled1.equals(y1scaled4) );
// Inappropriately dimensioned matrix computation will throw an
// exception. Because the number of columns (2) doesn't equal the
// dimensionality of x1 (3), the system will throw an exception.
Matrix Ibad = MatrixFactory.getDefault().createIdentity(4,2);
try
{
// This will barf and throw an exception.
Ibad.times( x1 );
// We will never get here.
}
catch (Exception e)
{
System.out.println( "Caught an exception of type: " + e.getClass().getName() );
}
// Here's a Matrix with the following columns:
Vector c1 = VectorFactory.getDefault().copyValues( 1.0, 2.0, 3.0 );
Vector c2 = VectorFactory.getDefault().copyValues( 0.0, 1.0, 0.0 );
Vector c3 = c1;
Vector c4 = c2;
Matrix matrix = MatrixFactory.getDefault().copyColumnVectors(
Arrays.asList(c1, c2, c3, c4 ) );
System.out.println( "matrix =\n" + matrix );
// The specific decompositions are located in an implementation-
// specific package. In this case, we use the MTJ package
// implementation for dense and sparse matrix computation.
// So, we'll use the MTJ SVD routine.
SingularValueDecomposition svd =
SingularValueDecompositionMTJ.create(matrix);
System.out.println( "U =\n" + svd.getU() );
System.out.println( "S =\n" + svd.getS() );
System.out.println( "Vt =\n" + svd.getVtranspose() );
// Note that we expect to have an effective rank of "matrix" of 2
// because 2 of the 4 columns are copies of another column:
final double tolerance = 1e-10;
System.out.println( "Effective Rank of A = " + svd.effectiveRank(tolerance) );
// Some applications require a sparse representation of matrices and
// vectors. This is true when the dimensions considered a large,
// but the number of nonzero entries of the matrix a few. Gererally
// speaking sparse matrices and vectors only store the nonzero entries.
// Let's look at how sparse matrices are stored using the MTJ package.
SparseMatrix sparse1 = SparseMatrixFactoryMTJ.INSTANCE.createMatrix(10,5);
// Initially, there will be no nonzero entries.
System.out.println( "Sparse with no nonzero entries\n" + sparse1 );
// Set a couple of values to nonzero entries... When we print out
// the sparse matrix, note that we will see the coordinates of the
// nonzero entries only.
sparse1.setElement(1,2, 1.0);
sparse1.setElement(5,3, 0.2);
System.out.println( "Sparse with 2 nonzero entries \n" + sparse1 );
// If we set a previously nonzero element back to zero, it doesn't
// automatically compress the matrix back. This must be done manually
// since it can be pretty time consuming.
sparse1.setElement(1,2, 0.0 );
System.out.println( "Sparse with a nonzero entry returned to zero\n" + sparse1 );
sparse1.compact();
System.out.println( "Sparse that has been compacted\n" + sparse1 );
}
}