Deprecated Vectors. Working on LUP optimization (not working yet)
This commit is contained in:
parent
98bb72a6a0
commit
f1b1010c4d
@ -1,10 +1,13 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
import koma.matrix.ejml.EJMLMatrixFactory
|
import koma.matrix.ejml.EJMLMatrixFactory
|
||||||
|
import scientifik.kmath.operations.RealField
|
||||||
import scientifik.kmath.structures.Matrix
|
import scientifik.kmath.structures.Matrix
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
@ExperimentalContracts
|
||||||
fun main() {
|
fun main() {
|
||||||
val random = Random(12224)
|
val random = Random(12224)
|
||||||
val dim = 100
|
val dim = 100
|
||||||
@ -32,10 +35,8 @@ fun main() {
|
|||||||
|
|
||||||
//commons-math
|
//commons-math
|
||||||
|
|
||||||
val cmContext = CMLUPSolver
|
|
||||||
|
|
||||||
val commonsTime = measureTimeMillis {
|
val commonsTime = measureTimeMillis {
|
||||||
cmContext.run {
|
CMMatrixContext.run {
|
||||||
val cm = matrix.toCM() //avoid overhead on conversion
|
val cm = matrix.toCM() //avoid overhead on conversion
|
||||||
repeat(n) {
|
repeat(n) {
|
||||||
val res = inverse(cm)
|
val res = inverse(cm)
|
||||||
@ -48,10 +49,8 @@ fun main() {
|
|||||||
|
|
||||||
//koma-ejml
|
//koma-ejml
|
||||||
|
|
||||||
val komaContext = KomaMatrixContext(EJMLMatrixFactory())
|
|
||||||
|
|
||||||
val komaTime = measureTimeMillis {
|
val komaTime = measureTimeMillis {
|
||||||
komaContext.run {
|
KomaMatrixContext(EJMLMatrixFactory(), RealField).run {
|
||||||
val km = matrix.toKoma() //avoid overhead on conversion
|
val km = matrix.toKoma() //avoid overhead on conversion
|
||||||
repeat(n) {
|
repeat(n) {
|
||||||
val res = inverse(km)
|
val res = inverse(km)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
import koma.matrix.ejml.EJMLMatrixFactory
|
import koma.matrix.ejml.EJMLMatrixFactory
|
||||||
|
import scientifik.kmath.operations.RealField
|
||||||
import scientifik.kmath.structures.Matrix
|
import scientifik.kmath.structures.Matrix
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
@ -27,7 +28,7 @@ fun main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
KomaMatrixContext(EJMLMatrixFactory()).run {
|
KomaMatrixContext(EJMLMatrixFactory(), RealField).run {
|
||||||
val komaMatrix1 = matrix1.toKoma()
|
val komaMatrix1 = matrix1.toKoma()
|
||||||
val komaMatrix2 = matrix2.toKoma()
|
val komaMatrix2 = matrix2.toKoma()
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ fun main() {
|
|||||||
|
|
||||||
val complexTime = measureTimeMillis {
|
val complexTime = measureTimeMillis {
|
||||||
complexField.run {
|
complexField.run {
|
||||||
var res: ComplexNDElement = one
|
var res = one
|
||||||
repeat(n) {
|
repeat(n) {
|
||||||
res += 1.0
|
res += 1.0
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import com.moowork.gradle.node.NodeExtension
|
import com.moowork.gradle.node.NodeExtension
|
||||||
import com.moowork.gradle.node.npm.NpmTask
|
import com.moowork.gradle.node.npm.NpmTask
|
||||||
import com.moowork.gradle.node.task.NodeTask
|
import com.moowork.gradle.node.task.NodeTask
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
val kotlinVersion: String by rootProject.extra("1.3.21")
|
val kotlinVersion: String by rootProject.extra("1.3.21")
|
||||||
@ -194,6 +191,8 @@ subprojects {
|
|||||||
sourceSets.all {
|
sourceSets.all {
|
||||||
languageSettings.progressiveMode = true
|
languageSettings.progressiveMode = true
|
||||||
languageSettings.enableLanguageFeature("InlineClasses")
|
languageSettings.enableLanguageFeature("InlineClasses")
|
||||||
|
languageSettings.useExperimentalAnnotation("ExperimentalContracts")
|
||||||
|
//languageSettings.enableLanguageFeature("Contracts")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1,19 @@
|
|||||||
**TODO**
|
## Basic linear algebra layout
|
||||||
|
|
||||||
|
Kmath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared
|
||||||
|
in context classes, and are not the members of classes that store data. This allows more flexible approach to maintain multiple
|
||||||
|
back-ends. The new operations added as extensions to contexts instead of being member functions of data structures.
|
||||||
|
|
||||||
|
Two major contexts used for linear algebra and hyper-geometry:
|
||||||
|
|
||||||
|
* `VectorSpace` forms a mathematical space on top of array-like structure (`Buffer` and its typealias `Point` used for geometry).
|
||||||
|
|
||||||
|
* `MatrixContext` forms a space-like context for 2d-structures. It does not store matrix size and therefore does not implement
|
||||||
|
`Space` interface (it is not possible to create zero element without knowing the matrix size).
|
||||||
|
|
||||||
|
## Vector spaces
|
||||||
|
|
||||||
|
|
||||||
|
## Matrix operations
|
||||||
|
|
||||||
|
## Back-end overview
|
@ -27,7 +27,7 @@ fun Matrix<Double>.toCM(): CMMatrix = if (this is CMMatrix) {
|
|||||||
CMMatrix(Array2DRowRealMatrix(array))
|
CMMatrix(Array2DRowRealMatrix(array))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RealMatrix.toMatrix() = CMMatrix(this)
|
fun RealMatrix.asMatrix() = CMMatrix(this)
|
||||||
|
|
||||||
class CMVector(val origin: RealVector) : Point<Double> {
|
class CMVector(val origin: RealVector) : Point<Double> {
|
||||||
override val size: Int get() = origin.dimension
|
override val size: Int get() = origin.dimension
|
||||||
@ -47,7 +47,6 @@ fun Point<Double>.toCM(): CMVector = if (this is CMVector) {
|
|||||||
fun RealVector.toPoint() = CMVector(this)
|
fun RealVector.toPoint() = CMVector(this)
|
||||||
|
|
||||||
object CMMatrixContext : MatrixContext<Double> {
|
object CMMatrixContext : MatrixContext<Double> {
|
||||||
|
|
||||||
override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): CMMatrix {
|
override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): CMMatrix {
|
||||||
val array = Array(rows) { i -> DoubleArray(columns) { j -> initializer(i, j) } }
|
val array = Array(rows) { i -> DoubleArray(columns) { j -> initializer(i, j) } }
|
||||||
return CMMatrix(Array2DRowRealMatrix(array))
|
return CMMatrix(Array2DRowRealMatrix(array))
|
||||||
@ -59,35 +58,19 @@ object CMMatrixContext : MatrixContext<Double> {
|
|||||||
override fun Matrix<Double>.dot(vector: Point<Double>): CMVector =
|
override fun Matrix<Double>.dot(vector: Point<Double>): CMVector =
|
||||||
CMVector(this.toCM().origin.preMultiply(vector.toCM().origin))
|
CMVector(this.toCM().origin.preMultiply(vector.toCM().origin))
|
||||||
|
|
||||||
|
|
||||||
override fun Matrix<Double>.unaryMinus(): CMMatrix =
|
override fun Matrix<Double>.unaryMinus(): CMMatrix =
|
||||||
produce(rowNum, colNum) { i, j -> -get(i, j) }
|
produce(rowNum, colNum) { i, j -> -get(i, j) }
|
||||||
|
|
||||||
override fun Matrix<Double>.plus(b: Matrix<Double>) =
|
override fun add(a: Matrix<Double>, b: Matrix<Double>) =
|
||||||
CMMatrix(this.toCM().origin.multiply(b.toCM().origin))
|
CMMatrix(a.toCM().origin.multiply(b.toCM().origin))
|
||||||
|
|
||||||
override fun Matrix<Double>.minus(b: Matrix<Double>) =
|
override fun Matrix<Double>.minus(b: Matrix<Double>) =
|
||||||
CMMatrix(this.toCM().origin.subtract(b.toCM().origin))
|
CMMatrix(this.toCM().origin.subtract(b.toCM().origin))
|
||||||
|
|
||||||
override fun Matrix<Double>.times(value: Double) =
|
override fun multiply(a: Matrix<Double>, k: Number) =
|
||||||
CMMatrix(this.toCM().origin.scalarMultiply(value.toDouble()))
|
CMMatrix(a.toCM().origin.scalarMultiply(k.toDouble()))
|
||||||
}
|
|
||||||
|
|
||||||
object CMLUPSolver: LinearSolver<Double>{
|
override fun Matrix<Double>.times(value: Double): Matrix<Double> = produce(rowNum,colNum){i,j-> get(i,j)*value}
|
||||||
override fun solve(a: Matrix<Double>, b: Matrix<Double>): CMMatrix {
|
|
||||||
val decomposition = LUDecomposition(a.toCM().origin)
|
|
||||||
return decomposition.solver.solve(b.toCM().origin).toMatrix()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun solve(a: Matrix<Double>, b: Point<Double>): CMVector {
|
|
||||||
val decomposition = LUDecomposition(a.toCM().origin)
|
|
||||||
return decomposition.solver.solve(b.toCM().origin).toPoint()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun inverse(a: Matrix<Double>): CMMatrix {
|
|
||||||
val decomposition = LUDecomposition(a.toCM().origin)
|
|
||||||
return decomposition.solver.inverse.toMatrix()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CMMatrix(this.origin.add(other.origin))
|
operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CMMatrix(this.origin.add(other.origin))
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.*
|
||||||
|
import scientifik.kmath.structures.Matrix
|
||||||
|
|
||||||
|
enum class CMDecomposition {
|
||||||
|
LUP,
|
||||||
|
QR,
|
||||||
|
RRQR,
|
||||||
|
EIGEN,
|
||||||
|
CHOLESKY
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun CMMatrixContext.solver(a: Matrix<Double>, decomposition: CMDecomposition = CMDecomposition.LUP) =
|
||||||
|
when (decomposition) {
|
||||||
|
CMDecomposition.LUP -> LUDecomposition(a.toCM().origin).solver
|
||||||
|
CMDecomposition.RRQR -> RRQRDecomposition(a.toCM().origin).solver
|
||||||
|
CMDecomposition.QR -> QRDecomposition(a.toCM().origin).solver
|
||||||
|
CMDecomposition.EIGEN -> EigenDecomposition(a.toCM().origin).solver
|
||||||
|
CMDecomposition.CHOLESKY -> CholeskyDecomposition(a.toCM().origin).solver
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CMMatrixContext.solve(
|
||||||
|
a: Matrix<Double>,
|
||||||
|
b: Matrix<Double>,
|
||||||
|
decomposition: CMDecomposition = CMDecomposition.LUP
|
||||||
|
) = solver(a, decomposition).solve(b.toCM().origin).asMatrix()
|
||||||
|
|
||||||
|
fun CMMatrixContext.solve(
|
||||||
|
a: Matrix<Double>,
|
||||||
|
b: Point<Double>,
|
||||||
|
decomposition: CMDecomposition = CMDecomposition.LUP
|
||||||
|
) = solver(a, decomposition).solve(b.toCM().origin).toPoint()
|
||||||
|
|
||||||
|
fun CMMatrixContext.inverse(
|
||||||
|
a: Matrix<Double>,
|
||||||
|
decomposition: CMDecomposition = CMDecomposition.LUP
|
||||||
|
) = solver(a, decomposition).inverse.asMatrix()
|
@ -1,6 +1,5 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
import scientifik.kmath.operations.RealField
|
|
||||||
import scientifik.kmath.operations.Ring
|
import scientifik.kmath.operations.Ring
|
||||||
import scientifik.kmath.structures.*
|
import scientifik.kmath.structures.*
|
||||||
|
|
||||||
|
@ -7,109 +7,6 @@ import scientifik.kmath.structures.*
|
|||||||
import scientifik.kmath.structures.Buffer.Companion.boxing
|
import scientifik.kmath.structures.Buffer.Companion.boxing
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic operations on matrices. Operates on [Matrix]
|
|
||||||
*/
|
|
||||||
interface MatrixContext<T : Any> {
|
|
||||||
/**
|
|
||||||
* Produce a matrix with this context and given dimensions
|
|
||||||
*/
|
|
||||||
fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): Matrix<T>
|
|
||||||
|
|
||||||
infix fun Matrix<T>.dot(other: Matrix<T>): Matrix<T>
|
|
||||||
|
|
||||||
infix fun Matrix<T>.dot(vector: Point<T>): Point<T>
|
|
||||||
|
|
||||||
operator fun Matrix<T>.unaryMinus(): Matrix<T>
|
|
||||||
|
|
||||||
operator fun Matrix<T>.plus(b: Matrix<T>): Matrix<T>
|
|
||||||
|
|
||||||
operator fun Matrix<T>.minus(b: Matrix<T>): Matrix<T>
|
|
||||||
|
|
||||||
operator fun Matrix<T>.times(value: T): Matrix<T>
|
|
||||||
|
|
||||||
operator fun T.times(m: Matrix<T>): Matrix<T> = m * this
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Non-boxing double matrix
|
|
||||||
*/
|
|
||||||
val real = BufferMatrixContext(RealField, Buffer.Companion::auto)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A structured matrix with custom buffer
|
|
||||||
*/
|
|
||||||
fun <T : Any, R : Ring<T>> buffered(
|
|
||||||
ring: R,
|
|
||||||
bufferFactory: BufferFactory<T> = ::boxing
|
|
||||||
): GenericMatrixContext<T, R> =
|
|
||||||
BufferMatrixContext(ring, bufferFactory)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatic buffered matrix, unboxed if it is possible
|
|
||||||
*/
|
|
||||||
inline fun <reified T : Any, R : Ring<T>> auto(ring: R): GenericMatrixContext<T, R> =
|
|
||||||
buffered(ring, Buffer.Companion::auto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GenericMatrixContext<T : Any, R : Ring<T>> : MatrixContext<T> {
|
|
||||||
/**
|
|
||||||
* The ring context for matrix elements
|
|
||||||
*/
|
|
||||||
val elementContext: R
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce a point compatible with matrix space
|
|
||||||
*/
|
|
||||||
fun point(size: Int, initializer: (Int) -> T): Point<T>
|
|
||||||
|
|
||||||
override infix fun Matrix<T>.dot(other: Matrix<T>): Matrix<T> {
|
|
||||||
//TODO add typed error
|
|
||||||
if (this.colNum != other.rowNum) error("Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})")
|
|
||||||
return produce(rowNum, other.colNum) { i, j ->
|
|
||||||
val row = rows[i]
|
|
||||||
val column = other.columns[j]
|
|
||||||
with(elementContext) {
|
|
||||||
sum(row.asSequence().zip(column.asSequence(), ::multiply))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override infix fun Matrix<T>.dot(vector: Point<T>): Point<T> {
|
|
||||||
//TODO add typed error
|
|
||||||
if (this.colNum != vector.size) error("Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})")
|
|
||||||
return point(rowNum) { i ->
|
|
||||||
val row = rows[i]
|
|
||||||
with(elementContext) {
|
|
||||||
sum(row.asSequence().zip(vector.asSequence(), ::multiply))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override operator fun Matrix<T>.unaryMinus() =
|
|
||||||
produce(rowNum, colNum) { i, j -> elementContext.run { -get(i, j) } }
|
|
||||||
|
|
||||||
override operator fun Matrix<T>.plus(b: Matrix<T>): Matrix<T> {
|
|
||||||
if (rowNum != b.rowNum || colNum != b.colNum) error("Matrix operation dimension mismatch. [$rowNum,$colNum] + [${b.rowNum},${b.colNum}]")
|
|
||||||
return produce(rowNum, colNum) { i, j -> elementContext.run { get(i, j) + b[i, j] } }
|
|
||||||
}
|
|
||||||
|
|
||||||
override operator fun Matrix<T>.minus(b: Matrix<T>): Matrix<T> {
|
|
||||||
if (rowNum != b.rowNum || colNum != b.colNum) error("Matrix operation dimension mismatch. [$rowNum,$colNum] - [${b.rowNum},${b.colNum}]")
|
|
||||||
return produce(rowNum, colNum) { i, j -> elementContext.run { get(i, j) + b[i, j] } }
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun Matrix<T>.times(number: Number): Matrix<T> =
|
|
||||||
produce(rowNum, colNum) { i, j -> elementContext.run { get(i, j) * number } }
|
|
||||||
|
|
||||||
operator fun Number.times(matrix: FeaturedMatrix<T>): Matrix<T> = matrix * this
|
|
||||||
|
|
||||||
override fun Matrix<T>.times(value: T): Matrix<T> =
|
|
||||||
produce(rowNum, colNum) { i, j -> elementContext.run { get(i, j) * value } }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 2d structure plus optional matrix-specific features
|
* A 2d structure plus optional matrix-specific features
|
||||||
*/
|
*/
|
||||||
|
@ -4,6 +4,9 @@ import scientifik.kmath.operations.Field
|
|||||||
import scientifik.kmath.operations.RealField
|
import scientifik.kmath.operations.RealField
|
||||||
import scientifik.kmath.operations.Ring
|
import scientifik.kmath.operations.Ring
|
||||||
import scientifik.kmath.structures.*
|
import scientifik.kmath.structures.*
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,7 +66,12 @@ class LUPDecomposition<T : Any>(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open class BufferAccessor<T : Any>(val type: KClass<T>, val field: Field<T>, val rowNum: Int, val colNum: Int) {
|
internal open class BufferAccessor<T : Any>(
|
||||||
|
val type: KClass<T>,
|
||||||
|
val field: Field<T>,
|
||||||
|
val rowNum: Int,
|
||||||
|
val colNum: Int
|
||||||
|
) {
|
||||||
open operator fun MutableBuffer<T>.get(i: Int, j: Int) = get(i + colNum * j)
|
open operator fun MutableBuffer<T>.get(i: Int, j: Int) = get(i + colNum * j)
|
||||||
open operator fun MutableBuffer<T>.set(i: Int, j: Int, value: T) {
|
open operator fun MutableBuffer<T>.set(i: Int, j: Int, value: T) {
|
||||||
set(i + colNum * j, value)
|
set(i + colNum * j, value)
|
||||||
@ -102,7 +110,8 @@ open class BufferAccessor<T : Any>(val type: KClass<T>, val field: Field<T>, val
|
|||||||
/**
|
/**
|
||||||
* Specialized LU operations for Doubles
|
* Specialized LU operations for Doubles
|
||||||
*/
|
*/
|
||||||
class RealBufferAccessor(rowNum: Int, colNum: Int) : BufferAccessor<Double>(Double::class, RealField, rowNum, colNum) {
|
private class RealBufferAccessor(rowNum: Int, colNum: Int) :
|
||||||
|
BufferAccessor<Double>(Double::class, RealField, rowNum, colNum) {
|
||||||
override fun MutableBuffer<Double>.get(i: Int, j: Int) = (this as DoubleBuffer).array[i + colNum * j]
|
override fun MutableBuffer<Double>.get(i: Int, j: Int) = (this as DoubleBuffer).array[i + colNum * j]
|
||||||
override fun MutableBuffer<Double>.set(i: Int, j: Int, value: Double) {
|
override fun MutableBuffer<Double>.set(i: Int, j: Int, value: Double) {
|
||||||
(this as DoubleBuffer).array[i + colNum * j] = value
|
(this as DoubleBuffer).array[i + colNum * j] = value
|
||||||
@ -125,24 +134,33 @@ class RealBufferAccessor(rowNum: Int, colNum: Int) : BufferAccessor<Double>(Doub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.buildAccessor(
|
@ExperimentalContracts
|
||||||
type:KClass<T>,
|
private inline fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.withAccessor(
|
||||||
|
type: KClass<T>,
|
||||||
rowNum: Int,
|
rowNum: Int,
|
||||||
colNum: Int
|
colNum: Int,
|
||||||
): BufferAccessor<T> {
|
block: BufferAccessor<T>.() -> Unit
|
||||||
return if (elementContext == RealField) {
|
) {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
if (elementContext == RealField) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
RealBufferAccessor(rowNum, colNum) as BufferAccessor<T>
|
RealBufferAccessor(rowNum, colNum) as BufferAccessor<T>
|
||||||
} else {
|
} else {
|
||||||
BufferAccessor(type, elementContext, rowNum, colNum)
|
BufferAccessor(type, elementContext, rowNum, colNum)
|
||||||
}
|
}.run(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.abs(value: T) =
|
private fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.abs(value: T) =
|
||||||
if (value > elementContext.zero) value else with(elementContext) { -value }
|
if (value > elementContext.zero) value else with(elementContext) { -value }
|
||||||
|
|
||||||
|
|
||||||
fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lupDecompose(
|
/**
|
||||||
|
* Create a lup decomposition of generic matrix
|
||||||
|
*/
|
||||||
|
@ExperimentalContracts
|
||||||
|
fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lup(
|
||||||
type: KClass<T>,
|
type: KClass<T>,
|
||||||
matrix: Matrix<T>,
|
matrix: Matrix<T>,
|
||||||
checkSingular: (T) -> Boolean
|
checkSingular: (T) -> Boolean
|
||||||
@ -155,7 +173,7 @@ fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lupDecompose(
|
|||||||
val m = matrix.colNum
|
val m = matrix.colNum
|
||||||
val pivot = IntArray(matrix.rowNum)
|
val pivot = IntArray(matrix.rowNum)
|
||||||
|
|
||||||
buildAccessor(type, matrix.rowNum, matrix.colNum).run {
|
withAccessor(type, matrix.rowNum, matrix.colNum) {
|
||||||
|
|
||||||
val lu = create(matrix)
|
val lu = create(matrix)
|
||||||
|
|
||||||
@ -170,21 +188,12 @@ fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lupDecompose(
|
|||||||
|
|
||||||
// upper
|
// upper
|
||||||
for (row in 0 until col) {
|
for (row in 0 until col) {
|
||||||
// var sum = lu[row, col]
|
|
||||||
// for (i in 0 until row) {
|
|
||||||
// sum -= lu[row, i] * lu[i, col]
|
|
||||||
// }
|
|
||||||
val sum = lu.innerProduct(row, col, row)
|
val sum = lu.innerProduct(row, col, row)
|
||||||
lu[row, col] = field.run { lu[row, col] - sum }
|
lu[row, col] = field.run { lu[row, col] - sum }
|
||||||
}
|
}
|
||||||
|
|
||||||
// lower
|
// lower
|
||||||
val max = (col until m).maxBy { row ->
|
val max = (col until m).maxBy { row ->
|
||||||
// var sum = lu[row, col]
|
|
||||||
// for (i in 0 until col) {
|
|
||||||
// sum -= lu[row, i] * lu[i, col]
|
|
||||||
// }
|
|
||||||
// lu[row, col] = sum
|
|
||||||
val sum = lu.innerProduct(row, col, col)
|
val sum = lu.innerProduct(row, col, col)
|
||||||
lu[row, col] = field.run { lu[row, col] - sum }
|
lu[row, col] = field.run { lu[row, col] - sum }
|
||||||
abs(sum)
|
abs(sum)
|
||||||
@ -214,14 +223,17 @@ fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lupDecompose(
|
|||||||
//lu[row, col] = lu[row, col] / luDiag
|
//lu[row, col] = lu[row, col] / luDiag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return scientifik.kmath.linear.LUPDecomposition(elementContext, lu.collect(), pivot, even)
|
return LUPDecomposition(elementContext, lu.collect(), pivot, even)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalContracts
|
||||||
|
fun GenericMatrixContext<Double, RealField>.lup(matrix: Matrix<Double>) = lup(Double::class, matrix) { it < 1e-11 }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Solve a linear equation **a*x = b**
|
* Solve a linear equation **a*x = b**
|
||||||
*/
|
*/
|
||||||
|
@ExperimentalContracts
|
||||||
fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.solve(
|
fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.solve(
|
||||||
type: KClass<T>,
|
type: KClass<T>,
|
||||||
a: Matrix<T>,
|
a: Matrix<T>,
|
||||||
@ -233,9 +245,9 @@ fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.solve(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use existing decomposition if it is provided by matrix
|
// Use existing decomposition if it is provided by matrix
|
||||||
val decomposition = a.getFeature() ?: lupDecompose(type, a, checkSingular)
|
val decomposition = a.getFeature() ?: lup(type, a, checkSingular)
|
||||||
|
|
||||||
buildAccessor(type, a.rowNum, a.colNum).run {
|
withAccessor(type, a.rowNum, a.colNum) {
|
||||||
|
|
||||||
val lu = create(decomposition.lu)
|
val lu = create(decomposition.lu)
|
||||||
|
|
||||||
@ -271,14 +283,19 @@ fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.solve(
|
|||||||
|
|
||||||
return produce(a.rowNum, a.colNum) { i, j -> bp[i, j] }
|
return produce(a.rowNum, a.colNum) { i, j -> bp[i, j] }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalContracts
|
||||||
|
fun GenericMatrixContext<Double, RealField>.solve(a: Matrix<Double>, b: Matrix<Double>) =
|
||||||
|
solve(Double::class, a, b) { it < 1e-11 }
|
||||||
|
|
||||||
|
@ExperimentalContracts
|
||||||
inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.inverse(
|
inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.inverse(
|
||||||
matrix: Matrix<T>,
|
matrix: Matrix<T>,
|
||||||
noinline checkSingular: (T) -> Boolean
|
noinline checkSingular: (T) -> Boolean
|
||||||
) =
|
) =
|
||||||
solve(T::class, matrix, one(matrix.rowNum, matrix.colNum), checkSingular)
|
solve(T::class, matrix, one(matrix.rowNum, matrix.colNum), checkSingular)
|
||||||
|
|
||||||
|
@ExperimentalContracts
|
||||||
fun GenericMatrixContext<Double, RealField>.inverse(matrix: Matrix<Double>) =
|
fun GenericMatrixContext<Double, RealField>.inverse(matrix: Matrix<Double>) =
|
||||||
inverse(matrix) { it < 1e-11 }
|
inverse(matrix) { it < 1e-11 }
|
@ -13,7 +13,7 @@ import scientifik.kmath.structures.asSequence
|
|||||||
*/
|
*/
|
||||||
interface LinearSolver<T : Any> {
|
interface LinearSolver<T : Any> {
|
||||||
fun solve(a: Matrix<T>, b: Matrix<T>): Matrix<T>
|
fun solve(a: Matrix<T>, b: Matrix<T>): Matrix<T>
|
||||||
fun solve(a: Matrix<T>, b: Point<T>): Point<T> = solve(a, b.toMatrix()).asPoint()
|
fun solve(a: Matrix<T>, b: Point<T>): Point<T> = solve(a, b.asMatrix()).asPoint()
|
||||||
fun inverse(a: Matrix<T>): Matrix<T>
|
fun inverse(a: Matrix<T>): Matrix<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,4 +43,4 @@ fun <T : Any> Matrix<T>.asPoint(): Point<T> =
|
|||||||
error("Can't convert matrix with more than one column to vector")
|
error("Can't convert matrix with more than one column to vector")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> Point<T>.toMatrix() = VirtualMatrix(size, 1) { i, _ -> get(i) }
|
fun <T : Any> Point<T>.asMatrix() = VirtualMatrix(size, 1) { i, _ -> get(i) }
|
@ -0,0 +1,106 @@
|
|||||||
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.RealField
|
||||||
|
import scientifik.kmath.operations.Ring
|
||||||
|
import scientifik.kmath.operations.SpaceOperations
|
||||||
|
import scientifik.kmath.operations.sum
|
||||||
|
import scientifik.kmath.structures.Buffer
|
||||||
|
import scientifik.kmath.structures.BufferFactory
|
||||||
|
import scientifik.kmath.structures.Matrix
|
||||||
|
import scientifik.kmath.structures.asSequence
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic operations on matrices. Operates on [Matrix]
|
||||||
|
*/
|
||||||
|
interface MatrixContext<T : Any> : SpaceOperations<Matrix<T>> {
|
||||||
|
/**
|
||||||
|
* Produce a matrix with this context and given dimensions
|
||||||
|
*/
|
||||||
|
fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): Matrix<T>
|
||||||
|
|
||||||
|
infix fun Matrix<T>.dot(other: Matrix<T>): Matrix<T>
|
||||||
|
|
||||||
|
infix fun Matrix<T>.dot(vector: Point<T>): Point<T>
|
||||||
|
|
||||||
|
operator fun Matrix<T>.times(value: T): Matrix<T>
|
||||||
|
|
||||||
|
operator fun T.times(m: Matrix<T>): Matrix<T> = m * this
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Non-boxing double matrix
|
||||||
|
*/
|
||||||
|
val real = BufferMatrixContext(RealField, Buffer.Companion::auto)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structured matrix with custom buffer
|
||||||
|
*/
|
||||||
|
fun <T : Any, R : Ring<T>> buffered(
|
||||||
|
ring: R,
|
||||||
|
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing
|
||||||
|
): GenericMatrixContext<T, R> =
|
||||||
|
BufferMatrixContext(ring, bufferFactory)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatic buffered matrix, unboxed if it is possible
|
||||||
|
*/
|
||||||
|
inline fun <reified T : Any, R : Ring<T>> auto(ring: R): GenericMatrixContext<T, R> =
|
||||||
|
buffered(ring, Buffer.Companion::auto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GenericMatrixContext<T : Any, R : Ring<T>> : MatrixContext<T> {
|
||||||
|
/**
|
||||||
|
* The ring context for matrix elements
|
||||||
|
*/
|
||||||
|
val elementContext: R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a point compatible with matrix space
|
||||||
|
*/
|
||||||
|
fun point(size: Int, initializer: (Int) -> T): Point<T>
|
||||||
|
|
||||||
|
override infix fun Matrix<T>.dot(other: Matrix<T>): Matrix<T> {
|
||||||
|
//TODO add typed error
|
||||||
|
if (this.colNum != other.rowNum) error("Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})")
|
||||||
|
return produce(rowNum, other.colNum) { i, j ->
|
||||||
|
val row = rows[i]
|
||||||
|
val column = other.columns[j]
|
||||||
|
with(elementContext) {
|
||||||
|
sum(row.asSequence().zip(column.asSequence(), ::multiply))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override infix fun Matrix<T>.dot(vector: Point<T>): Point<T> {
|
||||||
|
//TODO add typed error
|
||||||
|
if (this.colNum != vector.size) error("Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})")
|
||||||
|
return point(rowNum) { i ->
|
||||||
|
val row = rows[i]
|
||||||
|
with(elementContext) {
|
||||||
|
sum(row.asSequence().zip(vector.asSequence(), ::multiply))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun Matrix<T>.unaryMinus() =
|
||||||
|
produce(rowNum, colNum) { i, j -> elementContext.run { -get(i, j) } }
|
||||||
|
|
||||||
|
override fun add(a: Matrix<T>, b: Matrix<T>): Matrix<T> {
|
||||||
|
if (a.rowNum != b.rowNum || a.colNum != b.colNum) error("Matrix operation dimension mismatch. [${a.rowNum},${a.colNum}] + [${b.rowNum},${b.colNum}]")
|
||||||
|
return produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a.get(i, j) + b[i, j] } }
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun Matrix<T>.minus(b: Matrix<T>): Matrix<T> {
|
||||||
|
if (rowNum != b.rowNum || colNum != b.colNum) error("Matrix operation dimension mismatch. [$rowNum,$colNum] - [${b.rowNum},${b.colNum}]")
|
||||||
|
return produce(rowNum, colNum) { i, j -> elementContext.run { get(i, j) + b[i, j] } }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun multiply(a: Matrix<T>, k: Number): Matrix<T> =
|
||||||
|
produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a.get(i, j) * k } }
|
||||||
|
|
||||||
|
operator fun Number.times(matrix: FeaturedMatrix<T>): Matrix<T> = matrix * this
|
||||||
|
|
||||||
|
override fun Matrix<T>.times(value: T): Matrix<T> =
|
||||||
|
produce(rowNum, colNum) { i, j -> elementContext.run { get(i, j) * value } }
|
||||||
|
}
|
@ -4,68 +4,23 @@ import scientifik.kmath.operations.RealField
|
|||||||
import scientifik.kmath.operations.Space
|
import scientifik.kmath.operations.Space
|
||||||
import scientifik.kmath.operations.SpaceElement
|
import scientifik.kmath.operations.SpaceElement
|
||||||
import scientifik.kmath.structures.Buffer
|
import scientifik.kmath.structures.Buffer
|
||||||
import scientifik.kmath.structures.BufferFactory
|
|
||||||
import scientifik.kmath.structures.asSequence
|
import scientifik.kmath.structures.asSequence
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
typealias Point<T> = Buffer<T>
|
typealias Point<T> = Buffer<T>
|
||||||
|
|
||||||
/**
|
|
||||||
* A linear space for vectors.
|
|
||||||
* Could be used on any point-like structure
|
|
||||||
*/
|
|
||||||
interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> {
|
|
||||||
|
|
||||||
val size: Int
|
fun <T : Any, S : Space<T>> BufferVectorSpace<T, S>.produceElement(initializer: (Int) -> T): Vector<T, S> =
|
||||||
|
BufferVector(this, produce(initializer))
|
||||||
val space: S
|
|
||||||
|
|
||||||
fun produce(initializer: (Int) -> T): Point<T>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce a space-element of this vector space for expressions
|
|
||||||
*/
|
|
||||||
fun produceElement(initializer: (Int) -> T): Vector<T, S>
|
|
||||||
|
|
||||||
override val zero: Point<T> get() = produce { space.zero }
|
|
||||||
|
|
||||||
override fun add(a: Point<T>, b: Point<T>): Point<T> = produce { with(space) { a[it] + b[it] } }
|
|
||||||
|
|
||||||
override fun multiply(a: Point<T>, k: Number): Point<T> = produce { with(space) { a[it] * k } }
|
|
||||||
|
|
||||||
//TODO add basis
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private val realSpaceCache = HashMap<Int, BufferVectorSpace<Double, RealField>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Non-boxing double vector space
|
|
||||||
*/
|
|
||||||
fun real(size: Int): BufferVectorSpace<Double, RealField> {
|
|
||||||
return realSpaceCache.getOrPut(size) { BufferVectorSpace(size, RealField, Buffer.Companion::auto) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A structured vector space with custom buffer
|
|
||||||
*/
|
|
||||||
fun <T : Any, S : Space<T>> buffered(
|
|
||||||
size: Int,
|
|
||||||
space: S,
|
|
||||||
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing
|
|
||||||
): VectorSpace<T, S> = BufferVectorSpace(size, space, bufferFactory)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatic buffered vector, unboxed if it is possible
|
|
||||||
*/
|
|
||||||
inline fun <reified T : Any, S : Space<T>> smart(size: Int, space: S): VectorSpace<T, S> =
|
|
||||||
buffered(size, space, Buffer.Companion::auto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@JvmName("produceRealElement")
|
||||||
|
fun BufferVectorSpace<Double, RealField>.produceElement(initializer: (Int) -> Double): Vector<Double, RealField> =
|
||||||
|
BufferVector(this, produce(initializer))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A point coupled to the linear space
|
* A point coupled to the linear space
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Use VectorContext instead")
|
||||||
interface Vector<T : Any, S : Space<T>> : SpaceElement<Point<T>, Vector<T, S>, VectorSpace<T, S>>, Point<T> {
|
interface Vector<T : Any, S : Space<T>> : SpaceElement<Point<T>, Vector<T, S>, VectorSpace<T, S>>, Point<T> {
|
||||||
override val size: Int get() = context.size
|
override val size: Int get() = context.size
|
||||||
|
|
||||||
@ -90,16 +45,7 @@ interface Vector<T : Any, S : Space<T>> : SpaceElement<Point<T>, Vector<T, S>, V
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class BufferVectorSpace<T : Any, S : Space<T>>(
|
@Deprecated("Use VectorContext instead")
|
||||||
override val size: Int,
|
|
||||||
override val space: S,
|
|
||||||
val bufferFactory: BufferFactory<T>
|
|
||||||
) : VectorSpace<T, S> {
|
|
||||||
override fun produce(initializer: (Int) -> T) = bufferFactory(size, initializer)
|
|
||||||
override fun produceElement(initializer: (Int) -> T): Vector<T, S> = BufferVector(this, produce(initializer))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
data class BufferVector<T : Any, S : Space<T>>(override val context: VectorSpace<T, S>, val buffer: Buffer<T>) :
|
data class BufferVector<T : Any, S : Space<T>>(override val context: VectorSpace<T, S>, val buffer: Buffer<T>) :
|
||||||
Vector<T, S> {
|
Vector<T, S> {
|
||||||
|
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.RealField
|
||||||
|
import scientifik.kmath.operations.Space
|
||||||
|
import scientifik.kmath.structures.Buffer
|
||||||
|
import scientifik.kmath.structures.BufferFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A linear space for vectors.
|
||||||
|
* Could be used on any point-like structure
|
||||||
|
*/
|
||||||
|
interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> {
|
||||||
|
|
||||||
|
val size: Int
|
||||||
|
|
||||||
|
val space: S
|
||||||
|
|
||||||
|
fun produce(initializer: (Int) -> T): Point<T>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a space-element of this vector space for expressions
|
||||||
|
*/
|
||||||
|
//fun produceElement(initializer: (Int) -> T): Vector<T, S>
|
||||||
|
|
||||||
|
override val zero: Point<T> get() = produce { space.zero }
|
||||||
|
|
||||||
|
override fun add(a: Point<T>, b: Point<T>): Point<T> = produce { with(space) { a[it] + b[it] } }
|
||||||
|
|
||||||
|
override fun multiply(a: Point<T>, k: Number): Point<T> = produce { with(space) { a[it] * k } }
|
||||||
|
|
||||||
|
//TODO add basis
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val realSpaceCache = HashMap<Int, BufferVectorSpace<Double, RealField>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-boxing double vector space
|
||||||
|
*/
|
||||||
|
fun real(size: Int): BufferVectorSpace<Double, RealField> {
|
||||||
|
return realSpaceCache.getOrPut(size) {
|
||||||
|
BufferVectorSpace(
|
||||||
|
size,
|
||||||
|
RealField,
|
||||||
|
Buffer.Companion::auto
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structured vector space with custom buffer
|
||||||
|
*/
|
||||||
|
fun <T : Any, S : Space<T>> buffered(
|
||||||
|
size: Int,
|
||||||
|
space: S,
|
||||||
|
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing
|
||||||
|
) = BufferVectorSpace(size, space, bufferFactory)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatic buffered vector, unboxed if it is possible
|
||||||
|
*/
|
||||||
|
inline fun <reified T : Any, S : Space<T>> auto(size: Int, space: S): VectorSpace<T, S> =
|
||||||
|
buffered(size, space, Buffer.Companion::auto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BufferVectorSpace<T : Any, S : Space<T>>(
|
||||||
|
override val size: Int,
|
||||||
|
override val space: S,
|
||||||
|
val bufferFactory: BufferFactory<T>
|
||||||
|
) : VectorSpace<T, S> {
|
||||||
|
override fun produce(initializer: (Int) -> T) = bufferFactory(size, initializer)
|
||||||
|
//override fun produceElement(initializer: (Int) -> T): Vector<T, S> = BufferVector(this, produce(initializer))
|
||||||
|
}
|
@ -73,6 +73,7 @@ interface NDSpace<T, S : Space<T>, N : NDStructure<T>> : Space<N>, NDAlgebra<T,
|
|||||||
*/
|
*/
|
||||||
override fun multiply(a: N, k: Number): N = map(a) { multiply(it, k) }
|
override fun multiply(a: N, k: Number): N = map(a) { multiply(it, k) }
|
||||||
|
|
||||||
|
//TODO move to extensions after KEEP-176
|
||||||
operator fun N.plus(arg: T) = map(this) { value -> add(arg, value) }
|
operator fun N.plus(arg: T) = map(this) { value -> add(arg, value) }
|
||||||
operator fun N.minus(arg: T) = map(this) { value -> add(arg, -value) }
|
operator fun N.minus(arg: T) = map(this) { value -> add(arg, -value) }
|
||||||
|
|
||||||
@ -90,6 +91,7 @@ interface NDRing<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T, R, N>
|
|||||||
*/
|
*/
|
||||||
override fun multiply(a: N, b: N): N = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) }
|
override fun multiply(a: N, b: N): N = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) }
|
||||||
|
|
||||||
|
//TODO move to extensions after KEEP-176
|
||||||
operator fun N.times(arg: T) = map(this) { value -> multiply(arg, value) }
|
operator fun N.times(arg: T) = map(this) { value -> multiply(arg, value) }
|
||||||
operator fun T.times(arg: N) = map(arg) { value -> multiply(this@times, value) }
|
operator fun T.times(arg: N) = map(arg) { value -> multiply(this@times, value) }
|
||||||
}
|
}
|
||||||
@ -109,6 +111,7 @@ interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F,
|
|||||||
*/
|
*/
|
||||||
override fun divide(a: N, b: N): N = combine(a, b) { aValue, bValue -> divide(aValue, bValue) }
|
override fun divide(a: N, b: N): N = combine(a, b) { aValue, bValue -> divide(aValue, bValue) }
|
||||||
|
|
||||||
|
//TODO move to extensions after KEEP-176
|
||||||
operator fun N.div(arg: T) = map(this) { value -> divide(arg, value) }
|
operator fun N.div(arg: T) = map(this) { value -> divide(arg, value) }
|
||||||
operator fun T.div(arg: N) = map(arg) { divide(it, this@div) }
|
operator fun T.div(arg: N) = map(arg) { divide(it, this@div) }
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
|
import scientifik.kmath.structures.Matrix
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ class MatrixTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testVectorToMatrix() {
|
fun testVectorToMatrix() {
|
||||||
val vector = Vector.real(5) { it.toDouble() }
|
val vector = Vector.real(5) { it.toDouble() }
|
||||||
val matrix = vector.toMatrix()
|
val matrix = vector.asMatrix()
|
||||||
assertEquals(4.0, matrix[4, 0])
|
assertEquals(4.0, matrix[4, 0])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,8 +34,8 @@ class MatrixTest {
|
|||||||
val vector1 = Vector.real(5) { it.toDouble() }
|
val vector1 = Vector.real(5) { it.toDouble() }
|
||||||
val vector2 = Vector.real(5) { 5 - it.toDouble() }
|
val vector2 = Vector.real(5) { 5 - it.toDouble() }
|
||||||
|
|
||||||
val matrix1 = vector1.toMatrix()
|
val matrix1 = vector1.asMatrix()
|
||||||
val matrix2 = vector2.toMatrix().transpose()
|
val matrix2 = vector2.asMatrix().transpose()
|
||||||
val product = MatrixContext.real.run { matrix1 dot matrix2 }
|
val product = MatrixContext.real.run { matrix1 dot matrix2 }
|
||||||
|
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ class MatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBuilder() {
|
fun testBuilder() {
|
||||||
val matrix = FeaturedMatrix.build<Double>(2, 3)(
|
val matrix = Matrix.build<Double>(2, 3)(
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0
|
0.0, 1.0, 2.0
|
||||||
)
|
)
|
||||||
|
@ -1,25 +1,28 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
|
import scientifik.kmath.structures.Matrix
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
@ExperimentalContracts
|
||||||
class RealLUSolverTest {
|
class RealLUSolverTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testInvertOne() {
|
fun testInvertOne() {
|
||||||
val matrix = MatrixContext.real.one(2, 2)
|
val matrix = MatrixContext.real.one(2, 2)
|
||||||
val inverted = LUSolver.real.inverse(matrix)
|
val inverted = MatrixContext.real.inverse(matrix)
|
||||||
assertEquals(matrix, inverted)
|
assertEquals(matrix, inverted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testInvert() {
|
fun testInvert() {
|
||||||
val matrix = FeaturedMatrix.square(
|
val matrix = Matrix.square(
|
||||||
3.0, 1.0,
|
3.0, 1.0,
|
||||||
1.0, 3.0
|
1.0, 3.0
|
||||||
)
|
)
|
||||||
|
|
||||||
val decomposed = LUSolver.real.decompose(matrix)
|
val decomposition = MatrixContext.real.lup(matrix)
|
||||||
val decomposition = decomposed.getFeature<LUPDecomposition<Double>>()!!
|
|
||||||
|
|
||||||
//Check determinant
|
//Check determinant
|
||||||
assertEquals(8.0, decomposition.determinant)
|
assertEquals(8.0, decomposition.determinant)
|
||||||
@ -29,9 +32,9 @@ class RealLUSolverTest {
|
|||||||
assertEquals(decomposition.p dot matrix, decomposition.l dot decomposition.u)
|
assertEquals(decomposition.p dot matrix, decomposition.l dot decomposition.u)
|
||||||
}
|
}
|
||||||
|
|
||||||
val inverted = LUSolver.real.inverse(decomposed)
|
val inverted = MatrixContext.real.inverse(matrix)
|
||||||
|
|
||||||
val expected = FeaturedMatrix.square(
|
val expected = Matrix.square(
|
||||||
0.375, -0.125,
|
0.375, -0.125,
|
||||||
-0.125, 0.375
|
-0.125, 0.375
|
||||||
)
|
)
|
||||||
|
@ -2,10 +2,14 @@ package scientifik.kmath.linear
|
|||||||
|
|
||||||
import koma.extensions.fill
|
import koma.extensions.fill
|
||||||
import koma.matrix.MatrixFactory
|
import koma.matrix.MatrixFactory
|
||||||
|
import scientifik.kmath.operations.Space
|
||||||
import scientifik.kmath.structures.Matrix
|
import scientifik.kmath.structures.Matrix
|
||||||
|
|
||||||
class KomaMatrixContext<T : Any>(val factory: MatrixFactory<koma.matrix.Matrix<T>>) : MatrixContext<T>,
|
class KomaMatrixContext<T : Any>(
|
||||||
LinearSolver<T> {
|
private val factory: MatrixFactory<koma.matrix.Matrix<T>>,
|
||||||
|
private val space: Space<T>
|
||||||
|
) :
|
||||||
|
MatrixContext<T> {
|
||||||
|
|
||||||
override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T) =
|
override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T) =
|
||||||
KomaMatrix(factory.zeros(rows, columns).fill(initializer))
|
KomaMatrix(factory.zeros(rows, columns).fill(initializer))
|
||||||
@ -32,28 +36,38 @@ class KomaMatrixContext<T : Any>(val factory: MatrixFactory<koma.matrix.Matrix<T
|
|||||||
override fun Matrix<T>.unaryMinus() =
|
override fun Matrix<T>.unaryMinus() =
|
||||||
KomaMatrix(this.toKoma().origin.unaryMinus())
|
KomaMatrix(this.toKoma().origin.unaryMinus())
|
||||||
|
|
||||||
override fun Matrix<T>.plus(b: Matrix<T>) =
|
override fun add(a: Matrix<T>, b: Matrix<T>) =
|
||||||
KomaMatrix(this.toKoma().origin + b.toKoma().origin)
|
KomaMatrix(a.toKoma().origin + b.toKoma().origin)
|
||||||
|
|
||||||
override fun Matrix<T>.minus(b: Matrix<T>) =
|
override fun Matrix<T>.minus(b: Matrix<T>) =
|
||||||
KomaMatrix(this.toKoma().origin - b.toKoma().origin)
|
KomaMatrix(this.toKoma().origin - b.toKoma().origin)
|
||||||
|
|
||||||
|
override fun multiply(a: Matrix<T>, k: Number): Matrix<T> =
|
||||||
|
produce(a.rowNum, a.colNum) { i, j -> space.run { a[i, j] * k } }
|
||||||
|
|
||||||
override fun Matrix<T>.times(value: T) =
|
override fun Matrix<T>.times(value: T) =
|
||||||
KomaMatrix(this.toKoma().origin * value)
|
KomaMatrix(this.toKoma().origin * value)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
override fun solve(a: Matrix<T>, b: Matrix<T>) =
|
}
|
||||||
KomaMatrix(a.toKoma().origin.solve(b.toKoma().origin))
|
|
||||||
|
|
||||||
override fun inverse(a: Matrix<T>) =
|
|
||||||
KomaMatrix(a.toKoma().origin.inv())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T : Any> KomaMatrixContext<T>.solve(a: Matrix<T>, b: Matrix<T>) =
|
||||||
|
KomaMatrix(a.toKoma().origin.solve(b.toKoma().origin))
|
||||||
|
|
||||||
|
fun <T : Any> KomaMatrixContext<T>.solve(a: Matrix<T>, b: Point<T>) =
|
||||||
|
KomaVector(a.toKoma().origin.solve(b.toKoma().origin))
|
||||||
|
|
||||||
|
fun <T : Any> KomaMatrixContext<T>.inverse(a: Matrix<T>) =
|
||||||
|
KomaMatrix(a.toKoma().origin.inv())
|
||||||
|
|
||||||
class KomaMatrix<T : Any>(val origin: koma.matrix.Matrix<T>, features: Set<MatrixFeature>? = null) : FeaturedMatrix<T> {
|
class KomaMatrix<T : Any>(val origin: koma.matrix.Matrix<T>, features: Set<MatrixFeature>? = null) : FeaturedMatrix<T> {
|
||||||
override val rowNum: Int get() = origin.numRows()
|
override val rowNum: Int get() = origin.numRows()
|
||||||
override val colNum: Int get() = origin.numCols()
|
override val colNum: Int get() = origin.numCols()
|
||||||
|
|
||||||
override val shape: IntArray get() = intArrayOf(origin.numRows(),origin.numCols())
|
override val shape: IntArray get() = intArrayOf(origin.numRows(), origin.numCols())
|
||||||
|
|
||||||
override val features: Set<MatrixFeature> = features ?: setOf(
|
override val features: Set<MatrixFeature> = features ?: setOf(
|
||||||
object : DeterminantFeature<T> {
|
object : DeterminantFeature<T> {
|
||||||
|
Loading…
Reference in New Issue
Block a user