Linear Algebra for Java
Open Source and 100% Java sparse/dense matrix library.
I've read through parts of la4j library. It's very well designed - not too much object poetry (though there's a bit of a factory proliferation), some good defaults, and so far - I'm using the sparse matrix and vector classes - performance is good.
John Stewart, Amplify, Inc, New York, USA
I found la4j-0.4.0 has a lot of changes compared to the previous version. And I am glad that the AbstractMatrix has included a lot new functionalities like Kronecker product and slice operations. What a wonderful job!
Xiaorui Jiang, Zhejiang University of Technology, Zhejiang, China
Download
The la4j might be simply downloaded by clicking big green button at the top of this page or by updating Maven's pom.xml.
<dependency> <groupId>org.la4j</groupId> <artifactId>la4j</artifactId> <version>0.4.0</version> </dependency>
Overview
The la4j is single threaded, open source and pure Java library that provides Linear Algebra primitives and algorithms. The key features of la4j listed bellow:
- No dependencies and tiny size (~130kb jar)
- Simple and object-oriented/functional API
- Sparse (CRS, CCS) and dense (1D/2D arrays) matrices
- Linear systems solving (Gaussian, Jacobi, Zeidel, Square Root, Sweep and other)
- Matrices decomposition (Eigenvalues, SVD, QR, LU, Cholesky and other)
- MatrixMarket/CSV IO formats support for matrices and vectors
Samples and Notes
Factories and Entries
There are four matrix classes in la4j which are used for handling dense and sparse matrices:
org.la4j.matrix.dense.Basic1DMatrixuses 1D double array as internal representationorg.la4j.matrix.dense.Basic2DMatrixuses 2D double array as internal representationorg.la4j.matrix.sparse.CRSMatrixuses CRS - Compressed Row Storage formatorg.la4j.matrix.sparse.CCSMatrixuses CCS - Compressed Colummn Storage format
The la4j also supports vectors as separate entries. There are two types of vectors which also represent dense and sparse storages:
org.la4j.vector.dense.BasicVectoruses 1D double array as internal representationorg.la4j.vector.sparse.CompressedVectoruses compressed array as internal representation
The cornerstown of la4j is its factories. Factories are used everywhere for producing new matrices and vectors. The following factories are supported:
org.la4j.factory.Basic1DFactoryproducesBasic1DMatrixandBasicVectororg.la4j.factory.Basic2DFactoryproducesBasic2DMatrixandBasicVectororg.la4j.factory.CRSFactoryproducesCRSMatrixandCompressedVectororg.la4j.factory.CCSFactoryproducesCCSMatrixandCompressedVector
Factory basic1d = new Basic1DFactory();
Factory basic2d = new Basic2DFactory();
Factory crs = new CRSFactory();
Factory ccs = new CCSFactory();
// 'a' is 3x3 dense 1D-Array matrix
Matrix a = basic1d.createMatrix(new double[][] {
{1.0, 0.0, 0.0},
{0.0, 5.0, 0.0},
{0.0, 0.0, 9.0}
});
// 'b' is 3x4 dense 2D-Array matrix
Matrix b = basic2d.createMatrix(3, 4);
// 'c' is 5x5 sparse CRS matrix that is copied from matrix 'a'
Matrix c = crs.createMatrix(a);
// 'd' is 10x10 constant sparse CCS matrix
Matrix d = ccs.createConstantMatrix(10, 10, 42.0);
// 'e' is 7x3 random dense 1D-Array matrix
Matrix e = basic1d.createRandomMatrix(7, 3);
// 'f' is 9x9 random symmetric dense 2D-Array matrix
Matrix f = basic2d.createRandomSymmetricMatrix(9);
// 'g' is 8x8 square sparse CRS matrix
Matrix g = crs.createSquareMatrix(8);
// 'h' is 4x4 identity sparse CCS matrix
Matrix h = ccs.createIdentityMatrix(4);
// 'i' is empty dense 1D-Array matrix
Matrix i = basic1d.createMatrix();
// 'j' is dense vector that handles 3 elements
Vector j = basic1d.createVector(new dobule[]{ 1.0, 2.0, 3.0 });
// 'k' is sparse vector that handles 4 elements
Vector k = crs.createVector(4);
// 'l' is dense vector that is copied from vector 'j'
Vector l = basic2d.createVector(j);
// 'm' is constant sparse vector that handles 6 elements
Vector m = ccs.createConstantVector(6, 42.0);
// 'n' is random dense vector that handles 7 elements
Vector n = basic1d.createRandomVector(7);
// 'o' is empty sparse vector
Vector o = crs.createVector();
The la4j provides predefined factories instances which might be accessed via special static classes. There are two major static classes in la4j:
org.la4j.matrix.Matrices and org.la4j.vector.Vectors. These classes handle loads of usefull defaults for matrices and vectors. Here is the list of predifined static factories for matrices:
org.la4j.matrix.Matrices.BASIC1D_FACTORYorg.la4j.matrix.Matrices.BASIC2D_FACTORYorg.la4j.matrix.Matrices.CRS_FACTORYorg.la4j.matrix.Matrices.CCS_FACTORY
org.la4j.vector.Vectors.BASIC_FACTORYorg.la4j.vector.Vectors.COMPRESSED_FACTORY
Safe and Unsafe entries
Entries in la4j might besafe and unsafe. Matrices and vectors in la4j are unsafe by default. Unsafe entries don't perfrom bound checks before accessors, while safe ones do.
// 'a' is unsafe dense 2D-Array matrix Matrix a = new Basic2DMatrix(...); // 'b' is safe dense 2D-array matrix Matrix b = a.safe(); // 'c' is equal to 'a' Matrix c = b.unsafe(); // 'd' is unsafe sparse vector Vector d = new CompressedVector(...); // 'e' is safe sparse vector Vector e = c.safe(); // 'f' is equal to 'd' Vector f = e.unsafe():
Rows and Columns
Matrices in la4j support access to their rows and columns via vectors.
// 'a' is sparse CRS matrix Matrix a = new CRSMatrix(...); // numbers of rows and columns in matrix 'a' int rows = a.rows(); int columns = a.columns(); // swaps columns in matrix 'a' a.swapColumns(0, columns - 1); // swaps rows in matrix 'a' a.swapRows(0, rows - 1); // 'r' is sparse compressed vector Vector r = a.getRow(0); // 'c' is dense basic vector Vector c = a.getColumn(0, Vector.BASIC_FACTORY); // sets matrix row to given vector a.setRow(1, r); // sets matrix column to given vector a.setColumn(1, c);
Copying
Matrix a = new CRSMatrix(...); // copies matrix 'a' to matrix 'b' // result matrix 'b' is produced by Matrices.CRS_FACTORY Matrix b = a.copy(); // copies matrix 'a' to matrix 'c' // result matrix 'c' is produced by Matrices.BASIC2D_FACTORY Matrix c = a.copy(Matrices.BASIC2D_FACTORY); // crates a blank copy (copy without values) of matrix 'a' // result matrix 'd' is produced by Matrices.BASIC1D_FACTORY Matrix d = a.blank(Matrices.BASIC1D_FACTORY) // 'h' is dense basic vector Vector e = BasicVector(...); // copies vector 'e' to vector 'f' // result vector 'f' is produced by Vectors.BASIC_FACTORY Vector f = e.copy(); // copies vector 'e' to vector 'g' // result vector 'm' is produced by Vectors.COMPRESSED_FACTORY Vector g = e.copy(Vectors.COMPRESSED_FACTORY); // creates a blank copy (copy without values) of vector 'e' // result vector 'h' is produced by Vectors.BASIC_FACTORY Vector h = e.blank();
Resizing and Slicing
Entries in la4j are immutable in terms of dimmensions. It means that resizing can't be done in-place.
Matrix a = new CRSMatrix(...); // rows and columns of matrix 'a' int rows = a.rows(); int columns = a.columns(); // resizes matrix 'a' to given size // result matrix 'b' is produced by Matrices.CRS_FACTORY Matrix b = a.resize(rows + 1, columns + 1); // resizes matrix 'a' to given size // result matrix 'c' is produced by Matrices.CCS_FACTORY Matrix c = a.resize(rows - 1, columns - 1, Matrices.CCS_FACTORY); // equivalent to 'resize(rows + 1, columns)' Matrix d = a.resizeRows(rows + 1); // equivalent to 'resize(rows, columns + 1)' Matrix e = a.resizeColumns(columns + 1); // slices matrix 'a' // signature of slice is (fromRow, fromColumn, untilRow, untilColumn) // result matrix 'f' is produced by Matrices.CRS_FACTORY Matrix f = a.slice(1, 1, rows - 1, columns - 1); // slices matrix 'a' // equivalent to slice(0, 0, rows - 1, columns - 1); Matrix g = a.sliceTopLeft(rows - 1, columns - 1); // slices matrix 'a' // equivalent to slice(1, 1, rows, columns); Matrix h = a.sliceBottomRight(1, 1); Vector j = BasicVector(...); // length of vector 'j' int length = j.length(); // resizes vector 'j' to new size // fesult vector 'k' is produced by Vectors.BASIC_FACTORY Vector k = j.resize(length + 1); // resizes vector 'j' to new size // fesult vector 'l' is produced Vectors.COMPRESSED_FACTORY Vector l = j.resize(length - 1, Vectors.COMPRESSED_FACTORY); // slices vector 'j' // signature of slice is (from, until) // fesult vector 'm' is produced by Vectors.BASIC_FACTORY Vector m = j.slice(1, length - 1); // slices vector 'j' // equivalent to slice(0, length - 1) Vector n = j.sliceLeft(length - 1); // slices vector 'j' // equivalent to slice(1, length) Vector o = j.sliceRight(1);
Basic operations
Matrix a = Basic2DMatrix(...); // assigns each element of matrix 'a' to 42.0 a.assign(42.0); Matrix b = new CCSMatrix(...); Matrix c = new Basic1DMatrix(...); // multiplies matrix 'b' by matrix 'c' // result matrix 'd' is produced by Matrices.CCS_FACTORY Matrix d = b.multiply(c); // multiplies matrix 'b' by matrix 'c' // result matrix 'e' is produced by Matrices.BASIC1D_FACTORY Matrix e = a.multiply(b, Matrices.BASIC1D_FACTORY); // adds matrix 'c' to matrix 'b' // result matrix 'f' is produced by Matrices.CCS_FACTORY Matrix f = b.add(c); // subtructs matrix 'c' from matrix 'b' // result matrix 'g' is produced by Matrices.BASIC2D_FACTORY Matrix g = b.subtruct(c, Matrices.BASIC2D_FACTORY); // scales matrix 'b' by value 42.0 // result matrix 'h' is produced by Matrices.CCS_FACTORY Matrix h = b.multiply(42.0); // scales matrix 'b' by vaule 1/42.0 // result matrix 'i' is produced by Matrices.BASIC1D_FACTORY Matrix i = b.divide(42.0, Matrices.BASIC1D_FACTORY); // adds 42.0 to each element of matrix 'b' // result matrix 'k' is produced by Matrices.CCS_FACTORY Matrix k = b.add(42.0); // subtructs 42.0 from each element of matrix 'b' // result matrix 'l' is produced by Matrices.CRS_FACTORY Matrix l = b.subtruct(42.0); // calculates Kronecker product of two matrices 'b' and 'c' // result matrix 'm' is produced by Matrices.CRS_FACTORY Matrix m = b.kronecker(c); Vector n = new BasicVector(...); // assigns each element of vector 'n' to 42.0 n.assign(42.0); // multiplies matrix 'b' by vector // result vector 'o is produced by Vectors.COMPRESSED_FACTORY Vector o = b.multiply(n, Vectors.COMPRESSED_FACTORY); // multiplies vector 'n' by matrix 'b' // result vector 'p' is produced by Vectors.BASIC_FACTORY Vector p = n.multiply(b);
Specific operations
Matrix a = new CCSMatrix(...); Matrix b = new Basic2DMatrix(...); Vector c = new BasicVector(...); // calculates the trace of matrix double t = a.trace(); // calculates the product of matrix double p = a.product(); // calculates the determinant of matrix double d = a.determinant(); // transposes matrix 'a' // result matrix 'e' is produced by Matrices.CCS_FACTORY Matrix e = a.transpose(); // transposes matrix 'a' // result matrix 'f' is produced by Matrices.BASIC1D_FACTORY Matrix f = a.transpose(Matrices.BASIC1D_FACTORY); // converts matrix 'a' into tridiagonal matrix // result matrix 'g' is produced by Matrices.CCS_FACTORY Matrix g = a.triangularize(); // converts matrix 'a' into tridiagonal matrix // result matrix 'h' is produced by Matrices.BASIC1D_FACTORY Matrix h = a.triangularize(Matrices.BASIC1D_FACTORY); // calculates L_1 norm of vector 'c' double n = c.norm(); // normalizes vector 'c' // result vector 'j' is produced by Vectors.COMPRESSED_VECTOR Vector j = c.normalize(Vectors.COMPRESSED_VECTOR);
Functors
The la4j supports four types of functors which are used in hi-order methods. Matrices support the followig set of hi-order methods:
Matrix.each(...)applies procedure to each element of matrixMatrix.transform(...)transforms matrix by applying function to each element of matrixMatrix.update(...)updates matrix (in-place transform)Matrix.is(...)checks whether each element of matrix corresponds to predicateMatrix.fold(...)reduces elements of matrix to single alue by using accumulator function
org.la4j.matrix.functor.MatrixProcedureorg.la4j.matrix.functor.MatrixFunctionorg.la4j.matrix.functor.MatrixPredicateorg.la4j.matrix.functor.MatrixAccumulator
Matrix a = new CCSMatrix(...);
// prints each element of matrix 'a' to stdout
a.each(new MatrixProcedure() {
@Override
public void apply(int i, int j, double value) {
System.out.println(String.format("a[%d][%d] = %f", i, j, value));
}
});
// increases each element of matrix 'a' by 1
Matrix b = a.transform(Matrices.INC_MATRIX);
// makes a diagonal matrix
Matrix c = a.transform(new MatrixFunction() {
@Override
public double evaluate(int i, int j, double value) {
return i == j ? value : 0.0;
}
}, Matrices.BASIC2D_FACTORY);
// calculates the sum of elements of matrix 'a'
double s = a.fold(Matrices.asSumAccumulator(0.0));
// calculates the product of elements of matrix 'a'
double p = a.fold(Matrices.asProductAccumulator(1.0));
// calculates the sum of '1' row
double s1 = a.foldRow(1, Matrices.asSumAccumulator(0));
// calculates the sum of '2'
double s2 = a.foldColumn(2, Matrices.asSumAccumulator(0));
// the helper class that fetches border elements of matrix
class PerimeterFetcher implements MatrixFunction {
private int rows;
private int columns;
public PerimeterFectcher(int rows, int columns) {
this.rows = rows;
this.columns = columns;
}
@Override
public double evaluate(int i, int j, double value) {
return i == 0 ? value : j == 0 ? value : (i + 1) == rows ? value
: (j + 1) == columns ? value : 0;
}
}
// calculates the perimeter of matrix
double p = a.fold(Matrices.asSumFunctionAccumulator(0,
new PerimeterFetcher(a.rows(), a.columns())));
// returns 'true' if matrix 'a' is symmetric matrix
boolean isSymmetric = a.is(Matrices.SYMMETRIC_MATRIX);
// returns 'true' if matrix 'a' is tridiagonal matrix
boolean isTridiagonal = a.is(Matrices.TRIDIAGONAL_MATRIX);
// returns 'true' if matrix a is identity matrix
boolean isIdentity = a.is(new MatrixPredicate() {
@Override
public boolean test(int i, int j, double value) {
return (i == j) ? Math.abs(value - 1.0) < Matrices.EPS
: Math.abs(value) < Matrices.EPS;
}
});
Sources
public class ConsoleMatrixSource implements MatrixSource {
@Override
public int rows() {
System.out.print("Enter matrix rows: ");
return new Scanner(System.in).nextInt();
}
@Override
public int columns() {
System.out.print("Enter matrix columns: ");
return new Scanner(System.in).nextInt();
}
@Override
public double get(int i, int j) {
System.out.printf("Enter matrix element [%d][%d]: ", i, j);
return new Scanner(System.in).nextDouble();
}
}
// a - is sparse CRS matrix got from stdin
Matrix a = new CRSMatrix(new ConsoleMatrixSource());
// b - is 5x10 random dense 1D-array matrix
Matrix b = new Basic1DMatrix(new RandomMatrixSource(5, 10));
// c - is 10x10 identity dense 2D-array matrix
Matrix c = new Basic2DMatrix(new IdentityMattixSource(10));
// d - is 10x10 random symmetric CCS matrix
Matrix d = new CCSMatrix(new RandomSymmetricMatrixSource(10));
Matrix inversion
There is only one inversion algorithm that is supported by la4j - org.la4j.inversion.GaussianInvertor. This invertor uses Gaussian Elimination as core method for finding inverted matrix and can be touched via static field Matrices.GAUSSIAN_INVERTOR of org.la4j.matrix.Matrices class.
Matrix a = new Basic2DMatrix(...); // 'b' is a dense 2D-array matrix // DEFAULT_INVERTOR is alias for GAUSSIAN_INVERTOR Matrix b = a.inverse(Matrices.DEFAULT_INVERTOR); // 'c' is a sparse CCS matrix Matrix c = a.inverse(Matrices.DEFAULT_INVERTOR, Matrices.CCS_FACTORY);
Solving linear systems
Following solvers are supported by la4j:
org.la4j.linear.GaussianSolverimplements Gaussian Eliminationorg.la4j.linear.JacobiSolverimplements Jacobi methodorg.la4j.linear.SeidelSolverimplements Gauss-Seidel methodorg.la4j.linear.SquareRootSolverimplements Square Roots methodorg.la4j.linear.SweepSolverimplements Tridiagonal Matrix algorithm
org.la4j.matrix.Matrices:
Matrices.GAUSSIAN_SOLVERMatrices.JACOBI_SOLVERMatrices.SEIDEL_SOLVERMatrices.SQUARE_ROOT_SOLVERMatrices.SWEEP_SOLVER
Matrix a = new CRSMatrix(...); Vector b = new BasicVector(...); LinearSystem system = Matrices.asLinearSystem(a, b); // solves the linear system with the most efficient solver // 'x' is a sparse compressed vector Vector x = system.solve(); // solves the linear system with specified solver // 'y' is a dense basic vector Vector y = system.solve(Matrices.GAUSSIAN_SOLVER, Matrices.BASIC2D_FACTORY);
Matrix decomposition
The la4j supports following decompositions:
org.la4j.decomposition.CholeskyDecompositorimplements Cholesky decompositionorg.la4j.decomposition.EigenDecompositorimplements Eigen decompositionorg.la4j.decomposition.LUDecompositorimplements LU decompositionorg.la4j.decomposition.QRDecompositionimplements QR decompositionorg.la4j.decomposition.SingularValueDecompositorimplements SVD
org.la4j.matrix.Matrices:
Matrices.CHOLESKY_DECOMPOSITORMatrices.EIGEN_DECOMPOSITORMatrices.LU_DECOMPOSITORMatrices.QR_DECOMPOSITORMatrices.SINGULAR_VALUE_DECOMPOSITOR
Matrix a = new Basic1DMatrix(...); // qr[0] = Q, qr[1] = R; Q, R - dense 1D-array matrices Matrix[] qr = a.decompose(Matrices.QR_DECOMPOSITOR); // lu[0] = L, lu[1] = U; L, U - sparse CRS matrices Matrix[] lu = a.decompose(Matrices.LU_DECOMPOSITOR, Matrices.CRS_FACTORY); // usv[0] = U, usv[1] = S, usv[2] = V; U, S, V - dense 1D-array matrices Matrix[] usv = a.decompose(Matrices.SINGULAR_VALUE_DECOMPOSITOR);
Input/Output
The la4j supports two IO formats:
org.la4j.io.MatrixMarketStreamimplements MatrixMarket formatorg.la4j.io.SymbolSeparatedStreamimplements CSS, TSS, etc.
Matrix a = new Basic2DMatrix(...);
// MatrixMarket format provider
// writing matrix 'a' to file
MatrixStream out = new MatrixMarketStream(new FileOutputStream("file.mm"));
out.writeMatrix(a);
// reading matrix from file
MatrixStream in = new MatrixMarketStream(new FileInputStream("file.mm"));
Matrix b = in.readMatrix(Matrices.CRS_FACTORY);
Contributions
The la4j project is maintained at GitHub, where you can:
- Fork and send pull-request
- Browse the source code using the Source Browser
- Submit a bug/feature using the Issues List
- Discuss usage/features/bugs using the Google Group or the maillist la4j@googlegroups.com
The last but not least way to contribute - drop a feedback to the author @vkostyukov.
References
- https://github.com/vkostyukov/la4j - GitHub page
- http://la4j.blogspot.com - Development Blog
- https://groups.google.com/forum/?fromgroups=#!forum/la4j - Google Group