From 598b2e158724e4d9dd73d8d80a361684893071cd Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 28 Apr 2021 18:03:28 +0700 Subject: [PATCH] Rewrite EJML module by dropping ejml-simple abstraction level; multiple build script changes --- .github/workflows/pages.yml | 4 +- CHANGELOG.md | 3 +- benchmarks/build.gradle.kts | 6 +- .../kscience/kmath/benchmarks/DotBenchmark.kt | 10 +- .../benchmarks/MatrixInverseBenchmark.kt | 21 +- build.gradle.kts | 30 +-- docs/templates/ARTIFACT-TEMPLATE.md | 6 +- examples/build.gradle.kts | 7 +- kmath-ast/README.md | 6 +- kmath-complex/README.md | 6 +- kmath-core/README.md | 6 +- kmath-ejml/README.md | 12 +- kmath-ejml/build.gradle.kts | 8 +- .../kscience/kmath/ejml/EjmlLinearSpace.kt | 251 +++++++++++------- .../space/kscience/kmath/ejml/EjmlMatrix.kt | 24 +- .../space/kscience/kmath/ejml/EjmlVector.kt | 36 +-- .../kscience/kmath/ejml/EjmlMatrixTest.kt | 53 ++-- .../kscience/kmath/ejml/EjmlVectorTest.kt | 23 +- kmath-for-real/README.md | 2 - kmath-functions/README.md | 2 - kmath-kotlingrad/build.gradle.kts | 4 +- kmath-nd4j/README.md | 6 +- kmath-viktor/build.gradle.kts | 4 +- 23 files changed, 281 insertions(+), 249 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 82b0fb303..c70227fce 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -30,9 +30,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: Build - run: | - ./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace - mv build/dokka/htmlMultiModule/-modules.html build/dokka/htmlMultiModule/index.html + run: ./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace - name: Deploy to GitHub Pages uses: JamesIves/github-pages-deploy-action@4.1.0 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 7464695ec..4266c5b70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ - Blocking chains and Statistics - Multiplatform integration - Integration for any Field element -- Extendend operations for ND4J fields +- Extended operations for ND4J fields ### Changed - Exponential operations merged with hyperbolic functions @@ -24,6 +24,7 @@ - Redesign MST. Remove MSTExpression. - Move MST to core - Separated benchmarks and examples +- Rewritten EJML module without ejml-simple ### Deprecated diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 88f034a2a..98ffc5a96 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -9,14 +9,10 @@ sourceSets.register("benchmarks") repositories { mavenCentral() - jcenter() maven("https://repo.kotlin.link") maven("https://clojars.org/repo") - maven("https://dl.bintray.com/egor-bogomolov/astminer/") - maven("https://dl.bintray.com/hotkeytlt/maven") maven("https://jitpack.io") - maven { - setUrl("http://logicrunch.research.it.uu.se/maven/") + maven("http://logicrunch.research.it.uu.se/maven") { isAllowInsecureProtocol = true } } 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 23e73cb5f..2c5a03a97 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,7 @@ import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Scope import kotlinx.benchmark.State import space.kscience.kmath.commons.linear.CMLinearSpace -import space.kscience.kmath.ejml.EjmlLinearSpace +import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.invoke import space.kscience.kmath.operations.DoubleField @@ -29,8 +29,8 @@ internal class DotBenchmark { val cmMatrix1 = CMLinearSpace { matrix1.toCM() } val cmMatrix2 = CMLinearSpace { matrix2.toCM() } - val ejmlMatrix1 = EjmlLinearSpace { matrix1.toEjml() } - val ejmlMatrix2 = EjmlLinearSpace { matrix2.toEjml() } + val ejmlMatrix1 = EjmlLinearSpaceDDRM { matrix1.toEjml() } + val ejmlMatrix2 = EjmlLinearSpaceDDRM { matrix2.toEjml() } } @Benchmark @@ -42,14 +42,14 @@ internal class DotBenchmark { @Benchmark fun ejmlDot(blackhole: Blackhole) { - EjmlLinearSpace { + EjmlLinearSpaceDDRM { blackhole.consume(ejmlMatrix1 dot ejmlMatrix2) } } @Benchmark fun ejmlDotWithConversion(blackhole: Blackhole) { - EjmlLinearSpace { + EjmlLinearSpaceDDRM { blackhole.consume(matrix1 dot matrix2) } } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt index d1803e389..7bb32af28 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt @@ -11,25 +11,26 @@ import kotlinx.benchmark.Scope import kotlinx.benchmark.State import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.commons.linear.inverse -import space.kscience.kmath.ejml.EjmlLinearSpace -import space.kscience.kmath.ejml.inverse +import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM +import space.kscience.kmath.linear.InverseMatrixFeature import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.inverseWithLup import space.kscience.kmath.linear.invoke +import space.kscience.kmath.nd.getFeature import kotlin.random.Random @State(Scope.Benchmark) internal class MatrixInverseBenchmark { - companion object { - val random = Random(1224) - const val dim = 100 + private companion object { + private val random = Random(1224) + private const val dim = 100 private val space = LinearSpace.real //creating invertible matrix - 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 } + private val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } + private val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 } + private val matrix = space { l dot u } } @Benchmark @@ -46,8 +47,8 @@ internal class MatrixInverseBenchmark { @Benchmark fun ejmlInverse(blackhole: Blackhole) { - with(EjmlLinearSpace) { - blackhole.consume(inverse(matrix)) + with(EjmlLinearSpaceDDRM) { + blackhole.consume(matrix.getFeature>()?.inverse) } } } diff --git a/build.gradle.kts b/build.gradle.kts index 4e0b6f256..506f51a0e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,14 +4,12 @@ plugins { allprojects { repositories { - jcenter() maven("https://clojars.org/repo") - maven("https://dl.bintray.com/egor-bogomolov/astminer/") - maven("https://dl.bintray.com/hotkeytlt/maven") maven("https://jitpack.io") - maven("http://logicrunch.research.it.uu.se/maven/") { + maven("http://logicrunch.research.it.uu.se/maven") { isAllowInsecureProtocol = true } + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") mavenCentral() } @@ -23,22 +21,16 @@ subprojects { if (name.startsWith("kmath")) apply() afterEvaluate { - tasks.withType { - dokkaSourceSets.all { - val readmeFile = File(this@subprojects.projectDir, "./README.md") - if (readmeFile.exists()) - includes.setFrom(includes + readmeFile.absolutePath) + tasks.withType { + dependsOn(tasks.getByName("assemble")) - arrayOf( - "http://ejml.org/javadoc/", - "https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/", - "https://deeplearning4j.org/api/latest/" - ).map { java.net.URL("${it}package-list") to java.net.URL(it) }.forEach { (a, b) -> - externalDocumentationLink { - packageListUrl.set(a) - url.set(b) - } - } + dokkaSourceSets.all { + val readmeFile = File(this@subprojects.projectDir, "README.md") + if (readmeFile.exists()) includes.setFrom(includes + readmeFile.absolutePath) + externalDocumentationLink("http://ejml.org/javadoc/") + externalDocumentationLink("https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/") + externalDocumentationLink("https://deeplearning4j.org/api/latest/") + externalDocumentationLink("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/") } } } diff --git a/docs/templates/ARTIFACT-TEMPLATE.md b/docs/templates/ARTIFACT-TEMPLATE.md index 01d9c51da..1bac2a8ff 100644 --- a/docs/templates/ARTIFACT-TEMPLATE.md +++ b/docs/templates/ARTIFACT-TEMPLATE.md @@ -6,8 +6,7 @@ The Maven coordinates of this project are `${group}:${name}:${version}`. ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { @@ -18,8 +17,7 @@ dependencies { ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 56feee9dc..571949b7b 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -4,14 +4,11 @@ plugins { repositories { mavenCentral() - jcenter() maven("https://repo.kotlin.link") maven("https://clojars.org/repo") - maven("https://dl.bintray.com/egor-bogomolov/astminer/") - maven("https://dl.bintray.com/hotkeytlt/maven") maven("https://jitpack.io") - maven{ - setUrl("http://logicrunch.research.it.uu.se/maven/") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers") + maven("http://logicrunch.research.it.uu.se/maven") { isAllowInsecureProtocol = true } } diff --git a/kmath-ast/README.md b/kmath-ast/README.md index 1ee78956e..4de165e72 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -16,8 +16,7 @@ The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-7` ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { @@ -28,8 +27,7 @@ dependencies { ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { diff --git a/kmath-complex/README.md b/kmath-complex/README.md index 70beab95a..06e10fa7a 100644 --- a/kmath-complex/README.md +++ b/kmath-complex/README.md @@ -14,8 +14,7 @@ The Maven coordinates of this project are `space.kscience:kmath-complex:0.3.0-de ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { @@ -26,8 +25,7 @@ dependencies { ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { diff --git a/kmath-core/README.md b/kmath-core/README.md index e28873045..36b30efcc 100644 --- a/kmath-core/README.md +++ b/kmath-core/README.md @@ -21,8 +21,7 @@ The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0-dev-7 ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { @@ -33,8 +32,7 @@ dependencies { ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { diff --git a/kmath-ejml/README.md b/kmath-ejml/README.md index 3bf29f803..97c5ae115 100644 --- a/kmath-ejml/README.md +++ b/kmath-ejml/README.md @@ -2,9 +2,9 @@ EJML based linear algebra implementation. - - [ejml-vector](src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : The Point implementation using SimpleMatrix. - - [ejml-matrix](src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : The Matrix implementation using SimpleMatrix. - - [ejml-linear-space](src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : The LinearSpace implementation using SimpleMatrix. + - [ejml-vector](src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : Point implementations. + - [ejml-matrix](src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : Matrix implementation. + - [ejml-linear-space](src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : LinearSpace implementations. ## Artifact: @@ -15,8 +15,7 @@ The Maven coordinates of this project are `space.kscience:kmath-ejml:0.3.0-dev-7 ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { @@ -27,8 +26,7 @@ dependencies { ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { diff --git a/kmath-ejml/build.gradle.kts b/kmath-ejml/build.gradle.kts index d3a49aeb0..c8e2ecd8b 100644 --- a/kmath-ejml/build.gradle.kts +++ b/kmath-ejml/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } dependencies { - api("org.ejml:ejml-simple:0.40") + api("org.ejml:ejml-ddense:0.40") api(project(":kmath-core")) } @@ -14,19 +14,19 @@ readme { feature( id = "ejml-vector", - description = "The Point implementation using SimpleMatrix.", + description = "Point implementations.", ref = "src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt" ) feature( id = "ejml-matrix", - description = "The Matrix implementation using SimpleMatrix.", + description = "Matrix implementation.", ref = "src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt" ) feature( id = "ejml-linear-space", - description = "The LinearSpace implementation using SimpleMatrix.", + description = "LinearSpace implementations.", ref = "src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt" ) } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt index 4b6421c9b..71cae4829 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt @@ -5,45 +5,71 @@ package space.kscience.kmath.ejml +import org.ejml.data.DMatrix +import org.ejml.data.DMatrixD1 +import org.ejml.data.DMatrixRMaj +import org.ejml.dense.row.CommonOps_DDRM import org.ejml.dense.row.factory.DecompositionFactory_DDRM -import org.ejml.simple.SimpleMatrix import space.kscience.kmath.linear.* import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.StructureFeature -import space.kscience.kmath.nd.getFeature import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Ring import space.kscience.kmath.structures.DoubleBuffer import kotlin.reflect.KClass import kotlin.reflect.cast /** - * Represents context of basic operations operating with [EjmlMatrix]. + * [LinearSpace] implementation specialized for a certain EJML type. + * + * @param T the type of items in the matrices. + * @param A the element context type. + * @param M the EJML matrix type. + * @author Iaroslav Postovalov + */ +public abstract class EjmlLinearSpace, M : org.ejml.data.Matrix> : LinearSpace { + /** + * Converts this matrix to EJML one. + */ + public abstract fun Matrix.toEjml(): EjmlMatrix + + /** + * Converts this vector to EJML one. + */ + public abstract fun Point.toEjml(): EjmlVector + + public abstract override fun buildMatrix( + rows: Int, + columns: Int, + initializer: A.(i: Int, j: Int) -> T, + ): EjmlMatrix + + public abstract override fun buildVector(size: Int, initializer: A.(Int) -> T): EjmlVector +} + +/** + * [EjmlLinearSpace] implementation based on [CommonOps_DDRM], [DecompositionFactory_DDRM] operations and + * [DMatrixRMaj] matrices. * * @author Iaroslav Postovalov - * @author Alexander Nozik */ -public object EjmlLinearSpace : LinearSpace { +public object EjmlLinearSpaceDDRM : EjmlLinearSpace() { /** * The [DoubleField] reference. */ public override val elementAlgebra: DoubleField get() = DoubleField - /** - * Converts this matrix to EJML one. - */ - @OptIn(UnstableKMathAPI::class) - public fun Matrix.toEjml(): EjmlMatrix = when (val matrix = origin) { - is EjmlMatrix -> matrix + @Suppress("UNCHECKED_CAST") + public override fun Matrix.toEjml(): EjmlDoubleMatrix = when { + this is EjmlDoubleMatrix<*> && origin is DMatrixRMaj -> this as EjmlDoubleMatrix else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } } - /** - * Converts this vector to EJML one. - */ - public fun Point.toEjml(): EjmlVector = when (this) { - is EjmlVector -> this - else -> EjmlVector(SimpleMatrix(size, 1).also { - (0 until it.numRows()).forEach { row -> it[row, 0] = get(row) } + @Suppress("UNCHECKED_CAST") + public override fun Point.toEjml(): EjmlDoubleVector = when { + this is EjmlDoubleVector<*> && origin is DMatrixRMaj -> this as EjmlDoubleVector + else -> EjmlDoubleVector(DMatrixRMaj(size, 1).also { + (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } }) } @@ -51,159 +77,178 @@ public object EjmlLinearSpace : LinearSpace { rows: Int, columns: Int, initializer: DoubleField.(i: Int, j: Int) -> Double, - ): EjmlMatrix = EjmlMatrix(SimpleMatrix(rows, columns).also { + ): EjmlDoubleMatrix = EjmlDoubleMatrix(DMatrixRMaj(rows, columns).also { (0 until rows).forEach { row -> - (0 until columns).forEach { col -> it[row, col] = DoubleField.initializer(row, col) } + (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } } }) - public override fun buildVector(size: Int, initializer: DoubleField.(Int) -> Double): Point = - EjmlVector(SimpleMatrix(size, 1).also { - (0 until it.numRows()).forEach { row -> it[row, 0] = DoubleField.initializer(row) } - }) + public override fun buildVector( + size: Int, + initializer: DoubleField.(Int) -> Double, + ): EjmlDoubleVector = EjmlDoubleVector(DMatrixRMaj(size, 1).also { + (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } + }) - private fun SimpleMatrix.wrapMatrix() = EjmlMatrix(this) - private fun SimpleMatrix.wrapVector() = EjmlVector(this) + private fun T.wrapMatrix() = EjmlDoubleMatrix(this) + private fun T.wrapVector() = EjmlDoubleVector(this) public override fun Matrix.unaryMinus(): Matrix = this * (-1.0) - public override fun Matrix.dot(other: Matrix): EjmlMatrix = - EjmlMatrix(toEjml().origin.mult(other.toEjml().origin)) + public override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.mult(toEjml().origin, other.toEjml().origin, out) + return out.wrapMatrix() + } - public override fun Matrix.dot(vector: Point): EjmlVector = - EjmlVector(toEjml().origin.mult(vector.toEjml().origin)) + public override fun Matrix.dot(vector: Point): EjmlDoubleVector { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.mult(toEjml().origin, vector.toEjml().origin, out) + return out.wrapVector() + } - public override operator fun Matrix.minus(other: Matrix): EjmlMatrix = - (toEjml().origin - other.toEjml().origin).wrapMatrix() + public override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.subtract(toEjml().origin, other.toEjml().origin, out) + return out.wrapMatrix() + } - public override operator fun Matrix.times(value: Double): EjmlMatrix = - toEjml().origin.scale(value).wrapMatrix() + public override operator fun Matrix.times(value: Double): EjmlDoubleMatrix { + val res = this.toEjml().origin.copy() + CommonOps_DDRM.scale(value, res) + return res.wrapMatrix() + } - public override fun Point.unaryMinus(): EjmlVector = - toEjml().origin.negative().wrapVector() + public override fun Point.unaryMinus(): EjmlDoubleVector { + val out = toEjml().origin.copy() + CommonOps_DDRM.changeSign(out) + return out.wrapVector() + } - public override fun Matrix.plus(other: Matrix): EjmlMatrix = - (toEjml().origin + other.toEjml().origin).wrapMatrix() + public override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.add(toEjml().origin, other.toEjml().origin, out) + return out.wrapMatrix() + } - public override fun Point.plus(other: Point): EjmlVector = - (toEjml().origin + other.toEjml().origin).wrapVector() + public override fun Point.plus(other: Point): EjmlDoubleVector { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.add(toEjml().origin, other.toEjml().origin, out) + return out.wrapVector() + } - public override fun Point.minus(other: Point): EjmlVector = - (toEjml().origin - other.toEjml().origin).wrapVector() + public override fun Point.minus(other: Point): EjmlDoubleVector { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.subtract(toEjml().origin, other.toEjml().origin, out) + return out.wrapVector() + } - public override fun Double.times(m: Matrix): EjmlMatrix = - m.toEjml().origin.scale(this).wrapMatrix() + public override fun Double.times(m: Matrix): EjmlDoubleMatrix = m * this - public override fun Point.times(value: Double): EjmlVector = - toEjml().origin.scale(value).wrapVector() + public override fun Point.times(value: Double): EjmlDoubleVector { + val res = this.toEjml().origin.copy() + CommonOps_DDRM.scale(value, res) + return res.wrapVector() + } - public override fun Double.times(v: Point): EjmlVector = - v.toEjml().origin.scale(this).wrapVector() + public override fun Double.times(v: Point): EjmlDoubleVector = v * this @UnstableKMathAPI public override fun getFeature(structure: Matrix, type: KClass): F? { - //Return the feature if it is intrinsic to the structure + // Return the feature if it is intrinsic to the structure structure.getFeature(type)?.let { return it } val origin = structure.toEjml().origin return when (type) { InverseMatrixFeature::class -> object : InverseMatrixFeature { - override val inverse: Matrix by lazy { EjmlMatrix(origin.invert()) } + override val inverse: Matrix by lazy { + val res = origin.copy() + CommonOps_DDRM.invert(res) + EjmlDoubleMatrix(res) + } } DeterminantFeature::class -> object : DeterminantFeature { - override val determinant: Double by lazy(origin::determinant) + override val determinant: Double by lazy { CommonOps_DDRM.det(DMatrixRMaj(origin)) } } SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature { private val svd by lazy { - DecompositionFactory_DDRM.svd(origin.numRows(), origin.numCols(), true, true, false) - .apply { decompose(origin.ddrm.copy()) } + DecompositionFactory_DDRM.svd(origin.numRows, origin.numCols, true, true, false) + .apply { decompose(origin.copy()) } } - override val u: Matrix by lazy { EjmlMatrix(SimpleMatrix(svd.getU(null, false))) } - override val s: Matrix by lazy { EjmlMatrix(SimpleMatrix(svd.getW(null))) } - override val v: Matrix by lazy { EjmlMatrix(SimpleMatrix(svd.getV(null, false))) } + override val u: Matrix by lazy { EjmlDoubleMatrix(svd.getU(null, false)) } + override val s: Matrix by lazy { EjmlDoubleMatrix(svd.getW(null)) } + override val v: Matrix by lazy { EjmlDoubleMatrix(svd.getV(null, false)) } override val singularValues: Point by lazy { DoubleBuffer(svd.singularValues) } } QRDecompositionFeature::class -> object : QRDecompositionFeature { private val qr by lazy { - DecompositionFactory_DDRM.qr().apply { decompose(origin.ddrm.copy()) } + DecompositionFactory_DDRM.qr().apply { decompose(origin.copy()) } } override val q: Matrix by lazy { - EjmlMatrix(SimpleMatrix(qr.getQ(null, false))) + OrthogonalFeature + EjmlDoubleMatrix(qr.getQ(null, false)) + OrthogonalFeature } - override val r: Matrix by lazy { EjmlMatrix(SimpleMatrix(qr.getR(null, false))) + UFeature } + override val r: Matrix by lazy { EjmlDoubleMatrix(qr.getR(null, false)) + UFeature } } CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { override val l: Matrix by lazy { val cholesky = - DecompositionFactory_DDRM.chol(structure.rowNum, true).apply { decompose(origin.ddrm.copy()) } + DecompositionFactory_DDRM.chol(structure.rowNum, true).apply { decompose(origin.copy()) } - EjmlMatrix(SimpleMatrix(cholesky.getT(null))) + LFeature + EjmlDoubleMatrix(cholesky.getT(null)) + LFeature } } LupDecompositionFeature::class -> object : LupDecompositionFeature { private val lup by lazy { - DecompositionFactory_DDRM.lu(origin.numRows(), origin.numCols()) - .apply { decompose(origin.ddrm.copy()) } + DecompositionFactory_DDRM.lu(origin.numRows, origin.numCols).apply { decompose(origin.copy()) } } override val l: Matrix by lazy { - EjmlMatrix(SimpleMatrix(lup.getLower(null))) + LFeature + EjmlDoubleMatrix(lup.getLower(null)) + LFeature } override val u: Matrix by lazy { - EjmlMatrix(SimpleMatrix(lup.getUpper(null))) + UFeature + EjmlDoubleMatrix(lup.getUpper(null)) + UFeature } - override val p: Matrix by lazy { EjmlMatrix(SimpleMatrix(lup.getRowPivot(null))) } + override val p: Matrix by lazy { EjmlDoubleMatrix(lup.getRowPivot(null)) } } else -> null }?.let(type::cast) } + + /** + * Solves for *x* in the following equation: *x = [a] -1 · [b]*. + * + * @param a the base matrix. + * @param b n by p matrix. + * @return the solution for 'x' that is n by p. + */ + public fun solve(a: Matrix, b: Matrix): EjmlDoubleMatrix { + val res = DMatrixRMaj(1, 1) + CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) + return EjmlDoubleMatrix(res) + } + + /** + * Solves for *x* in the following equation: *x = [a] -1 · [b]*. + * + * @param a the base matrix. + * @param b n by p vector. + * @return the solution for 'x' that is n by p. + */ + public fun solve(a: Matrix, b: Point): EjmlDoubleVector { + val res = DMatrixRMaj(1, 1) + CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) + return EjmlDoubleVector(res) + } } - -/** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p matrix. - * @return the solution for 'x' that is n by p. - * @author Iaroslav Postovalov - */ -public fun EjmlLinearSpace.solve(a: Matrix, b: Matrix): EjmlMatrix = - EjmlMatrix(a.toEjml().origin.solve(b.toEjml().origin)) - -/** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p vector. - * @return the solution for 'x' that is n by p. - * @author Iaroslav Postovalov - */ -public fun EjmlLinearSpace.solve(a: Matrix, b: Point): EjmlVector = - EjmlVector(a.toEjml().origin.solve(b.toEjml().origin)) - -/** - * Inverts this matrix. - * - * @author Alexander Nozik - */ -@OptIn(UnstableKMathAPI::class) -public fun EjmlMatrix.inverted(): EjmlMatrix = getFeature>()!!.inverse as EjmlMatrix - -/** - * Inverts the given matrix. - * - * @author Alexander Nozik - */ -public fun EjmlLinearSpace.inverse(matrix: Matrix): Matrix = matrix.toEjml().inverted() \ No newline at end of file diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt index 32907d199..92c4d1cf0 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt @@ -5,18 +5,28 @@ package space.kscience.kmath.ejml -import org.ejml.simple.SimpleMatrix -import space.kscience.kmath.linear.Matrix +import org.ejml.data.DMatrix +import org.ejml.data.Matrix +import space.kscience.kmath.nd.Structure2D /** - * The matrix implementation over EJML [SimpleMatrix]. + * [space.kscience.kmath.linear.Matrix] implementation based on EJML [Matrix]. * - * @property origin the underlying [SimpleMatrix]. + * @param T the type of elements contained in the buffer. + * @param M the type of EJML matrix. + * @property origin The underlying EJML matrix. * @author Iaroslav Postovalov */ -public class EjmlMatrix(public val origin: SimpleMatrix) : Matrix { - public override val rowNum: Int get() = origin.numRows() - public override val colNum: Int get() = origin.numCols() +public abstract class EjmlMatrix(public open val origin: M) : Structure2D { + public override val rowNum: Int get() = origin.numRows + public override val colNum: Int get() = origin.numCols +} +/** + * [EjmlMatrix] specialization for [Double]. + * + * @author Iaroslav Postovalov + */ +public class EjmlDoubleMatrix(public override val origin: M) : EjmlMatrix(origin) { public override operator fun get(i: Int, j: Int): Double = origin[i, j] } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt index 2f4b4a8e2..81502d6d0 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt @@ -5,35 +5,41 @@ package space.kscience.kmath.ejml -import org.ejml.simple.SimpleMatrix +import org.ejml.data.DMatrixD1 +import org.ejml.data.Matrix import space.kscience.kmath.linear.Point /** - * Represents point over EJML [SimpleMatrix]. + * [Point] implementation based on EJML [Matrix]. * - * @property origin the underlying [SimpleMatrix]. + * @param T the type of elements contained in the buffer. + * @param M the type of EJML matrix. + * @property origin The underlying matrix. * @author Iaroslav Postovalov */ -public class EjmlVector internal constructor(public val origin: SimpleMatrix) : Point { +public abstract class EjmlVector(public open val origin: M) : Point { public override val size: Int - get() = origin.numRows() + get() = origin.numRows - init { - require(origin.numCols() == 1) { "Only single column matrices are allowed" } - } - - public override operator fun get(index: Int): Double = origin[index] - - public override operator fun iterator(): Iterator = object : Iterator { + public override operator fun iterator(): Iterator = object : Iterator { private var cursor: Int = 0 - override fun next(): Double { + override fun next(): T { cursor += 1 - return origin[cursor - 1] + return this@EjmlVector[cursor - 1] } - override fun hasNext(): Boolean = cursor < origin.numCols() * origin.numRows() + override fun hasNext(): Boolean = cursor < origin.numCols * origin.numRows } public override fun toString(): String = "EjmlVector(origin=$origin)" } + +/** + * [EjmlVector] specialization for [Double]. + * + * @author Iaroslav Postovalov + */ +public class EjmlDoubleVector(public override val origin: M) : EjmlVector(origin) { + public override operator fun get(index: Int): Double = origin[index] +} diff --git a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt index 59f9602d6..485c53c38 100644 --- a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt +++ b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt @@ -5,12 +5,15 @@ package space.kscience.kmath.ejml +import org.ejml.data.DMatrixRMaj +import org.ejml.dense.row.CommonOps_DDRM +import org.ejml.dense.row.RandomMatrices_DDRM import org.ejml.dense.row.factory.DecompositionFactory_DDRM -import org.ejml.simple.SimpleMatrix -import space.kscience.kmath.linear.* +import space.kscience.kmath.linear.DeterminantFeature +import space.kscience.kmath.linear.LupDecompositionFeature +import space.kscience.kmath.linear.getFeature import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.getFeature import kotlin.random.Random import kotlin.random.asJavaRandom import kotlin.test.* @@ -22,65 +25,59 @@ fun assertMatrixEquals(expected: StructureND, actual: StructureND = EjmlLinearSpace.getFeature(w) ?: fail() - assertEquals(m.determinant(), det.determinant) - val lup: LupDecompositionFeature = EjmlLinearSpace.getFeature(w) ?: fail() + val w = EjmlDoubleMatrix(m) + val det: DeterminantFeature = EjmlLinearSpaceDDRM.getFeature(w) ?: fail() + assertEquals(CommonOps_DDRM.det(m), det.determinant) + val lup: LupDecompositionFeature = EjmlLinearSpaceDDRM.getFeature(w) ?: fail() - val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows(), m.numCols()) - .also { it.decompose(m.ddrm.copy()) } + val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows, m.numCols) + .also { it.decompose(m.copy()) } - assertMatrixEquals(EjmlMatrix(SimpleMatrix(ludecompositionF64.getLower(null))), lup.l) - assertMatrixEquals(EjmlMatrix(SimpleMatrix(ludecompositionF64.getUpper(null))), lup.u) - assertMatrixEquals(EjmlMatrix(SimpleMatrix(ludecompositionF64.getRowPivot(null))), lup.p) - } - - private object SomeFeature : MatrixFeature {} - - @OptIn(UnstableKMathAPI::class) - @Test - fun suggestFeature() { - assertNotNull((EjmlMatrix(randomMatrix) + SomeFeature).getFeature()) + assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getLower(null)), lup.l) + assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getUpper(null)), lup.u) + assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getRowPivot(null)), lup.p) } @Test fun get() { val m = randomMatrix - assertEquals(m[0, 0], EjmlMatrix(m)[0, 0]) + assertEquals(m[0, 0], EjmlDoubleMatrix(m)[0, 0]) } @Test fun origin() { val m = randomMatrix - assertSame(m, EjmlMatrix(m).origin) + assertSame(m, EjmlDoubleMatrix(m).origin) } } diff --git a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt index e1bcd269e..9bf76033d 100644 --- a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt +++ b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt @@ -5,7 +5,8 @@ package space.kscience.kmath.ejml -import org.ejml.simple.SimpleMatrix +import org.ejml.data.DMatrixRMaj +import org.ejml.dense.row.RandomMatrices_DDRM import kotlin.random.Random import kotlin.random.asJavaRandom import kotlin.test.Test @@ -15,30 +16,34 @@ import kotlin.test.assertSame internal class EjmlVectorTest { private val random = Random(0) - private val randomMatrix: SimpleMatrix - get() = SimpleMatrix.random_DDRM(random.nextInt(2, 100), 1, 0.0, 10.0, random.asJavaRandom()) + private val randomMatrix: DMatrixRMaj + get() { + val d = DMatrixRMaj(random.nextInt(2, 100), 1) + RandomMatrices_DDRM.fillUniform(d, random.asJavaRandom()) + return d + } @Test fun size() { val m = randomMatrix - val w = EjmlVector(m) - assertEquals(m.numRows(), w.size) + val w = EjmlDoubleVector(m) + assertEquals(m.numRows, w.size) } @Test fun get() { val m = randomMatrix - val w = EjmlVector(m) + val w = EjmlDoubleVector(m) assertEquals(m[0, 0], w[0]) } @Test fun iterator() { val m = randomMatrix - val w = EjmlVector(m) + val w = EjmlDoubleVector(m) assertEquals( - m.iterator(true, 0, 0, m.numRows() - 1, 0).asSequence().toList(), + m.iterator(true, 0, 0, m.numRows - 1, 0).asSequence().toList(), w.iterator().asSequence().toList() ) } @@ -46,7 +51,7 @@ internal class EjmlVectorTest { @Test fun origin() { val m = randomMatrix - val w = EjmlVector(m) + val w = EjmlDoubleVector(m) assertSame(m, w.origin) } } diff --git a/kmath-for-real/README.md b/kmath-for-real/README.md index 20e52deb2..46bf657c3 100644 --- a/kmath-for-real/README.md +++ b/kmath-for-real/README.md @@ -15,7 +15,6 @@ The Maven coordinates of this project are `space.kscience:kmath-for-real:0.3.0-d ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap } @@ -28,7 +27,6 @@ dependencies { repositories { maven("https://repo.kotlin.link") maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a } dependencies { diff --git a/kmath-functions/README.md b/kmath-functions/README.md index d5907f1c5..c7c30f1a1 100644 --- a/kmath-functions/README.md +++ b/kmath-functions/README.md @@ -17,7 +17,6 @@ The Maven coordinates of this project are `space.kscience:kmath-functions:0.3.0- ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap } @@ -30,7 +29,6 @@ dependencies { repositories { maven("https://repo.kotlin.link") maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a } dependencies { diff --git a/kmath-kotlingrad/build.gradle.kts b/kmath-kotlingrad/build.gradle.kts index f627beec9..ed5b5bcb4 100644 --- a/kmath-kotlingrad/build.gradle.kts +++ b/kmath-kotlingrad/build.gradle.kts @@ -4,8 +4,8 @@ plugins { } dependencies { - implementation("com.github.breandan:kaliningraph:0.1.4") - implementation("com.github.breandan:kotlingrad:0.4.0") + api("com.github.breandan:kaliningraph:0.1.4") + api("com.github.breandan:kotlingrad:0.4.5") api(project(":kmath-ast")) } diff --git a/kmath-nd4j/README.md b/kmath-nd4j/README.md index 66e0483a4..b4b586ea9 100644 --- a/kmath-nd4j/README.md +++ b/kmath-nd4j/README.md @@ -15,8 +15,7 @@ The Maven coordinates of this project are `space.kscience:kmath-nd4j:0.3.0-dev-7 ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { @@ -27,8 +26,7 @@ dependencies { ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { diff --git a/kmath-viktor/build.gradle.kts b/kmath-viktor/build.gradle.kts index 232bd1388..2e932b441 100644 --- a/kmath-viktor/build.gradle.kts +++ b/kmath-viktor/build.gradle.kts @@ -7,9 +7,9 @@ description = "Binding for https://github.com/JetBrains-Research/viktor" dependencies { api(project(":kmath-core")) - api("org.jetbrains.bio:viktor:1.0.1") + api("org.jetbrains.bio:viktor:1.1.0") } readme { maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT -} \ No newline at end of file +} -- 2.34.1