I’m trying to find the inverse of a matrix using the Apache Commons Math Library.
Below is my attempt at doing just that:
BigReal[][] leftMatrixData = new BigReal[][] { { new BigReal(1), new BigReal(0), new BigReal(0), new BigReal(0) }, { new BigReal(1), new BigReal(0), new BigReal(1), new BigReal(0) }, { new BigReal(1), new BigReal(1), new BigReal(0), new BigReal(0) }, { new BigReal(1), new BigReal(1), new BigReal(1), new BigReal(1) }, }; FieldMatrix<BigReal> leftMatrix = MatrixUtils.createFieldMatrix(leftMatrixData); FieldMatrix<BigReal> leftMatrixInverse = new FieldLUDecomposition<>(leftMatrix) .getSolver() .getInverse();
When I run this, I get the following error:
org.apache.commons.math3.exception.MathArithmeticException: zero not allowed here at org.apache.commons.math3.util.BigReal.divide(BigReal.java:255) at org.apache.commons.math3.util.BigReal.divide(BigReal.java:39) at org.apache.commons.math3.linear.FieldLUDecomposition.<init>(FieldLUDecomposition.java:160)
When I go to line 160 of FieldLUDecomposition.java
per the above error message, I see that the library thinks this matrix is Singular i.e. it thinks it has no inverse:
public T getDeterminant() { if (this.singular) { <---- this is line 160 return (FieldElement)this.field.getZero(); } else { int m = this.pivot.length; T determinant = this.even ? (FieldElement)this.field.getOne() : (FieldElement)((FieldElement)this.field.getZero()).subtract(this.field.getOne());
However, doing a quick check on WolframAlpha shows that this matrix has a non-zero determinant and indeed has an inverse:
So the question is – what am I doing wrong and how do I find the inverse of my matrix? Am I using the wrong solver?
Advertisement
Answer
Below is based on apache common math 3.6.1
A ticket is raised concerning this issue, I submitted a patch to fix the problem and the fix version will be 4.0 (not yet released as of 2021-07-19)
The reason of issue is equals
method in BigReal
@Override public boolean equals(Object other) { if (this == other){ return true; } if (other instanceof BigReal){ return d.equals(((BigReal) other).d); } return false; }
where d is BigDecimal
, the backing value of BigReal
. This cause equals comparison return undesired result when two BigReal
has d
with same value but different scale, and cause error when initializing FieldLUDecomposition
. For BigDecimal
we should check
return d.compareTo((BigReal) other) == 0;
instead.
Solution:
- Check if workaround section(Copy
BigReal
as local class and changeequals
) helps. - Wait for version 4.0 release.
- If double value matrix is acceptable,
Use
RealMatrix
instead, andMatrixUtils
provide handyinverse
method
import org.apache.commons.math3.linear.MatrixUtils; import org.apache.commons.math3.linear.RealMatrix; public class CalculateInverse { public static void main(String[] args) { double[][] matrixData = new double[][]{ {1, 0, 0, 0}, {1, 0, 1, 0}, {1, 1, 0, 0}, {1, 1, 1, 1} }; RealMatrix matrix = MatrixUtils.createRealMatrix(matrixData); RealMatrix inverse = MatrixUtils.inverse(matrix); System.out.println(inverse); } }