Fork me on GitHub

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 la4j-0.4.0.zip


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:


Samples and Notes


Factories and Entries

There are four matrix classes in la4j which are used for handling dense and sparse matrices:

The la4j also supports vectors as separate entries. There are two types of vectors which also represent dense and sparse storages:

The cornerstown of la4j is its factories. Factories are used everywhere for producing new matrices and vectors. The following factories are supported:

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();
Tip! Don't use factories everywhere. Try public constructors!

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:

, and for vectors:

Safe and Unsafe entries

Entries in la4j might be safe 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:

, and following set of functors:

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:

, which can be accessed via predefined static fields of org.la4j.matrix.Matrices:

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:

, which can be used via predefined singleton instances in class org.la4j.matrix.Matrices:

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:

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:

The last but not least way to contribute - drop a feedback to the author @vkostyukov.


References