Please visit Scala Linear Algebra & Statistics Hacks to benefit from the most current version of the library.
This pure scala Matrix library provides matrix math capabilities to Scala JVM, Scala Native, and Scala.js. You may try the demo directly in the browser.
This matrix library differs most significantly from others like JAMA and Apache Commons Math, by providing compile time dimensionality checks. Instead of encoding matrix row and column dimensions with method parameters or Array[Double].length
values, this library relies on dependent types. For example:
// create an 3 x 2 matrix of zeros.
val m:Matrix[3, 2] = Matrix.zeros[3, 2]
By encoding the matrix's row and column dimensions into its type, the compiler can prevent a whole category of runtime errors that arise from mismatched matrix dimensions:
// create an 3 x 2 matrix of zeros.
val m0:Matrix[3, 2] = Matrix.zeros[3, 2]
val m1:Matrix[2, 3] = Matrix.zeros[2, 3]
val m2:Matrix[3, 3] = m0 * m1
val m = m2 * m1 // compiler error!
Relatedly, many matrix operations like determinant
, Cholesky decomposition, etc, only pertain to square matrices. This library relies on type conditioned extension methods so that users simply cannot attempt to invoke these operations on rectangular matrices. More specifically:
extension [MN <: Int] (m: Matrix[MN, MN])(using ValueOf[MN]) {
def determinant: Double = LU[MN, MN](m).determinant
}
Instead of including a determinant
method directly in the Matrix
class, this extension method makes a determinant
method available only for square matrices. Trying to invoke the determinant
method on a rectangular metrix, for which M != N, will yield a compiler error.
- Matrix math:
- multiplication for Matrix * Matrix, Matrix * Vector, and Scalar * Matrix
- element wise operations: add, subtract, multiply, and divide
- sub-matrix, column, row, and element operations: get, set
- determinant
- transpose
- inverse
- norm operations: one, two, infinity, and Frobenius
- decompositions: Cholesky, Eigen, LU, QR, and Singular Value
- In memory data sets: unsupervised and supervised
- Linear Regression based on both QR Decomposition and Singular Value Decomposition.
- Principal Components Analysis
libraryDependencies += "ai.dragonfly" %%% "matrix" % "<LATEST_VERSION>"
Because matrix relies on NArr, JavaScript environments store matrix data as:
var matrixArray:NArray[NArray[Double]]
which is equivalent to:
var matrixArray:js.Array[Float64Array]
In JVM and Native environments, matrix data occupies normal scala Array[Double]
.
Although it began as a 1:1 port of JAMA for Scala 3 and Scala.js projects, it has expanded to include features that make matrix operations more comfortable in idiomatic Scala. Past versions of this library JAMA from the maven repository on the JVM side, and provided facades for a JavaScript version of JAMA ported through Jsweet and included through the scalajs-bundler sbt plugin. As scalajs-bundler has slipped into an unmaintained status, this library evolved into a pure scala port of JAMA and has begun to take its own shape.
This implementation of JAMA excludes test and I/O functionality as well as some constructors that comments in the original JAMA library describe as dangerous and unnecessary.
See the verification subproject of this repository to evaluate the fidelity of this port from Java to Scala. Given the original JaMa implementation of hypot, these two matrix libraries produce identical output, however, modern Java includes a more advanced implementation of the hypot function and using it produces tiny discrepancies between Jama and this scala implementation.