# EJML: Java linear algebra with three ways to write the same equation

> EJML is a pure Java linear algebra library for dense and sparse, real and complex matrices, built around runtime algorithm selection and offered through three interfaces: procedural Operations, the object oriented SimpleMatrix, and a symbolic Equations layer. The cost of that flexibility is a learning curve and a module layout that needs care.

**lessthanoptimal/ejml** — A fast and easy to use linear algebra library written in Java for dense, sparse, real, and complex matrices.

- Repository: https://github.com/lessthanoptimal/ejml
- Website: https://ejml.org
- Stars: 615 · Forks: 119
- Language: Java
- License: not declared
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/lessthanoptimal-ejml

## The same Kalman gain, three ways

EJML exposes three distinct interfaces, and the project deliberately lets you trade ease of use against control. The Kalman gain computation is the example used to show all three, and reading them side by side says more about the library than any feature list. The procedural version, called Operations, is the one with almost complete control over memory creation, speed and the specific algorithm used:
```java
mult(H,P,c);
multTransB(c,H,S);
addEquals(S,R);
if( !invert(S,S_inv) ) throw new RuntimeException("Invert failed");
multTransA(H,S_inv,d);
mult(P,d,K);
```
SimpleMatrix is the simplified subset with a flow styled object oriented API inspired by Jama:
```java
SimpleMatrix S = H.mult(P).mult(H.transpose()).plus(R);
SimpleMatrix K = P.mult(H.transpose().mult(S.invert()));
```
And Equations is the symbolic layer, in the spirit of Matlab and other computer algebra systems, which writes the whole thing as a string:
```java
eq.process("K = P*H'*inv( H*P*H' + R )");
```
The choice is not cosmetic. Operations lets you hold onto the intermediate matrices and reuse them, which is exactly what you want in a hot loop, and that is also where the chance of a mistake lives.

## Runtime algorithm selection is the core design idea

The stated design goals of the library are computational and memory efficiency for both small and large matrices, and accessibility to novices and experts, and the first is pursued by not asking the caller to choose an algorithm. The documentation says these goals are accomplished by dynamically selecting the best algorithms to use at runtime, along with a clean API and multiple interfaces. The feature list backs that up: linear solvers in linear, least squares and incremental flavours, decompositions including LU, QR, Cholesky, SVD and eigenvalue work, matrix features such as rank, symmetry and definiteness, random matrices, several internal formats including row major, block and sparse, Graph BLAS semirings, and both single threaded and concurrent implementations. Unit tests are used extensively to check each algorithm, and internal benchmarks together with the separate Java Matrix Benchmark project are used to check speed.

The naming convention carries the type information in the class name, with a single character for data type and a suffix for the internal structure, and the matrix class names follow the pattern of a data type character followed by Matrix and the structure name, where the data type letter is D for real double, F for real float, Z for complex double, C for complex float and B for binary.

## The modules are split by data type and density

EJML is on Maven Central and can be added to Gradle, Maven and similar project managers. The simplest route takes the whole library:
```xml
<groupId>org.ejml</groupId>
<artifactId>ejml-all</artifactId>
<version>0.46.0/version>
```
The alternative is to include only the modules you need, and the split is by numeric type and matrix density. `ejml-core` holds the core data structures and common code, `ejml-fdense` and `ejml-ddense` hold dense real algorithms for 32-bit float and 64-bit double, `ejml-cdense` and `ejml-zdense` do the same for complex numbers, `ejml-fsparse` and `ejml-dsparse` hold the sparse real variants, `ejml-simple` contains the object oriented SimpleMatrix and Equations interfaces, and `ejml-java9module` is a single unified jar for the Java module system. One detail to check before you pin a version: the snippet in the project documentation shows 0.46.0, while the most recent tagged release listed for the repository is v0.45.0, published on 2026-05-15.

## On the module path, use the aggregated artifact

There is a packaging conflict that will bite anyone using JPMS, and the project explains it. EJML's individual modules cannot all be placed on the Java module path at the same time, because several of them contribute to the same `org.ejml` package, which the module system treats as a split package. The fix is the aggregated artifact `ejml-java9module`, and referencing it from a `module-info.java` is short:
```java
module my.application {
    requires ejml.java9module;
}
```
Mixing the individual `ejml-*` modules on both paths produces split package errors, so the choice is between the aggregate for module path users and the individual artifacts for classpath users. That is the kind of detail that turns a two minute dependency into an afternoon, and it is documented, which is the main thing.

