From 061398b009ae4819adbe95c570bc95fccfb46f82 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 22 Jan 2021 23:22:24 +0300 Subject: [PATCH 01/10] [WIP] Refactor NDStructures --- CHANGELOG.md | 1 + README.md | 2 +- build.gradle.kts | 2 +- .../kscience/kmath/benchmarks/DotBenchmark.kt | 4 +- .../benchmarks/LinearAlgebraBenchmark.kt | 1 - .../kmath/benchmarks/NDFieldBenchmark.kt | 9 +- .../kmath/benchmarks/ViktorBenchmark.kt | 4 +- .../kscience/kmath/operations/ComplexDemo.kt | 3 +- .../kscience/kmath/structures/ComplexND.kt | 13 +- .../kscience/kmath/structures/NDField.kt | 17 +- .../structures/StructureReadBenchmark.kt | 4 +- .../structures/StructureWriteBenchmark.kt | 2 + .../kscience/kmath/commons/linear/CMMatrix.kt | 3 +- .../kscience/kmath/commons/linear/CMSolver.kt | 2 +- kmath-core/README.md | 2 +- .../kmath/expressions/SymbolIndexer.kt | 2 +- .../kscience/kmath/linear/BufferMatrix.kt | 13 +- .../kscience/kmath/linear/LinearAlgebra.kt | 1 - .../kscience/kmath/linear/LupDecomposition.kt | 1 + .../kscience/kmath/linear/MatrixBuilder.kt | 9 +- .../kscience/kmath/linear/MatrixContext.kt | 1 - .../kscience/kmath/linear/MatrixFeatures.kt | 2 - .../kscience/kmath/linear/MatrixWrapper.kt | 8 +- .../kmath/linear/RealMatrixContext.kt | 1 - .../kscience/kmath/linear/VirtualMatrix.kt | 2 - .../kscience/kmath/nd/BufferNDAlgebra.kt | 128 +++++++++ .../kscience/kmath/nd/ComplexNDField.kt | 113 ++++++++ .../kotlin/kscience/kmath/nd/NDAlgebra.kt | 262 ++++++++++++++++++ .../kmath/{structures => nd}/NDStructure.kt | 96 ++++--- .../kotlin/kscience/kmath/nd/RealNDField.kt | 111 ++++++++ .../kotlin/kscience/kmath/nd/ShortNDRing.kt | 36 +++ .../kmath/{structures => nd}/Structure1D.kt | 7 +- .../kmath/{structures => nd}/Structure2D.kt | 12 +- .../kscience/kmath/operations/BigInt.kt | 1 + .../kmath/structures/BoxingNDField.kt | 81 ------ .../kscience/kmath/structures/BoxingNDRing.kt | 71 ----- .../kmath/structures/BufferAccessor2D.kt | 5 + .../kmath/structures/BufferedNDAlgebra.kt | 43 --- .../kmath/structures/BufferedNDElement.kt | 86 ------ .../kscience/kmath/structures/Buffers.kt | 2 +- .../kmath/structures/ComplexNDField.kt | 158 ----------- .../kmath/structures/ExtendedNDField.kt | 44 --- .../kmath/structures/FlaggedBuffer.kt | 2 +- .../kscience/kmath/structures/MemoryBuffer.kt | 2 +- .../kscience/kmath/structures/NDAlgebra.kt | 259 ----------------- .../kscience/kmath/structures/NDElement.kt | 134 --------- .../kscience/kmath/structures/RealNDField.kt | 140 ---------- .../kscience/kmath/structures/ShortNDRing.kt | 93 ------- .../kscience/kmath/linear/MatrixTest.kt | 5 +- .../kscience/kmath/linear/RealLUSolverTest.kt | 1 - .../kscience/kmath/structures/NDFieldTest.kt | 1 + .../kmath/structures/NumberNDFieldTest.kt | 3 +- .../kmath/structures/LazyNDStructure.kt | 4 +- .../kscience/kmath/dimensions/Wrappers.kt | 3 +- .../kotlin/kscience/kmath/ejml/EjmlMatrix.kt | 3 +- .../kscience/kmath/ejml/EjmlMatrixContext.kt | 5 +- .../kscience/kmath/ejml/EjmlMatrixTest.kt | 2 +- .../kotlin/kscience/kmath/real/RealMatrix.kt | 6 +- .../kotlin/kscience/kmath/real/realND.kt | 31 +++ .../kaceince/kmath/real/RealMatrixTest.kt | 4 +- .../kmath/interpolation/XYPointSet.kt | 2 +- .../kscience/kmath/histogram/RealHistogram.kt | 8 +- .../kscience.kmath.nd4j/Nd4jArrayAlgebra.kt | 132 ++++----- .../kscience.kmath.nd4j/Nd4jArrayStructure.kt | 4 +- .../kmath/nd4j/Nd4jArrayStructureTest.kt | 2 +- .../kmath/viktor/ViktorNDStructure.kt | 8 +- 66 files changed, 889 insertions(+), 1330 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt create mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt create mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDAlgebra.kt rename kmath-core/src/commonMain/kotlin/kscience/kmath/{structures => nd}/NDStructure.kt (84%) create mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt create mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ShortNDRing.kt rename kmath-core/src/commonMain/kotlin/kscience/kmath/{structures => nd}/Structure1D.kt (89%) rename kmath-core/src/commonMain/kotlin/kscience/kmath/{structures => nd}/Structure2D.kt (92%) delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDElement.kt delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ExtendedNDField.kt delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDElement.kt delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt delete mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortNDRing.kt create mode 100644 kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realND.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index aa70e6116..7536690b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - Matrix LUP `inverse` renamed to `inverseWithLUP` - `NumericAlgebra` moved outside of regular algebra chain (`Ring` no longer implements it). - Features moved to NDStructure and became transparent. +- Refactored `NDStructure` algebra to be more simple, preferring under-the-hood conversion to explicit NDStructure types ### Deprecated diff --git a/README.md b/README.md index 0899f77cc..258d79be5 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ submit a feature request if you want something to be implemented first. > > **Features:** > - [algebras](kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : Algebraic structures: contexts and elements -> - [nd](kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Many-dimensional structures +> - [nd](kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt) : Many-dimensional structures > - [buffers](kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt) : One-dimensional structure > - [expressions](kmath-core/src/commonMain/kotlin/kscience/kmath/expressions) : Functional Expressions > - [domains](kmath-core/src/commonMain/kotlin/kscience/kmath/domains) : Domains diff --git a/build.gradle.kts b/build.gradle.kts index d171bd608..0572217af 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { id("ru.mipt.npm.project") } -internal val kmathVersion: String by extra("0.2.0-dev-5") +internal val kmathVersion: String by extra("0.2.0-dev-6") internal val bintrayRepo: String by extra("kscience") internal val githubProject: String by extra("kmath") diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt index 5c59afaee..4a0c96f78 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt @@ -3,14 +3,12 @@ package kscience.kmath.benchmarks import kotlinx.benchmark.Benchmark import kscience.kmath.commons.linear.CMMatrixContext import kscience.kmath.ejml.EjmlMatrixContext - import kscience.kmath.linear.BufferMatrixContext import kscience.kmath.linear.RealMatrixContext -import kscience.kmath.linear.real +import kscience.kmath.nd.Matrix import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke import kscience.kmath.structures.Buffer -import kscience.kmath.structures.Matrix import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State import kotlin.random.Random diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt index 5ff43ef80..9e5f76112 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt @@ -8,7 +8,6 @@ import kscience.kmath.commons.linear.inverse import kscience.kmath.ejml.EjmlMatrixContext import kscience.kmath.ejml.inverse import kscience.kmath.operations.invoke -import kscience.kmath.structures.Matrix import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State import kotlin.random.Random diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/NDFieldBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/NDFieldBenchmark.kt index 1be8e7236..3f103dd37 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/NDFieldBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/NDFieldBenchmark.kt @@ -1,5 +1,8 @@ package kscience.kmath.benchmarks +import kscience.kmath.nd.AbstractNDBuffer +import kscience.kmath.nd.NDField +import kscience.kmath.nd.RealNDField import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke import kscience.kmath.structures.* @@ -12,7 +15,7 @@ internal class NDFieldBenchmark { @Benchmark fun autoFieldAdd() { bufferedField { - var res: NDBuffer = one + var res: AbstractNDBuffer = one repeat(n) { res += one } } } @@ -26,7 +29,7 @@ internal class NDFieldBenchmark { @Benchmark fun specializedFieldAdd() { specializedField { - var res: NDBuffer = one + var res: AbstractNDBuffer = one repeat(n) { res += 1.0 } } } @@ -35,7 +38,7 @@ internal class NDFieldBenchmark { @Benchmark fun boxingFieldAdd() { genericField { - var res: NDBuffer = one + var res: AbstractNDBuffer = one repeat(n) { res += one } } } diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt index 8663e353c..49b30cf7e 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt @@ -1,10 +1,10 @@ package kscience.kmath.benchmarks +import kscience.kmath.nd.NDField +import kscience.kmath.nd.RealNDField import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke import kscience.kmath.structures.BufferedNDField -import kscience.kmath.structures.NDField -import kscience.kmath.structures.RealNDField import kscience.kmath.viktor.ViktorNDField import org.jetbrains.bio.viktor.F64Array import org.openjdk.jmh.annotations.Benchmark diff --git a/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt b/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt index e84fd8df3..f90221582 100644 --- a/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt @@ -1,8 +1,7 @@ package kscience.kmath.operations +import kscience.kmath.nd.NDField import kscience.kmath.structures.NDElement -import kscience.kmath.structures.NDField -import kscience.kmath.structures.complex fun main() { // 2d element diff --git a/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt index b69590473..7dd725f0e 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt @@ -1,6 +1,7 @@ package kscience.kmath.structures import kscience.kmath.linear.transpose +import kscience.kmath.nd.* import kscience.kmath.operations.Complex import kscience.kmath.operations.ComplexField import kscience.kmath.operations.invoke @@ -10,12 +11,12 @@ fun main() { val dim = 1000 val n = 1000 - val realField = NDField.real(dim, dim) - val complexField: ComplexNDField = NDField.complex(dim, dim) + val realField = NDAlgebra.real(dim, dim) + val complexField: ComplexNDField = NDAlgebra.complex(dim, dim) val realTime = measureTimeMillis { realField { - var res: NDBuffer = one + var res: NDStructure = one repeat(n) { res += 1.0 } @@ -26,8 +27,10 @@ fun main() { val complexTime = measureTimeMillis { complexField { - var res: NDBuffer = one - repeat(n) { res += 1.0 } + var res: NDStructure = one + repeat(n) { + res += 1.0 + } } } diff --git a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt index b5130c92b..e26b8cbce 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt @@ -1,6 +1,7 @@ package kscience.kmath.structures import kotlinx.coroutines.GlobalScope +import kscience.kmath.nd.* import kscience.kmath.nd4j.Nd4jArrayField import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke @@ -22,29 +23,29 @@ fun main() { val n = 1000 // automatically build context most suited for given type. - val autoField = NDField.auto(RealField, dim, dim) + val autoField = NDAlgebra.field(RealField, Buffer.Companion::auto, dim, dim) // specialized nd-field for Double. It works as generic Double field as well - val specializedField = NDField.real(dim, dim) + val specializedField = NDAlgebra.real(dim, dim) //A generic boxing field. It should be used for objects, not primitives. - val genericField = NDField.boxing(RealField, dim, dim) + val boxingField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim) // Nd4j specialized field. val nd4jField = Nd4jArrayField.real(dim, dim) measureAndPrint("Automatic field addition") { autoField { - var res: NDBuffer = one + var res: NDStructure = one repeat(n) { res += 1.0 } } } measureAndPrint("Element addition") { - var res = genericField.one + var res: NDStructure = boxingField.one repeat(n) { res += 1.0 } } measureAndPrint("Specialized addition") { specializedField { - var res: NDBuffer = one + var res: NDStructure = one repeat(n) { res += 1.0 } } } @@ -70,8 +71,8 @@ fun main() { measureAndPrint("Generic addition") { //genericField.run(action) - genericField { - var res: NDBuffer = one + boxingField { + var res: NDStructure = one repeat(n) { res += 1.0 // couldn't avoid using `one` due to resolution ambiguity } } diff --git a/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt index 51fd4f956..ba7bcb5f2 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt @@ -1,5 +1,7 @@ package kscience.kmath.structures +import kscience.kmath.nd.DefaultStrides +import kscience.kmath.nd.NDBuffer import kotlin.system.measureTimeMillis fun main() { @@ -7,7 +9,7 @@ fun main() { val array = DoubleArray(n * n) { 1.0 } val buffer = RealBuffer(array) val strides = DefaultStrides(intArrayOf(n, n)) - val structure = BufferNDStructure(strides, buffer) + val structure = NDBuffer(strides, buffer) measureTimeMillis { var res = 0.0 diff --git a/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt index db55b454f..307b068df 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt @@ -1,5 +1,7 @@ package kscience.kmath.structures +import kscience.kmath.nd.NDStructure +import kscience.kmath.nd.mapToBuffer import kotlin.system.measureTimeMillis fun main() { diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt index 850446afa..b9fc8b72a 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt @@ -3,9 +3,8 @@ package kscience.kmath.commons.linear import kscience.kmath.linear.DiagonalFeature import kscience.kmath.linear.MatrixContext import kscience.kmath.linear.Point -import kscience.kmath.linear.origin import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.structures.Matrix +import kscience.kmath.nd.Matrix import org.apache.commons.math3.linear.* import kotlin.reflect.KClass import kotlin.reflect.cast diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt index 210014e1a..aa0bf4e1a 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt @@ -1,7 +1,7 @@ package kscience.kmath.commons.linear import kscience.kmath.linear.Point -import kscience.kmath.structures.Matrix +import kscience.kmath.nd.Matrix import org.apache.commons.math3.linear.* public enum class CMDecomposition { diff --git a/kmath-core/README.md b/kmath-core/README.md index 7882e5252..9ed54b9eb 100644 --- a/kmath-core/README.md +++ b/kmath-core/README.md @@ -3,7 +3,7 @@ The core features of KMath: - [algebras](src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : Algebraic structures: contexts and elements - - [nd](src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Many-dimensional structures + - [nd](src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt) : Many-dimensional structures - [buffers](src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt) : One-dimensional structure - [expressions](src/commonMain/kotlin/kscience/kmath/expressions) : Functional Expressions - [domains](src/commonMain/kotlin/kscience/kmath/domains) : Domains diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SymbolIndexer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SymbolIndexer.kt index 6c61c7c7d..fd0661080 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SymbolIndexer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SymbolIndexer.kt @@ -1,8 +1,8 @@ package kscience.kmath.expressions import kscience.kmath.linear.Point +import kscience.kmath.nd.Structure2D import kscience.kmath.structures.BufferFactory -import kscience.kmath.structures.Structure2D /** * An environment to easy transform indexed variables to symbols and back. diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt index a74d948fc..bcd1c9976 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt @@ -1,7 +1,18 @@ package kscience.kmath.linear +import kscience.kmath.nd.NDStructure +import kscience.kmath.nd.Structure2D import kscience.kmath.operations.Ring -import kscience.kmath.structures.* +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.BufferFactory +import kscience.kmath.structures.asSequence + +/** + * Alias for [Structure2D] with more familiar name. + * + * @param T the type of items. + */ +public typealias Matrix = Structure2D /** * Basic implementation of Matrix space based on [NDStructure] diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LinearAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LinearAlgebra.kt index 034decc2f..d9201d0f1 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LinearAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LinearAlgebra.kt @@ -1,7 +1,6 @@ package kscience.kmath.linear import kscience.kmath.structures.Buffer -import kscience.kmath.structures.Matrix import kscience.kmath.structures.VirtualBuffer public typealias Point = Buffer diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LupDecomposition.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LupDecomposition.kt index 5cf7c8f70..ddf7c3fd2 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LupDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LupDecomposition.kt @@ -1,6 +1,7 @@ package kscience.kmath.linear import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.nd.getFeature import kscience.kmath.operations.* import kscience.kmath.structures.* diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixBuilder.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixBuilder.kt index c0c209248..cb01939cc 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixBuilder.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixBuilder.kt @@ -1,6 +1,9 @@ package kscience.kmath.linear -import kscience.kmath.structures.* +import kscience.kmath.nd.Structure2D +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.BufferFactory +import kscience.kmath.structures.asBuffer public class MatrixBuilder(public val rows: Int, public val columns: Int) { public operator fun invoke(vararg elements: T): Matrix { @@ -22,7 +25,7 @@ public fun Structure2D.Companion.row(vararg values: T): Matrix { public inline fun Structure2D.Companion.row( size: Int, factory: BufferFactory = Buffer.Companion::auto, - noinline builder: (Int) -> T + noinline builder: (Int) -> T, ): Matrix { val buffer = factory(size, builder) return BufferMatrix(1, size, buffer) @@ -36,7 +39,7 @@ public fun Structure2D.Companion.column(vararg values: T): Matrix { public inline fun Structure2D.Companion.column( size: Int, factory: BufferFactory = Buffer.Companion::auto, - noinline builder: (Int) -> T + noinline builder: (Int) -> T, ): Matrix { val buffer = factory(size, builder) return BufferMatrix(size, 1, buffer) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixContext.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixContext.kt index 59a41f840..8766f2e5f 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixContext.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixContext.kt @@ -6,7 +6,6 @@ import kscience.kmath.operations.invoke import kscience.kmath.operations.sum import kscience.kmath.structures.Buffer import kscience.kmath.structures.BufferFactory -import kscience.kmath.structures.Matrix import kscience.kmath.structures.asSequence /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt index e61feec6c..250e0cc04 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt @@ -1,7 +1,5 @@ package kscience.kmath.linear -import kscience.kmath.structures.Matrix - /** * A marker interface representing some properties of matrices or additional transformations of them. Features are used * to optimize matrix operations performance in some cases or retrieve the APIs. diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixWrapper.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixWrapper.kt index 362db1fe7..726a91e97 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixWrapper.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixWrapper.kt @@ -1,11 +1,10 @@ package kscience.kmath.linear import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.nd.Structure2D +import kscience.kmath.nd.getFeature import kscience.kmath.operations.Ring -import kscience.kmath.structures.Matrix -import kscience.kmath.structures.Structure2D import kscience.kmath.structures.asBuffer -import kscience.kmath.structures.getFeature import kotlin.math.sqrt import kotlin.reflect.KClass import kotlin.reflect.safeCast @@ -39,7 +38,8 @@ public class MatrixWrapper internal constructor( * Origin does not necessary store all features. */ @UnstableKMathAPI -public val Matrix.origin: Matrix get() = (this as? MatrixWrapper)?.origin ?: this +public val Matrix.origin: Matrix + get() = (this as? MatrixWrapper)?.origin ?: this /** * Add a single feature to a [Matrix] diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/RealMatrixContext.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/RealMatrixContext.kt index 8e197672f..3d544a9af 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/RealMatrixContext.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/RealMatrixContext.kt @@ -1,6 +1,5 @@ package kscience.kmath.linear -import kscience.kmath.structures.Matrix import kscience.kmath.structures.RealBuffer @Suppress("OVERRIDE_BY_INLINE") diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VirtualMatrix.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VirtualMatrix.kt index 0269a64d1..df4e4de09 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VirtualMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VirtualMatrix.kt @@ -1,7 +1,5 @@ package kscience.kmath.linear -import kscience.kmath.structures.Matrix - public class VirtualMatrix( override val rowNum: Int, override val colNum: Int, diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt new file mode 100644 index 000000000..a92e08c60 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt @@ -0,0 +1,128 @@ +package kscience.kmath.nd + +import kscience.kmath.nd.* +import kscience.kmath.operations.* +import kscience.kmath.structures.BufferFactory +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +public interface BufferNDAlgebra : NDAlgebra { + public val strides: Strides + public val bufferFactory: BufferFactory + + override fun produce(initializer: C.(IntArray) -> T): NDBuffer = NDBuffer( + strides, + bufferFactory(strides.linearSize) { offset -> + elementContext.initializer(strides.index(offset)) + } + ) + + public val NDStructure.ndBuffer: NDBuffer + get() = when { + !shape.contentEquals(this@BufferNDAlgebra.shape) -> throw ShapeMismatchException( + this@BufferNDAlgebra.shape, + shape + ) + this is NDBuffer && this.strides == this@BufferNDAlgebra.strides -> this + else -> produce { this@ndBuffer[it] } + } + + override fun map(arg: NDStructure, transform: C.(T) -> T): NDBuffer { + val argAsBuffer = arg.ndBuffer + val buffer = bufferFactory(strides.linearSize) { offset -> + elementContext.transform(argAsBuffer.buffer[offset]) + } + return NDBuffer(strides, buffer) + } + + override fun mapIndexed(arg: NDStructure, transform: C.(index: IntArray, T) -> T): NDStructure { + val argAsBuffer = arg.ndBuffer + val buffer = bufferFactory(strides.linearSize) { offset -> + elementContext.transform( + strides.index(offset), + argAsBuffer[offset] + ) + } + return NDBuffer(strides, buffer) + } + + override fun combine(a: NDStructure, b: NDStructure, transform: C.(T, T) -> T): NDStructure { + val aBuffer = a.ndBuffer + val bBuffer = b.ndBuffer + val buffer = bufferFactory(strides.linearSize) { offset -> + elementContext.transform(aBuffer.buffer[offset], bBuffer[offset]) + } + return NDBuffer(strides, buffer) + } +} + +public open class BufferedNDSpace>( + final override val shape: IntArray, + final override val elementContext: R, + final override val bufferFactory: BufferFactory, +) : NDSpace, BufferNDAlgebra { + override val strides: Strides = DefaultStrides(shape) + override val zero: NDBuffer by lazy { produce { zero } } +} + +public open class BufferedNDRing>( + shape: IntArray, + elementContext: R, + bufferFactory: BufferFactory, +) : BufferedNDSpace(shape, elementContext, bufferFactory), NDRing { + override val one: NDBuffer by lazy { produce { one } } +} + +public open class BufferedNDField>( + shape: IntArray, + elementContext: R, + bufferFactory: BufferFactory, +) : BufferedNDRing(shape, elementContext, bufferFactory), NDField + +// space factories +public fun > NDAlgebra.Companion.space( + space: A, + bufferFactory: BufferFactory, + vararg shape: Int, +): BufferedNDSpace = BufferedNDSpace(shape, space, bufferFactory) + +public inline fun , R> A.nd( + noinline bufferFactory: BufferFactory, + vararg shape: Int, + action: BufferedNDSpace.() -> R, +): R { + contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } + return NDAlgebra.space(this, bufferFactory, *shape).run(action) +} + +//ring factories +public fun > NDAlgebra.Companion.ring( + ring: A, + bufferFactory: BufferFactory, + vararg shape: Int, +): BufferedNDRing = BufferedNDRing(shape, ring, bufferFactory) + +public inline fun , R> A.nd( + noinline bufferFactory: BufferFactory, + vararg shape: Int, + action: BufferedNDRing.() -> R, +): R { + contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } + return NDAlgebra.ring(this, bufferFactory, *shape).run(action) +} + +//field factories +public fun > NDAlgebra.Companion.field( + field: A, + bufferFactory: BufferFactory, + vararg shape: Int, +): BufferedNDField = BufferedNDField(shape, field, bufferFactory) + +public inline fun , R> A.nd( + noinline bufferFactory: BufferFactory, + vararg shape: Int, + action: BufferedNDField.() -> R, +): R { + contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } + return NDAlgebra.field(this, bufferFactory, *shape).run(action) +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt new file mode 100644 index 000000000..dbcce5440 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt @@ -0,0 +1,113 @@ +package kscience.kmath.nd + +import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.operations.* +import kscience.kmath.structures.Buffer +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + + +/** + * An optimized nd-field for complex numbers + */ +@OptIn(UnstableKMathAPI::class) +public open class ComplexNDField( + shape: IntArray, +) : BufferedNDField(shape, ComplexField, Buffer.Companion::complex), + RingWithNumbers>, + ExtendedField> { + + override val zero: NDBuffer by lazy { produce { zero } } + override val one: NDBuffer by lazy { produce { one } } + + override fun number(value: Number): NDBuffer { + val d = value.toComplex() // minimize conversions + return produce { d } + } +// +// @Suppress("OVERRIDE_BY_INLINE") +// override inline fun map( +// arg: AbstractNDBuffer, +// transform: RealField.(Double) -> Double, +// ): RealNDElement { +// check(arg) +// val array = RealBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) } +// return BufferedNDFieldElement(this, array) +// } +// +// @Suppress("OVERRIDE_BY_INLINE") +// override inline fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement { +// val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } +// return BufferedNDFieldElement(this, array) +// } +// +// @Suppress("OVERRIDE_BY_INLINE") +// override inline fun mapIndexed( +// arg: AbstractNDBuffer, +// transform: RealField.(index: IntArray, Double) -> Double, +// ): RealNDElement { +// check(arg) +// return BufferedNDFieldElement( +// this, +// RealBuffer(arg.strides.linearSize) { offset -> +// elementContext.transform( +// arg.strides.index(offset), +// arg.buffer[offset] +// ) +// }) +// } +// +// @Suppress("OVERRIDE_BY_INLINE") +// override inline fun combine( +// a: AbstractNDBuffer, +// b: AbstractNDBuffer, +// transform: RealField.(Double, Double) -> Double, +// ): RealNDElement { +// check(a, b) +// val buffer = RealBuffer(strides.linearSize) { offset -> +// elementContext.transform(a.buffer[offset], b.buffer[offset]) +// } +// return BufferedNDFieldElement(this, buffer) +// } + + override fun power(arg: NDStructure, pow: Number): NDBuffer = map(arg) { power(it, pow) } + + override fun exp(arg: NDStructure): NDBuffer = map(arg) { exp(it) } + + override fun ln(arg: NDStructure): NDBuffer = map(arg) { ln(it) } + + override fun sin(arg: NDStructure): NDBuffer = map(arg) { sin(it) } + override fun cos(arg: NDStructure): NDBuffer = map(arg) { cos(it) } + override fun tan(arg: NDStructure): NDBuffer = map(arg) { tan(it) } + override fun asin(arg: NDStructure): NDBuffer = map(arg) { asin(it) } + override fun acos(arg: NDStructure): NDBuffer = map(arg) { acos(it) } + override fun atan(arg: NDStructure): NDBuffer = map(arg) { atan(it) } + + override fun sinh(arg: NDStructure): NDBuffer = map(arg) { sinh(it) } + override fun cosh(arg: NDStructure): NDBuffer = map(arg) { cosh(it) } + override fun tanh(arg: NDStructure): NDBuffer = map(arg) { tanh(it) } + override fun asinh(arg: NDStructure): NDBuffer = map(arg) { asinh(it) } + override fun acosh(arg: NDStructure): NDBuffer = map(arg) { acosh(it) } + override fun atanh(arg: NDStructure): NDBuffer = map(arg) { atanh(it) } +} + + +/** + * Fast element production using function inlining + */ +public inline fun BufferedNDField.produceInline(initializer: ComplexField.(Int) -> Complex): NDBuffer { + contract { callsInPlace(initializer, InvocationKind.EXACTLY_ONCE) } + val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.initializer(offset) } + return NDBuffer(strides, buffer) +} + + +public fun NDAlgebra.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDField(shape) + +/** + * Produce a context for n-dimensional operations inside this real field + */ +public inline fun ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R { + contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } + return ComplexNDField(shape).action() +} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDAlgebra.kt new file mode 100644 index 000000000..facc7eb38 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDAlgebra.kt @@ -0,0 +1,262 @@ +package kscience.kmath.nd + +import kscience.kmath.operations.Field +import kscience.kmath.operations.Ring +import kscience.kmath.operations.Space +import kscience.kmath.structures.* + +/** + * An exception is thrown when the expected ans actual shape of NDArray differs. + * + * @property expected the expected shape. + * @property actual the actual shape. + */ +public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) : + RuntimeException("Shape ${actual.contentToString()} doesn't fit in expected shape ${expected.contentToString()}.") + +/** + * The base interface for all ND-algebra implementations. + * + * @param T the type of ND-structure element. + * @param C the type of the element context. + * @param N the type of the structure. + */ +public interface NDAlgebra { + /** + * The shape of ND-structures this algebra operates on. + */ + public val shape: IntArray + + /** + * The algebra over elements of ND structure. + */ + public val elementContext: C + + /** + * Produces a new [N] structure using given initializer function. + */ + public fun produce(initializer: C.(IntArray) -> T): NDStructure + + /** + * Maps elements from one structure to another one by applying [transform] to them. + */ + public fun map(arg: NDStructure, transform: C.(T) -> T): NDStructure + + /** + * Maps elements from one structure to another one by applying [transform] to them alongside with their indices. + */ + public fun mapIndexed(arg: NDStructure, transform: C.(index: IntArray, T) -> T): NDStructure + + /** + * Combines two structures into one. + */ + public fun combine(a: NDStructure, b: NDStructure, transform: C.(T, T) -> T): NDStructure + + /** + * Element-wise invocation of function working on [T] on a [NDStructure]. + */ + public operator fun Function1.invoke(structure: NDStructure): NDStructure = + map(structure) { value -> this@invoke(value) } + + public companion object +} + +/** + * Checks if given elements are consistent with this context. + * + * @param structures the structures to check. + * @return the array of valid structures. + */ +internal fun NDAlgebra.checkShape(vararg structures: NDStructure): Array> = structures + .map(NDStructure::shape) + .singleOrNull { !shape.contentEquals(it) } + ?.let>> { throw ShapeMismatchException(shape, it) } + ?: structures + +/** + * Checks if given element is consistent with this context. + * + * @param element the structure to check. + * @return the valid structure. + */ +internal fun NDAlgebra.checkShape(element: NDStructure): NDStructure { + if (!element.shape.contentEquals(shape)) throw ShapeMismatchException(shape, element.shape) + return element +} + +/** + * Space of [NDStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param S the type of space of structure elements. + */ +public interface NDSpace> : Space>, NDAlgebra { + /** + * Element-wise addition. + * + * @param a the addend. + * @param b the augend. + * @return the sum. + */ + public override fun add(a: NDStructure, b: NDStructure): NDStructure = + combine(a, b) { aValue, bValue -> add(aValue, bValue) } + + /** + * Element-wise multiplication by scalar. + * + * @param a the multiplicand. + * @param k the multiplier. + * @return the product. + */ + public override fun multiply(a: NDStructure, k: Number): NDStructure = map(a) { multiply(it, k) } + + // TODO move to extensions after KEEP-176 + + /** + * Adds an ND structure to an element of it. + * + * @receiver the addend. + * @param arg the augend. + * @return the sum. + */ + public operator fun NDStructure.plus(arg: T): NDStructure = map(this) { value -> add(arg, value) } + + /** + * Subtracts an element from ND structure of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ + public operator fun NDStructure.minus(arg: T): NDStructure = map(this) { value -> add(arg, -value) } + + /** + * Adds an element to ND structure of it. + * + * @receiver the addend. + * @param arg the augend. + * @return the sum. + */ + public operator fun T.plus(arg: NDStructure): NDStructure = map(arg) { value -> add(this@plus, value) } + + /** + * Subtracts an ND structure from an element of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ + public operator fun T.minus(arg: NDStructure): NDStructure = map(arg) { value -> add(-this@minus, value) } + + public companion object +} + +/** + * Ring of [NDStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param R the type of ring of structure elements. + */ +public interface NDRing> : Ring>, NDSpace { + /** + * Element-wise multiplication. + * + * @param a the multiplicand. + * @param b the multiplier. + * @return the product. + */ + public override fun multiply(a: NDStructure, b: NDStructure): NDStructure = + combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } + + //TODO move to extensions after KEEP-176 + + /** + * Multiplies an ND structure by an element of it. + * + * @receiver the multiplicand. + * @param arg the multiplier. + * @return the product. + */ + public operator fun NDStructure.times(arg: T): NDStructure = map(this) { value -> multiply(arg, value) } + + /** + * Multiplies an element by a ND structure of it. + * + * @receiver the multiplicand. + * @param arg the multiplier. + * @return the product. + */ + public operator fun T.times(arg: NDStructure): NDStructure = map(arg) { value -> multiply(this@times, value) } + + public companion object +} + +/** + * Field of [NDStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param F the type field of structure elements. + */ +public interface NDField> : Field>, NDRing { + /** + * Element-wise division. + * + * @param a the dividend. + * @param b the divisor. + * @return the quotient. + */ + public override fun divide(a: NDStructure, b: NDStructure): NDStructure = + combine(a, b) { aValue, bValue -> divide(aValue, bValue) } + + //TODO move to extensions after KEEP-176 + /** + * Divides an ND structure by an element of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ + public operator fun NDStructure.div(arg: T): NDStructure = map(this) { value -> divide(arg, value) } + + /** + * Divides an element by an ND structure of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ + public operator fun T.div(arg: NDStructure): NDStructure = map(arg) { divide(it, this@div) } + +// @ThreadLocal +// public companion object { +// private val realNDFieldCache: MutableMap = hashMapOf() +// +// /** +// * Create a nd-field for [Double] values or pull it from cache if it was created previously. +// */ +// public fun real(vararg shape: Int): RealNDField = realNDFieldCache.getOrPut(shape) { RealNDField(shape) } +// +// /** +// * Create an ND field with boxing generic buffer. +// */ +// public fun > boxing( +// field: F, +// vararg shape: Int, +// bufferFactory: BufferFactory = Buffer.Companion::boxing, +// ): BufferedNDField = BufferedNDField(shape, field, bufferFactory) +// +// /** +// * Create a most suitable implementation for nd-field using reified class. +// */ +// @Suppress("UNCHECKED_CAST") +// public inline fun > auto(field: F, vararg shape: Int): NDField = +// when { +// T::class == Double::class -> real(*shape) as NDField +// T::class == Complex::class -> complex(*shape) as BufferedNDField +// else -> BoxingNDField(shape, field, Buffer.Companion::auto) +// } +// } +} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt similarity index 84% rename from kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt index e7d89ca7e..09c6fa198 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt @@ -1,6 +1,10 @@ -package kscience.kmath.structures +package kscience.kmath.nd import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.BufferFactory +import kscience.kmath.structures.MutableBuffer +import kscience.kmath.structures.asSequence import kotlin.jvm.JvmName import kotlin.native.concurrent.ThreadLocal import kotlin.reflect.KClass @@ -74,8 +78,8 @@ public interface NDStructure { strides: Strides, bufferFactory: BufferFactory = Buffer.Companion::boxing, initializer: (IntArray) -> T, - ): BufferNDStructure = - BufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) }) + ): NDBuffer = + NDBuffer(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) }) /** * Inline create NDStructure with non-boxing buffer implementation if it is possible @@ -83,40 +87,40 @@ public interface NDStructure { public inline fun auto( strides: Strides, crossinline initializer: (IntArray) -> T, - ): BufferNDStructure = - BufferNDStructure(strides, Buffer.auto(strides.linearSize) { i -> initializer(strides.index(i)) }) + ): NDBuffer = + NDBuffer(strides, Buffer.auto(strides.linearSize) { i -> initializer(strides.index(i)) }) public inline fun auto( type: KClass, strides: Strides, crossinline initializer: (IntArray) -> T, - ): BufferNDStructure = - BufferNDStructure(strides, Buffer.auto(type, strides.linearSize) { i -> initializer(strides.index(i)) }) + ): NDBuffer = + NDBuffer(strides, Buffer.auto(type, strides.linearSize) { i -> initializer(strides.index(i)) }) public fun build( shape: IntArray, bufferFactory: BufferFactory = Buffer.Companion::boxing, initializer: (IntArray) -> T, - ): BufferNDStructure = build(DefaultStrides(shape), bufferFactory, initializer) + ): NDBuffer = build(DefaultStrides(shape), bufferFactory, initializer) public inline fun auto( shape: IntArray, crossinline initializer: (IntArray) -> T, - ): BufferNDStructure = + ): NDBuffer = auto(DefaultStrides(shape), initializer) @JvmName("autoVarArg") public inline fun auto( vararg shape: Int, crossinline initializer: (IntArray) -> T, - ): BufferNDStructure = + ): NDBuffer = auto(DefaultStrides(shape), initializer) public inline fun auto( type: KClass, vararg shape: Int, crossinline initializer: (IntArray) -> T, - ): BufferNDStructure = + ): NDBuffer = auto(type, DefaultStrides(shape), initializer) } } @@ -156,7 +160,7 @@ public inline fun MutableNDStructure.mapInPlace(action: (IntArray, T) -> */ public interface Strides { /** - * Shape of NDstructure + * Shape of NDStructure */ public val shape: IntArray @@ -185,7 +189,9 @@ public interface Strides { /** * Iterate over ND indices in a natural order */ - public fun indices(): Sequence = (0 until linearSize).asSequence().map { index(it) } + public fun indices(): Sequence = (0 until linearSize).asSequence().map { + index(it) + } } /** @@ -211,7 +217,7 @@ public class DefaultStrides private constructor(override val shape: IntArray) : } override fun offset(index: IntArray): Int = index.mapIndexed { i, value -> - if (value < 0 || value >= this.shape[i]) + if (value < 0 || value >= shape[i]) throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${this.shape[i]})") value * strides[i] @@ -256,23 +262,29 @@ public class DefaultStrides private constructor(override val shape: IntArray) : * Represents [NDStructure] over [Buffer]. * * @param T the type of items. + * @param strides The strides to access elements of [Buffer] by linear indices. + * @param buffer The underlying buffer. */ -public abstract class NDBuffer : NDStructure { - /** - * The underlying buffer. - */ - public abstract val buffer: Buffer +public open class NDBuffer( + public val strides: Strides, + buffer: Buffer, +) : NDStructure { - /** - * The strides to access elements of [Buffer] by linear indices. - */ - public abstract val strides: Strides + init { + if (strides.linearSize != buffer.size) { + error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}") + } + } + + public open val buffer: Buffer = buffer override operator fun get(index: IntArray): T = buffer[strides.offset(index)] override val shape: IntArray get() = strides.shape - override fun elements(): Sequence> = strides.indices().map { it to this[it] } + override fun elements(): Sequence> = strides.indices().map { + it to this[it] + } override fun equals(other: Any?): Boolean { return NDStructure.contentEquals(this, other as? NDStructure<*> ?: return false) @@ -297,46 +309,30 @@ public abstract class NDBuffer : NDStructure { } return "NDBuffer(shape=${shape.contentToString()}, buffer=$bufferRepr)" } - - } /** - * Boxing generic [NDStructure] - */ -public class BufferNDStructure( - override val strides: Strides, - override val buffer: Buffer, -) : NDBuffer() { - init { - if (strides.linearSize != buffer.size) { - error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}") - } - } -} - -/** - * Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferNDStructure] + * Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [NDBuffer] */ public inline fun NDStructure.mapToBuffer( factory: BufferFactory = Buffer.Companion::auto, crossinline transform: (T) -> R, -): BufferNDStructure { - return if (this is BufferNDStructure) - BufferNDStructure(this.strides, factory.invoke(strides.linearSize) { transform(buffer[it]) }) +): NDBuffer { + return if (this is NDBuffer) + NDBuffer(this.strides, factory.invoke(strides.linearSize) { transform(buffer[it]) }) else { val strides = DefaultStrides(shape) - BufferNDStructure(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) + NDBuffer(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) } } /** * Mutable ND buffer based on linear [MutableBuffer]. */ -public class MutableBufferNDStructure( - override val strides: Strides, - override val buffer: MutableBuffer, -) : NDBuffer(), MutableNDStructure { +public class MutableNDBuffer( + strides: Strides, + buffer: MutableBuffer, +) : NDBuffer(strides,buffer), MutableNDStructure { init { require(strides.linearSize == buffer.size) { @@ -344,6 +340,8 @@ public class MutableBufferNDStructure( } } + override val buffer: MutableBuffer = super.buffer as MutableBuffer + override operator fun set(index: IntArray, value: T): Unit = buffer.set(strides.offset(index), value) } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt new file mode 100644 index 000000000..feb3c509a --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt @@ -0,0 +1,111 @@ +package kscience.kmath.nd + +import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.operations.ExtendedField +import kscience.kmath.operations.RealField +import kscience.kmath.operations.RingWithNumbers +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.RealBuffer +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +@OptIn(UnstableKMathAPI::class) +public open class RealNDField( + shape: IntArray, +) : BufferedNDField(shape, RealField, Buffer.Companion::real), + RingWithNumbers>, + ExtendedField> { + + override val zero: NDBuffer by lazy { produce { zero } } + override val one: NDBuffer by lazy { produce { one } } + + override fun number(value: Number): NDBuffer { + val d = value.toDouble() // minimize conversions + return produce { d } + } +// +// @Suppress("OVERRIDE_BY_INLINE") +// override inline fun map( +// arg: AbstractNDBuffer, +// transform: RealField.(Double) -> Double, +// ): RealNDElement { +// check(arg) +// val array = RealBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) } +// return BufferedNDFieldElement(this, array) +// } +// +// @Suppress("OVERRIDE_BY_INLINE") +// override inline fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement { +// val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } +// return BufferedNDFieldElement(this, array) +// } +// +// @Suppress("OVERRIDE_BY_INLINE") +// override inline fun mapIndexed( +// arg: AbstractNDBuffer, +// transform: RealField.(index: IntArray, Double) -> Double, +// ): RealNDElement { +// check(arg) +// return BufferedNDFieldElement( +// this, +// RealBuffer(arg.strides.linearSize) { offset -> +// elementContext.transform( +// arg.strides.index(offset), +// arg.buffer[offset] +// ) +// }) +// } +// +// @Suppress("OVERRIDE_BY_INLINE") +// override inline fun combine( +// a: AbstractNDBuffer, +// b: AbstractNDBuffer, +// transform: RealField.(Double, Double) -> Double, +// ): RealNDElement { +// check(a, b) +// val buffer = RealBuffer(strides.linearSize) { offset -> +// elementContext.transform(a.buffer[offset], b.buffer[offset]) +// } +// return BufferedNDFieldElement(this, buffer) +// } + + override fun power(arg: NDStructure, pow: Number): NDBuffer = map(arg) { power(it, pow) } + + override fun exp(arg: NDStructure): NDBuffer = map(arg) { exp(it) } + + override fun ln(arg: NDStructure): NDBuffer = map(arg) { ln(it) } + + override fun sin(arg: NDStructure): NDBuffer = map(arg) { sin(it) } + override fun cos(arg: NDStructure): NDBuffer = map(arg) { cos(it) } + override fun tan(arg: NDStructure): NDBuffer = map(arg) { tan(it) } + override fun asin(arg: NDStructure): NDBuffer = map(arg) { asin(it) } + override fun acos(arg: NDStructure): NDBuffer = map(arg) { acos(it) } + override fun atan(arg: NDStructure): NDBuffer = map(arg) { atan(it) } + + override fun sinh(arg: NDStructure): NDBuffer = map(arg) { sinh(it) } + override fun cosh(arg: NDStructure): NDBuffer = map(arg) { cosh(it) } + override fun tanh(arg: NDStructure): NDBuffer = map(arg) { tanh(it) } + override fun asinh(arg: NDStructure): NDBuffer = map(arg) { asinh(it) } + override fun acosh(arg: NDStructure): NDBuffer = map(arg) { acosh(it) } + override fun atanh(arg: NDStructure): NDBuffer = map(arg) { atanh(it) } +} + + +/** + * Fast element production using function inlining + */ +public inline fun BufferedNDField.produceInline(crossinline initializer: RealField.(Int) -> Double): NDBuffer { + contract { callsInPlace(initializer, InvocationKind.EXACTLY_ONCE) } + val array = DoubleArray(strides.linearSize) { offset -> RealField.initializer(offset) } + return NDBuffer(strides, RealBuffer(array)) +} + +public fun NDAlgebra.Companion.real(vararg shape: Int): RealNDField = RealNDField(shape) + +/** + * Produce a context for n-dimensional operations inside this real field + */ +public inline fun RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R { + contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } + return RealNDField(shape).run(action) +} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ShortNDRing.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ShortNDRing.kt new file mode 100644 index 000000000..8663c4249 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ShortNDRing.kt @@ -0,0 +1,36 @@ +package kscience.kmath.nd + +import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.operations.RingWithNumbers +import kscience.kmath.operations.ShortRing +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.ShortBuffer +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +@OptIn(UnstableKMathAPI::class) +public open class ShortNDRing( + shape: IntArray, +) : BufferedNDRing(shape, ShortRing, Buffer.Companion::auto), + RingWithNumbers> { + + override val zero: NDBuffer by lazy { produce { zero } } + override val one: NDBuffer by lazy { produce { one } } + + override fun number(value: Number): NDBuffer { + val d = value.toShort() // minimize conversions + return produce { d } + } +} + +/** + * Fast element production using function inlining. + */ +public inline fun BufferedNDRing.produceInline(crossinline initializer: ShortRing.(Int) -> Short): NDBuffer { + return NDBuffer(strides, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) })) +} + +public inline fun ShortRing.nd(vararg shape: Int, action: ShortNDRing.() -> R): R { + contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } + return ShortNDRing(shape).run(action) +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure1D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure1D.kt similarity index 89% rename from kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure1D.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure1D.kt index 95422ac60..d69c23fb4 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure1D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure1D.kt @@ -1,4 +1,7 @@ -package kscience.kmath.structures +package kscience.kmath.nd + +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.asSequence /** * A structure that is guaranteed to be one-dimensional @@ -34,7 +37,7 @@ private inline class Buffer1DWrapper(val buffer: Buffer) : Structure1D override val size: Int get() = buffer.size override fun elements(): Sequence> = - asSequence().mapIndexed { index, value -> intArrayOf(index) to value } + buffer.asSequence().mapIndexed { index, value -> intArrayOf(index) to value } override operator fun get(index: Int): T = buffer[index] } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt similarity index 92% rename from kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt index d20e9e53b..4c730877c 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt @@ -1,4 +1,7 @@ -package kscience.kmath.structures +package kscience.kmath.nd + +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.VirtualBuffer /** * A structure that is guaranteed to be two-dimensional. @@ -73,10 +76,3 @@ public fun NDStructure.as2D(): Structure2D = if (shape.size == 2) Structure2DWrapper(this) else error("Can't create 2d-structure from ${shape.size}d-structure") - -/** - * Alias for [Structure2D] with more familiar name. - * - * @param T the type of items. - */ -public typealias Matrix = Structure2D diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt index 0be72e80c..9599401bc 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt @@ -1,6 +1,7 @@ package kscience.kmath.operations import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.nd.NDAlgebra import kscience.kmath.operations.BigInt.Companion.BASE import kscience.kmath.operations.BigInt.Companion.BASE_SIZE import kscience.kmath.structures.* diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt deleted file mode 100644 index dc65b12c4..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt +++ /dev/null @@ -1,81 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.operations.Field -import kscience.kmath.operations.FieldElement - -public class BoxingNDField>( - public override val shape: IntArray, - public override val elementContext: F, - public val bufferFactory: BufferFactory -) : BufferedNDField { - public override val zero: BufferedNDFieldElement by lazy { produce { zero } } - public override val one: BufferedNDFieldElement by lazy { produce { one } } - public override val strides: Strides = DefaultStrides(shape) - - public fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = - bufferFactory(size, initializer) - - public override fun check(vararg elements: NDBuffer): Array> { - require(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } - return elements - } - - public override fun produce(initializer: F.(IntArray) -> T): BufferedNDFieldElement = - BufferedNDFieldElement( - this, - buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }) - - public override fun map(arg: NDBuffer, transform: F.(T) -> T): BufferedNDFieldElement { - check(arg) - - return BufferedNDFieldElement( - this, - buildBuffer(arg.strides.linearSize) { offset -> elementContext.transform(arg.buffer[offset]) }) - -// val buffer = arg.buffer.transform { _, value -> elementContext.transform(value) } -// return BufferedNDFieldElement(this, buffer) - - } - - public override fun mapIndexed( - arg: NDBuffer, - transform: F.(index: IntArray, T) -> T - ): BufferedNDFieldElement { - check(arg) - return BufferedNDFieldElement( - this, - buildBuffer(arg.strides.linearSize) { offset -> - elementContext.transform( - arg.strides.index(offset), - arg.buffer[offset] - ) - }) - -// val buffer = -// arg.buffer.transform { offset, value -> elementContext.transform(arg.strides.index(offset), value) } -// return BufferedNDFieldElement(this, buffer) - } - - public override fun combine( - a: NDBuffer, - b: NDBuffer, - transform: F.(T, T) -> T - ): BufferedNDFieldElement { - check(a, b) - return BufferedNDFieldElement( - this, - buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) - } - - public override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = - BufferedNDFieldElement(this@BoxingNDField, buffer) -} - -public inline fun , R> F.nd( - noinline bufferFactory: BufferFactory, - vararg shape: Int, - action: NDField.() -> R -): R { - val ndfield = NDField.boxing(this, *shape, bufferFactory = bufferFactory) - return ndfield.action() -} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt deleted file mode 100644 index b6794984c..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt +++ /dev/null @@ -1,71 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.operations.Ring -import kscience.kmath.operations.RingElement - -public class BoxingNDRing>( - override val shape: IntArray, - override val elementContext: R, - public val bufferFactory: BufferFactory -) : BufferedNDRing { - override val strides: Strides = DefaultStrides(shape) - override val zero: BufferedNDRingElement by lazy { produce { zero } } - override val one: BufferedNDRingElement by lazy { produce { one } } - - public fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) - - override fun check(vararg elements: NDBuffer): Array> { - if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") - return elements - } - - override fun produce(initializer: R.(IntArray) -> T): BufferedNDRingElement = - BufferedNDRingElement( - this, - buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }) - - override fun map(arg: NDBuffer, transform: R.(T) -> T): BufferedNDRingElement { - check(arg) - return BufferedNDRingElement( - this, - buildBuffer(arg.strides.linearSize) { offset -> elementContext.transform(arg.buffer[offset]) }) - -// val buffer = arg.buffer.transform { _, value -> elementContext.transform(value) } -// return BufferedNDFieldElement(this, buffer) - - } - - override fun mapIndexed( - arg: NDBuffer, - transform: R.(index: IntArray, T) -> T - ): BufferedNDRingElement { - check(arg) - return BufferedNDRingElement( - this, - buildBuffer(arg.strides.linearSize) { offset -> - elementContext.transform( - arg.strides.index(offset), - arg.buffer[offset] - ) - }) - -// val buffer = -// arg.buffer.transform { offset, value -> elementContext.transform(arg.strides.index(offset), value) } -// return BufferedNDFieldElement(this, buffer) - } - - override fun combine( - a: NDBuffer, - b: NDBuffer, - transform: R.(T, T) -> T - ): BufferedNDRingElement { - check(a, b) - - return BufferedNDRingElement( - this, - buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) - } - - override fun NDBuffer.toElement(): RingElement, *, out BufferedNDRing> = - BufferedNDRingElement(this@BoxingNDRing, buffer) -} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferAccessor2D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferAccessor2D.kt index 5d7ba611f..3c53d6c51 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferAccessor2D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferAccessor2D.kt @@ -1,5 +1,10 @@ package kscience.kmath.structures +import kscience.kmath.nd.DefaultStrides +import kscience.kmath.nd.NDStructure +import kscience.kmath.nd.Structure2D +import kscience.kmath.nd.as2D + /** * A context that allows to operate on a [MutableBuffer] as on 2d array */ diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt deleted file mode 100644 index 3dcd0322c..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt +++ /dev/null @@ -1,43 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.operations.* - -public interface BufferedNDAlgebra : NDAlgebra> { - public val strides: Strides - - public override fun check(vararg elements: NDBuffer): Array> { - require(elements.all { it.strides == strides }) { "Strides mismatch" } - return elements - } - - /** - * Convert any [NDStructure] to buffered structure using strides from this context. - * If the structure is already [NDBuffer], conversion is free. If not, it could be expensive because iteration over - * indices. - * - * If the argument is [NDBuffer] with different strides structure, the new element will be produced. - */ - public fun NDStructure.toBuffer(): NDBuffer = - if (this is NDBuffer && this.strides == this@BufferedNDAlgebra.strides) - this - else - produce { index -> this@toBuffer[index] } - - /** - * Convert a buffer to element of this algebra - */ - public fun NDBuffer.toElement(): MathElement> -} - - -public interface BufferedNDSpace> : NDSpace>, BufferedNDAlgebra { - public override fun NDBuffer.toElement(): SpaceElement, *, out BufferedNDSpace> -} - -public interface BufferedNDRing> : NDRing>, BufferedNDSpace { - override fun NDBuffer.toElement(): RingElement, *, out BufferedNDRing> -} - -public interface BufferedNDField> : NDField>, BufferedNDRing { - override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> -} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDElement.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDElement.kt deleted file mode 100644 index d53702566..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDElement.kt +++ /dev/null @@ -1,86 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.operations.* - -/** - * Base class for an element with context, containing strides - */ -public abstract class BufferedNDElement : NDBuffer(), NDElement> { - abstract override val context: BufferedNDAlgebra - - override val strides: Strides get() = context.strides - - override val shape: IntArray get() = context.shape -} - -public class BufferedNDSpaceElement>( - override val context: BufferedNDSpace, - override val buffer: Buffer -) : BufferedNDElement(), SpaceElement, BufferedNDSpaceElement, BufferedNDSpace> { - - override fun unwrap(): NDBuffer = this - - override fun NDBuffer.wrap(): BufferedNDSpaceElement { - context.check(this) - return BufferedNDSpaceElement(context, buffer) - } -} - -public class BufferedNDRingElement>( - override val context: BufferedNDRing, - override val buffer: Buffer -) : BufferedNDElement(), RingElement, BufferedNDRingElement, BufferedNDRing> { - override fun unwrap(): NDBuffer = this - - override fun NDBuffer.wrap(): BufferedNDRingElement { - context.check(this) - return BufferedNDRingElement(context, buffer) - } -} - -public class BufferedNDFieldElement>( - override val context: BufferedNDField, - override val buffer: Buffer -) : BufferedNDElement(), FieldElement, BufferedNDFieldElement, BufferedNDField> { - override fun unwrap(): NDBuffer = this - - override fun NDBuffer.wrap(): BufferedNDFieldElement { - context.check(this) - return BufferedNDFieldElement(context, buffer) - } -} - - -/** - * Element by element application of any operation on elements to the whole array. Just like in numpy. - */ -public operator fun > Function1.invoke(ndElement: BufferedNDElement): MathElement> = - ndElement.context.run { map(ndElement) { invoke(it) }.toElement() } - -/* plus and minus */ - -/** - * Summation operation for [BufferedNDElement] and single element - */ -public operator fun > BufferedNDElement.plus(arg: T): NDElement> = - context.map(this) { it + arg }.wrap() - -/** - * Subtraction operation between [BufferedNDElement] and single element - */ -public operator fun > BufferedNDElement.minus(arg: T): NDElement> = - context.map(this) { it - arg }.wrap() - -/* prod and div */ - -/** - * Product operation for [BufferedNDElement] and single element - */ -public operator fun > BufferedNDElement.times(arg: T): NDElement> = - context.map(this) { it * arg }.wrap() - -/** - * Division operation between [BufferedNDElement] and single element - */ -public operator fun > BufferedNDElement.div(arg: T): NDElement> = - context.map(this) { it / arg }.wrap() diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt index bfec6f871..4e7c0d1b1 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt @@ -43,7 +43,7 @@ public interface Buffer { * Checks content equality with another buffer. */ public fun contentEquals(other: Buffer<*>): Boolean = - asSequence().mapIndexed { index, value -> value == other[index] }.all { it } + kscience.kmath.nd.mapIndexed { index, value -> value == other[index] }.all { it } public companion object { /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt deleted file mode 100644 index 6de69cabe..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt +++ /dev/null @@ -1,158 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.operations.* -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - -public typealias ComplexNDElement = BufferedNDFieldElement - -/** - * An optimized nd-field for complex numbers - */ -@OptIn(UnstableKMathAPI::class) -public class ComplexNDField(override val shape: IntArray) : - BufferedNDField, - ExtendedNDField>, - RingWithNumbers>{ - - override val strides: Strides = DefaultStrides(shape) - override val elementContext: ComplexField get() = ComplexField - override val zero: ComplexNDElement by lazy { produce { zero } } - override val one: ComplexNDElement by lazy { produce { one } } - - override fun number(value: Number): NDBuffer { - val c = value.toComplex() - return produce { c } - } - - public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Complex): Buffer = - Buffer.complex(size) { initializer(it) } - - /** - * Inline transform an NDStructure to another structure - */ - override fun map( - arg: NDBuffer, - transform: ComplexField.(Complex) -> Complex, - ): ComplexNDElement { - check(arg) - val array = buildBuffer(arg.strides.linearSize) { offset -> ComplexField.transform(arg.buffer[offset]) } - return BufferedNDFieldElement(this, array) - } - - override fun produce(initializer: ComplexField.(IntArray) -> Complex): ComplexNDElement { - val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } - return BufferedNDFieldElement(this, array) - } - - override fun mapIndexed( - arg: NDBuffer, - transform: ComplexField.(index: IntArray, Complex) -> Complex, - ): ComplexNDElement { - check(arg) - - return BufferedNDFieldElement( - this, - buildBuffer(arg.strides.linearSize) { offset -> - elementContext.transform( - arg.strides.index(offset), - arg.buffer[offset] - ) - }) - } - - override fun combine( - a: NDBuffer, - b: NDBuffer, - transform: ComplexField.(Complex, Complex) -> Complex, - ): ComplexNDElement { - check(a, b) - - return BufferedNDFieldElement( - this, - buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) - } - - override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = - BufferedNDFieldElement(this@ComplexNDField, buffer) - - override fun power(arg: NDBuffer, pow: Number): ComplexNDElement = - map(arg) { power(it, pow) } - - override fun exp(arg: NDBuffer): ComplexNDElement = map(arg) { exp(it) } - override fun ln(arg: NDBuffer): ComplexNDElement = map(arg) { ln(it) } - - override fun sin(arg: NDBuffer): ComplexNDElement = map(arg) { sin(it) } - override fun cos(arg: NDBuffer): ComplexNDElement = map(arg) { cos(it) } - override fun tan(arg: NDBuffer): ComplexNDElement = map(arg) { tan(it) } - override fun asin(arg: NDBuffer): ComplexNDElement = map(arg) { asin(it) } - override fun acos(arg: NDBuffer): ComplexNDElement = map(arg) { acos(it) } - override fun atan(arg: NDBuffer): ComplexNDElement = map(arg) { atan(it) } - - override fun sinh(arg: NDBuffer): ComplexNDElement = map(arg) { sinh(it) } - override fun cosh(arg: NDBuffer): ComplexNDElement = map(arg) { cosh(it) } - override fun tanh(arg: NDBuffer): ComplexNDElement = map(arg) { tanh(it) } - override fun asinh(arg: NDBuffer): ComplexNDElement = map(arg) { asinh(it) } - override fun acosh(arg: NDBuffer): ComplexNDElement = map(arg) { acosh(it) } - override fun atanh(arg: NDBuffer): ComplexNDElement = map(arg) { atanh(it) } -} - - -/** - * Fast element production using function inlining - */ -public inline fun BufferedNDField.produceInline(initializer: ComplexField.(Int) -> Complex): ComplexNDElement { - val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.initializer(offset) } - return BufferedNDFieldElement(this, buffer) -} - -/** - * Map one [ComplexNDElement] using function with indices. - */ -public inline fun ComplexNDElement.mapIndexed(transform: ComplexField.(index: IntArray, Complex) -> Complex): ComplexNDElement = - context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } - -/** - * Map one [ComplexNDElement] using function without indices. - */ -public inline fun ComplexNDElement.map(transform: ComplexField.(Complex) -> Complex): ComplexNDElement { - val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.transform(buffer[offset]) } - return BufferedNDFieldElement(context, buffer) -} - -/** - * Element by element application of any operation on elements to the whole array. Just like in numpy - */ -public operator fun Function1.invoke(ndElement: ComplexNDElement): ComplexNDElement = - ndElement.map { this@invoke(it) } - -/* plus and minus */ - -/** - * Summation operation for [BufferedNDElement] and single element - */ -public operator fun ComplexNDElement.plus(arg: Complex): ComplexNDElement = map { it + arg } - -/** - * Subtraction operation between [BufferedNDElement] and single element - */ -public operator fun ComplexNDElement.minus(arg: Complex): ComplexNDElement = map { it - arg } - -public operator fun ComplexNDElement.plus(arg: Double): ComplexNDElement = map { it + arg } -public operator fun ComplexNDElement.minus(arg: Double): ComplexNDElement = map { it - arg } - -public fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDField(shape) - -public fun NDElement.Companion.complex( - vararg shape: Int, - initializer: ComplexField.(IntArray) -> Complex, -): ComplexNDElement = NDField.complex(*shape).produce(initializer) - -/** - * Produce a context for n-dimensional operations inside this real field - */ -public inline fun ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R { - contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } - return NDField.complex(*shape).action() -} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ExtendedNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ExtendedNDField.kt deleted file mode 100644 index a9fa2763b..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ExtendedNDField.kt +++ /dev/null @@ -1,44 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.operations.ExtendedField - -/** - * [ExtendedField] over [NDStructure]. - * - * @param T the type of the element contained in ND structure. - * @param N the type of ND structure. - * @param F the extended field of structure elements. - */ -public interface ExtendedNDField, N : NDStructure> : NDField, ExtendedField - -///** -// * NDField that supports [ExtendedField] operations on its elements -// */ -//class ExtendedNDFieldWrapper, N : NDStructure>(private val ndField: NDField) : -// ExtendedNDField, NDField by ndField { -// -// override val shape: IntArray get() = ndField.shape -// override val elementContext: F get() = ndField.elementContext -// -// override fun produce(initializer: F.(IntArray) -> T) = ndField.produce(initializer) -// -// override fun power(arg: N, pow: Double): N { -// return produce { with(elementContext) { power(arg[it], pow) } } -// } -// -// override fun exp(arg: N): N { -// return produce { with(elementContext) { exp(arg[it]) } } -// } -// -// override fun ln(arg: N): N { -// return produce { with(elementContext) { ln(arg[it]) } } -// } -// -// override fun sin(arg: N): N { -// return produce { with(elementContext) { sin(arg[it]) } } -// } -// -// override fun cos(arg: N): N { -// return produce { with(elementContext) { cos(arg[it]) } } -// } -//} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt index 4965e37cf..7828cea10 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt @@ -60,7 +60,7 @@ public class FlaggedRealBuffer(public val values: DoubleArray, public val flags: override operator fun get(index: Int): Double? = if (isValid(index)) values[index] else null - override operator fun iterator(): Iterator = values.indices.asSequence().map { + override operator fun iterator(): Iterator = kscience.kmath.nd.map { if (isValid(it)) values[it] else null }.iterator() } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt index 66c9212cf..22331ae81 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt @@ -15,7 +15,7 @@ public open class MemoryBuffer(protected val memory: Memory, protected private val reader: MemoryReader = memory.reader() override operator fun get(index: Int): T = reader.read(spec, spec.objectSize * index) - override operator fun iterator(): Iterator = (0 until size).asSequence().map { get(it) }.iterator() + override operator fun iterator(): Iterator = kscience.kmath.nd.map { get(it) }.iterator() public companion object { public fun create(spec: MemorySpec, size: Int): MemoryBuffer = diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt deleted file mode 100644 index d7b019c65..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt +++ /dev/null @@ -1,259 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.operations.Complex -import kscience.kmath.operations.Field -import kscience.kmath.operations.Ring -import kscience.kmath.operations.Space -import kotlin.native.concurrent.ThreadLocal - -/** - * An exception is thrown when the expected ans actual shape of NDArray differs. - * - * @property expected the expected shape. - * @property actual the actual shape. - */ -public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) : - RuntimeException("Shape ${actual.contentToString()} doesn't fit in expected shape ${expected.contentToString()}.") - -/** - * The base interface for all ND-algebra implementations. - * - * @param T the type of ND-structure element. - * @param C the type of the element context. - * @param N the type of the structure. - */ -public interface NDAlgebra> { - /** - * The shape of ND-structures this algebra operates on. - */ - public val shape: IntArray - - /** - * The algebra over elements of ND structure. - */ - public val elementContext: C - - /** - * Produces a new [N] structure using given initializer function. - */ - public fun produce(initializer: C.(IntArray) -> T): N - - /** - * Maps elements from one structure to another one by applying [transform] to them. - */ - public fun map(arg: N, transform: C.(T) -> T): N - - /** - * Maps elements from one structure to another one by applying [transform] to them alongside with their indices. - */ - public fun mapIndexed(arg: N, transform: C.(index: IntArray, T) -> T): N - - /** - * Combines two structures into one. - */ - public fun combine(a: N, b: N, transform: C.(T, T) -> T): N - - /** - * Checks if given element is consistent with this context. - * - * @param element the structure to check. - * @return the valid structure. - */ - public fun check(element: N): N { - if (!element.shape.contentEquals(shape)) throw ShapeMismatchException(shape, element.shape) - return element - } - - /** - * Checks if given elements are consistent with this context. - * - * @param elements the structures to check. - * @return the array of valid structures. - */ - public fun check(vararg elements: N): Array = elements - .map(NDStructure::shape) - .singleOrNull { !shape.contentEquals(it) } - ?.let> { throw ShapeMismatchException(shape, it) } - ?: elements - - /** - * Element-wise invocation of function working on [T] on a [NDStructure]. - */ - public operator fun Function1.invoke(structure: N): N = map(structure) { value -> this@invoke(value) } - - public companion object -} - -/** - * Space of [NDStructure]. - * - * @param T the type of the element contained in ND structure. - * @param N the type of ND structure. - * @param S the type of space of structure elements. - */ -public interface NDSpace, N : NDStructure> : Space, NDAlgebra { - /** - * Element-wise addition. - * - * @param a the addend. - * @param b the augend. - * @return the sum. - */ - public override fun add(a: N, b: N): N = combine(a, b) { aValue, bValue -> add(aValue, bValue) } - - /** - * Element-wise multiplication by scalar. - * - * @param a the multiplicand. - * @param k the multiplier. - * @return the product. - */ - public override fun multiply(a: N, k: Number): N = map(a) { multiply(it, k) } - - // TODO move to extensions after KEEP-176 - - /** - * Adds an ND structure to an element of it. - * - * @receiver the addend. - * @param arg the augend. - * @return the sum. - */ - public operator fun N.plus(arg: T): N = map(this) { value -> add(arg, value) } - - /** - * Subtracts an element from ND structure of it. - * - * @receiver the dividend. - * @param arg the divisor. - * @return the quotient. - */ - public operator fun N.minus(arg: T): N = map(this) { value -> add(arg, -value) } - - /** - * Adds an element to ND structure of it. - * - * @receiver the addend. - * @param arg the augend. - * @return the sum. - */ - public operator fun T.plus(arg: N): N = map(arg) { value -> add(this@plus, value) } - - /** - * Subtracts an ND structure from an element of it. - * - * @receiver the dividend. - * @param arg the divisor. - * @return the quotient. - */ - public operator fun T.minus(arg: N): N = map(arg) { value -> add(-this@minus, value) } - - public companion object -} - -/** - * Ring of [NDStructure]. - * - * @param T the type of the element contained in ND structure. - * @param N the type of ND structure. - * @param R the type of ring of structure elements. - */ -public interface NDRing, N : NDStructure> : Ring, NDSpace { - /** - * Element-wise multiplication. - * - * @param a the multiplicand. - * @param b the multiplier. - * @return the product. - */ - public override fun multiply(a: N, b: N): N = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } - - //TODO move to extensions after KEEP-176 - - /** - * Multiplies an ND structure by an element of it. - * - * @receiver the multiplicand. - * @param arg the multiplier. - * @return the product. - */ - public operator fun N.times(arg: T): N = map(this) { value -> multiply(arg, value) } - - /** - * Multiplies an element by a ND structure of it. - * - * @receiver the multiplicand. - * @param arg the multiplier. - * @return the product. - */ - public operator fun T.times(arg: N): N = map(arg) { value -> multiply(this@times, value) } - - public companion object -} - -/** - * Field of [NDStructure]. - * - * @param T the type of the element contained in ND structure. - * @param N the type of ND structure. - * @param F the type field of structure elements. - */ -public interface NDField, N : NDStructure> : Field, NDRing { - /** - * Element-wise division. - * - * @param a the dividend. - * @param b the divisor. - * @return the quotient. - */ - public override fun divide(a: N, b: N): N = combine(a, b) { aValue, bValue -> divide(aValue, bValue) } - - //TODO move to extensions after KEEP-176 - /** - * Divides an ND structure by an element of it. - * - * @receiver the dividend. - * @param arg the divisor. - * @return the quotient. - */ - public operator fun N.div(arg: T): N = map(this) { value -> divide(arg, value) } - - /** - * Divides an element by an ND structure of it. - * - * @receiver the dividend. - * @param arg the divisor. - * @return the quotient. - */ - public operator fun T.div(arg: N): N = map(arg) { divide(it, this@div) } - - @ThreadLocal - public companion object { - private val realNDFieldCache: MutableMap = hashMapOf() - - /** - * Create a nd-field for [Double] values or pull it from cache if it was created previously. - */ - public fun real(vararg shape: Int): RealNDField = realNDFieldCache.getOrPut(shape) { RealNDField(shape) } - - /** - * Create an ND field with boxing generic buffer. - */ - public fun > boxing( - field: F, - vararg shape: Int, - bufferFactory: BufferFactory = Buffer.Companion::boxing - ): BoxingNDField = BoxingNDField(shape, field, bufferFactory) - - /** - * Create a most suitable implementation for nd-field using reified class. - */ - @Suppress("UNCHECKED_CAST") - public inline fun > auto(field: F, vararg shape: Int): BufferedNDField = - when { - T::class == Double::class -> real(*shape) as BufferedNDField - T::class == Complex::class -> complex(*shape) as BufferedNDField - else -> BoxingNDField(shape, field, Buffer.Companion::auto) - } - } -} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDElement.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDElement.kt deleted file mode 100644 index f2f565064..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDElement.kt +++ /dev/null @@ -1,134 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.operations.Field -import kscience.kmath.operations.RealField -import kscience.kmath.operations.Ring -import kscience.kmath.operations.Space - -/** - * The root for all [NDStructure] based algebra elements. Does not implement algebra element root because of problems with recursive self-types - * @param T the type of the element of the structure - * @param C the type of the context for the element - * @param N the type of the underlying [NDStructure] - */ -public interface NDElement> : NDStructure { - public val context: NDAlgebra - - public fun unwrap(): N - - public fun N.wrap(): NDElement - - public companion object { - /** - * Create a optimized NDArray of doubles - */ - public fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }): RealNDElement = - NDField.real(*shape).produce(initializer) - - public inline fun real1D(dim: Int, crossinline initializer: (Int) -> Double = { _ -> 0.0 }): RealNDElement = - real(intArrayOf(dim)) { initializer(it[0]) } - - public inline fun real2D( - dim1: Int, - dim2: Int, - crossinline initializer: (Int, Int) -> Double = { _, _ -> 0.0 } - ): RealNDElement = real(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } - - public inline fun real3D( - dim1: Int, - dim2: Int, - dim3: Int, - crossinline initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 } - ): RealNDElement = real(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } - - - /** - * Simple boxing NDArray - */ - public fun > boxing( - shape: IntArray, - field: F, - initializer: F.(IntArray) -> T - ): BufferedNDElement { - val ndField = BoxingNDField(shape, field, Buffer.Companion::boxing) - return ndField.produce(initializer) - } - - public inline fun > auto( - shape: IntArray, - field: F, - noinline initializer: F.(IntArray) -> T - ): BufferedNDFieldElement { - val ndField = NDField.auto(field, *shape) - return BufferedNDFieldElement(ndField, ndField.produce(initializer).buffer) - } - } -} - -public fun > NDElement.mapIndexed(transform: C.(index: IntArray, T) -> T): NDElement = - context.mapIndexed(unwrap(), transform).wrap() - -public fun > NDElement.map(transform: C.(T) -> T): NDElement = - context.map(unwrap(), transform).wrap() - -/** - * Element by element application of any operation on elements to the whole [NDElement] - */ -public operator fun > Function1.invoke(ndElement: NDElement): NDElement = - ndElement.map { value -> this@invoke(value) } - -/* plus and minus */ - -/** - * Summation operation for [NDElement] and single element - */ -public operator fun , N : NDStructure> NDElement.plus(arg: T): NDElement = - map { value -> arg + value } - -/** - * Subtraction operation between [NDElement] and single element - */ -public operator fun , N : NDStructure> NDElement.minus(arg: T): NDElement = - map { value -> arg - value } - -/* prod and div */ - -/** - * Product operation for [NDElement] and single element - */ -public operator fun , N : NDStructure> NDElement.times(arg: T): NDElement = - map { value -> arg * value } - -/** - * Division operation between [NDElement] and single element - */ -public operator fun , N : NDStructure> NDElement.div(arg: T): NDElement = - map { value -> arg / value } - -// /** -// * Reverse sum operation -// */ -// operator fun T.plus(arg: NDStructure): NDElement = produce { index -> -// field.run { this@plus + arg[index] } -// } -// -// /** -// * Reverse minus operation -// */ -// operator fun T.minus(arg: NDStructure): NDElement = produce { index -> -// field.run { this@minus - arg[index] } -// } -// -// /** -// * Reverse product operation -// */ -// operator fun T.times(arg: NDStructure): NDElement = produce { index -> -// field.run { this@times * arg[index] } -// } -// -// /** -// * Reverse division operation -// */ -// operator fun T.div(arg: NDStructure): NDElement = produce { index -> -// field.run { this@div / arg[index] } -// } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt deleted file mode 100644 index 60e6de440..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt +++ /dev/null @@ -1,140 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.operations.FieldElement -import kscience.kmath.operations.RealField -import kscience.kmath.operations.RingWithNumbers - -public typealias RealNDElement = BufferedNDFieldElement - -@OptIn(UnstableKMathAPI::class) -public class RealNDField(override val shape: IntArray) : - BufferedNDField, - ExtendedNDField>, - RingWithNumbers> { - - override val strides: Strides = DefaultStrides(shape) - - override val elementContext: RealField get() = RealField - override val zero: RealNDElement by lazy { produce { zero } } - override val one: RealNDElement by lazy { produce { one } } - - override fun number(value: Number): NDBuffer { - val d = value.toDouble() - return produce { d } - } - - @Suppress("OVERRIDE_BY_INLINE") - override inline fun map( - arg: NDBuffer, - transform: RealField.(Double) -> Double, - ): RealNDElement { - check(arg) - val array = RealBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) } - return BufferedNDFieldElement(this, array) - } - - @Suppress("OVERRIDE_BY_INLINE") - override inline fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement { - val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } - return BufferedNDFieldElement(this, array) - } - - @Suppress("OVERRIDE_BY_INLINE") - override inline fun mapIndexed( - arg: NDBuffer, - transform: RealField.(index: IntArray, Double) -> Double, - ): RealNDElement { - check(arg) - return BufferedNDFieldElement( - this, - RealBuffer(arg.strides.linearSize) { offset -> - elementContext.transform( - arg.strides.index(offset), - arg.buffer[offset] - ) - }) - } - - @Suppress("OVERRIDE_BY_INLINE") - override inline fun combine( - a: NDBuffer, - b: NDBuffer, - transform: RealField.(Double, Double) -> Double, - ): RealNDElement { - check(a, b) - val buffer = RealBuffer(strides.linearSize) { offset -> - elementContext.transform(a.buffer[offset], b.buffer[offset]) - } - return BufferedNDFieldElement(this, buffer) - } - - override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = - BufferedNDFieldElement(this@RealNDField, buffer) - - override fun power(arg: NDBuffer, pow: Number): RealNDElement = map(arg) { power(it, pow) } - - override fun exp(arg: NDBuffer): RealNDElement = map(arg) { exp(it) } - - override fun ln(arg: NDBuffer): RealNDElement = map(arg) { ln(it) } - - override fun sin(arg: NDBuffer): RealNDElement = map(arg) { sin(it) } - override fun cos(arg: NDBuffer): RealNDElement = map(arg) { cos(it) } - override fun tan(arg: NDBuffer): RealNDElement = map(arg) { tan(it) } - override fun asin(arg: NDBuffer): RealNDElement = map(arg) { asin(it) } - override fun acos(arg: NDBuffer): RealNDElement = map(arg) { acos(it) } - override fun atan(arg: NDBuffer): RealNDElement = map(arg) { atan(it) } - - override fun sinh(arg: NDBuffer): RealNDElement = map(arg) { sinh(it) } - override fun cosh(arg: NDBuffer): RealNDElement = map(arg) { cosh(it) } - override fun tanh(arg: NDBuffer): RealNDElement = map(arg) { tanh(it) } - override fun asinh(arg: NDBuffer): RealNDElement = map(arg) { asinh(it) } - override fun acosh(arg: NDBuffer): RealNDElement = map(arg) { acosh(it) } - override fun atanh(arg: NDBuffer): RealNDElement = map(arg) { atanh(it) } -} - - -/** - * Fast element production using function inlining - */ -public inline fun BufferedNDField.produceInline(crossinline initializer: RealField.(Int) -> Double): RealNDElement { - val array = DoubleArray(strides.linearSize) { offset -> RealField.initializer(offset) } - return BufferedNDFieldElement(this, RealBuffer(array)) -} - -/** - * Map one [RealNDElement] using function with indices. - */ -public inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double): RealNDElement = - context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } - -/** - * Map one [RealNDElement] using function without indices. - */ -public inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double): RealNDElement { - val array = DoubleArray(strides.linearSize) { offset -> RealField.transform(buffer[offset]) } - return BufferedNDFieldElement(context, RealBuffer(array)) -} - -/** - * Element by element application of any operation on elements to the whole array. Just like in numpy. - */ -public operator fun Function1.invoke(ndElement: RealNDElement): RealNDElement = - ndElement.map { this@invoke(it) } - -/* plus and minus */ - -/** - * Summation operation for [BufferedNDElement] and single element - */ -public operator fun RealNDElement.plus(arg: Double): RealNDElement = map { it + arg } - -/** - * Subtraction operation between [BufferedNDElement] and single element - */ -public operator fun RealNDElement.minus(arg: Double): RealNDElement = map { it - arg } - -/** - * Produce a context for n-dimensional operations inside this real field - */ -public inline fun RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R = NDField.real(*shape).run(action) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortNDRing.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortNDRing.kt deleted file mode 100644 index 3b506a26a..000000000 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortNDRing.kt +++ /dev/null @@ -1,93 +0,0 @@ -package kscience.kmath.structures - -import kscience.kmath.operations.RingElement -import kscience.kmath.operations.ShortRing - -public typealias ShortNDElement = BufferedNDRingElement - -public class ShortNDRing(override val shape: IntArray) : - BufferedNDRing { - - override val strides: Strides = DefaultStrides(shape) - override val elementContext: ShortRing get() = ShortRing - override val zero: ShortNDElement by lazy { produce { zero } } - override val one: ShortNDElement by lazy { produce { one } } - - public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Short): Buffer = - ShortBuffer(ShortArray(size) { initializer(it) }) - - /** - * Inline transform an NDStructure to - */ - override fun map( - arg: NDBuffer, - transform: ShortRing.(Short) -> Short - ): ShortNDElement { - check(arg) - val array = buildBuffer(arg.strides.linearSize) { offset -> ShortRing.transform(arg.buffer[offset]) } - return BufferedNDRingElement(this, array) - } - - override fun produce(initializer: ShortRing.(IntArray) -> Short): ShortNDElement { - val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } - return BufferedNDRingElement(this, array) - } - - override fun mapIndexed( - arg: NDBuffer, - transform: ShortRing.(index: IntArray, Short) -> Short - ): ShortNDElement { - check(arg) - - return BufferedNDRingElement( - this, - buildBuffer(arg.strides.linearSize) { offset -> - elementContext.transform( - arg.strides.index(offset), - arg.buffer[offset] - ) - }) - } - - override fun combine( - a: NDBuffer, - b: NDBuffer, - transform: ShortRing.(Short, Short) -> Short - ): ShortNDElement { - check(a, b) - return BufferedNDRingElement( - this, - buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) - } - - override fun NDBuffer.toElement(): RingElement, *, out BufferedNDRing> = - BufferedNDRingElement(this@ShortNDRing, buffer) -} - - -/** - * Fast element production using function inlining. - */ -public inline fun BufferedNDRing.produceInline(crossinline initializer: ShortRing.(Int) -> Short): ShortNDElement = - BufferedNDRingElement(this, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) })) - -/** - * Element by element application of any operation on elements to the whole array. - */ -public operator fun Function1.invoke(ndElement: ShortNDElement): ShortNDElement = - ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) } - - -/* plus and minus */ - -/** - * Summation operation for [ShortNDElement] and single element. - */ -public operator fun ShortNDElement.plus(arg: Short): ShortNDElement = - context.produceInline { i -> (buffer[i] + arg).toShort() } - -/** - * Subtraction operation between [ShortNDElement] and single element. - */ -public operator fun ShortNDElement.minus(arg: Short): ShortNDElement = - context.produceInline { i -> (buffer[i] - arg).toShort() } diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/MatrixTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/MatrixTest.kt index d7755dcb5..a0ac92172 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/MatrixTest.kt @@ -1,9 +1,8 @@ package kscience.kmath.linear +import kscience.kmath.nd.NDStructure +import kscience.kmath.nd.as2D import kscience.kmath.operations.invoke -import kscience.kmath.structures.Matrix -import kscience.kmath.structures.NDStructure -import kscience.kmath.structures.as2D import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt index 28dfe46ec..522e711b6 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt @@ -1,6 +1,5 @@ package kscience.kmath.linear -import kscience.kmath.structures.Matrix import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt index 1129a8a36..0f5f99b49 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt @@ -1,5 +1,6 @@ package kscience.kmath.structures +import kscience.kmath.nd.NDField import kscience.kmath.operations.internal.FieldVerifier import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt index 22a0d3629..baf3656fc 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt @@ -1,7 +1,8 @@ package kscience.kmath.structures +import kscience.kmath.nd.NDField +import kscience.kmath.nd.NDStructure import kscience.kmath.operations.Norm -import kscience.kmath.operations.invoke import kscience.kmath.structures.NDElement.Companion.real2D import kotlin.math.abs import kotlin.math.pow diff --git a/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt index 7aa746797..cac692eb2 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt @@ -2,6 +2,8 @@ package kscience.kmath.structures import kotlinx.coroutines.* import kscience.kmath.coroutines.Math +import kscience.kmath.nd.DefaultStrides +import kscience.kmath.nd.NDStructure public class LazyNDStructure( public val scope: CoroutineScope, @@ -19,7 +21,7 @@ public class LazyNDStructure( public override fun elements(): Sequence> { val strides = DefaultStrides(shape) - val res = runBlocking { strides.indices().toList().map { index -> index to await(index) } } + val res = runBlocking { kscience.kmath.nd.map { index -> index to await(index) } } return res.asSequence() } diff --git a/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Wrappers.kt b/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Wrappers.kt index 0244eae7f..52443da0e 100644 --- a/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Wrappers.kt +++ b/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Wrappers.kt @@ -1,9 +1,8 @@ package kscience.kmath.dimensions import kscience.kmath.linear.* +import kscience.kmath.nd.Structure2D import kscience.kmath.operations.invoke -import kscience.kmath.structures.Matrix -import kscience.kmath.structures.Structure2D /** * A matrix with compile-time controlled dimension diff --git a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt index 82a5399fd..13e5f009b 100644 --- a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt +++ b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt @@ -2,8 +2,7 @@ package kscience.kmath.ejml import kscience.kmath.linear.* import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.structures.Matrix -import kscience.kmath.structures.NDStructure +import kscience.kmath.nd.NDStructure import kscience.kmath.structures.RealBuffer import org.ejml.dense.row.factory.DecompositionFactory_DDRM import org.ejml.simple.SimpleMatrix diff --git a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt index 8184d0110..ebd4dd586 100644 --- a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt +++ b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt @@ -3,10 +3,9 @@ package kscience.kmath.ejml import kscience.kmath.linear.InverseMatrixFeature import kscience.kmath.linear.MatrixContext import kscience.kmath.linear.Point -import kscience.kmath.linear.origin import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.structures.Matrix -import kscience.kmath.structures.getFeature +import kscience.kmath.nd.Matrix +import kscience.kmath.nd.getFeature import org.ejml.simple.SimpleMatrix /** diff --git a/kmath-ejml/src/test/kotlin/kscience/kmath/ejml/EjmlMatrixTest.kt b/kmath-ejml/src/test/kotlin/kscience/kmath/ejml/EjmlMatrixTest.kt index 455b52d9d..6af70b6f5 100644 --- a/kmath-ejml/src/test/kotlin/kscience/kmath/ejml/EjmlMatrixTest.kt +++ b/kmath-ejml/src/test/kotlin/kscience/kmath/ejml/EjmlMatrixTest.kt @@ -5,7 +5,7 @@ import kscience.kmath.linear.LupDecompositionFeature import kscience.kmath.linear.MatrixFeature import kscience.kmath.linear.plus import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.structures.getFeature +import kscience.kmath.nd.getFeature import org.ejml.dense.row.factory.DecompositionFactory_DDRM import org.ejml.simple.SimpleMatrix import kotlin.random.Random diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt index 274030aff..defaba126 100644 --- a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt @@ -1,15 +1,11 @@ package kscience.kmath.real import kscience.kmath.linear.MatrixContext -import kscience.kmath.linear.VirtualMatrix -import kscience.kmath.linear.inverseWithLUP import kscience.kmath.linear.real import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.nd.Matrix import kscience.kmath.structures.Buffer -import kscience.kmath.structures.Matrix import kscience.kmath.structures.RealBuffer -import kscience.kmath.structures.asIterable -import kotlin.math.pow /* * Functions for convenient "numpy-like" operations with Double matrices. diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realND.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realND.kt new file mode 100644 index 000000000..f8757b132 --- /dev/null +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realND.kt @@ -0,0 +1,31 @@ +package kscience.kmath.real + +import kscience.kmath.nd.NDBuffer +import kscience.kmath.operations.RealField +import kscience.kmath.structures.RealBuffer + +/** + * Map one [NDBuffer] using function without indices. + */ +public inline fun NDBuffer.mapInline(crossinline transform: RealField.(Double) -> Double): NDBuffer { + val array = DoubleArray(strides.linearSize) { offset -> RealField.transform(buffer[offset]) } + return NDBuffer(strides, RealBuffer(array)) +} + +/** + * Element by element application of any operation on elements to the whole array. Just like in numpy. + */ +public operator fun Function1.invoke(ndElement: NDBuffer): NDBuffer = + ndElement.mapInline { this@invoke(it) } + +/* plus and minus */ + +/** + * Summation operation for [NDBuffer] and single element + */ +public operator fun NDBuffer.plus(arg: Double): NDBuffer = mapInline { it + arg } + +/** + * Subtraction operation between [NDBuffer] and single element + */ +public operator fun NDBuffer.minus(arg: Double): NDBuffer = mapInline { it - arg } \ No newline at end of file diff --git a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/RealMatrixTest.kt b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/RealMatrixTest.kt index a89f99b3c..68aa8cf52 100644 --- a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/RealMatrixTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/RealMatrixTest.kt @@ -1,9 +1,7 @@ package kaceince.kmath.real -import kscience.kmath.linear.build +import kscience.kmath.nd.Matrix import kscience.kmath.real.* -import kscience.kmath.structures.Matrix -import kscience.kmath.structures.contentEquals import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue diff --git a/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/XYPointSet.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/XYPointSet.kt index 2abb7742c..bdece28e7 100644 --- a/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/XYPointSet.kt +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/XYPointSet.kt @@ -1,7 +1,7 @@ package kscience.kmath.interpolation +import kscience.kmath.nd.Structure2D import kscience.kmath.structures.Buffer -import kscience.kmath.structures.Structure2D public interface XYPointSet { public val size: Int diff --git a/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt index f95264ee1..42dbaabac 100644 --- a/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt @@ -1,6 +1,8 @@ package kscience.kmath.histogram import kscience.kmath.linear.Point +import kscience.kmath.nd.DefaultStrides +import kscience.kmath.nd.NDStructure import kscience.kmath.operations.SpaceOperations import kscience.kmath.operations.invoke import kscience.kmath.structures.* @@ -15,7 +17,7 @@ public data class BinDef>( require(vector.size == center.size) { "Dimension mismatch for input vector. Expected ${center.size}, but found ${vector.size}" } val upper = space { center + sizes / 2.0 } val lower = space { center - sizes / 2.0 } - return vector.asSequence().mapIndexed { i, value -> value in lower[i]..upper[i] }.all { it } + return kscience.kmath.nd.mapIndexed { i, value -> value in lower[i]..upper[i] }.all { it } } } @@ -68,7 +70,7 @@ public class RealHistogram( public fun getValue(point: Buffer): Long = getValue(getIndex(point)) private fun getDef(index: IntArray): BinDef { - val center = index.mapIndexed { axis, i -> + val center = kscience.kmath.nd.mapIndexed { axis, i -> when (i) { 0 -> Double.NEGATIVE_INFINITY strides.shape[axis] - 1 -> Double.POSITIVE_INFINITY @@ -98,7 +100,7 @@ public class RealHistogram( } public override operator fun iterator(): Iterator> = - weights.elements().map { (index, value) -> MultivariateBin(getDef(index), value.sum()) } + kscience.kmath.nd.map { (index, value) -> MultivariateBin(getDef(index), value.sum()) } .iterator() /** diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt index db2a44861..b958d1ccf 100644 --- a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt @@ -1,11 +1,9 @@ package kscience.kmath.nd4j import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.nd.* import kscience.kmath.operations.* -import kscience.kmath.structures.NDAlgebra -import kscience.kmath.structures.NDField -import kscience.kmath.structures.NDRing -import kscience.kmath.structures.NDSpace +import kscience.kmath.structures.* import org.nd4j.linalg.api.ndarray.INDArray import org.nd4j.linalg.factory.Nd4j @@ -15,11 +13,22 @@ import org.nd4j.linalg.factory.Nd4j * @param T the type of ND-structure element. * @param C the type of the element context. */ -public interface Nd4jArrayAlgebra : NDAlgebra> { +public interface Nd4jArrayAlgebra : NDAlgebra { /** * Wraps [INDArray] to [N]. */ public fun INDArray.wrap(): Nd4jArrayStructure + public val NDStructure.ndArray: INDArray + get() = when { + !shape.contentEquals(this@Nd4jArrayAlgebra.shape) -> throw ShapeMismatchException( + this@Nd4jArrayAlgebra.shape, + shape + ) + this is Nd4jArrayStructure -> ndArray //TODO check strides + else -> { + TODO() + } + } public override fun produce(initializer: C.(IntArray) -> T): Nd4jArrayStructure { val struct = Nd4j.create(*shape)!!.wrap() @@ -27,29 +36,26 @@ public interface Nd4jArrayAlgebra : NDAlgebra> return struct } - public override fun map(arg: Nd4jArrayStructure, transform: C.(T) -> T): Nd4jArrayStructure { - check(arg) + public override fun map(arg: NDStructure, transform: C.(T) -> T): Nd4jArrayStructure { val newStruct = arg.ndArray.dup().wrap() newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } return newStruct } public override fun mapIndexed( - arg: Nd4jArrayStructure, + arg: NDStructure, transform: C.(index: IntArray, T) -> T, ): Nd4jArrayStructure { - check(arg) val new = Nd4j.create(*shape).wrap() new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(idx, arg[idx]) } return new } public override fun combine( - a: Nd4jArrayStructure, - b: Nd4jArrayStructure, + a: NDStructure, + b: NDStructure, transform: C.(T, T) -> T, ): Nd4jArrayStructure { - check(a, b) val new = Nd4j.create(*shape).wrap() new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(a[idx], b[idx]) } return new @@ -62,38 +68,32 @@ public interface Nd4jArrayAlgebra : NDAlgebra> * @param T the type of the element contained in ND structure. * @param S the type of space of structure elements. */ -public interface Nd4jArraySpace> : NDSpace>, Nd4jArrayAlgebra { +public interface Nd4jArraySpace> : NDSpace, Nd4jArrayAlgebra { public override val zero: Nd4jArrayStructure get() = Nd4j.zeros(*shape).wrap() - public override fun add(a: Nd4jArrayStructure, b: Nd4jArrayStructure): Nd4jArrayStructure { - check(a, b) + public override fun add(a: NDStructure, b: NDStructure): Nd4jArrayStructure { return a.ndArray.add(b.ndArray).wrap() } - public override operator fun Nd4jArrayStructure.minus(b: Nd4jArrayStructure): Nd4jArrayStructure { - check(this, b) + public override operator fun NDStructure.minus(b: NDStructure): Nd4jArrayStructure { return ndArray.sub(b.ndArray).wrap() } - public override operator fun Nd4jArrayStructure.unaryMinus(): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.unaryMinus(): Nd4jArrayStructure { return ndArray.neg().wrap() } - public override fun multiply(a: Nd4jArrayStructure, k: Number): Nd4jArrayStructure { - check(a) + public override fun multiply(a: NDStructure, k: Number): Nd4jArrayStructure { return a.ndArray.mul(k).wrap() } - public override operator fun Nd4jArrayStructure.div(k: Number): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.div(k: Number): Nd4jArrayStructure { return ndArray.div(k).wrap() } - public override operator fun Nd4jArrayStructure.times(k: Number): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.times(k: Number): Nd4jArrayStructure { return ndArray.mul(k).wrap() } } @@ -105,13 +105,12 @@ public interface Nd4jArraySpace> : NDSpace> : NDRing>, Nd4jArraySpace { +public interface Nd4jArrayRing> : NDRing, Nd4jArraySpace { public override val one: Nd4jArrayStructure get() = Nd4j.ones(*shape).wrap() - public override fun multiply(a: Nd4jArrayStructure, b: Nd4jArrayStructure): Nd4jArrayStructure { - check(a, b) + public override fun multiply(a: NDStructure, b: NDStructure): Nd4jArrayStructure { return a.ndArray.mul(b.ndArray).wrap() } // @@ -168,17 +167,12 @@ public interface Nd4jArrayRing> : NDRing> : NDField>, Nd4jArrayRing { +public interface Nd4jArrayField> : NDField, Nd4jArrayRing { - public override fun divide(a: Nd4jArrayStructure, b: Nd4jArrayStructure): Nd4jArrayStructure { - check(a, b) - return a.ndArray.div(b.ndArray).wrap() - } + public override fun divide(a: NDStructure, b: NDStructure): Nd4jArrayStructure = + a.ndArray.div(b.ndArray).wrap() - public override operator fun Number.div(b: Nd4jArrayStructure): Nd4jArrayStructure { - check(b) - return b.ndArray.rdiv(this).wrap() - } + public override operator fun Number.div(b: NDStructure): Nd4jArrayStructure = b.ndArray.rdiv(this).wrap() public companion object { @@ -219,35 +213,29 @@ public class RealNd4jArrayField(public override val shape: IntArray) : Nd4jArray public override val elementContext: RealField get() = RealField - public override fun INDArray.wrap(): Nd4jArrayStructure = check(asRealStructure()) + public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(asRealStructure()) - public override operator fun Nd4jArrayStructure.div(arg: Double): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.div(arg: Double): Nd4jArrayStructure { return ndArray.div(arg).wrap() } - public override operator fun Nd4jArrayStructure.plus(arg: Double): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.plus(arg: Double): Nd4jArrayStructure { return ndArray.add(arg).wrap() } - public override operator fun Nd4jArrayStructure.minus(arg: Double): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.minus(arg: Double): Nd4jArrayStructure { return ndArray.sub(arg).wrap() } - public override operator fun Nd4jArrayStructure.times(arg: Double): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.times(arg: Double): Nd4jArrayStructure { return ndArray.mul(arg).wrap() } - public override operator fun Double.div(arg: Nd4jArrayStructure): Nd4jArrayStructure { - check(arg) + public override operator fun Double.div(arg: NDStructure): Nd4jArrayStructure { return arg.ndArray.rdiv(this).wrap() } - public override operator fun Double.minus(arg: Nd4jArrayStructure): Nd4jArrayStructure { - check(arg) + public override operator fun Double.minus(arg: NDStructure): Nd4jArrayStructure { return arg.ndArray.rsub(this).wrap() } } @@ -259,35 +247,29 @@ public class FloatNd4jArrayField(public override val shape: IntArray) : Nd4jArra public override val elementContext: FloatField get() = FloatField - public override fun INDArray.wrap(): Nd4jArrayStructure = check(asFloatStructure()) + public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(asFloatStructure()) - public override operator fun Nd4jArrayStructure.div(arg: Float): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.div(arg: Float): Nd4jArrayStructure { return ndArray.div(arg).wrap() } - public override operator fun Nd4jArrayStructure.plus(arg: Float): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.plus(arg: Float): Nd4jArrayStructure { return ndArray.add(arg).wrap() } - public override operator fun Nd4jArrayStructure.minus(arg: Float): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.minus(arg: Float): Nd4jArrayStructure { return ndArray.sub(arg).wrap() } - public override operator fun Nd4jArrayStructure.times(arg: Float): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.times(arg: Float): Nd4jArrayStructure { return ndArray.mul(arg).wrap() } - public override operator fun Float.div(arg: Nd4jArrayStructure): Nd4jArrayStructure { - check(arg) + public override operator fun Float.div(arg: NDStructure): Nd4jArrayStructure { return arg.ndArray.rdiv(this).wrap() } - public override operator fun Float.minus(arg: Nd4jArrayStructure): Nd4jArrayStructure { - check(arg) + public override operator fun Float.minus(arg: NDStructure): Nd4jArrayStructure { return arg.ndArray.rsub(this).wrap() } } @@ -301,23 +283,19 @@ public class IntNd4jArrayRing(public override val shape: IntArray) : Nd4jArrayRi public override fun INDArray.wrap(): Nd4jArrayStructure = check(asIntStructure()) - public override operator fun Nd4jArrayStructure.plus(arg: Int): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.plus(arg: Int): Nd4jArrayStructure { return ndArray.add(arg).wrap() } - public override operator fun Nd4jArrayStructure.minus(arg: Int): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.minus(arg: Int): Nd4jArrayStructure { return ndArray.sub(arg).wrap() } - public override operator fun Nd4jArrayStructure.times(arg: Int): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.times(arg: Int): Nd4jArrayStructure { return ndArray.mul(arg).wrap() } - public override operator fun Int.minus(arg: Nd4jArrayStructure): Nd4jArrayStructure { - check(arg) + public override operator fun Int.minus(arg: NDStructure): Nd4jArrayStructure { return arg.ndArray.rsub(this).wrap() } } @@ -331,23 +309,19 @@ public class LongNd4jArrayRing(public override val shape: IntArray) : Nd4jArrayR public override fun INDArray.wrap(): Nd4jArrayStructure = check(asLongStructure()) - public override operator fun Nd4jArrayStructure.plus(arg: Long): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.plus(arg: Long): Nd4jArrayStructure { return ndArray.add(arg).wrap() } - public override operator fun Nd4jArrayStructure.minus(arg: Long): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.minus(arg: Long): Nd4jArrayStructure { return ndArray.sub(arg).wrap() } - public override operator fun Nd4jArrayStructure.times(arg: Long): Nd4jArrayStructure { - check(this) + public override operator fun NDStructure.times(arg: Long): Nd4jArrayStructure { return ndArray.mul(arg).wrap() } - public override operator fun Long.minus(arg: Nd4jArrayStructure): Nd4jArrayStructure { - check(arg) + public override operator fun Long.minus(arg: NDStructure): Nd4jArrayStructure { return arg.ndArray.rsub(this).wrap() } } diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayStructure.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayStructure.kt index d47a293c3..2a6b56602 100644 --- a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayStructure.kt +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayStructure.kt @@ -1,7 +1,7 @@ package kscience.kmath.nd4j -import kscience.kmath.structures.MutableNDStructure -import kscience.kmath.structures.NDStructure +import kscience.kmath.nd.MutableNDStructure +import kscience.kmath.nd.NDStructure import org.nd4j.linalg.api.ndarray.INDArray /** diff --git a/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayStructureTest.kt b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayStructureTest.kt index 7e46211c1..2f1606061 100644 --- a/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayStructureTest.kt +++ b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayStructureTest.kt @@ -1,6 +1,6 @@ package kscience.kmath.nd4j -import kscience.kmath.structures.get +import kscience.kmath.nd.get import org.nd4j.linalg.factory.Nd4j import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt index 2471362cb..5dbc297b4 100644 --- a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt +++ b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt @@ -1,10 +1,10 @@ package kscience.kmath.viktor +import kscience.kmath.nd.DefaultStrides +import kscience.kmath.nd.MutableNDStructure +import kscience.kmath.nd.NDField +import kscience.kmath.nd.Strides import kscience.kmath.operations.RealField -import kscience.kmath.structures.DefaultStrides -import kscience.kmath.structures.MutableNDStructure -import kscience.kmath.structures.NDField -import kscience.kmath.structures.Strides import org.jetbrains.bio.viktor.F64Array @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") From 332c04b573e638a45401e334be5682f1fbaea03c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 23 Jan 2021 19:19:13 +0300 Subject: [PATCH 02/10] [WIP] Refactor NDStructures --- .../kscience/kmath/operations/ComplexDemo.kt | 8 ++-- .../kscience/kmath/structures/NDField.kt | 13 ++++-- .../kscience/kmath/commons/linear/CMMatrix.kt | 5 +-- .../kscience/kmath/commons/linear/CMSolver.kt | 2 +- .../kscience/kmath/nd/BufferNDAlgebra.kt | 6 +-- .../kotlin/kscience/kmath/nd/RealNDField.kt | 7 ++- .../kotlin/kscience/kmath/nd/Structure2D.kt | 10 ++++- .../kscience/kmath/operations/BigInt.kt | 13 +++--- .../kscience/kmath/structures/Buffers.kt | 2 +- .../kmath/structures/FlaggedBuffer.kt | 2 +- .../kscience/kmath/structures/MemoryBuffer.kt | 2 +- .../kscience/kmath/structures/NDFieldTest.kt | 8 ++-- .../kmath/structures/NumberNDFieldTest.kt | 44 ++++++++++++------- .../kmath/structures/LazyNDStructure.kt | 2 +- .../kscience/kmath/ejml/EjmlMatrixContext.kt | 5 +-- .../kotlin/kscience/kmath/real/RealMatrix.kt | 6 +-- .../kaceince/kmath/real/RealMatrixTest.kt | 4 +- .../kscience/kmath/histogram/RealHistogram.kt | 6 +-- .../kscience.kmath.nd4j/Nd4jArrayAlgebra.kt | 18 +++++--- .../kmath/viktor/ViktorNDStructure.kt | 40 ++++++++++------- 20 files changed, 120 insertions(+), 83 deletions(-) diff --git a/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt b/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt index f90221582..821618af5 100644 --- a/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt @@ -1,17 +1,17 @@ package kscience.kmath.operations -import kscience.kmath.nd.NDField -import kscience.kmath.structures.NDElement +import kscience.kmath.nd.NDAlgebra +import kscience.kmath.nd.complex fun main() { // 2d element - val element = NDElement.complex(2, 2) { (i,j) -> + val element = NDAlgebra.complex(2, 2).produce { (i,j) -> Complex(i.toDouble() - j.toDouble(), i.toDouble() + j.toDouble()) } println(element) // 1d element operation - val result = with(NDField.complex(8)) { + val result = with(NDAlgebra.complex(8)) { val a = produce { (it) -> i * it - it.toDouble() } val b = 3 val c = Complex(1.0, 1.0) diff --git a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt index e26b8cbce..3e84c0be0 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt @@ -1,7 +1,10 @@ package kscience.kmath.structures import kotlinx.coroutines.GlobalScope -import kscience.kmath.nd.* +import kscience.kmath.nd.NDAlgebra +import kscience.kmath.nd.NDStructure +import kscience.kmath.nd.field +import kscience.kmath.nd.real import kscience.kmath.nd4j.Nd4jArrayField import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke @@ -39,8 +42,10 @@ fun main() { } measureAndPrint("Element addition") { - var res: NDStructure = boxingField.one - repeat(n) { res += 1.0 } + boxingField { + var res: NDStructure = one + repeat(n) { res += 1.0 } + } } measureAndPrint("Specialized addition") { @@ -52,7 +57,7 @@ fun main() { measureAndPrint("Nd4j specialized addition") { nd4jField { - var res = one + var res:NDStructure = one repeat(n) { res += 1.0 } } } diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt index b9fc8b72a..e168b3d7c 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt @@ -1,10 +1,7 @@ package kscience.kmath.commons.linear -import kscience.kmath.linear.DiagonalFeature -import kscience.kmath.linear.MatrixContext -import kscience.kmath.linear.Point +import kscience.kmath.linear.* import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.nd.Matrix import org.apache.commons.math3.linear.* import kotlin.reflect.KClass import kotlin.reflect.cast diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt index aa0bf4e1a..1c0896597 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt @@ -1,7 +1,7 @@ package kscience.kmath.commons.linear +import kscience.kmath.linear.Matrix import kscience.kmath.linear.Point -import kscience.kmath.nd.Matrix import org.apache.commons.math3.linear.* public enum class CMDecomposition { diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt index a92e08c60..cc1764643 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt @@ -86,7 +86,7 @@ public fun > NDAlgebra.Companion.space( vararg shape: Int, ): BufferedNDSpace = BufferedNDSpace(shape, space, bufferFactory) -public inline fun , R> A.nd( +public inline fun , R> A.ndSpace( noinline bufferFactory: BufferFactory, vararg shape: Int, action: BufferedNDSpace.() -> R, @@ -102,7 +102,7 @@ public fun > NDAlgebra.Companion.ring( vararg shape: Int, ): BufferedNDRing = BufferedNDRing(shape, ring, bufferFactory) -public inline fun , R> A.nd( +public inline fun , R> A.ndRing( noinline bufferFactory: BufferFactory, vararg shape: Int, action: BufferedNDRing.() -> R, @@ -118,7 +118,7 @@ public fun > NDAlgebra.Companion.field( vararg shape: Int, ): BufferedNDField = BufferedNDField(shape, field, bufferFactory) -public inline fun , R> A.nd( +public inline fun , R> A.ndField( noinline bufferFactory: BufferFactory, vararg shape: Int, action: BufferedNDField.() -> R, diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt index feb3c509a..7a0d8f8f9 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt @@ -94,9 +94,12 @@ public open class RealNDField( /** * Fast element production using function inlining */ -public inline fun BufferedNDField.produceInline(crossinline initializer: RealField.(Int) -> Double): NDBuffer { +public inline fun BufferedNDField.produceInline(crossinline initializer: RealField.(IntArray) -> Double): NDBuffer { contract { callsInPlace(initializer, InvocationKind.EXACTLY_ONCE) } - val array = DoubleArray(strides.linearSize) { offset -> RealField.initializer(offset) } + val array = DoubleArray(strides.linearSize) { offset -> + val index = strides.index(offset) + RealField.initializer(index) + } return NDBuffer(strides, RealBuffer(array)) } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt index 4c730877c..b092d48e8 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt @@ -52,7 +52,15 @@ public interface Structure2D : NDStructure { for (j in 0 until colNum) yield(intArrayOf(i, j) to get(i, j)) } - public companion object + public companion object { + public inline fun real( + rows: Int, + columns: Int, + crossinline init: (i: Int, j: Int) -> Double, + ): NDBuffer = NDAlgebra.real(rows, columns).produceInline { (i, j) -> + init(i, j) + } + } } /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt index 9599401bc..e0bf817ef 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt @@ -1,10 +1,12 @@ package kscience.kmath.operations import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.nd.BufferedNDRing import kscience.kmath.nd.NDAlgebra import kscience.kmath.operations.BigInt.Companion.BASE import kscience.kmath.operations.BigInt.Companion.BASE_SIZE -import kscience.kmath.structures.* +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.MutableBuffer import kotlin.math.log2 import kotlin.math.max import kotlin.math.min @@ -463,10 +465,5 @@ public inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigIn public inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer = boxing(size, initializer) -public fun NDAlgebra.Companion.bigInt(vararg shape: Int): BoxingNDRing = - BoxingNDRing(shape, BigIntField, Buffer.Companion::bigInt) - -public fun NDElement.Companion.bigInt( - vararg shape: Int, - initializer: BigIntField.(IntArray) -> BigInt -): BufferedNDRingElement = NDAlgebra.bigInt(*shape).produce(initializer) +public fun NDAlgebra.Companion.bigInt(vararg shape: Int): BufferedNDRing = + BufferedNDRing(shape, BigIntField, Buffer.Companion::bigInt) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt index 4e7c0d1b1..bfec6f871 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt @@ -43,7 +43,7 @@ public interface Buffer { * Checks content equality with another buffer. */ public fun contentEquals(other: Buffer<*>): Boolean = - kscience.kmath.nd.mapIndexed { index, value -> value == other[index] }.all { it } + asSequence().mapIndexed { index, value -> value == other[index] }.all { it } public companion object { /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt index 7828cea10..4965e37cf 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt @@ -60,7 +60,7 @@ public class FlaggedRealBuffer(public val values: DoubleArray, public val flags: override operator fun get(index: Int): Double? = if (isValid(index)) values[index] else null - override operator fun iterator(): Iterator = kscience.kmath.nd.map { + override operator fun iterator(): Iterator = values.indices.asSequence().map { if (isValid(it)) values[it] else null }.iterator() } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt index 22331ae81..66c9212cf 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt @@ -15,7 +15,7 @@ public open class MemoryBuffer(protected val memory: Memory, protected private val reader: MemoryReader = memory.reader() override operator fun get(index: Int): T = reader.read(spec, spec.objectSize * index) - override operator fun iterator(): Iterator = kscience.kmath.nd.map { get(it) }.iterator() + override operator fun iterator(): Iterator = (0 until size).asSequence().map { get(it) }.iterator() public companion object { public fun create(spec: MemorySpec, size: Int): MemoryBuffer = diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt index 0f5f99b49..35d49e29d 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt @@ -1,6 +1,8 @@ package kscience.kmath.structures -import kscience.kmath.nd.NDField +import kscience.kmath.nd.NDAlgebra +import kscience.kmath.nd.get +import kscience.kmath.nd.real import kscience.kmath.operations.internal.FieldVerifier import kotlin.test.Test import kotlin.test.assertEquals @@ -8,12 +10,12 @@ import kotlin.test.assertEquals internal class NDFieldTest { @Test fun verify() { - NDField.real(12, 32).run { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) } + NDAlgebra.real(12, 32).run { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) } } @Test fun testStrides() { - val ndArray = NDElement.real(intArrayOf(10, 10)) { (it[0] + it[1]).toDouble() } + val ndArray = NDAlgebra.real(10, 10).produce { (it[0] + it[1]).toDouble() } assertEquals(ndArray[5, 5], 10.0) } } diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt index baf3656fc..b90e0f07f 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt @@ -1,9 +1,8 @@ package kscience.kmath.structures -import kscience.kmath.nd.NDField -import kscience.kmath.nd.NDStructure +import kscience.kmath.nd.* import kscience.kmath.operations.Norm -import kscience.kmath.structures.NDElement.Companion.real2D +import kscience.kmath.operations.invoke import kotlin.math.abs import kotlin.math.pow import kotlin.test.Test @@ -11,25 +10,30 @@ import kotlin.test.assertEquals @Suppress("UNUSED_VARIABLE") class NumberNDFieldTest { - val array1: RealNDElement = real2D(3, 3) { i, j -> (i + j).toDouble() } - val array2: RealNDElement = real2D(3, 3) { i, j -> (i - j).toDouble() } + val algebra = NDAlgebra.real(3,3) + val array1 = algebra.produceInline { (i, j) -> (i + j).toDouble() } + val array2 = algebra.produceInline { (i, j) -> (i - j).toDouble() } @Test fun testSum() { - val sum = array1 + array2 - assertEquals(4.0, sum[2, 2]) + algebra { + val sum = array1 + array2 + assertEquals(4.0, sum[2, 2]) + } } @Test fun testProduct() { - val product = array1 * array2 - assertEquals(0.0, product[2, 2]) + algebra { + val product = array1 * array2 + assertEquals(0.0, product[2, 2]) + } } @Test fun testGeneration() { - val array = real2D(3, 3) { i, j -> (i * 10 + j).toDouble() } + val array = Structure2D.real(3, 3) { i, j -> (i * 10 + j).toDouble() } for (i in 0..2) { for (j in 0..2) { @@ -41,16 +45,20 @@ class NumberNDFieldTest { @Test fun testExternalFunction() { - val function: (Double) -> Double = { x -> x.pow(2) + 2 * x + 1 } - val result = function(array1) + 1.0 - assertEquals(10.0, result[1, 1]) + algebra { + val function: (Double) -> Double = { x -> x.pow(2) + 2 * x + 1 } + val result = function(array1) + 1.0 + assertEquals(10.0, result[1, 1]) + } } @Test fun testLibraryFunction() { - val abs: (Double) -> Double = ::abs - val result = abs(array2) - assertEquals(2.0, result[0, 2]) + algebra { + val abs: (Double) -> Double = ::abs + val result = abs(array2) + assertEquals(2.0, result[0, 2]) + } } @Test @@ -65,6 +73,8 @@ class NumberNDFieldTest { @Test fun testInternalContext() { - (NDField.real(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } } + algebra { + (NDAlgebra.real(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } } + } } } diff --git a/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt index cac692eb2..3933ef28b 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt @@ -21,7 +21,7 @@ public class LazyNDStructure( public override fun elements(): Sequence> { val strides = DefaultStrides(shape) - val res = runBlocking { kscience.kmath.nd.map { index -> index to await(index) } } + val res = runBlocking { strides.indices().toList().map { index -> index to await(index) } } return res.asSequence() } diff --git a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt index ebd4dd586..eb126e00a 100644 --- a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt +++ b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt @@ -1,10 +1,7 @@ package kscience.kmath.ejml -import kscience.kmath.linear.InverseMatrixFeature -import kscience.kmath.linear.MatrixContext -import kscience.kmath.linear.Point +import kscience.kmath.linear.* import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.nd.Matrix import kscience.kmath.nd.getFeature import org.ejml.simple.SimpleMatrix diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt index defaba126..e7bde9980 100644 --- a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt @@ -1,11 +1,11 @@ package kscience.kmath.real -import kscience.kmath.linear.MatrixContext -import kscience.kmath.linear.real +import kscience.kmath.linear.* import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.nd.Matrix import kscience.kmath.structures.Buffer import kscience.kmath.structures.RealBuffer +import kscience.kmath.structures.asIterable +import kotlin.math.pow /* * Functions for convenient "numpy-like" operations with Double matrices. diff --git a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/RealMatrixTest.kt b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/RealMatrixTest.kt index 68aa8cf52..309997ae3 100644 --- a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/RealMatrixTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/RealMatrixTest.kt @@ -1,7 +1,9 @@ package kaceince.kmath.real -import kscience.kmath.nd.Matrix +import kscience.kmath.linear.Matrix +import kscience.kmath.linear.build import kscience.kmath.real.* +import kscience.kmath.structures.contentEquals import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue diff --git a/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt index 42dbaabac..085641106 100644 --- a/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt @@ -17,7 +17,7 @@ public data class BinDef>( require(vector.size == center.size) { "Dimension mismatch for input vector. Expected ${center.size}, but found ${vector.size}" } val upper = space { center + sizes / 2.0 } val lower = space { center - sizes / 2.0 } - return kscience.kmath.nd.mapIndexed { i, value -> value in lower[i]..upper[i] }.all { it } + return vector.asSequence().mapIndexed { i, value -> value in lower[i]..upper[i] }.all { it } } } @@ -70,7 +70,7 @@ public class RealHistogram( public fun getValue(point: Buffer): Long = getValue(getIndex(point)) private fun getDef(index: IntArray): BinDef { - val center = kscience.kmath.nd.mapIndexed { axis, i -> + val center = index.mapIndexed { axis, i -> when (i) { 0 -> Double.NEGATIVE_INFINITY strides.shape[axis] - 1 -> Double.POSITIVE_INFINITY @@ -100,7 +100,7 @@ public class RealHistogram( } public override operator fun iterator(): Iterator> = - kscience.kmath.nd.map { (index, value) -> MultivariateBin(getDef(index), value.sum()) } + weights.elements().map { (index, value) -> MultivariateBin(getDef(index), value.sum()) } .iterator() /** diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt index b958d1ccf..91d45dccd 100644 --- a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt @@ -7,6 +7,13 @@ import kscience.kmath.structures.* import org.nd4j.linalg.api.ndarray.INDArray import org.nd4j.linalg.factory.Nd4j +internal fun NDAlgebra<*, *>.checkShape(array: INDArray): INDArray { + val arrayShape = array.shape().toIntArray() + if (!shape.contentEquals(arrayShape)) throw ShapeMismatchException(shape, arrayShape) + return array +} + + /** * Represents [NDAlgebra] over [Nd4jArrayAlgebra]. * @@ -18,8 +25,9 @@ public interface Nd4jArrayAlgebra : NDAlgebra { * Wraps [INDArray] to [N]. */ public fun INDArray.wrap(): Nd4jArrayStructure + public val NDStructure.ndArray: INDArray - get() = when { + get() = when { !shape.contentEquals(this@Nd4jArrayAlgebra.shape) -> throw ShapeMismatchException( this@Nd4jArrayAlgebra.shape, shape @@ -213,7 +221,7 @@ public class RealNd4jArrayField(public override val shape: IntArray) : Nd4jArray public override val elementContext: RealField get() = RealField - public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(asRealStructure()) + public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(this).asRealStructure() public override operator fun NDStructure.div(arg: Double): Nd4jArrayStructure { return ndArray.div(arg).wrap() @@ -247,7 +255,7 @@ public class FloatNd4jArrayField(public override val shape: IntArray) : Nd4jArra public override val elementContext: FloatField get() = FloatField - public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(asFloatStructure()) + public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(this).asFloatStructure() public override operator fun NDStructure.div(arg: Float): Nd4jArrayStructure { return ndArray.div(arg).wrap() @@ -281,7 +289,7 @@ public class IntNd4jArrayRing(public override val shape: IntArray) : Nd4jArrayRi public override val elementContext: IntRing get() = IntRing - public override fun INDArray.wrap(): Nd4jArrayStructure = check(asIntStructure()) + public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(this).asIntStructure() public override operator fun NDStructure.plus(arg: Int): Nd4jArrayStructure { return ndArray.add(arg).wrap() @@ -307,7 +315,7 @@ public class LongNd4jArrayRing(public override val shape: IntArray) : Nd4jArrayR public override val elementContext: LongRing get() = LongRing - public override fun INDArray.wrap(): Nd4jArrayStructure = check(asLongStructure()) + public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(this).asLongStructure() public override operator fun NDStructure.plus(arg: Long): Nd4jArrayStructure { return ndArray.add(arg).wrap() diff --git a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt index 5dbc297b4..a6c4f3ce0 100644 --- a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt +++ b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt @@ -1,9 +1,6 @@ package kscience.kmath.viktor -import kscience.kmath.nd.DefaultStrides -import kscience.kmath.nd.MutableNDStructure -import kscience.kmath.nd.NDField -import kscience.kmath.nd.Strides +import kscience.kmath.nd.* import kscience.kmath.operations.RealField import org.jetbrains.bio.viktor.F64Array @@ -24,14 +21,25 @@ public inline class ViktorNDStructure(public val f64Buffer: F64Array) : MutableN public fun F64Array.asStructure(): ViktorNDStructure = ViktorNDStructure(this) @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -public class ViktorNDField(public override val shape: IntArray) : NDField { +public class ViktorNDField(public override val shape: IntArray) : NDField { + + public val NDStructure.f64Buffer: F64Array + get() = when { + !shape.contentEquals(this@ViktorNDField.shape) -> throw ShapeMismatchException( + this@ViktorNDField.shape, + shape + ) + this is ViktorNDStructure && this.f64Buffer.shape.contentEquals(this@ViktorNDField.shape) -> this.f64Buffer + else -> produce { this@f64Buffer[it] }.f64Buffer + } + public override val zero: ViktorNDStructure get() = F64Array.full(init = 0.0, shape = shape).asStructure() public override val one: ViktorNDStructure get() = F64Array.full(init = 1.0, shape = shape).asStructure() - public val strides: Strides = DefaultStrides(shape) + private val strides: Strides = DefaultStrides(shape) public override val elementContext: RealField get() = RealField @@ -42,7 +50,7 @@ public class ViktorNDField(public override val shape: IntArray) : NDField Double): ViktorNDStructure = + public override fun map(arg: NDStructure, transform: RealField.(Double) -> Double): ViktorNDStructure = F64Array(*shape).apply { this@ViktorNDField.strides.indices().forEach { index -> set(value = RealField.transform(arg[index]), indices = index) @@ -50,7 +58,7 @@ public class ViktorNDField(public override val shape: IntArray) : NDField, transform: RealField.(index: IntArray, Double) -> Double ): ViktorNDStructure = F64Array(*shape).apply { this@ViktorNDField.strides.indices().forEach { index -> @@ -59,8 +67,8 @@ public class ViktorNDField(public override val shape: IntArray) : NDField, + b: NDStructure, transform: RealField.(Double, Double) -> Double ): ViktorNDStructure = F64Array(*shape).apply { this@ViktorNDField.strides.indices().forEach { index -> @@ -68,21 +76,21 @@ public class ViktorNDField(public override val shape: IntArray) : NDField, b: NDStructure): ViktorNDStructure = (a.f64Buffer + b.f64Buffer).asStructure() - public override fun multiply(a: ViktorNDStructure, k: Number): ViktorNDStructure = + public override fun multiply(a: NDStructure, k: Number): ViktorNDStructure = (a.f64Buffer * k.toDouble()).asStructure() - public override inline fun ViktorNDStructure.plus(b: ViktorNDStructure): ViktorNDStructure = + public override inline fun NDStructure.plus(b: NDStructure): ViktorNDStructure = (f64Buffer + b.f64Buffer).asStructure() - public override inline fun ViktorNDStructure.minus(b: ViktorNDStructure): ViktorNDStructure = + public override inline fun NDStructure.minus(b: NDStructure): ViktorNDStructure = (f64Buffer - b.f64Buffer).asStructure() - public override inline fun ViktorNDStructure.times(k: Number): ViktorNDStructure = + public override inline fun NDStructure.times(k: Number): ViktorNDStructure = (f64Buffer * k.toDouble()).asStructure() - public override inline fun ViktorNDStructure.plus(arg: Double): ViktorNDStructure = + public override inline fun NDStructure.plus(arg: Double): ViktorNDStructure = (f64Buffer.plus(arg)).asStructure() } \ No newline at end of file From 0baec140593ceaf32eeef23d0969d64807ba062e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 23 Jan 2021 21:50:48 +0300 Subject: [PATCH 03/10] [WIP] Refactor NDStructures --- .../commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt | 2 +- .../src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt index cc1764643..c07726ad6 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt @@ -50,7 +50,7 @@ public interface BufferNDAlgebra : NDAlgebra { val aBuffer = a.ndBuffer val bBuffer = b.ndBuffer val buffer = bufferFactory(strides.linearSize) { offset -> - elementContext.transform(aBuffer.buffer[offset], bBuffer[offset]) + elementContext.transform(aBuffer.buffer[offset], bBuffer.buffer[offset]) } return NDBuffer(strides, buffer) } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt index 09c6fa198..4aa1c7d52 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDStructure.kt @@ -217,9 +217,7 @@ public class DefaultStrides private constructor(override val shape: IntArray) : } override fun offset(index: IntArray): Int = index.mapIndexed { i, value -> - if (value < 0 || value >= shape[i]) - throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${this.shape[i]})") - + if (value < 0 || value >= shape[i]) throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${this.shape[i]})") value * strides[i] }.sum() @@ -332,7 +330,7 @@ public inline fun NDStructure.mapToBuffer( public class MutableNDBuffer( strides: Strides, buffer: MutableBuffer, -) : NDBuffer(strides,buffer), MutableNDStructure { +) : NDBuffer(strides, buffer), MutableNDStructure { init { require(strides.linearSize == buffer.size) { From 1cb41f4dc2e936964937148f75bd4b52a0d80cd2 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 23 Jan 2021 22:16:27 +0300 Subject: [PATCH 04/10] Optimize Real NDField --- .../kscience/kmath/benchmarks/DotBenchmark.kt | 2 +- .../kmath/benchmarks/NDFieldBenchmark.kt | 28 ++---- .../kmath/benchmarks/ViktorBenchmark.kt | 10 +- .../kscience/kmath/structures/NDField.kt | 25 ++--- .../kscience/kmath/nd/BufferNDAlgebra.kt | 6 ++ .../kotlin/kscience/kmath/nd/RealNDField.kt | 93 ++++++++++--------- .../kotlin/kscience/kmath/nd/Structure2D.kt | 5 +- 7 files changed, 77 insertions(+), 92 deletions(-) diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt index 4a0c96f78..cc8043664 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt @@ -4,8 +4,8 @@ import kotlinx.benchmark.Benchmark import kscience.kmath.commons.linear.CMMatrixContext import kscience.kmath.ejml.EjmlMatrixContext import kscience.kmath.linear.BufferMatrixContext +import kscience.kmath.linear.Matrix import kscience.kmath.linear.RealMatrixContext -import kscience.kmath.nd.Matrix import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke import kscience.kmath.structures.Buffer diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/NDFieldBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/NDFieldBenchmark.kt index 3f103dd37..e465403ad 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/NDFieldBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/NDFieldBenchmark.kt @@ -1,11 +1,9 @@ package kscience.kmath.benchmarks -import kscience.kmath.nd.AbstractNDBuffer -import kscience.kmath.nd.NDField -import kscience.kmath.nd.RealNDField +import kscience.kmath.nd.* import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke -import kscience.kmath.structures.* +import kscience.kmath.structures.Buffer import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State @@ -14,22 +12,16 @@ import org.openjdk.jmh.annotations.State internal class NDFieldBenchmark { @Benchmark fun autoFieldAdd() { - bufferedField { - var res: AbstractNDBuffer = one + autoField { + var res: NDStructure = one repeat(n) { res += one } } } - @Benchmark - fun autoElementAdd() { - var res = genericField.one - repeat(n) { res += 1.0 } - } - @Benchmark fun specializedFieldAdd() { specializedField { - var res: AbstractNDBuffer = one + var res: NDStructure = one repeat(n) { res += 1.0 } } } @@ -38,16 +30,16 @@ internal class NDFieldBenchmark { @Benchmark fun boxingFieldAdd() { genericField { - var res: AbstractNDBuffer = one - repeat(n) { res += one } + var res: NDStructure = one + repeat(n) { res += 1.0 } } } companion object { const val dim: Int = 1000 const val n: Int = 100 - val bufferedField: BufferedNDField = NDField.auto(RealField, dim, dim) - val specializedField: RealNDField = NDField.real(dim, dim) - val genericField: BoxingNDField = NDField.boxing(RealField, dim, dim) + val autoField = NDAlgebra.auto(RealField, dim, dim) + val specializedField: RealNDField = NDAlgebra.real(dim, dim) + val genericField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim) } } \ No newline at end of file diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt index 49b30cf7e..892fb3457 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt @@ -1,10 +1,8 @@ package kscience.kmath.benchmarks -import kscience.kmath.nd.NDField -import kscience.kmath.nd.RealNDField +import kscience.kmath.nd.* import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke -import kscience.kmath.structures.BufferedNDField import kscience.kmath.viktor.ViktorNDField import org.jetbrains.bio.viktor.F64Array import org.openjdk.jmh.annotations.Benchmark @@ -17,14 +15,14 @@ internal class ViktorBenchmark { final val n: Int = 100 // automatically build context most suited for given type. - final val autoField: BufferedNDField = NDField.auto(RealField, dim, dim) - final val realField: RealNDField = NDField.real(dim, dim) + final val autoField: BufferedNDField = NDAlgebra.auto(RealField, dim, dim) + final val realField: RealNDField = NDAlgebra.real(dim, dim) final val viktorField: ViktorNDField = ViktorNDField(intArrayOf(dim, dim)) @Benchmark fun automaticFieldAddition() { autoField { - var res = one + var res: NDStructure = one repeat(n) { res += one } } } diff --git a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt index 3e84c0be0..697affeca 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt @@ -1,10 +1,7 @@ package kscience.kmath.structures import kotlinx.coroutines.GlobalScope -import kscience.kmath.nd.NDAlgebra -import kscience.kmath.nd.NDStructure -import kscience.kmath.nd.field -import kscience.kmath.nd.real +import kscience.kmath.nd.* import kscience.kmath.nd4j.Nd4jArrayField import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke @@ -26,11 +23,11 @@ fun main() { val n = 1000 // automatically build context most suited for given type. - val autoField = NDAlgebra.field(RealField, Buffer.Companion::auto, dim, dim) + val autoField = NDAlgebra.auto(RealField, dim, dim) // specialized nd-field for Double. It works as generic Double field as well val specializedField = NDAlgebra.real(dim, dim) //A generic boxing field. It should be used for objects, not primitives. - val boxingField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim) + val boxingField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim) // Nd4j specialized field. val nd4jField = Nd4jArrayField.real(dim, dim) @@ -41,9 +38,9 @@ fun main() { } } - measureAndPrint("Element addition") { + measureAndPrint("Boxing addition") { boxingField { - var res: NDStructure = one + var res: NDStructure = one repeat(n) { res += 1.0 } } } @@ -57,7 +54,7 @@ fun main() { measureAndPrint("Nd4j specialized addition") { nd4jField { - var res:NDStructure = one + var res: NDStructure = one repeat(n) { res += 1.0 } } } @@ -73,14 +70,4 @@ fun main() { res.elements().forEach { it.second } } - - measureAndPrint("Generic addition") { - //genericField.run(action) - boxingField { - var res: NDStructure = one - repeat(n) { - res += 1.0 // couldn't avoid using `one` due to resolution ambiguity } - } - } - } } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt index c07726ad6..901bb5a44 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt @@ -2,6 +2,7 @@ package kscience.kmath.nd import kscience.kmath.nd.* import kscience.kmath.operations.* +import kscience.kmath.structures.Buffer import kscience.kmath.structures.BufferFactory import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -118,6 +119,11 @@ public fun > NDAlgebra.Companion.field( vararg shape: Int, ): BufferedNDField = BufferedNDField(shape, field, bufferFactory) +public inline fun > NDAlgebra.Companion.auto( + field: A, + vararg shape: Int, +): BufferedNDField = BufferedNDField(shape, field, Buffer.Companion::auto) + public inline fun , R> A.ndField( noinline bufferFactory: BufferFactory, vararg shape: Int, diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt index 7a0d8f8f9..b96ee0e9b 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt @@ -10,7 +10,7 @@ import kotlin.contracts.InvocationKind import kotlin.contracts.contract @OptIn(UnstableKMathAPI::class) -public open class RealNDField( +public class RealNDField( shape: IntArray, ) : BufferedNDField(shape, RealField, Buffer.Companion::real), RingWithNumbers>, @@ -23,51 +23,52 @@ public open class RealNDField( val d = value.toDouble() // minimize conversions return produce { d } } -// -// @Suppress("OVERRIDE_BY_INLINE") -// override inline fun map( -// arg: AbstractNDBuffer, -// transform: RealField.(Double) -> Double, -// ): RealNDElement { -// check(arg) -// val array = RealBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) } -// return BufferedNDFieldElement(this, array) -// } -// -// @Suppress("OVERRIDE_BY_INLINE") -// override inline fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement { -// val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } -// return BufferedNDFieldElement(this, array) -// } -// -// @Suppress("OVERRIDE_BY_INLINE") -// override inline fun mapIndexed( -// arg: AbstractNDBuffer, -// transform: RealField.(index: IntArray, Double) -> Double, -// ): RealNDElement { -// check(arg) -// return BufferedNDFieldElement( -// this, -// RealBuffer(arg.strides.linearSize) { offset -> -// elementContext.transform( -// arg.strides.index(offset), -// arg.buffer[offset] -// ) -// }) -// } -// -// @Suppress("OVERRIDE_BY_INLINE") -// override inline fun combine( -// a: AbstractNDBuffer, -// b: AbstractNDBuffer, -// transform: RealField.(Double, Double) -> Double, -// ): RealNDElement { -// check(a, b) -// val buffer = RealBuffer(strides.linearSize) { offset -> -// elementContext.transform(a.buffer[offset], b.buffer[offset]) -// } -// return BufferedNDFieldElement(this, buffer) -// } + + @Suppress("OVERRIDE_BY_INLINE") + override inline fun map( + arg: NDStructure, + transform: RealField.(Double) -> Double, + ): NDBuffer { + val argAsBuffer = arg.ndBuffer + val buffer = RealBuffer(strides.linearSize) { offset -> RealField.transform(argAsBuffer.buffer[offset]) } + return NDBuffer(strides, buffer) + } + + @Suppress("OVERRIDE_BY_INLINE") + override inline fun produce(initializer: RealField.(IntArray) -> Double): NDBuffer { + val buffer = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } + return NDBuffer(strides, buffer) + } + + @Suppress("OVERRIDE_BY_INLINE") + override inline fun mapIndexed( + arg: NDStructure, + transform: RealField.(index: IntArray, Double) -> Double, + ): NDBuffer { + val argAsBuffer = arg.ndBuffer + return NDBuffer( + strides, + RealBuffer(strides.linearSize) { offset -> + elementContext.transform( + strides.index(offset), + argAsBuffer.buffer[offset] + ) + }) + } + + @Suppress("OVERRIDE_BY_INLINE") + override inline fun combine( + a: NDStructure, + b: NDStructure, + transform: RealField.(Double, Double) -> Double, + ): NDBuffer { + val aBuffer = a.ndBuffer + val bBuffer = b.ndBuffer + val buffer = RealBuffer(strides.linearSize) { offset -> + elementContext.transform(aBuffer.buffer[offset], bBuffer.buffer[offset]) + } + return NDBuffer(strides, buffer) + } override fun power(arg: NDStructure, pow: Number): NDBuffer = map(arg) { power(it, pow) } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt index b092d48e8..48fba45d9 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt @@ -1,5 +1,6 @@ package kscience.kmath.nd +import kscience.kmath.linear.Matrix import kscience.kmath.structures.Buffer import kscience.kmath.structures.VirtualBuffer @@ -57,9 +58,9 @@ public interface Structure2D : NDStructure { rows: Int, columns: Int, crossinline init: (i: Int, j: Int) -> Double, - ): NDBuffer = NDAlgebra.real(rows, columns).produceInline { (i, j) -> + ): Matrix = NDAlgebra.real(rows, columns).produceInline { (i, j) -> init(i, j) - } + }.as2D() } } From 9829a16a32197cd819645f6105976f6829d858de Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 24 Jan 2021 10:15:16 +0300 Subject: [PATCH 05/10] Optimize Real NDField --- .../kmath/benchmarks/ViktorBenchmark.kt | 30 ++--- .../kmath/benchmarks/ViktorLogBenchmark.kt | 40 +++++++ .../kscience/kmath/structures/NDField.kt | 39 +++++-- .../kmath/structures/ParallelRealNDField.kt | 103 ++++++++++++++++++ .../kscience/kmath/nd/BufferNDAlgebra.kt | 22 ++-- .../kscience/kmath/nd/ComplexNDField.kt | 2 +- .../kotlin/kscience/kmath/nd/RealNDField.kt | 56 +++++----- .../kotlin/kscience/kmath/nd/ShortNDRing.kt | 2 +- .../kotlin/kscience/kmath/nd/Structure2D.kt | 2 +- .../kmath/structures/NumberNDFieldTest.kt | 4 +- .../kmath/viktor/ViktorNDStructure.kt | 4 +- 11 files changed, 226 insertions(+), 78 deletions(-) create mode 100644 examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorLogBenchmark.kt create mode 100644 examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt index 892fb3457..85c5d8289 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt @@ -15,9 +15,9 @@ internal class ViktorBenchmark { final val n: Int = 100 // automatically build context most suited for given type. - final val autoField: BufferedNDField = NDAlgebra.auto(RealField, dim, dim) + final val autoField: NDField = NDAlgebra.auto(RealField, dim, dim) final val realField: RealNDField = NDAlgebra.real(dim, dim) - final val viktorField: ViktorNDField = ViktorNDField(intArrayOf(dim, dim)) + final val viktorField: ViktorNDField = ViktorNDField(dim, dim) @Benchmark fun automaticFieldAddition() { @@ -27,6 +27,14 @@ internal class ViktorBenchmark { } } + @Benchmark + fun realFieldAddition() { + realField { + var res: NDStructure = one + repeat(n) { res += one } + } + } + @Benchmark fun viktorFieldAddition() { viktorField { @@ -41,22 +49,4 @@ internal class ViktorBenchmark { var res = one repeat(n) { res = res + one } } - - @Benchmark - fun realFieldLog() { - realField { - val fortyTwo = produce { 42.0 } - var res = one - repeat(n) { res = ln(fortyTwo) } - } - } - - @Benchmark - fun rawViktorLog() { - val fortyTwo = F64Array.full(dim, dim, init = 42.0) - var res: F64Array - repeat(n) { - res = fortyTwo.log() - } - } } \ No newline at end of file diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorLogBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorLogBenchmark.kt new file mode 100644 index 000000000..e841c53c9 --- /dev/null +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorLogBenchmark.kt @@ -0,0 +1,40 @@ +package kscience.kmath.benchmarks + +import kscience.kmath.nd.* +import kscience.kmath.operations.RealField +import kscience.kmath.operations.invoke +import kscience.kmath.viktor.ViktorNDField +import org.jetbrains.bio.viktor.F64Array +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State + +@State(Scope.Benchmark) +internal class ViktorLogBenchmark { + final val dim: Int = 1000 + final val n: Int = 100 + + // automatically build context most suited for given type. + final val autoField: BufferedNDField = NDAlgebra.auto(RealField, dim, dim) + final val realField: RealNDField = NDAlgebra.real(dim, dim) + final val viktorField: ViktorNDField = ViktorNDField(intArrayOf(dim, dim)) + + + @Benchmark + fun realFieldLog() { + realField { + val fortyTwo = produce { 42.0 } + var res = one + repeat(n) { res = ln(fortyTwo) } + } + } + + @Benchmark + fun rawViktorLog() { + val fortyTwo = F64Array.full(dim, dim, init = 42.0) + var res: F64Array + repeat(n) { + res = fortyTwo.log() + } + } +} \ No newline at end of file diff --git a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt index 697affeca..1e7a19cf8 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt @@ -5,6 +5,7 @@ import kscience.kmath.nd.* import kscience.kmath.nd4j.Nd4jArrayField import kscience.kmath.operations.RealField import kscience.kmath.operations.invoke +import kscience.kmath.viktor.ViktorNDField import org.nd4j.linalg.factory.Nd4j import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -25,18 +26,15 @@ fun main() { // automatically build context most suited for given type. val autoField = NDAlgebra.auto(RealField, dim, dim) // specialized nd-field for Double. It works as generic Double field as well - val specializedField = NDAlgebra.real(dim, dim) + val realField = NDAlgebra.real(dim, dim) //A generic boxing field. It should be used for objects, not primitives. val boxingField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim) // Nd4j specialized field. val nd4jField = Nd4jArrayField.real(dim, dim) - - measureAndPrint("Automatic field addition") { - autoField { - var res: NDStructure = one - repeat(n) { res += 1.0 } - } - } + //viktor field + val viktorField = ViktorNDField(dim,dim) + //parallel processing based on Java Streams + val parallelField = NDAlgebra.realWithStream(dim,dim) measureAndPrint("Boxing addition") { boxingField { @@ -46,7 +44,7 @@ fun main() { } measureAndPrint("Specialized addition") { - specializedField { + realField { var res: NDStructure = one repeat(n) { res += 1.0 } } @@ -59,8 +57,29 @@ fun main() { } } + measureAndPrint("Viktor addition") { + viktorField { + var res: NDStructure = one + repeat(n) { res += 1.0 } + } + } + + measureAndPrint("Parallel stream addition") { + parallelField { + var res: NDStructure = one + repeat(n) { res += 1.0 } + } + } + + measureAndPrint("Automatic field addition") { + autoField { + var res: NDStructure = one + repeat(n) { res += 1.0 } + } + } + measureAndPrint("Lazy addition") { - val res = specializedField.one.mapAsync(GlobalScope) { + val res = realField.one.mapAsync(GlobalScope) { var c = 0.0 repeat(n) { c += 1.0 diff --git a/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt b/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt new file mode 100644 index 000000000..bce625f7f --- /dev/null +++ b/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt @@ -0,0 +1,103 @@ +package kscience.kmath.structures + +import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.nd.* +import kscience.kmath.operations.ExtendedField +import kscience.kmath.operations.RealField +import kscience.kmath.operations.RingWithNumbers +import java.util.* +import java.util.stream.IntStream + +/** + * A demonstration implementation of NDField over Real using Java [DoubleStream] for parallel execution + */ +@OptIn(UnstableKMathAPI::class) +public class StreamRealNDField( + shape: IntArray, +) : BufferedNDField(shape, RealField, Buffer.Companion::real), + RingWithNumbers>, + ExtendedField> { + + override val zero: NDBuffer by lazy { produce { zero } } + override val one: NDBuffer by lazy { produce { one } } + + override fun number(value: Number): NDBuffer { + val d = value.toDouble() // minimize conversions + return produce { d } + } + + override val NDStructure.buffer: RealBuffer + get() = when { + !shape.contentEquals(this@StreamRealNDField.shape) -> throw ShapeMismatchException( + this@StreamRealNDField.shape, + shape + ) + this is NDBuffer && this.strides == this@StreamRealNDField.strides -> this.buffer as RealBuffer + else -> RealBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } + } + + + override fun produce(initializer: RealField.(IntArray) -> Double): NDBuffer { + val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> + val index = strides.index(offset) + RealField.initializer(index) + }.toArray() + + return NDBuffer(strides, array.asBuffer()) + } + + override fun map( + arg: NDStructure, + transform: RealField.(Double) -> Double, + ): NDBuffer { + val array = Arrays.stream(arg.buffer.array).parallel().map { RealField.transform(it) }.toArray() + return NDBuffer(strides, array.asBuffer()) + } + + override fun mapIndexed( + arg: NDStructure, + transform: RealField.(index: IntArray, Double) -> Double, + ): NDBuffer { + val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> + RealField.transform( + strides.index(offset), + arg.buffer.array[offset] + ) + }.toArray() + + return NDBuffer(strides, array.asBuffer()) + } + + override fun combine( + a: NDStructure, + b: NDStructure, + transform: RealField.(Double, Double) -> Double, + ): NDBuffer { + val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> + RealField.transform(a.buffer.array[offset], b.buffer.array[offset]) + }.toArray() + return NDBuffer(strides, array.asBuffer()) + } + + override fun power(arg: NDStructure, pow: Number): NDBuffer = map(arg) { power(it, pow) } + + override fun exp(arg: NDStructure): NDBuffer = map(arg) { exp(it) } + + override fun ln(arg: NDStructure): NDBuffer = map(arg) { ln(it) } + + override fun sin(arg: NDStructure): NDBuffer = map(arg) { sin(it) } + override fun cos(arg: NDStructure): NDBuffer = map(arg) { cos(it) } + override fun tan(arg: NDStructure): NDBuffer = map(arg) { tan(it) } + override fun asin(arg: NDStructure): NDBuffer = map(arg) { asin(it) } + override fun acos(arg: NDStructure): NDBuffer = map(arg) { acos(it) } + override fun atan(arg: NDStructure): NDBuffer = map(arg) { atan(it) } + + override fun sinh(arg: NDStructure): NDBuffer = map(arg) { sinh(it) } + override fun cosh(arg: NDStructure): NDBuffer = map(arg) { cosh(it) } + override fun tanh(arg: NDStructure): NDBuffer = map(arg) { tanh(it) } + override fun asinh(arg: NDStructure): NDBuffer = map(arg) { asinh(it) } + override fun acosh(arg: NDStructure): NDBuffer = map(arg) { acosh(it) } + override fun atanh(arg: NDStructure): NDBuffer = map(arg) { atanh(it) } +} + +fun NDAlgebra.Companion.realWithStream(vararg shape: Int): StreamRealNDField = StreamRealNDField(shape) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt index 901bb5a44..93add36eb 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt @@ -18,40 +18,36 @@ public interface BufferNDAlgebra : NDAlgebra { } ) - public val NDStructure.ndBuffer: NDBuffer + public val NDStructure.buffer: Buffer get() = when { !shape.contentEquals(this@BufferNDAlgebra.shape) -> throw ShapeMismatchException( this@BufferNDAlgebra.shape, shape ) - this is NDBuffer && this.strides == this@BufferNDAlgebra.strides -> this - else -> produce { this@ndBuffer[it] } + this is NDBuffer && this.strides == this@BufferNDAlgebra.strides -> this.buffer + else -> bufferFactory(strides.linearSize) { offset -> get(strides.index(offset)) } } override fun map(arg: NDStructure, transform: C.(T) -> T): NDBuffer { - val argAsBuffer = arg.ndBuffer val buffer = bufferFactory(strides.linearSize) { offset -> - elementContext.transform(argAsBuffer.buffer[offset]) + elementContext.transform(arg.buffer[offset]) } return NDBuffer(strides, buffer) } override fun mapIndexed(arg: NDStructure, transform: C.(index: IntArray, T) -> T): NDStructure { - val argAsBuffer = arg.ndBuffer val buffer = bufferFactory(strides.linearSize) { offset -> elementContext.transform( strides.index(offset), - argAsBuffer[offset] + arg.buffer[offset] ) } return NDBuffer(strides, buffer) } override fun combine(a: NDStructure, b: NDStructure, transform: C.(T, T) -> T): NDStructure { - val aBuffer = a.ndBuffer - val bBuffer = b.ndBuffer val buffer = bufferFactory(strides.linearSize) { offset -> - elementContext.transform(aBuffer.buffer[offset], bBuffer.buffer[offset]) + elementContext.transform(a.buffer[offset], b.buffer[offset]) } return NDBuffer(strides, buffer) } @@ -119,10 +115,14 @@ public fun > NDAlgebra.Companion.field( vararg shape: Int, ): BufferedNDField = BufferedNDField(shape, field, bufferFactory) +@Suppress("UNCHECKED_CAST") public inline fun > NDAlgebra.Companion.auto( field: A, vararg shape: Int, -): BufferedNDField = BufferedNDField(shape, field, Buffer.Companion::auto) +): NDField = when (field) { + RealField -> RealNDField(shape) as NDField + else -> BufferedNDField(shape, field, Buffer.Companion::auto) +} public inline fun , R> A.ndField( noinline bufferFactory: BufferFactory, diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt index dbcce5440..074a1185b 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt @@ -11,7 +11,7 @@ import kotlin.contracts.contract * An optimized nd-field for complex numbers */ @OptIn(UnstableKMathAPI::class) -public open class ComplexNDField( +public class ComplexNDField( shape: IntArray, ) : BufferedNDField(shape, ComplexField, Buffer.Companion::complex), RingWithNumbers>, diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt index b96ee0e9b..91b5500cd 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt @@ -24,37 +24,46 @@ public class RealNDField( return produce { d } } + override val NDStructure.buffer: RealBuffer + get() = when { + !shape.contentEquals(this@RealNDField.shape) -> throw ShapeMismatchException( + this@RealNDField.shape, + shape + ) + this is NDBuffer && this.strides == this@RealNDField.strides -> this.buffer as RealBuffer + else -> RealBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } + } + @Suppress("OVERRIDE_BY_INLINE") override inline fun map( arg: NDStructure, transform: RealField.(Double) -> Double, ): NDBuffer { - val argAsBuffer = arg.ndBuffer - val buffer = RealBuffer(strides.linearSize) { offset -> RealField.transform(argAsBuffer.buffer[offset]) } + val buffer = RealBuffer(strides.linearSize) { offset -> RealField.transform(arg.buffer.array[offset]) } return NDBuffer(strides, buffer) } @Suppress("OVERRIDE_BY_INLINE") override inline fun produce(initializer: RealField.(IntArray) -> Double): NDBuffer { - val buffer = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } - return NDBuffer(strides, buffer) + val array = DoubleArray(strides.linearSize) { offset -> + val index = strides.index(offset) + RealField.initializer(index) + } + return NDBuffer(strides, RealBuffer(array)) } @Suppress("OVERRIDE_BY_INLINE") override inline fun mapIndexed( arg: NDStructure, transform: RealField.(index: IntArray, Double) -> Double, - ): NDBuffer { - val argAsBuffer = arg.ndBuffer - return NDBuffer( - strides, - RealBuffer(strides.linearSize) { offset -> - elementContext.transform( - strides.index(offset), - argAsBuffer.buffer[offset] - ) - }) - } + ): NDBuffer = NDBuffer( + strides, + buffer = RealBuffer(strides.linearSize) { offset -> + RealField.transform( + strides.index(offset), + arg.buffer.array[offset] + ) + }) @Suppress("OVERRIDE_BY_INLINE") override inline fun combine( @@ -62,10 +71,8 @@ public class RealNDField( b: NDStructure, transform: RealField.(Double, Double) -> Double, ): NDBuffer { - val aBuffer = a.ndBuffer - val bBuffer = b.ndBuffer val buffer = RealBuffer(strides.linearSize) { offset -> - elementContext.transform(aBuffer.buffer[offset], bBuffer.buffer[offset]) + RealField.transform(a.buffer.array[offset], b.buffer.array[offset]) } return NDBuffer(strides, buffer) } @@ -91,19 +98,6 @@ public class RealNDField( override fun atanh(arg: NDStructure): NDBuffer = map(arg) { atanh(it) } } - -/** - * Fast element production using function inlining - */ -public inline fun BufferedNDField.produceInline(crossinline initializer: RealField.(IntArray) -> Double): NDBuffer { - contract { callsInPlace(initializer, InvocationKind.EXACTLY_ONCE) } - val array = DoubleArray(strides.linearSize) { offset -> - val index = strides.index(offset) - RealField.initializer(index) - } - return NDBuffer(strides, RealBuffer(array)) -} - public fun NDAlgebra.Companion.real(vararg shape: Int): RealNDField = RealNDField(shape) /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ShortNDRing.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ShortNDRing.kt index 8663c4249..7ffe688cb 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ShortNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ShortNDRing.kt @@ -9,7 +9,7 @@ import kotlin.contracts.InvocationKind import kotlin.contracts.contract @OptIn(UnstableKMathAPI::class) -public open class ShortNDRing( +public class ShortNDRing( shape: IntArray, ) : BufferedNDRing(shape, ShortRing, Buffer.Companion::auto), RingWithNumbers> { diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt index 48fba45d9..42a643db1 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt @@ -58,7 +58,7 @@ public interface Structure2D : NDStructure { rows: Int, columns: Int, crossinline init: (i: Int, j: Int) -> Double, - ): Matrix = NDAlgebra.real(rows, columns).produceInline { (i, j) -> + ): Matrix = NDAlgebra.real(rows, columns).produce { (i, j) -> init(i, j) }.as2D() } diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt index b90e0f07f..4f0c9fc6d 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt @@ -11,8 +11,8 @@ import kotlin.test.assertEquals @Suppress("UNUSED_VARIABLE") class NumberNDFieldTest { val algebra = NDAlgebra.real(3,3) - val array1 = algebra.produceInline { (i, j) -> (i + j).toDouble() } - val array2 = algebra.produceInline { (i, j) -> (i - j).toDouble() } + val array1 = algebra.produce { (i, j) -> (i + j).toDouble() } + val array2 = algebra.produce { (i, j) -> (i - j).toDouble() } @Test fun testSum() { diff --git a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt index a6c4f3ce0..f91359a0c 100644 --- a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt +++ b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt @@ -93,4 +93,6 @@ public class ViktorNDField(public override val shape: IntArray) : NDField.plus(arg: Double): ViktorNDStructure = (f64Buffer.plus(arg)).asStructure() -} \ No newline at end of file +} + +public fun ViktorNDField(vararg shape: Int): ViktorNDField = ViktorNDField(shape) \ No newline at end of file From 3bbb39f30d423e0889133befa9db2d4b7cb07677 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 24 Jan 2021 10:26:56 +0300 Subject: [PATCH 06/10] cleanup parallel field example --- .../kscience/kmath/structures/ParallelRealNDField.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt b/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt index bce625f7f..e13f5dd47 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt @@ -12,12 +12,14 @@ import java.util.stream.IntStream * A demonstration implementation of NDField over Real using Java [DoubleStream] for parallel execution */ @OptIn(UnstableKMathAPI::class) -public class StreamRealNDField( - shape: IntArray, -) : BufferedNDField(shape, RealField, Buffer.Companion::real), +class StreamRealNDField( + override val shape: IntArray, +) : NDField, RingWithNumbers>, ExtendedField> { + private val strides = DefaultStrides(shape) + override val elementContext: RealField get() = RealField override val zero: NDBuffer by lazy { produce { zero } } override val one: NDBuffer by lazy { produce { one } } @@ -26,7 +28,7 @@ public class StreamRealNDField( return produce { d } } - override val NDStructure.buffer: RealBuffer + private val NDStructure.buffer: RealBuffer get() = when { !shape.contentEquals(this@StreamRealNDField.shape) -> throw ShapeMismatchException( this@StreamRealNDField.shape, From aff7bbab4131380929fb906ec57901b776c395e3 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 24 Jan 2021 17:07:19 +0300 Subject: [PATCH 07/10] Matrix performance optimization --- .../kscience/kmath/benchmarks/DotBenchmark.kt | 15 ++--- .../benchmarks/LinearAlgebraBenchmark.kt | 4 +- .../kmath/benchmarks/ViktorBenchmark.kt | 6 +- .../kmath/benchmarks/ViktorLogBenchmark.kt | 11 +++- .../kmath/structures/ParallelRealNDField.kt | 40 +++++++------ .../kscience/kmath/linear/BufferMatrix.kt | 57 +++++++++++++++++++ .../kscience/kmath/linear/MatrixWrapper.kt | 6 -- .../kmath/linear/RealMatrixContext.kt | 28 +++++---- .../kscience/kmath/nd/BufferNDAlgebra.kt | 10 ++-- .../kscience/kmath/nd/ComplexNDField.kt | 30 +++++----- .../kotlin/kscience/kmath/nd/NDAlgebra.kt | 24 ++++---- .../kotlin/kscience/kmath/nd/RealNDField.kt | 46 +++++++-------- .../kotlin/kscience/kmath/nd/Structure2D.kt | 7 ++- .../kotlin/kscience/kmath/real/RealMatrix.kt | 2 +- .../kscience.kmath.nd4j/Nd4jArrayAlgebra.kt | 11 ++-- .../kmath/nd4j/Nd4jArrayAlgebraTest.kt | 4 +- .../kmath/viktor/ViktorNDStructure.kt | 43 ++++++++++---- 17 files changed, 215 insertions(+), 129 deletions(-) diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt index cc8043664..2256a3e02 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/DotBenchmark.kt @@ -31,38 +31,35 @@ class DotBenchmark { } @Benchmark - fun commonsMathMultiplication() { + fun cmDot() { CMMatrixContext { cmMatrix1 dot cmMatrix2 } } @Benchmark - fun ejmlMultiplication() { + fun ejmlDot() { EjmlMatrixContext { ejmlMatrix1 dot ejmlMatrix2 } } @Benchmark - fun ejmlMultiplicationwithConversion() { + fun ejmlDotWithConversion() { EjmlMatrixContext { - val ejmlMatrix1 = matrix1.toEjml() - val ejmlMatrix2 = matrix2.toEjml() - - ejmlMatrix1 dot ejmlMatrix2 + matrix1 dot matrix2 } } @Benchmark - fun bufferedMultiplication() { + fun bufferedDot() { BufferMatrixContext(RealField, Buffer.Companion::real).invoke { matrix1 dot matrix2 } } @Benchmark - fun realMultiplication() { + fun realDot() { RealMatrixContext { matrix1 dot matrix2 } diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt index 9e5f76112..4a06724ec 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt @@ -26,7 +26,9 @@ class LinearAlgebraBenchmark { @Benchmark fun kmathLUPInversion() { - MatrixContext.real.inverseWithLUP(matrix) + MatrixContext.real{ + inverseWithLUP(matrix) + } } @Benchmark diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt index 85c5d8289..e246936f0 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorBenchmark.kt @@ -23,7 +23,7 @@ internal class ViktorBenchmark { fun automaticFieldAddition() { autoField { var res: NDStructure = one - repeat(n) { res += one } + repeat(n) { res += 1.0 } } } @@ -31,7 +31,7 @@ internal class ViktorBenchmark { fun realFieldAddition() { realField { var res: NDStructure = one - repeat(n) { res += one } + repeat(n) { res += 1.0 } } } @@ -39,7 +39,7 @@ internal class ViktorBenchmark { fun viktorFieldAddition() { viktorField { var res = one - repeat(n) { res += one } + repeat(n) { res += 1.0 } } } diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorLogBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorLogBenchmark.kt index e841c53c9..b9c39b088 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorLogBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/ViktorLogBenchmark.kt @@ -15,7 +15,7 @@ internal class ViktorLogBenchmark { final val n: Int = 100 // automatically build context most suited for given type. - final val autoField: BufferedNDField = NDAlgebra.auto(RealField, dim, dim) + final val autoField: NDField = NDAlgebra.auto(RealField, dim, dim) final val realField: RealNDField = NDAlgebra.real(dim, dim) final val viktorField: ViktorNDField = ViktorNDField(intArrayOf(dim, dim)) @@ -29,6 +29,15 @@ internal class ViktorLogBenchmark { } } + @Benchmark + fun viktorFieldLog() { + viktorField { + val fortyTwo = produce { 42.0 } + var res = one + repeat(n) { res = ln(fortyTwo) } + } + } + @Benchmark fun rawViktorLog() { val fortyTwo = F64Array.full(dim, dim, init = 42.0) diff --git a/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt b/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt index e13f5dd47..48286c140 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/ParallelRealNDField.kt @@ -48,22 +48,20 @@ class StreamRealNDField( return NDBuffer(strides, array.asBuffer()) } - override fun map( - arg: NDStructure, + override fun NDStructure.map( transform: RealField.(Double) -> Double, ): NDBuffer { - val array = Arrays.stream(arg.buffer.array).parallel().map { RealField.transform(it) }.toArray() + val array = Arrays.stream(buffer.array).parallel().map { RealField.transform(it) }.toArray() return NDBuffer(strides, array.asBuffer()) } - override fun mapIndexed( - arg: NDStructure, + override fun NDStructure.mapIndexed( transform: RealField.(index: IntArray, Double) -> Double, ): NDBuffer { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> RealField.transform( strides.index(offset), - arg.buffer.array[offset] + buffer.array[offset] ) }.toArray() @@ -81,25 +79,25 @@ class StreamRealNDField( return NDBuffer(strides, array.asBuffer()) } - override fun power(arg: NDStructure, pow: Number): NDBuffer = map(arg) { power(it, pow) } + override fun power(arg: NDStructure, pow: Number): NDBuffer = arg.map() { power(it, pow) } - override fun exp(arg: NDStructure): NDBuffer = map(arg) { exp(it) } + override fun exp(arg: NDStructure): NDBuffer = arg.map() { exp(it) } - override fun ln(arg: NDStructure): NDBuffer = map(arg) { ln(it) } + override fun ln(arg: NDStructure): NDBuffer = arg.map() { ln(it) } - override fun sin(arg: NDStructure): NDBuffer = map(arg) { sin(it) } - override fun cos(arg: NDStructure): NDBuffer = map(arg) { cos(it) } - override fun tan(arg: NDStructure): NDBuffer = map(arg) { tan(it) } - override fun asin(arg: NDStructure): NDBuffer = map(arg) { asin(it) } - override fun acos(arg: NDStructure): NDBuffer = map(arg) { acos(it) } - override fun atan(arg: NDStructure): NDBuffer = map(arg) { atan(it) } + override fun sin(arg: NDStructure): NDBuffer = arg.map() { sin(it) } + override fun cos(arg: NDStructure): NDBuffer = arg.map() { cos(it) } + override fun tan(arg: NDStructure): NDBuffer = arg.map() { tan(it) } + override fun asin(arg: NDStructure): NDBuffer = arg.map() { asin(it) } + override fun acos(arg: NDStructure): NDBuffer = arg.map() { acos(it) } + override fun atan(arg: NDStructure): NDBuffer = arg.map() { atan(it) } - override fun sinh(arg: NDStructure): NDBuffer = map(arg) { sinh(it) } - override fun cosh(arg: NDStructure): NDBuffer = map(arg) { cosh(it) } - override fun tanh(arg: NDStructure): NDBuffer = map(arg) { tanh(it) } - override fun asinh(arg: NDStructure): NDBuffer = map(arg) { asinh(it) } - override fun acosh(arg: NDStructure): NDBuffer = map(arg) { acosh(it) } - override fun atanh(arg: NDStructure): NDBuffer = map(arg) { atanh(it) } + override fun sinh(arg: NDStructure): NDBuffer = arg.map() { sinh(it) } + override fun cosh(arg: NDStructure): NDBuffer = arg.map() { cosh(it) } + override fun tanh(arg: NDStructure): NDBuffer = arg.map() { tanh(it) } + override fun asinh(arg: NDStructure): NDBuffer = arg.map() { asinh(it) } + override fun acosh(arg: NDStructure): NDBuffer = arg.map() { acosh(it) } + override fun atanh(arg: NDStructure): NDBuffer = arg.map() { atanh(it) } } fun NDAlgebra.Companion.realWithStream(vararg shape: Int): StreamRealNDField = StreamRealNDField(shape) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt index bcd1c9976..6a66e91c8 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt @@ -3,6 +3,7 @@ package kscience.kmath.linear import kscience.kmath.nd.NDStructure import kscience.kmath.nd.Structure2D import kscience.kmath.operations.Ring +import kscience.kmath.operations.invoke import kscience.kmath.structures.Buffer import kscience.kmath.structures.BufferFactory import kscience.kmath.structures.asSequence @@ -28,6 +29,62 @@ public class BufferMatrixContext>( public override fun point(size: Int, initializer: (Int) -> T): Point = bufferFactory(size, initializer) + private fun Matrix.toBufferMatrix(): BufferMatrix = if (this is BufferMatrix) this else { + produce(rowNum, colNum) { i, j -> get(i, j) } + } + + public fun one(rows: Int, columns: Int): Matrix = VirtualMatrix(rows, columns) { i, j -> + if (i == j) 1.0 else 0.0 + } + DiagonalFeature + + public override infix fun Matrix.dot(other: Matrix): BufferMatrix { + require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" } + val bufferMatrix = toBufferMatrix() + val otherBufferMatrix = other.toBufferMatrix() + return elementContext { + produce(rowNum, other.colNum) { i, j -> + var res = one + for (l in 0 until colNum) { + res += bufferMatrix[i, l] * otherBufferMatrix[l, j] + } + res + } + } + } + + public override infix fun Matrix.dot(vector: Point): Point { + require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" } + val bufferMatrix = toBufferMatrix() + return elementContext { + bufferFactory(rowNum) { i -> + var res = one + for (j in 0 until colNum) { + res += bufferMatrix[i, j] * vector[j] + } + res + } + } + } + + override fun add(a: Matrix, b: Matrix): BufferMatrix { + require(a.rowNum == b.rowNum) { "Row number mismatch in matrix addition. Left side: ${a.rowNum}, right side: ${b.rowNum}" } + require(a.colNum == b.colNum) { "Column number mismatch in matrix addition. Left side: ${a.colNum}, right side: ${b.colNum}" } + val aBufferMatrix = a.toBufferMatrix() + val bBufferMatrix = b.toBufferMatrix() + return elementContext { + produce(a.rowNum, a.colNum) { i, j -> + aBufferMatrix[i, j] + bBufferMatrix[i, j] + } + } + } + + override fun multiply(a: Matrix, k: Number): BufferMatrix { + val aBufferMatrix = a.toBufferMatrix() + return elementContext { + produce(a.rowNum, a.colNum) { i, j -> aBufferMatrix[i, j] * k.toDouble() } + } + } + public companion object } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixWrapper.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixWrapper.kt index 726a91e97..df967e3c1 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixWrapper.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixWrapper.kt @@ -60,12 +60,6 @@ public operator fun Matrix.plus(newFeatures: Collection Double, -): BufferMatrix = MatrixContext.real.produce(rows, columns, initializer) - /** * Build a square matrix from given elements. */ diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/RealMatrixContext.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/RealMatrixContext.kt index 3d544a9af..da795f56b 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/RealMatrixContext.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/RealMatrixContext.kt @@ -2,10 +2,9 @@ package kscience.kmath.linear import kscience.kmath.structures.RealBuffer -@Suppress("OVERRIDE_BY_INLINE") public object RealMatrixContext : MatrixContext> { - public override inline fun produce( + public override fun produce( rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double, @@ -14,7 +13,7 @@ public object RealMatrixContext : MatrixContext> { return BufferMatrix(rows, columns, buffer) } - private fun Matrix.wrap(): BufferMatrix = if (this is BufferMatrix) this else { + public fun Matrix.toBufferMatrix(): BufferMatrix = if (this is BufferMatrix) this else { produce(rowNum, colNum) { i, j -> get(i, j) } } @@ -24,10 +23,12 @@ public object RealMatrixContext : MatrixContext> { public override infix fun Matrix.dot(other: Matrix): BufferMatrix { require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" } + val bufferMatrix = toBufferMatrix() + val otherBufferMatrix = other.toBufferMatrix() return produce(rowNum, other.colNum) { i, j -> var res = 0.0 for (l in 0 until colNum) { - res += get(i, l) * other.get(l, j) + res += bufferMatrix[i, l] * otherBufferMatrix[l, j] } res } @@ -35,10 +36,11 @@ public object RealMatrixContext : MatrixContext> { public override infix fun Matrix.dot(vector: Point): Point { require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" } + val bufferMatrix = toBufferMatrix() return RealBuffer(rowNum) { i -> var res = 0.0 for (j in 0 until colNum) { - res += get(i, j) * vector[j] + res += bufferMatrix[i, j] * vector[j] } res } @@ -47,17 +49,23 @@ public object RealMatrixContext : MatrixContext> { override fun add(a: Matrix, b: Matrix): BufferMatrix { require(a.rowNum == b.rowNum) { "Row number mismatch in matrix addition. Left side: ${a.rowNum}, right side: ${b.rowNum}" } require(a.colNum == b.colNum) { "Column number mismatch in matrix addition. Left side: ${a.colNum}, right side: ${b.colNum}" } + val aBufferMatrix = a.toBufferMatrix() + val bBufferMatrix = b.toBufferMatrix() return produce(a.rowNum, a.colNum) { i, j -> - a[i, j] + b[i, j] + aBufferMatrix[i, j] + bBufferMatrix[i, j] } } - override fun Matrix.times(value: Double): BufferMatrix = - produce(rowNum, colNum) { i, j -> get(i, j) * value } + override fun Matrix.times(value: Double): BufferMatrix { + val bufferMatrix = toBufferMatrix() + return produce(rowNum, colNum) { i, j -> bufferMatrix[i, j] * value } + } - override fun multiply(a: Matrix, k: Number): BufferMatrix = - produce(a.rowNum, a.colNum) { i, j -> a[i, j] * k.toDouble() } + override fun multiply(a: Matrix, k: Number): BufferMatrix { + val aBufferMatrix = a.toBufferMatrix() + return produce(a.rowNum, a.colNum) { i, j -> aBufferMatrix[i, j] * k.toDouble() } + } } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt index 93add36eb..2d72162a6 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/BufferNDAlgebra.kt @@ -28,24 +28,24 @@ public interface BufferNDAlgebra : NDAlgebra { else -> bufferFactory(strides.linearSize) { offset -> get(strides.index(offset)) } } - override fun map(arg: NDStructure, transform: C.(T) -> T): NDBuffer { + override fun NDStructure.map(transform: C.(T) -> T): NDBuffer { val buffer = bufferFactory(strides.linearSize) { offset -> - elementContext.transform(arg.buffer[offset]) + elementContext.transform(buffer[offset]) } return NDBuffer(strides, buffer) } - override fun mapIndexed(arg: NDStructure, transform: C.(index: IntArray, T) -> T): NDStructure { + override fun NDStructure.mapIndexed(transform: C.(index: IntArray, T) -> T): NDBuffer { val buffer = bufferFactory(strides.linearSize) { offset -> elementContext.transform( strides.index(offset), - arg.buffer[offset] + buffer[offset] ) } return NDBuffer(strides, buffer) } - override fun combine(a: NDStructure, b: NDStructure, transform: C.(T, T) -> T): NDStructure { + override fun combine(a: NDStructure, b: NDStructure, transform: C.(T, T) -> T): NDBuffer { val buffer = bufferFactory(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt index 074a1185b..00e79f2e4 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/ComplexNDField.kt @@ -70,25 +70,25 @@ public class ComplexNDField( // return BufferedNDFieldElement(this, buffer) // } - override fun power(arg: NDStructure, pow: Number): NDBuffer = map(arg) { power(it, pow) } + override fun power(arg: NDStructure, pow: Number): NDBuffer = arg.map() { power(it, pow) } - override fun exp(arg: NDStructure): NDBuffer = map(arg) { exp(it) } + override fun exp(arg: NDStructure): NDBuffer = arg.map() { exp(it) } - override fun ln(arg: NDStructure): NDBuffer = map(arg) { ln(it) } + override fun ln(arg: NDStructure): NDBuffer = arg.map() { ln(it) } - override fun sin(arg: NDStructure): NDBuffer = map(arg) { sin(it) } - override fun cos(arg: NDStructure): NDBuffer = map(arg) { cos(it) } - override fun tan(arg: NDStructure): NDBuffer = map(arg) { tan(it) } - override fun asin(arg: NDStructure): NDBuffer = map(arg) { asin(it) } - override fun acos(arg: NDStructure): NDBuffer = map(arg) { acos(it) } - override fun atan(arg: NDStructure): NDBuffer = map(arg) { atan(it) } + override fun sin(arg: NDStructure): NDBuffer = arg.map() { sin(it) } + override fun cos(arg: NDStructure): NDBuffer = arg.map() { cos(it) } + override fun tan(arg: NDStructure): NDBuffer = arg.map() { tan(it) } + override fun asin(arg: NDStructure): NDBuffer = arg.map() { asin(it) } + override fun acos(arg: NDStructure): NDBuffer = arg.map() { acos(it) } + override fun atan(arg: NDStructure): NDBuffer = arg.map() { atan(it) } - override fun sinh(arg: NDStructure): NDBuffer = map(arg) { sinh(it) } - override fun cosh(arg: NDStructure): NDBuffer = map(arg) { cosh(it) } - override fun tanh(arg: NDStructure): NDBuffer = map(arg) { tanh(it) } - override fun asinh(arg: NDStructure): NDBuffer = map(arg) { asinh(it) } - override fun acosh(arg: NDStructure): NDBuffer = map(arg) { acosh(it) } - override fun atanh(arg: NDStructure): NDBuffer = map(arg) { atanh(it) } + override fun sinh(arg: NDStructure): NDBuffer = arg.map() { sinh(it) } + override fun cosh(arg: NDStructure): NDBuffer = arg.map() { cosh(it) } + override fun tanh(arg: NDStructure): NDBuffer = arg.map() { tanh(it) } + override fun asinh(arg: NDStructure): NDBuffer = arg.map() { asinh(it) } + override fun acosh(arg: NDStructure): NDBuffer = arg.map() { acosh(it) } + override fun atanh(arg: NDStructure): NDBuffer = arg.map() { atanh(it) } } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDAlgebra.kt index facc7eb38..749fb1a13 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/NDAlgebra.kt @@ -40,12 +40,12 @@ public interface NDAlgebra { /** * Maps elements from one structure to another one by applying [transform] to them. */ - public fun map(arg: NDStructure, transform: C.(T) -> T): NDStructure + public fun NDStructure.map(transform: C.(T) -> T): NDStructure /** * Maps elements from one structure to another one by applying [transform] to them alongside with their indices. */ - public fun mapIndexed(arg: NDStructure, transform: C.(index: IntArray, T) -> T): NDStructure + public fun NDStructure.mapIndexed(transform: C.(index: IntArray, T) -> T): NDStructure /** * Combines two structures into one. @@ -56,7 +56,7 @@ public interface NDAlgebra { * Element-wise invocation of function working on [T] on a [NDStructure]. */ public operator fun Function1.invoke(structure: NDStructure): NDStructure = - map(structure) { value -> this@invoke(value) } + structure.map() { value -> this@invoke(value) } public companion object } @@ -109,7 +109,7 @@ public interface NDSpace> : Space>, NDAlgebra, k: Number): NDStructure = map(a) { multiply(it, k) } + public override fun multiply(a: NDStructure, k: Number): NDStructure = a.map() { multiply(it, k) } // TODO move to extensions after KEEP-176 @@ -120,7 +120,7 @@ public interface NDSpace> : Space>, NDAlgebra.plus(arg: T): NDStructure = map(this) { value -> add(arg, value) } + public operator fun NDStructure.plus(arg: T): NDStructure = this.map() { value -> add(arg, value) } /** * Subtracts an element from ND structure of it. @@ -129,7 +129,7 @@ public interface NDSpace> : Space>, NDAlgebra.minus(arg: T): NDStructure = map(this) { value -> add(arg, -value) } + public operator fun NDStructure.minus(arg: T): NDStructure = this.map() { value -> add(arg, -value) } /** * Adds an element to ND structure of it. @@ -138,7 +138,7 @@ public interface NDSpace> : Space>, NDAlgebra): NDStructure = map(arg) { value -> add(this@plus, value) } + public operator fun T.plus(arg: NDStructure): NDStructure = arg.map() { value -> add(this@plus, value) } /** * Subtracts an ND structure from an element of it. @@ -147,7 +147,7 @@ public interface NDSpace> : Space>, NDAlgebra): NDStructure = map(arg) { value -> add(-this@minus, value) } + public operator fun T.minus(arg: NDStructure): NDStructure = arg.map() { value -> add(-this@minus, value) } public companion object } @@ -179,7 +179,7 @@ public interface NDRing> : Ring>, NDSpace { * @param arg the multiplier. * @return the product. */ - public operator fun NDStructure.times(arg: T): NDStructure = map(this) { value -> multiply(arg, value) } + public operator fun NDStructure.times(arg: T): NDStructure = this.map() { value -> multiply(arg, value) } /** * Multiplies an element by a ND structure of it. @@ -188,7 +188,7 @@ public interface NDRing> : Ring>, NDSpace { * @param arg the multiplier. * @return the product. */ - public operator fun T.times(arg: NDStructure): NDStructure = map(arg) { value -> multiply(this@times, value) } + public operator fun T.times(arg: NDStructure): NDStructure = arg.map() { value -> multiply(this@times, value) } public companion object } @@ -219,7 +219,7 @@ public interface NDField> : Field>, NDRing * @param arg the divisor. * @return the quotient. */ - public operator fun NDStructure.div(arg: T): NDStructure = map(this) { value -> divide(arg, value) } + public operator fun NDStructure.div(arg: T): NDStructure = this.map() { value -> divide(arg, value) } /** * Divides an element by an ND structure of it. @@ -228,7 +228,7 @@ public interface NDField> : Field>, NDRing * @param arg the divisor. * @return the quotient. */ - public operator fun T.div(arg: NDStructure): NDStructure = map(arg) { divide(it, this@div) } + public operator fun T.div(arg: NDStructure): NDStructure = arg.map() { divide(it, this@div) } // @ThreadLocal // public companion object { diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt index 91b5500cd..8e0f9e171 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/RealNDField.kt @@ -35,16 +35,15 @@ public class RealNDField( } @Suppress("OVERRIDE_BY_INLINE") - override inline fun map( - arg: NDStructure, + override inline fun NDStructure.map( transform: RealField.(Double) -> Double, ): NDBuffer { - val buffer = RealBuffer(strides.linearSize) { offset -> RealField.transform(arg.buffer.array[offset]) } + val buffer = RealBuffer(strides.linearSize) { offset -> RealField.transform(buffer.array[offset]) } return NDBuffer(strides, buffer) } @Suppress("OVERRIDE_BY_INLINE") - override inline fun produce(initializer: RealField.(IntArray) -> Double): NDBuffer { + override inline fun produce(initializer: RealField.(IntArray) -> Double): NDBuffer { val array = DoubleArray(strides.linearSize) { offset -> val index = strides.index(offset) RealField.initializer(index) @@ -53,15 +52,14 @@ public class RealNDField( } @Suppress("OVERRIDE_BY_INLINE") - override inline fun mapIndexed( - arg: NDStructure, + override inline fun NDStructure.mapIndexed( transform: RealField.(index: IntArray, Double) -> Double, ): NDBuffer = NDBuffer( strides, buffer = RealBuffer(strides.linearSize) { offset -> RealField.transform( strides.index(offset), - arg.buffer.array[offset] + buffer.array[offset] ) }) @@ -70,32 +68,32 @@ public class RealNDField( a: NDStructure, b: NDStructure, transform: RealField.(Double, Double) -> Double, - ): NDBuffer { + ): NDBuffer { val buffer = RealBuffer(strides.linearSize) { offset -> RealField.transform(a.buffer.array[offset], b.buffer.array[offset]) } - return NDBuffer(strides, buffer) + return NDBuffer(strides, buffer) } - override fun power(arg: NDStructure, pow: Number): NDBuffer = map(arg) { power(it, pow) } + override fun power(arg: NDStructure, pow: Number): NDBuffer = arg.map { power(it, pow) } - override fun exp(arg: NDStructure): NDBuffer = map(arg) { exp(it) } + override fun exp(arg: NDStructure): NDBuffer = arg.map { exp(it) } - override fun ln(arg: NDStructure): NDBuffer = map(arg) { ln(it) } + override fun ln(arg: NDStructure): NDBuffer = arg.map { ln(it) } - override fun sin(arg: NDStructure): NDBuffer = map(arg) { sin(it) } - override fun cos(arg: NDStructure): NDBuffer = map(arg) { cos(it) } - override fun tan(arg: NDStructure): NDBuffer = map(arg) { tan(it) } - override fun asin(arg: NDStructure): NDBuffer = map(arg) { asin(it) } - override fun acos(arg: NDStructure): NDBuffer = map(arg) { acos(it) } - override fun atan(arg: NDStructure): NDBuffer = map(arg) { atan(it) } + override fun sin(arg: NDStructure): NDBuffer = arg.map { sin(it) } + override fun cos(arg: NDStructure): NDBuffer = arg.map { cos(it) } + override fun tan(arg: NDStructure): NDBuffer = arg.map { tan(it) } + override fun asin(arg: NDStructure): NDBuffer = arg.map { asin(it) } + override fun acos(arg: NDStructure): NDBuffer = arg.map { acos(it) } + override fun atan(arg: NDStructure): NDBuffer = arg.map { atan(it) } - override fun sinh(arg: NDStructure): NDBuffer = map(arg) { sinh(it) } - override fun cosh(arg: NDStructure): NDBuffer = map(arg) { cosh(it) } - override fun tanh(arg: NDStructure): NDBuffer = map(arg) { tanh(it) } - override fun asinh(arg: NDStructure): NDBuffer = map(arg) { asinh(it) } - override fun acosh(arg: NDStructure): NDBuffer = map(arg) { acosh(it) } - override fun atanh(arg: NDStructure): NDBuffer = map(arg) { atanh(it) } + override fun sinh(arg: NDStructure): NDBuffer = arg.map { sinh(it) } + override fun cosh(arg: NDStructure): NDBuffer = arg.map { cosh(it) } + override fun tanh(arg: NDStructure): NDBuffer = arg.map { tanh(it) } + override fun asinh(arg: NDStructure): NDBuffer = arg.map { asinh(it) } + override fun acosh(arg: NDStructure): NDBuffer = arg.map { acosh(it) } + override fun atanh(arg: NDStructure): NDBuffer = arg.map { atanh(it) } } public fun NDAlgebra.Companion.real(vararg shape: Int): RealNDField = RealNDField(shape) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt index 42a643db1..32e0704fc 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/nd/Structure2D.kt @@ -1,6 +1,7 @@ package kscience.kmath.nd -import kscience.kmath.linear.Matrix +import kscience.kmath.linear.BufferMatrix +import kscience.kmath.linear.RealMatrixContext import kscience.kmath.structures.Buffer import kscience.kmath.structures.VirtualBuffer @@ -58,9 +59,9 @@ public interface Structure2D : NDStructure { rows: Int, columns: Int, crossinline init: (i: Int, j: Int) -> Double, - ): Matrix = NDAlgebra.real(rows, columns).produce { (i, j) -> + ): BufferMatrix = RealMatrixContext.produce(rows,columns) { i, j -> init(i, j) - }.as2D() + } } } diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt index e7bde9980..044cafb0b 100644 --- a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt @@ -140,7 +140,7 @@ public fun RealMatrix.min(): Double? = elements().map { (_, value) -> value }.mi public fun RealMatrix.max(): Double? = elements().map { (_, value) -> value }.maxOrNull() public fun RealMatrix.average(): Double = elements().map { (_, value) -> value }.average() -public inline fun RealMatrix.map(transform: (Double) -> Double): RealMatrix = +public inline fun RealMatrix.map(crossinline transform: (Double) -> Double): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { i, j -> transform(get(i, j)) } diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt index 91d45dccd..b9c95034e 100644 --- a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt @@ -44,18 +44,17 @@ public interface Nd4jArrayAlgebra : NDAlgebra { return struct } - public override fun map(arg: NDStructure, transform: C.(T) -> T): Nd4jArrayStructure { - val newStruct = arg.ndArray.dup().wrap() + public override fun NDStructure.map(transform: C.(T) -> T): Nd4jArrayStructure { + val newStruct = ndArray.dup().wrap() newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } return newStruct } - public override fun mapIndexed( - arg: NDStructure, + public override fun NDStructure.mapIndexed( transform: C.(index: IntArray, T) -> T, ): Nd4jArrayStructure { - val new = Nd4j.create(*shape).wrap() - new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(idx, arg[idx]) } + val new = Nd4j.create(*this@Nd4jArrayAlgebra.shape).wrap() + new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(idx, this[idx]) } return new } diff --git a/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt index 650d5670c..04959d290 100644 --- a/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt +++ b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt @@ -1,7 +1,7 @@ package kscience.kmath.nd4j -import org.nd4j.linalg.factory.Nd4j import kscience.kmath.operations.invoke +import org.nd4j.linalg.factory.Nd4j import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.fail @@ -20,7 +20,7 @@ internal class Nd4jArrayAlgebraTest { @Test fun testMap() { - val res = (IntNd4jArrayRing(intArrayOf(2, 2))) { map(one) { it + it * 2 } } + val res = (IntNd4jArrayRing(intArrayOf(2, 2))) { one.map() { it + it * 2 } } val expected = (Nd4j.create(2, 2) ?: fail()).asIntStructure() expected[intArrayOf(0, 0)] = 3 expected[intArrayOf(0, 1)] = 3 diff --git a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt index f91359a0c..d3e4806b0 100644 --- a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt +++ b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt @@ -1,7 +1,10 @@ package kscience.kmath.viktor +import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.nd.* +import kscience.kmath.operations.ExtendedField import kscience.kmath.operations.RealField +import kscience.kmath.operations.RingWithNumbers import org.jetbrains.bio.viktor.F64Array @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @@ -20,8 +23,10 @@ public inline class ViktorNDStructure(public val f64Buffer: F64Array) : MutableN public fun F64Array.asStructure(): ViktorNDStructure = ViktorNDStructure(this) +@OptIn(UnstableKMathAPI::class) @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -public class ViktorNDField(public override val shape: IntArray) : NDField { +public class ViktorNDField(public override val shape: IntArray) : NDField, + RingWithNumbers>, ExtendedField> { public val NDStructure.f64Buffer: F64Array get() = when { @@ -50,26 +55,25 @@ public class ViktorNDField(public override val shape: IntArray) : NDField, transform: RealField.(Double) -> Double): ViktorNDStructure = - F64Array(*shape).apply { + public override fun NDStructure.map(transform: RealField.(Double) -> Double): ViktorNDStructure = + F64Array(*this@ViktorNDField.shape).apply { this@ViktorNDField.strides.indices().forEach { index -> - set(value = RealField.transform(arg[index]), indices = index) + set(value = RealField.transform(this@map[index]), indices = index) } }.asStructure() - public override fun mapIndexed( - arg: NDStructure, - transform: RealField.(index: IntArray, Double) -> Double - ): ViktorNDStructure = F64Array(*shape).apply { + public override fun NDStructure.mapIndexed( + transform: RealField.(index: IntArray, Double) -> Double, + ): ViktorNDStructure = F64Array(*this@ViktorNDField.shape).apply { this@ViktorNDField.strides.indices().forEach { index -> - set(value = RealField.transform(index, arg[index]), indices = index) + set(value = RealField.transform(index, this@mapIndexed[index]), indices = index) } }.asStructure() public override fun combine( a: NDStructure, b: NDStructure, - transform: RealField.(Double, Double) -> Double + transform: RealField.(Double, Double) -> Double, ): ViktorNDStructure = F64Array(*shape).apply { this@ViktorNDField.strides.indices().forEach { index -> set(value = RealField.transform(a[index], b[index]), indices = index) @@ -93,6 +97,25 @@ public class ViktorNDField(public override val shape: IntArray) : NDField.plus(arg: Double): ViktorNDStructure = (f64Buffer.plus(arg)).asStructure() + + override fun number(value: Number): ViktorNDStructure = + F64Array.full(init = value.toDouble(), shape = shape).asStructure() + + override fun sin(arg: NDStructure): ViktorNDStructure = arg.map { sin(it) } + + override fun cos(arg: NDStructure): ViktorNDStructure = arg.map { cos(it) } + + override fun asin(arg: NDStructure): ViktorNDStructure = arg.map { asin(it) } + + override fun acos(arg: NDStructure): ViktorNDStructure = arg.map { acos(it) } + + override fun atan(arg: NDStructure): ViktorNDStructure = arg.map { atan(it) } + + override fun power(arg: NDStructure, pow: Number): ViktorNDStructure = arg.map { it.pow(pow) } + + override fun exp(arg: NDStructure): ViktorNDStructure = arg.f64Buffer.exp().asStructure() + + override fun ln(arg: NDStructure): ViktorNDStructure = arg.f64Buffer.log().asStructure() } public fun ViktorNDField(vararg shape: Int): ViktorNDField = ViktorNDField(shape) \ No newline at end of file From 8a2f1586e20394844b5be88e8ac15072c9601a9c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 24 Jan 2021 17:11:14 +0300 Subject: [PATCH 08/10] suppress unused --- examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt index 7dd725f0e..b6cac4b27 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt @@ -1,3 +1,5 @@ +@file:Suppress("unused") + package kscience.kmath.structures import kscience.kmath.linear.transpose From 228c0b3886862a0b1a03d18ba5e9b366ccd994d4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 28 Jan 2021 20:04:33 +0300 Subject: [PATCH 09/10] misc --- examples/build.gradle.kts | 3 ++- kmath-histograms/build.gradle.kts | 18 ++++++++++++++---- .../histogram/MultivariateHistogramTest.kt | 1 - .../kmath/histogram/UnivariateHistogram.kt | 11 ++++++----- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index c079eaa84..4e780ff36 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -68,11 +68,12 @@ benchmark { targets.register("benchmarks") // This one matches sourceSet name above - configurations.register("fast") { + configurations.register("dot") { warmups = 1 // number of warmup iterations iterations = 3 // number of iterations iterationTime = 500 // time in seconds per iteration iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds + include("DotBenchmark") } } diff --git a/kmath-histograms/build.gradle.kts b/kmath-histograms/build.gradle.kts index 7de21ad89..40196416e 100644 --- a/kmath-histograms/build.gradle.kts +++ b/kmath-histograms/build.gradle.kts @@ -1,8 +1,18 @@ plugins { id("ru.mipt.npm.mpp") } -kotlin.sourceSets.commonMain { - dependencies { - api(project(":kmath-core")) - api(project(":kmath-for-real")) +kotlin.sourceSets { + commonMain { + dependencies { + api(project(":kmath-core")) + } + } + commonTest{ + dependencies{ + implementation(project(":kmath-for-real")) + } } } + +readme { + this.maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE +} diff --git a/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt b/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt index af22afc6b..87a2b3e68 100644 --- a/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt +++ b/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt @@ -5,7 +5,6 @@ import kscience.kmath.histogram.fill import kscience.kmath.histogram.put import kscience.kmath.real.RealVector import kscience.kmath.real.invoke -import kscience.kmath.structures.Buffer import kotlin.random.Random import kotlin.test.* diff --git a/kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/UnivariateHistogram.kt b/kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/UnivariateHistogram.kt index 2f3855892..d07c2ba01 100644 --- a/kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/UnivariateHistogram.kt +++ b/kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/UnivariateHistogram.kt @@ -1,6 +1,6 @@ package kscience.kmath.histogram -import kscience.kmath.real.RealVector +import kscience.kmath.linear.Point import kscience.kmath.structures.Buffer import kscience.kmath.structures.asBuffer import java.util.* @@ -11,12 +11,12 @@ import kotlin.math.floor public class UnivariateBin( public val position: Double, public val size: Double, - public val counter: LongCounter = LongCounter() + public val counter: LongCounter = LongCounter(), ) : Bin { //TODO add weighting public override val value: Number get() = counter.sum() - public override val center: RealVector get() = doubleArrayOf(position).asBuffer() + public override val center: Point get() = doubleArrayOf(position).asBuffer() public override val dimension: Int get() = 1 public operator fun contains(value: Double): Boolean = value in (position - size / 2)..(position + size / 2) @@ -27,8 +27,9 @@ public class UnivariateBin( /** * Univariate histogram with log(n) bin search speed */ -public class UnivariateHistogram private constructor(private val factory: (Double) -> UnivariateBin) : - MutableHistogram { +public class UnivariateHistogram private constructor( + private val factory: (Double) -> UnivariateBin, +) : MutableHistogram { private val bins: TreeMap = TreeMap() From 2e52457b96836c54590dd1fb2674adad86cadd90 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 28 Jan 2021 20:10:42 +0300 Subject: [PATCH 10/10] fix merge --- .../main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt | 1 - .../src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt index 16034e5f9..811c0c2d5 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt @@ -2,7 +2,6 @@ package kscience.kmath.commons.linear import kscience.kmath.linear.* import kscience.kmath.misc.UnstableKMathAPI -import kscience.kmath.structures.Matrix import kscience.kmath.structures.RealBuffer import org.apache.commons.math3.linear.* import kotlin.reflect.KClass diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt index 98abf9a73..d0e4a7325 100644 --- a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt @@ -1,12 +1,8 @@ package kscience.kmath.real -import kscience.kmath.linear.MatrixContext -import kscience.kmath.linear.VirtualMatrix -import kscience.kmath.linear.inverseWithLup -import kscience.kmath.linear.real +import kscience.kmath.linear.* import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.structures.Buffer -import kscience.kmath.structures.Matrix import kscience.kmath.structures.RealBuffer import kscience.kmath.structures.asIterable import kotlin.math.pow