Matrices & Linear Algebra
While it is always possible to use the underlying container classes directly
(see Modules for more information on backends), Koma provides a set of
top-level convenience functions to make the experience more similar to other scientific
environments. All these functions reside in the koma
namespace and use the underlying
object-oriented hierarchy internally.
When working with matrices in Koma you'll almost always want to
import koma.extensions.*
This namespace defines some extension functions which would ordinarily be defined on Matrix
itself but can't be for performance reasons. Since there are only Matrix
extension functions defined in koma.extensions
, it should be safe to star import it without polluting your workspace. If you don't import koma.extensions
you'll be missing a lot of functionality including basic things like getters and setters.
Creating Matrices
Koma provides several functions for creating new matrices:
// x is a 3x3 identity matrix var x = eye(3) // x is a 3x3 matrix of zeros x = zeros(3,3) // x is a 3x3 with Gaussian random noise, distribution 0,1 x = randn(3,3) // x is a 3x3 with uniform random noise x = rand(3,3) // x is a 4x5 matrix where the ith row and jth column has the value i+j*2 x = fill(4,5) { row, col -> row+col*2.0 } val someData = arrayOf(doubleArrayOf(1.0,2.0,3.0), doubleArrayOf(4.0,5.0,6.0)) // x is a 2x3 matrix constructed from the data in a java array of arrays x = create(someData)
See here for a complete list of available creators. There is also a DSL for matrix literals:
// a is a 2x3 matrix filled with the numbers passed in val a = mat[1,2,3.3 end 4,5,6]
Math Functions
Matrices have useful map functions that return matrices for chaining operations (see here for a complete list).
// Create a 3x3 identity and then add 0.1 to all elements val x = eye(3) + 0.1 // Map each element through a function that adds .01 val y = x.map { it + .01 } // Map each element through a function that adds or subtracts depending on the element index val z = x.mapIndexed { row, col, ele -> if (row > col) ele + 1 else ele - 1 } // Are there any elements greater than 1? val hasGreater = x.any { it > 1 } // Are all elements greater than 1? val allGreater = x.all { it > 1 } // Print all the elements in row-order x.forEach { println(it) } // Print all the elements including their column number x.forEachIndexed { row, col, ele -> println("$col $it") } // Apply a function to a row at a time and store the outputs in a contiguous matrix val sins = x.mapRows { row -> sin(row) } // Print all elements greater than 1 x.each { if (it>1) println(it) }
We can also do some linear algebra:
// Matrix literal syntax, see creators.kt for // convenience functions like zeros(5,5) var A = mat[1,0,0 end 0,3,0 end 0,0,4] // Calculate the matrix inverse var Ainv = A.inv() var b = mat[2,2,4].T // Use overloaded operators: // * is matrix multiplication var c = A*b + 1 // emul is element-wise multiplication var d = (A emul A) + 1 // Number of decimals to show format("short") println(c)
Which produces:
Output: mat[ 3.00 end 7.00 end 17.00 ]
Many special functions are supported (see the matrix interface for a complete list):
val a = 2*eye(3)+.01 // eye is identity matrix a.chol() // Cholesky decomposition a.det() // Determinant a.diag() // Diagonal vector a.inv() // Matrix inverse a.norm() // Matrix norm
Scalar functions can be applied elementwise to matrices (see here for a complete list):
val x = create(0..100)/5.0 // Matrix of 0, 1/5, 2/5, ... val y = sin(x) // Sin applied elementwise plot(y) // Plot of sin function
Matrix indexing and slicing is supported (see here for a list of operators as well as the Matrix
val x = randn(5,5) val y = x[0,0..4] // Grab the first row x[0..2,0..3] = zeros(3,4) // Set the upper-left 3x4 sub-matrix of x to zero
A Matrix is convertible to an Iterable:
val x = randn(5,5).toIterable() // Adds all elements and returns sum x.reduce { x, y -> x+y } // Returns list of all elements greater than 4 x.find { it > 4 }