## Building the library, and the cost of doing so

The project says plainly that unless you need a bleeding edge feature or you are contributing, you probably do not need to build it yourself, because pre-built jars are on Maven Central. Gradle is the official tool environment. Java 17 or higher is required to build the library, but it generates Java 11 byte code, because a few recent language features are used. The command line build generates the source code and installs the result in your local Maven repository:
```bash
cd ejml
./gradlew autogenerate
./gradlew publishToMavenLocal
```
Two other Gradle targets are worth knowing: `createLibraryDirectory` builds all the modules as jars and saves them under `ejml/libraries`, and `oneJar` compiles everything into a single jar at `ejml/EJML.jar`. The repository layout backs this up, with `main/` holding the library source, `examples/` holding worked examples of how the library solves different problems and how it can be modified, `docs/` holding documentation that the project itself says is often out of date, `change.txt` recording what changed between versions, and a `regression/` directory alongside the build scripts.

## Where EJML is the right and the wrong tool

EJML is a good fit when the problem is genuinely numerical: state estimation, least squares, decompositions, working with sparse structures that would be wasteful as dense arrays, or needing a complex type next to a real one. The library is written entirely in Java, so there is no native dependency to ship, and the type suffix convention means the compiler helps you keep the precision and structure straight.

It is the wrong tool when the algebra is trivial, because a three element vector does not need a decomposition framework, and the wrong tool when a single well known expression written by hand in plain Java would be clearer than a call into a matrix library. It is also worth noting what the library does not try to be: there is no Python or Scala binding in the project, the Kotlin extensions are a convenience on top of the Java API rather than a separate port, and Equations is symbolic in a small sense, a string interface that still evaluates numerically, not a computer algebra system that simplifies expressions for you. The stated licence is Apache v2.0, with the licence text in the repository root, and the last push was on 2026-07-31 with the default branch named SNAPSHOT.

## Conclusion

EJML suits a Java team that needs real linear algebra in production, especially where the matrix type and precision vary between call sites, because the library covers dense and sparse, real and complex, float and double, and its runtime algorithm selection removes the need to hand pick a decomposition. It is a poor fit for a project that wants one obvious way to write a matrix expression, since choosing between Operations, SimpleMatrix and Equations is a decision every call site forces, and the in-repository documentation notes that the docs directory is often out of date, so the online manual and JavaDoc are the reference to trust. Before adopting it, check the module set you actually need, since pulling ejml-all brings the whole library while the individual modules are split by data type and density, and if you run on the module path, take ejml-java9module rather than mixing individual ejml modules, which produce split package errors.

## FAQ

### How do I add EJML to a Maven or Gradle project?

Add the whole library with the ejml-all artifact, or include only the modules you need: ejml-core, ejml-fdense, ejml-ddense, ejml-cdense, ejml-zdense, ejml-fsparse, ejml-dsparse, ejml-simple and ejml-java9module. The project is published on Maven Central.

### What is the difference between Operations, SimpleMatrix and Equations?

They are three ways to use the same library. Operations is the procedural API with almost complete control over memory and algorithm choice, SimpleMatrix is the simplified object oriented subset inspired by Jama, and Equations is a symbolic string interface in the spirit of Matlab.

### What is ejml-java9module for?

Several individual EJML modules contribute to the same org.ejml package, so they cannot all sit on the Java module path at once without split package errors. The aggregated ejml-java9module artifact exists to give module path users a single jar, referenced with a requires ejml.java9module line in module-info.java.

### Which Java version do I need to build EJML?

Java 17 or higher is required to build the library, and the build generates Java 11 byte code because a few recent language features are used. The build itself uses Gradle, with the autogenerate and publishToMavenLocal targets.

### Which matrix types does EJML support?

Dense and sparse matrices, real and complex, in 32-bit float and 64-bit double, plus a binary type. The data type is encoded in the class name, with D for real double, F for real float, Z for complex double, C for complex float and B for binary, and several internal formats such as row major, block and sparse are supported.

## Sources

- [Issues](https://github.com/lessthanoptimal/ejml/issues)
- [lessthanoptimal/ejml on GitHub](https://github.com/lessthanoptimal/ejml)
- [Project website](https://ejml.org)
- [README](https://github.com/lessthanoptimal/ejml/blob/SNAPSHOT/README.md)
- [Releases](https://github.com/lessthanoptimal/ejml/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lessthanoptimal-ejml
