1. Introduction
The SuanShu is a Java math library for numerical analysis, statistics, root finding, linear algebra, optimization, and lots more. One of the things it provides is functionality for both real and complex numbers.
There’s an open-source version of the library, as well as a version that requires a license -with different forms of the license: academic, commercial, and contributor.
Note that the examples below use the licensed version through the pom.xml. The open-source version is currently not available in a Maven repository; the licensed version requires a license server to be running. As a result, there aren’t any tests for this package in GitHub.
2. Setup for SuanShu
Let’s start with adding the Maven dependency to the pom.xml:
<dependencies> <dependency> <groupId>com.numericalmethod</groupId> <artifactId>suanshu</artifactId> <version>4.0.0</version> </dependency> </dependencies> <repositories> <repository> <id>nm-repo</id> <name>Numerical Method's Maven Repository</name> <url>http://repo.numericalmethod.com/maven/</url> <layout>default</layout> </repository> </repositories>
3. Working With Vectors
The SuanShu library provides classes for both dense vectors and sparse vectors. A dense vector is a vector where most elements have a non-zero value, as opposed to a sparse vector where most values have a zero value.
The implementation of a dense vector simply uses a Java array of real/complex numbers while the implementation of a sparse vector uses a Java array of entries, where each entry has an index and a real/complex value.
We can see how that would make a huge difference in storage when we have a large vector where most values are zero. Most mathematical libraries use an approach like this when they need to support vectors of large sizes.
Let’s look at some of the basic vector operations.
3.1. Adding Vectors
Adding 2 vectors is pretty straightforward using the add() method:
public void addingVectors() throws Exception { Vector v1 = new DenseVector(new double[] {1, 2, 3, 4, 5}); Vector v2 = new DenseVector(new double[] {5, 4, 3, 2, 1}); Vector v3 = v1.add(v2); log.info("Adding vectors: {}", v3); }
The output we’ll see is:
[6.000000, 6.000000, 6.000000, 6.000000, 6.000000]
We can also add the same numbers to all elements using the add(double) method.
3.2. Scaling Vectors
Scaling a vector (i.e. multiplying by a constant) is also very easy:
public void scaleVector() throws Exception { Vector v1 = new DenseVector(new double[]{1, 2, 3, 4, 5}); Vector v2 = v1.scaled(2.0); log.info("Scaling a vector: {}", v2); }
The output:
[2.000000, 4.000000, 6.000000, 8.000000, 10.000000]
3.3. Vector Inner Product
Calculating the inner product of 2 vectors requires a call to the innerProduct(Vector) method:
public void innerProductVectors() throws Exception { Vector v1 = new DenseVector(new double[]{1, 2, 3, 4, 5}); Vector v2 = new DenseVector(new double[]{5, 4, 3, 2, 1}); double inner = v1.innerProduct(v2); log.info("Vector inner product: {}", inner); }
3.4. Dealing With Errors
The library verifies that the vectors we are operating on are compatible with the operation we are executing. For instance, adding a size-2 vector to a size-3 vector should not be possible. So the code below should result in an exception:
public void addingIncorrectVectors() throws Exception { Vector v1 = new DenseVector(new double[] {1, 2, 3}); Vector v2 = new DenseVector(new double[] {5, 4}); Vector v3 = v1.add(v2); }
And indeed it does – running this code results in:
Exception in thread "main" com.numericalmethod.suanshu.vector.doubles.IsVector$SizeMismatch: vectors do not have the same size: 3 and 2 at com.numericalmethod.suanshu.vector.doubles.IsVector.throwIfNotEqualSize(IsVector.java:101) at com.numericalmethod.suanshu.vector.doubles.dense.DenseVector.add(DenseVector.java:174) at com.baeldung.suanshu.SuanShuMath.addingIncorrectVectors(SuanShuMath.java:21) at com.baeldung.suanshu.SuanShuMath.main(SuanShuMath.java:8)
4. Working With Matrices
In addition to vectors, the library also provides support for matrix operations. Similar to vectors, matrices are supported in dense and sparse format, and for real and complex numbers.
4.1. Adding Matrices
Adding matrices is just as simple as working with vectors:
public void addingMatrices() throws Exception { Matrix m1 = new DenseMatrix(new double[][]{ {1, 2, 3}, {4, 5, 6} }); Matrix m2 = new DenseMatrix(new double[][]{ {3, 2, 1}, {6, 5, 4} }); Matrix m3 = m1.add(m2); log.info("Adding matrices: {}", m3); }
4.2. Multiplying Matrices
The math library can be used to multiply matrices:
public void multiplyMatrices() throws Exception { Matrix m1 = new DenseMatrix(new double[][]{ {1, 2, 3}, {4, 5, 6} }); Matrix m2 = new DenseMatrix(new double[][]{ {1, 4}, {2, 5}, {3, 6} }); Matrix m3 = m1.multiply(m2); log.info("Multiplying matrices: {}", m3); }
Multiplying a 2×3 matrix with a 3×2 matrix will result in a 2×2 matrix.
And to prove the library does proper checks of the matrix sizes, let’s try to do a multiplication that should fail:
public void multiplyIncorrectMatrices() throws Exception { Matrix m1 = new DenseMatrix(new double[][]{ {1, 2, 3}, {4, 5, 6} }); Matrix m2 = new DenseMatrix(new double[][]{ {3, 2, 1}, {6, 5, 4} }); Matrix m3 = m1.multiply(m2); }
Executing that will result in the following output.
Exception in thread "main" com.numericalmethod.suanshu.matrix.MatrixMismatchException: matrix with 3 columns and matrix with 2 rows cannot multiply due to mis-matched dimension at com.numericalmethod.suanshu.datastructure.DimensionCheck.throwIfIncompatible4Multiplication(DimensionCheck.java:164) at com.numericalmethod.suanshu.matrix.doubles.matrixtype.dense.DenseMatrix.multiply(DenseMatrix.java:374) at com.baeldung.suanshu.SuanShuMath.multiplyIncorrectMatrices(SuanShuMath.java:98) at com.baeldung.suanshu.SuanShuMath.main(SuanShuMath.java:22)
4.3. Calculating A Matrix Inverse
Calculating the inverse of a matrix can be a lengthy process to do manually but the SuanShu math library makes it easy:
public void inverseMatrix() { Matrix m1 = new DenseMatrix(new double[][]{ {1, 2}, {3, 4} }); Inverse m2 = new Inverse(m1); log.info("Inverting a matrix: {}", m2); }
We can verify this using the SuanShu library but multiplying the matrix with its inverse: the result should be the identity matrix. We can do this by adding the following to the method above:
log.info("Verifying a matrix inverse: {}", m1.multiply(m2));
5. Solving Polynomials
One of the other areas the SuanShu provides support for is polynomials. It provides methods for evaluating a polynomial but also for finding its root (input values where the polynomial evaluates to 0).
5.1. Creating A Polynomial
A polynomial can be created by specifying its coefficients. So a polynomial like 3x2-5x+1 can be created with:
public Polynomial createPolynomial() { return new Polynomial(new double[]{3, -5, 1}); }
As we can see, we start with the coefficient for the highest degree first.
5.2. Evaluating A Polynomial
The evaluate() method can be used to evaluate the polynomial. This can be done for real and complex inputs.
public void evaluatePolynomial(Polynomial p) { log.info("Evaluating a polynomial using a real number: {}", p.evaluate(5)); log.info("Evaluating a polynomial using a complex number: {}", p.evaluate(new Complex(1, 2))); }
The output we’ll see is:
51.0 -13.000000+2.000000i
5.3. Finding A Polynomial’s Roots
Finding a polynomial’s roots is made easy by the SuanShu math library. It provides well-known algorithms to determine the roots for polynomials of various degrees and based on the highest degree of the polynomial, the PolyRoot class chooses the best method:
public void solvePolynomial() { Polynomial p = new Polynomial(new double[]{2, 2, -4}); PolyRootSolver solver = new PolyRoot(); List<? extends Number> roots = solver.solve(p); log.info("Finding polynomial roots: {}", roots); }
The output:
[-2.0, 1.0]
So 2 real roots were found for this sample polynomial: -2 and 1. Naturally, complex roots are supported as well.
6. Conclusion
This article is just a short introduction to the SuanShu math library.
As always, the full source code of the article is available over on GitHub.