From b2b64f35d02a72616c811c1a60606adeb706520e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 12 Jan 2025 13:04:46 +0300 Subject: [PATCH] Change EJML implementation to match CM and Ojalgo --- CHANGELOG.md | 1 + .../kscience/kmath/benchmarks/DotBenchmark.kt | 9 +- .../kscience/kmath/ejml/EjmlLinearSpace.kt | 6 +- .../space/kscience/kmath/ejml/EjmlMatrix.kt | 8 +- .../space/kscience/kmath/ejml/EjmlVector.kt | 10 +- .../kscience/kmath/ejml/implementations.kt | 409 +++++++++--------- .../kscience/kmath/ejml/EjmlMatrixTest.kt | 4 +- .../kscience/kmath/ejml/EjmlVectorTest.kt | 2 +- .../kscience/kmath/geometry/lineExtensions.kt | 2 + 9 files changed, 232 insertions(+), 219 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b1250dcd..27c2bc74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - attributes-kt moved to a separate project, and the version used is 0.3.0 - Kotlin 2.1. Now use cross-compilation to deploy macOS targets. - Changed `origin` to `cmMatrix` in kmath-commons to avoid property name clash. Expose bidirectional conversion in `CMLinearSpace` +- (BREAKING CHANGE) Changed implementations in `kmath-ejml` to match CM and ojalgo style. Specifically, provide bidirectional conversion for library types. ### Deprecated diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt index 0a3cddc9a..3c81f3418 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt @@ -10,7 +10,6 @@ import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Scope import kotlinx.benchmark.State import space.kscience.kmath.commons.linear.CMLinearSpace -import space.kscience.kmath.commons.linear.CMLinearSpace.dot import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.linear.Float64ParallelLinearSpace import space.kscience.kmath.linear.invoke @@ -59,13 +58,13 @@ internal class DotBenchmark { } @Benchmark - fun cmDot(blackhole: Blackhole) = CMLinearSpace { - blackhole.consume(cmMatrix1 dot cmMatrix2) + fun cmDot(blackhole: Blackhole): Unit = CMLinearSpace { + blackhole.consume(cmMatrix1.asMatrix() dot cmMatrix2.asMatrix()) } @Benchmark - fun ejmlDot(blackhole: Blackhole) = EjmlLinearSpaceDDRM { - blackhole.consume(ejmlMatrix1 dot ejmlMatrix2) + fun ejmlDot(blackhole: Blackhole): Unit = EjmlLinearSpaceDDRM { + blackhole.consume(ejmlMatrix1.asMatrix() dot ejmlMatrix2.asMatrix()) } @Benchmark diff --git a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt index fe522844d..75caad56a 100644 --- a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt +++ b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt @@ -22,16 +22,16 @@ import space.kscience.kmath.structures.Float64 * @param M the EJML matrix type. * @author Iaroslav Postovalov */ -public abstract class EjmlLinearSpace, out M : org.ejml.data.Matrix> : LinearSpace { +public interface EjmlLinearSpace, M : org.ejml.data.Matrix> : LinearSpace { /** * Converts this matrix to EJML one. */ - public abstract fun Matrix.toEjml(): EjmlMatrix + public fun Matrix.toEjml(): M /** * Converts this vector to EJML one. */ - public abstract fun Point.toEjml(): EjmlVector + public fun Point.toEjml(): M public abstract override fun buildMatrix( rows: Int, diff --git a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt index d24f56042..c0f90b433 100644 --- a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt +++ b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt @@ -13,10 +13,10 @@ import space.kscience.kmath.nd.Structure2D * * @param T the type of elements contained in the buffer. * @param M the type of EJML matrix. - * @property origin The underlying EJML matrix. + * @property ejmlMatrix The underlying EJML matrix. * @author Iaroslav Postovalov */ -public abstract class EjmlMatrix(public open val origin: M) : Structure2D { - override val rowNum: Int get() = origin.numRows - override val colNum: Int get() = origin.numCols +public abstract class EjmlMatrix(public open val ejmlMatrix: M) : Structure2D { + override val rowNum: Int get() = ejmlMatrix.numRows + override val colNum: Int get() = ejmlMatrix.numCols } diff --git a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlVector.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlVector.kt index 4056d419b..99a843394 100644 --- a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlVector.kt +++ b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlVector.kt @@ -13,12 +13,12 @@ import space.kscience.kmath.linear.Point * * @param T the type of elements contained in the buffer. * @param M the type of EJML matrix. - * @property origin The underlying matrix, must have only one row. + * @property ejmlVector The underlying matrix, must have only one row. * @author Iaroslav Postovalov */ -public abstract class EjmlVector(public open val origin: M) : Point { +public abstract class EjmlVector(public open val ejmlVector: M) : Point { override val size: Int - get() = origin.numRows + get() = ejmlVector.numRows override operator fun iterator(): Iterator = object : Iterator { private var cursor: Int = 0 @@ -28,8 +28,8 @@ public abstract class EjmlVector(public open val origin: return this@EjmlVector[cursor - 1] } - override fun hasNext(): Boolean = cursor < origin.numCols * origin.numRows + override fun hasNext(): Boolean = cursor < ejmlVector.numCols * ejmlVector.numRows } - override fun toString(): String = "EjmlVector(origin=$origin)" + override fun toString(): String = "EjmlVector(origin=$ejmlVector)" } diff --git a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt index 3d628c030..aa488a091 100644 --- a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt +++ b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt @@ -43,39 +43,39 @@ public fun Complex_F64.toKMathComplex(): Complex = Complex(real, imaginary) /** * [EjmlVector] specialization for [Double]. */ -public class EjmlDoubleVector(override val origin: M) : EjmlVector(origin) { +public class EjmlDoubleVector(override val ejmlVector: M) : EjmlVector(ejmlVector) { init { - require(origin.numCols == 1) { "The origin matrix must have only one column to form a vector" } + require(ejmlVector.numCols == 1) { "The origin matrix must have only one column to form a vector" } } - override operator fun get(index: Int): Double = origin[index, 0] + override operator fun get(index: Int): Double = ejmlVector[index, 0] } /** * [EjmlVector] specialization for [Float]. */ -public class EjmlFloatVector(override val origin: M) : EjmlVector(origin) { +public class EjmlFloatVector(override val ejmlVector: M) : EjmlVector(ejmlVector) { init { - require(origin.numCols == 1) { "The origin matrix must have only one column to form a vector" } + require(ejmlVector.numCols == 1) { "The origin matrix must have only one column to form a vector" } } - override operator fun get(index: Int): Float = origin[index, 0] + override operator fun get(index: Int): Float = ejmlVector[index, 0] } /** * [EjmlMatrix] specialization for [Double]. */ -public class EjmlDoubleMatrix(override val origin: M) : EjmlMatrix(origin) { - override operator fun get(i: Int, j: Int): Double = origin[i, j] +public class EjmlDoubleMatrix(override val ejmlMatrix: M) : EjmlMatrix(ejmlMatrix) { + override operator fun get(i: Int, j: Int): Double = ejmlMatrix[i, j] } /** * [EjmlMatrix] specialization for [Float]. */ -public class EjmlFloatMatrix(override val origin: M) : EjmlMatrix(origin) { +public class EjmlFloatMatrix(override val ejmlMatrix: M) : EjmlMatrix(ejmlMatrix) { - override operator fun get(i: Int, j: Int): Float = origin[i, j] + override operator fun get(i: Int, j: Int): Float = ejmlMatrix[i, j] } @@ -83,7 +83,7 @@ public class EjmlFloatMatrix(override val origin: M) : EjmlMatr * [EjmlLinearSpace] implementation based on [CommonOps_DDRM], [DecompositionFactory_DDRM] operations and * [DMatrixRMaj] matrices. */ -public object EjmlLinearSpaceDDRM : EjmlLinearSpace() { +public object EjmlLinearSpaceDDRM : EjmlLinearSpace { /** * The [Float64Field] reference. */ @@ -91,20 +91,25 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace get() = safeTypeOf() - @Suppress("UNCHECKED_CAST") - override fun Matrix.toEjml(): EjmlDoubleMatrix = when { - this is EjmlDoubleMatrix<*> && origin is DMatrixRMaj -> this as EjmlDoubleMatrix - else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } + @OptIn(UnstableKMathAPI::class) + override fun Matrix.toEjml(): DMatrixRMaj { + val matrix = origin + return when { + matrix is EjmlDoubleMatrix<*> && matrix.ejmlMatrix is DMatrixRMaj -> matrix.ejmlMatrix + else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) }.ejmlMatrix + } } - @Suppress("UNCHECKED_CAST") - override fun Point.toEjml(): EjmlDoubleVector = when { - this is EjmlDoubleVector<*> && origin is DMatrixRMaj -> this as EjmlDoubleVector - else -> EjmlDoubleVector(DMatrixRMaj(size, 1).also { + override fun Point.toEjml(): DMatrixRMaj = when { + this is EjmlDoubleVector<*> && ejmlVector is DMatrixRMaj -> ejmlVector + else -> DMatrixRMaj(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } - }) + } } + public fun T.asMatrix() = EjmlDoubleMatrix(this) + public fun T.asVector() = EjmlDoubleVector(this) + override fun buildMatrix( rows: Int, columns: Int, @@ -113,7 +118,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } } - }.wrapMatrix() + }.asMatrix() override fun buildVector( size: Int, @@ -122,21 +127,18 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace it[row, 0] = elementAlgebra.initializer(row) } }) - private fun T.wrapMatrix() = EjmlDoubleMatrix(this) - private fun T.wrapVector() = EjmlDoubleVector(this) - override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { val out = DMatrixRMaj(1, 1) - CommonOps_DDRM.mult(toEjml().origin, other.toEjml().origin, out) - return out.wrapMatrix() + CommonOps_DDRM.mult(toEjml(), other.toEjml(), out) + return out.asMatrix() } override fun Matrix.dot(vector: Point): EjmlDoubleVector { val out = DMatrixRMaj(1, 1) - CommonOps_DDRM.mult(toEjml().origin, vector.toEjml().origin, out) - return out.wrapVector() + CommonOps_DDRM.mult(toEjml(), vector.toEjml(), out) + return out.asVector() } override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { @@ -144,25 +146,25 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace.times(value: Double): EjmlDoubleMatrix { val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.scale(value, toEjml().origin, res) - return res.wrapMatrix() + CommonOps_DDRM.scale(value, toEjml(), res) + return res.asMatrix() } override fun Point.unaryMinus(): EjmlDoubleVector { val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.changeSign(toEjml().origin, res) - return res.wrapVector() + CommonOps_DDRM.changeSign(toEjml(), res) + return res.asVector() } override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { @@ -170,13 +172,13 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace.plus(other: Point): EjmlDoubleVector { @@ -184,13 +186,13 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace.minus(other: Point): EjmlDoubleVector { @@ -198,34 +200,34 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace): EjmlDoubleMatrix = m * this override fun Point.times(value: Double): EjmlDoubleVector { val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.scale(value, toEjml().origin, res) - return res.wrapVector() + CommonOps_DDRM.scale(value, toEjml(), res) + return res.asVector() } override fun Double.times(v: Point): EjmlDoubleVector = v * this @OptIn(UnstableKMathAPI::class) override fun > computeAttribute(structure: Structure2D, attribute: A): V? { - val origin: DMatrixRMaj = structure.toEjml().origin + val origin: DMatrixRMaj = structure.toEjml() val raw: Any? = when (attribute) { Inverted -> { val res = origin.copy() CommonOps_DDRM.invert(res) - res.wrapMatrix() + res.asMatrix() } Determinant -> CommonOps_DDRM.det(origin) @@ -236,18 +238,18 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace get() = ejmlSvd.getU(null, false).wrapMatrix() + override val u: Matrix get() = ejmlSvd.getU(null, false).asMatrix() - override val s: Matrix get() = ejmlSvd.getW(null).wrapMatrix() - override val v: Matrix get() = ejmlSvd.getV(null, false).wrapMatrix() + override val s: Matrix get() = ejmlSvd.getW(null).asMatrix() + override val v: Matrix get() = ejmlSvd.getV(null, false).asMatrix() override val singularValues: Point get() = ejmlSvd.singularValues.asBuffer() } QR -> object : QRDecomposition { val ejmlQr by lazy { DecompositionFactory_DDRM.qr().apply { decompose(origin.copy()) } } - override val q: Matrix get() = ejmlQr.getQ(null, false).wrapMatrix() - override val r: Matrix get() = ejmlQr.getR(null, false).wrapMatrix() + override val q: Matrix get() = ejmlQr.getQ(null, false).asMatrix() + override val r: Matrix get() = ejmlQr.getR(null, false).asMatrix() } Cholesky -> object : CholeskyDecomposition { @@ -255,7 +257,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace - get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) + get() = lup.getLower(null).asMatrix().withAttribute(LowerTriangular) override val u: Matrix - get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) + get() = lup.getUpper(null).asMatrix().withAttribute(UpperTriangular) override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } @@ -318,8 +320,8 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace, b: Matrix): EjmlDoubleMatrix { val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) - return res.wrapMatrix() + CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml()), DMatrixRMaj(b.toEjml()), res) + return res.asMatrix() } /** @@ -331,7 +333,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace, b: Point): EjmlDoubleVector { val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) + CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml()), DMatrixRMaj(b.toEjml()), res) return EjmlDoubleVector(res) } } @@ -341,7 +343,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace() { +public object EjmlLinearSpaceFDRM : EjmlLinearSpace { /** * The [Float32Field] reference. */ @@ -349,20 +351,25 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace get() = safeTypeOf() - @Suppress("UNCHECKED_CAST") - override fun Matrix.toEjml(): EjmlFloatMatrix = when { - this is EjmlFloatMatrix<*> && origin is FMatrixRMaj -> this as EjmlFloatMatrix - else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } + @OptIn(UnstableKMathAPI::class) + override fun Matrix.toEjml(): FMatrixRMaj { + val matrix = origin + return when { + matrix is EjmlFloatMatrix<*> && matrix.ejmlMatrix is FMatrixRMaj -> matrix.ejmlMatrix + else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) }.ejmlMatrix + } } - @Suppress("UNCHECKED_CAST") - override fun Point.toEjml(): EjmlFloatVector = when { - this is EjmlFloatVector<*> && origin is FMatrixRMaj -> this as EjmlFloatVector - else -> EjmlFloatVector(FMatrixRMaj(size, 1).also { + override fun Point.toEjml(): FMatrixRMaj = when { + this is EjmlFloatVector<*> && ejmlVector is FMatrixRMaj -> ejmlVector + else -> FMatrixRMaj(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } - }) + } } + public fun T.asMatrix() = EjmlFloatMatrix(this) + public fun T.asVector() = EjmlFloatVector(this) + override fun buildMatrix( rows: Int, columns: Int, @@ -371,7 +378,7 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } } - }.wrapMatrix() + }.asMatrix() override fun buildVector( size: Int, @@ -380,21 +387,18 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace it[row, 0] = elementAlgebra.initializer(row) } }) - private fun T.wrapMatrix() = EjmlFloatMatrix(this) - private fun T.wrapVector() = EjmlFloatVector(this) - override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } override fun Matrix.dot(other: Matrix): EjmlFloatMatrix { val out = FMatrixRMaj(1, 1) - CommonOps_FDRM.mult(toEjml().origin, other.toEjml().origin, out) - return out.wrapMatrix() + CommonOps_FDRM.mult(toEjml(), other.toEjml(), out) + return out.asMatrix() } override fun Matrix.dot(vector: Point): EjmlFloatVector { val out = FMatrixRMaj(1, 1) - CommonOps_FDRM.mult(toEjml().origin, vector.toEjml().origin, out) - return out.wrapVector() + CommonOps_FDRM.mult(toEjml(), vector.toEjml(), out) + return out.asVector() } override operator fun Matrix.minus(other: Matrix): EjmlFloatMatrix { @@ -402,25 +406,25 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace.times(value: Float): EjmlFloatMatrix { val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.scale(value, toEjml().origin, res) - return res.wrapMatrix() + CommonOps_FDRM.scale(value, toEjml(), res) + return res.asMatrix() } override fun Point.unaryMinus(): EjmlFloatVector { val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.changeSign(toEjml().origin, res) - return res.wrapVector() + CommonOps_FDRM.changeSign(toEjml(), res) + return res.asVector() } override fun Matrix.plus(other: Matrix): EjmlFloatMatrix { @@ -428,13 +432,13 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace.plus(other: Point): EjmlFloatVector { @@ -442,13 +446,13 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace.minus(other: Point): EjmlFloatVector { @@ -456,34 +460,34 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace): EjmlFloatMatrix = m * this override fun Point.times(value: Float): EjmlFloatVector { val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.scale(value, toEjml().origin, res) - return res.wrapVector() + CommonOps_FDRM.scale(value, toEjml(), res) + return res.asVector() } override fun Float.times(v: Point): EjmlFloatVector = v * this @OptIn(UnstableKMathAPI::class) override fun > computeAttribute(structure: Structure2D, attribute: A): V? { - val origin = structure.toEjml().origin + val origin = structure.toEjml() val raw: Any? = when (attribute) { Inverted -> { val res = origin.copy() CommonOps_FDRM.invert(res) - res.wrapMatrix() + res.asMatrix() } Determinant -> CommonOps_FDRM.det(origin) @@ -493,18 +497,18 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace get() = ejmlSvd.getU(null, false).wrapMatrix() + override val u: Matrix get() = ejmlSvd.getU(null, false).asMatrix() - override val s: Matrix get() = ejmlSvd.getW(null).wrapMatrix() - override val v: Matrix get() = ejmlSvd.getV(null, false).wrapMatrix() + override val s: Matrix get() = ejmlSvd.getW(null).asMatrix() + override val v: Matrix get() = ejmlSvd.getV(null, false).asMatrix() override val singularValues: Point get() = ejmlSvd.singularValues.asBuffer() } QR -> object : QRDecomposition { val ejmlQr by lazy { DecompositionFactory_FDRM.qr().apply { decompose(origin.copy()) } } - override val q: Matrix get() = ejmlQr.getQ(null, false).wrapMatrix() - override val r: Matrix get() = ejmlQr.getR(null, false).wrapMatrix() + override val q: Matrix get() = ejmlQr.getQ(null, false).asMatrix() + override val r: Matrix get() = ejmlQr.getR(null, false).asMatrix() } Cholesky -> object : CholeskyDecomposition { @@ -512,7 +516,7 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace - get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) + get() = lup.getLower(null).asMatrix().withAttribute(LowerTriangular) override val u: Matrix - get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) + get() = lup.getUpper(null).asMatrix().withAttribute(UpperTriangular) override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } @@ -575,8 +579,8 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace, b: Matrix): EjmlFloatMatrix { val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.solve(FMatrixRMaj(a.toEjml().origin), FMatrixRMaj(b.toEjml().origin), res) - return res.wrapMatrix() + CommonOps_FDRM.solve(FMatrixRMaj(a.toEjml()), FMatrixRMaj(b.toEjml()), res) + return res.asMatrix() } /** @@ -588,7 +592,7 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace, b: Point): EjmlFloatVector { val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.solve(FMatrixRMaj(a.toEjml().origin), FMatrixRMaj(b.toEjml().origin), res) + CommonOps_FDRM.solve(FMatrixRMaj(a.toEjml()), FMatrixRMaj(b.toEjml()), res) return EjmlFloatVector(res) } } @@ -598,7 +602,7 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace() { +public object EjmlLinearSpaceDSCC : EjmlLinearSpace { /** * The [Float64Field] reference. */ @@ -606,20 +610,26 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace get() = safeTypeOf() - @Suppress("UNCHECKED_CAST") - override fun Matrix.toEjml(): EjmlDoubleMatrix = when { - this is EjmlDoubleMatrix<*> && origin is DMatrixSparseCSC -> this as EjmlDoubleMatrix - else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } + @OptIn(UnstableKMathAPI::class) + override fun Matrix.toEjml(): DMatrixSparseCSC { + val matrix = origin + return when { + matrix is EjmlDoubleMatrix<*> && matrix.ejmlMatrix is DMatrixSparseCSC -> matrix.ejmlMatrix + else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) }.ejmlMatrix + } } - @Suppress("UNCHECKED_CAST") - override fun Point.toEjml(): EjmlDoubleVector = when { - this is EjmlDoubleVector<*> && origin is DMatrixSparseCSC -> this as EjmlDoubleVector - else -> EjmlDoubleVector(DMatrixSparseCSC(size, 1).also { + override fun Point.toEjml(): DMatrixSparseCSC = when { + this is EjmlDoubleVector<*> && ejmlVector is DMatrixSparseCSC -> ejmlVector + else -> DMatrixSparseCSC(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } - }) + } } + public fun T.asMatrix() = EjmlDoubleMatrix(this) + public fun T.asVector() = EjmlDoubleVector(this) + + override fun buildMatrix( rows: Int, columns: Int, @@ -628,7 +638,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } } - }.wrapMatrix() + }.asMatrix() override fun buildVector( size: Int, @@ -637,21 +647,18 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace it[row, 0] = elementAlgebra.initializer(row) } }) - private fun T.wrapMatrix() = EjmlDoubleMatrix(this) - private fun T.wrapVector() = EjmlDoubleVector(this) - override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { val out = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.mult(toEjml().origin, other.toEjml().origin, out) - return out.wrapMatrix() + CommonOps_DSCC.mult(toEjml(), other.toEjml(), out) + return out.asMatrix() } override fun Matrix.dot(vector: Point): EjmlDoubleVector { val out = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.mult(toEjml().origin, vector.toEjml().origin, out) - return out.wrapVector() + CommonOps_DSCC.mult(toEjml(), vector.toEjml(), out) + return out.asVector() } override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { @@ -659,27 +666,27 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.times(value: Double): EjmlDoubleMatrix { val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.scale(value, toEjml().origin, res) - return res.wrapMatrix() + CommonOps_DSCC.scale(value, toEjml(), res) + return res.asMatrix() } override fun Point.unaryMinus(): EjmlDoubleVector { val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.changeSign(toEjml().origin, res) - return res.wrapVector() + CommonOps_DSCC.changeSign(toEjml(), res) + return res.asVector() } override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { @@ -687,15 +694,15 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.plus(other: Point): EjmlDoubleVector { @@ -703,15 +710,15 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.minus(other: Point): EjmlDoubleVector { @@ -719,35 +726,35 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace): EjmlDoubleMatrix = m * this override fun Point.times(value: Double): EjmlDoubleVector { val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.scale(value, toEjml().origin, res) - return res.wrapVector() + CommonOps_DSCC.scale(value, toEjml(), res) + return res.asVector() } override fun Double.times(v: Point): EjmlDoubleVector = v * this override fun > computeAttribute(structure: Structure2D, attribute: A): V? { - val origin = structure.toEjml().origin + val origin = structure.toEjml() val raw: Any? = when (attribute) { Inverted -> { val res = DMatrixRMaj(origin.numRows, origin.numCols) CommonOps_DSCC.invert(origin, res) - res.wrapMatrix() + res.asMatrix() } Determinant -> CommonOps_DSCC.det(origin) @@ -756,8 +763,8 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace get() = ejmlQr.getQ(null, false).wrapMatrix() - override val r: Matrix get() = ejmlQr.getR(null, false).wrapMatrix() + override val q: Matrix get() = ejmlQr.getQ(null, false).asMatrix() + override val r: Matrix get() = ejmlQr.getR(null, false).asMatrix() } Cholesky -> object : CholeskyDecomposition { @@ -765,7 +772,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace - get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) + get() = lup.getLower(null).asMatrix().withAttribute(LowerTriangular) override val u: Matrix - get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) + get() = lup.getUpper(null).asMatrix().withAttribute(UpperTriangular) override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } @@ -799,8 +806,8 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace, b: Matrix): EjmlDoubleMatrix { val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.solve(DMatrixSparseCSC(a.toEjml().origin), DMatrixSparseCSC(b.toEjml().origin), res) - return res.wrapMatrix() + CommonOps_DSCC.solve(DMatrixSparseCSC(a.toEjml()), DMatrixSparseCSC(b.toEjml()), res) + return res.asMatrix() } /** @@ -812,7 +819,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace, b: Point): EjmlDoubleVector { val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.solve(DMatrixSparseCSC(a.toEjml().origin), DMatrixSparseCSC(b.toEjml().origin), res) + CommonOps_DSCC.solve(DMatrixSparseCSC(a.toEjml()), DMatrixSparseCSC(b.toEjml()), res) return EjmlDoubleVector(res) } } @@ -822,7 +829,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace() { +public object EjmlLinearSpaceFSCC : EjmlLinearSpace { /** * The [Float32Field] reference. */ @@ -830,20 +837,25 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace get() = safeTypeOf() - @Suppress("UNCHECKED_CAST") - override fun Matrix.toEjml(): EjmlFloatMatrix = when { - this is EjmlFloatMatrix<*> && origin is FMatrixSparseCSC -> this as EjmlFloatMatrix - else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } + @OptIn(UnstableKMathAPI::class) + override fun Matrix.toEjml(): FMatrixSparseCSC { + val matrix = origin + return when { + matrix is EjmlFloatMatrix<*> && matrix.ejmlMatrix is FMatrixSparseCSC -> matrix.ejmlMatrix + else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) }.ejmlMatrix + } } - @Suppress("UNCHECKED_CAST") - override fun Point.toEjml(): EjmlFloatVector = when { - this is EjmlFloatVector<*> && origin is FMatrixSparseCSC -> this as EjmlFloatVector - else -> EjmlFloatVector(FMatrixSparseCSC(size, 1).also { + override fun Point.toEjml(): FMatrixSparseCSC = when { + this is EjmlFloatVector<*> && ejmlVector is FMatrixSparseCSC -> ejmlVector + else -> FMatrixSparseCSC(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } - }) + } } + public fun T.asMatrix() = EjmlFloatMatrix(this) + public fun T.asVector() = EjmlFloatVector(this) + override fun buildMatrix( rows: Int, columns: Int, @@ -852,7 +864,7 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } } - }.wrapMatrix() + }.asMatrix() override fun buildVector( size: Int, @@ -861,21 +873,20 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace it[row, 0] = elementAlgebra.initializer(row) } }) - private fun T.wrapMatrix() = EjmlFloatMatrix(this) - private fun T.wrapVector() = EjmlFloatVector(this) + override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } override fun Matrix.dot(other: Matrix): EjmlFloatMatrix { val out = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.mult(toEjml().origin, other.toEjml().origin, out) - return out.wrapMatrix() + CommonOps_FSCC.mult(toEjml(), other.toEjml(), out) + return out.asMatrix() } override fun Matrix.dot(vector: Point): EjmlFloatVector { val out = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.mult(toEjml().origin, vector.toEjml().origin, out) - return out.wrapVector() + CommonOps_FSCC.mult(toEjml(), vector.toEjml(), out) + return out.asVector() } override operator fun Matrix.minus(other: Matrix): EjmlFloatMatrix { @@ -883,27 +894,27 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace.times(value: Float): EjmlFloatMatrix { val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.scale(value, toEjml().origin, res) - return res.wrapMatrix() + CommonOps_FSCC.scale(value, toEjml(), res) + return res.asMatrix() } override fun Point.unaryMinus(): EjmlFloatVector { val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.changeSign(toEjml().origin, res) - return res.wrapVector() + CommonOps_FSCC.changeSign(toEjml(), res) + return res.asVector() } override fun Matrix.plus(other: Matrix): EjmlFloatMatrix { @@ -911,15 +922,15 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace.plus(other: Point): EjmlFloatVector { @@ -927,15 +938,15 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace.minus(other: Point): EjmlFloatVector { @@ -943,34 +954,34 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace): EjmlFloatMatrix = m * this override fun Point.times(value: Float): EjmlFloatVector { val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.scale(value, toEjml().origin, res) - return res.wrapVector() + CommonOps_FSCC.scale(value, toEjml(), res) + return res.asVector() } override fun Float.times(v: Point): EjmlFloatVector = v * this override fun > computeAttribute(structure: Structure2D, attribute: A): V? { - val origin = structure.toEjml().origin + val origin = structure.toEjml() val raw: Any? = when (attribute) { Inverted -> { val res = FMatrixRMaj(origin.numRows, origin.numCols) CommonOps_FSCC.invert(origin, res) - res.wrapMatrix() + res.asMatrix() } Determinant -> CommonOps_FSCC.det(origin) @@ -979,8 +990,8 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace get() = ejmlQr.getQ(null, false).wrapMatrix() - override val r: Matrix get() = ejmlQr.getR(null, false).wrapMatrix() + override val q: Matrix get() = ejmlQr.getQ(null, false).asMatrix() + override val r: Matrix get() = ejmlQr.getR(null, false).asMatrix() } Cholesky -> object : CholeskyDecomposition { @@ -988,7 +999,7 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace - get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) + get() = lup.getLower(null).asMatrix().withAttribute(LowerTriangular) override val u: Matrix - get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) + get() = lup.getUpper(null).asMatrix().withAttribute(UpperTriangular) override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } @@ -1022,8 +1033,8 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace, b: Matrix): EjmlFloatMatrix { val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.solve(FMatrixSparseCSC(a.toEjml().origin), FMatrixSparseCSC(b.toEjml().origin), res) - return res.wrapMatrix() + CommonOps_FSCC.solve(FMatrixSparseCSC(a.toEjml()), FMatrixSparseCSC(b.toEjml()), res) + return res.asMatrix() } /** @@ -1035,7 +1046,7 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace, b: Point): EjmlFloatVector { val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.solve(FMatrixSparseCSC(a.toEjml().origin), FMatrixSparseCSC(b.toEjml().origin), res) + CommonOps_FSCC.solve(FMatrixSparseCSC(a.toEjml()), FMatrixSparseCSC(b.toEjml()), res) return EjmlFloatVector(res) } } diff --git a/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt b/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt index 239889e04..2a1171c1f 100644 --- a/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt +++ b/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt @@ -86,7 +86,7 @@ internal class EjmlMatrixTest { @Test fun origin() { val m = randomMatrix - assertSame(m, EjmlDoubleMatrix(m).origin) + assertSame(m, EjmlDoubleMatrix(m).ejmlMatrix) } @Test @@ -100,7 +100,7 @@ internal class EjmlMatrixTest { val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 } val matrix = space { l dot u } - val inverted = matrix.toEjml().inverted() + val inverted = matrix.inverted() val res = matrix dot inverted diff --git a/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt b/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt index 49e50ad8f..8d1ae463d 100644 --- a/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt +++ b/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt @@ -55,7 +55,7 @@ internal class EjmlVectorTest { fun origin() { val m = randomMatrix val w = EjmlDoubleVector(m) - assertSame(m, w.origin) + assertSame(m, w.ejmlVector) } @Test diff --git a/kmath-geometry/src/jvmMain/kotlin/space/kscience/kmath/geometry/lineExtensions.kt b/kmath-geometry/src/jvmMain/kotlin/space/kscience/kmath/geometry/lineExtensions.kt index c263398ea..f0e3a07d5 100644 --- a/kmath-geometry/src/jvmMain/kotlin/space/kscience/kmath/geometry/lineExtensions.kt +++ b/kmath-geometry/src/jvmMain/kotlin/space/kscience/kmath/geometry/lineExtensions.kt @@ -3,6 +3,8 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ +@file:Suppress("CONTEXT_RECEIVERS_DEPRECATED") + import space.kscience.kmath.geometry.GeometrySpace import space.kscience.kmath.geometry.Line import space.kscience.kmath.geometry.LineSegment