From 0ac5363acf7c6717275989ec6c3376a05002b953 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 16 Oct 2021 11:10:34 +0300 Subject: [PATCH 01/10] Multik wrapper prototype --- examples/build.gradle.kts | 14 +- .../space/kscience/kmath/tensors/multik.kt | 21 ++ kmath-multik/build.gradle.kts | 14 ++ .../kmath/multik/MultikTensorAlgebra.kt | 199 ++++++++++++++++++ 4 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt create mode 100644 kmath-multik/build.gradle.kts create mode 100644 kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index d06005321..7b1bce26a 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -29,6 +29,11 @@ dependencies { implementation(project(":kmath-tensors")) implementation(project(":kmath-symja")) implementation(project(":kmath-for-real")) + //jafama + implementation(project(":kmath-jafama")) + //multik + implementation(projects.kmathMultik) + implementation("org.nd4j:nd4j-native:1.0.0-beta7") @@ -42,11 +47,12 @@ dependencies { // } else implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") - implementation("org.slf4j:slf4j-simple:1.7.31") + // multik implementation + implementation("org.jetbrains.kotlinx:multik-default:0.1.0") + + implementation("org.slf4j:slf4j-simple:1.7.32") // plotting - implementation("space.kscience:plotlykt-server:0.4.2") - //jafama - implementation(project(":kmath-jafama")) + implementation("space.kscience:plotlykt-server:0.5.0") } kotlin.sourceSets.all { diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt new file mode 100644 index 000000000..820a793ac --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package space.kscience.kmath.tensors + +import org.jetbrains.kotlinx.multik.api.Multik +import org.jetbrains.kotlinx.multik.api.linalg.dot +import org.jetbrains.kotlinx.multik.api.ndarray +import org.jetbrains.kotlinx.multik.ndarray.operations.minus +import org.jetbrains.kotlinx.multik.ndarray.operations.plus +import org.jetbrains.kotlinx.multik.ndarray.operations.unaryMinus + +fun main() { + val a = Multik.ndarray(intArrayOf(1, 2, 3)) + val b = Multik.ndarray(doubleArrayOf(1.0, 2.0, 3.0)) + 2 + (-a) - 2 + + a dot a +} \ No newline at end of file diff --git a/kmath-multik/build.gradle.kts b/kmath-multik/build.gradle.kts new file mode 100644 index 000000000..16a7ab652 --- /dev/null +++ b/kmath-multik/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("ru.mipt.npm.gradle.jvm") +} + +description = "JetBrains Multik connector" + +dependencies { + api(project(":kmath-tensors")) + api("org.jetbrains.kotlinx:multik-api:0.1.0") +} + +readme { + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE +} \ No newline at end of file diff --git a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt new file mode 100644 index 000000000..b25d3ef56 --- /dev/null +++ b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt @@ -0,0 +1,199 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package space.kscience.kmath.multik + +import org.jetbrains.kotlinx.multik.ndarray.data.* +import org.jetbrains.kotlinx.multik.ndarray.operations.* +import space.kscience.kmath.misc.PerformancePitfall +import space.kscience.kmath.nd.mapInPlace +import space.kscience.kmath.operations.Ring +import space.kscience.kmath.tensors.api.Tensor +import space.kscience.kmath.tensors.api.TensorAlgebra + +@JvmInline +public value class MultikTensor(public val array: MutableMultiArray) : Tensor { + override val shape: IntArray get() = array.shape + + override fun get(index: IntArray): T = array[index] + + @PerformancePitfall + override fun elements(): Sequence> = + array.multiIndices.iterator().asSequence().map { it to get(it) } + + override fun set(index: IntArray, value: T) { + array[index] = value + } +} + + +public abstract class MultikTensorAlgebra( + public val elementAlgebra: Ring, + public val comparator: Comparator +) : TensorAlgebra { + + /** + * Convert a tensor to [MultikTensor] if necessary. If tensor is converted, changes on the resulting tensor + * are not reflected back onto the source + */ + public fun Tensor.asMultik(): MultikTensor { + return if (this is MultikTensor) { + this + } else { + TODO() + } + } + + public fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) + + override fun Tensor.valueOrNull(): T? = if (shape contentEquals intArrayOf(1)) { + get(intArrayOf(0)) + } else null + + override fun T.plus(other: Tensor): MultikTensor = + other.plus(this) + + override fun Tensor.plus(value: T): MultikTensor = + asMultik().array.deepCopy().apply { plusAssign(value) }.wrap() + + override fun Tensor.plus(other: Tensor): MultikTensor = + asMultik().array.plus(other.asMultik().array).wrap() + + override fun Tensor.plusAssign(value: T) { + if (this is MultikTensor) { + array.plusAssign(value) + } else { + mapInPlace { _, t -> elementAlgebra.add(t, value) } + } + } + + override fun Tensor.plusAssign(other: Tensor) { + if (this is MultikTensor) { + array.plusAssign(other.asMultik().array) + } else { + mapInPlace { index, t -> elementAlgebra.add(t, other[index]) } + } + } + + //TODO avoid additional copy + override fun T.minus(other: Tensor): MultikTensor = -(other - this) + + override fun Tensor.minus(value: T): MultikTensor = + asMultik().array.deepCopy().apply { minusAssign(value) }.wrap() + + override fun Tensor.minus(other: Tensor): MultikTensor = + asMultik().array.minus(other.asMultik().array).wrap() + + override fun Tensor.minusAssign(value: T) { + if (this is MultikTensor) { + array.minusAssign(value) + } else { + mapInPlace { _, t -> elementAlgebra.run { t - value } } + } + } + + override fun Tensor.minusAssign(other: Tensor) { + if (this is MultikTensor) { + array.minusAssign(other.asMultik().array) + } else { + mapInPlace { index, t -> elementAlgebra.run { t - other[index] } } + } + } + + override fun T.times(other: Tensor): MultikTensor = + other.asMultik().array.deepCopy().apply { timesAssign(this@times) }.wrap() + + override fun Tensor.times(value: T): Tensor = + asMultik().array.deepCopy().apply { timesAssign(value) }.wrap() + + override fun Tensor.times(other: Tensor): MultikTensor = + asMultik().array.times(other.asMultik().array).wrap() + + override fun Tensor.timesAssign(value: T) { + if (this is MultikTensor) { + array.timesAssign(value) + } else { + mapInPlace { _, t -> elementAlgebra.multiply(t, value) } + } + } + + override fun Tensor.timesAssign(other: Tensor) { + if (this is MultikTensor) { + array.timesAssign(other.asMultik().array) + } else { + mapInPlace { index, t -> elementAlgebra.multiply(t, other[index]) } + } + } + + override fun Tensor.unaryMinus(): MultikTensor = + asMultik().array.unaryMinus().wrap() + + override fun Tensor.get(i: Int): MultikTensor { + TODO("Not yet implemented") + } + + override fun Tensor.transpose(i: Int, j: Int): MultikTensor { + TODO("Not yet implemented") + } + + override fun Tensor.view(shape: IntArray): MultikTensor { + require(shape.all { it > 0 }) + require(shape.fold(1, Int::times) == this.shape.size) { + "Cannot reshape array of size ${this.shape.size} into a new shape ${ + shape.joinToString( + prefix = "(", + postfix = ")" + ) + }" + } + + val mt = asMultik().array + return if (mt.shape.contentEquals(shape)) { + @Suppress("UNCHECKED_CAST") + this as NDArray + } else { + NDArray(mt.data, mt.offset, shape, dim = DN(shape.size), base = mt.base ?: mt) + }.wrap() + } + + override fun Tensor.viewAs(other: Tensor): MultikTensor { + TODO("Not yet implemented") + } + + override fun Tensor.dot(other: Tensor): MultikTensor { + TODO("Not yet implemented") + } + + override fun diagonalEmbedding(diagonalEntries: Tensor, offset: Int, dim1: Int, dim2: Int): MultikTensor { + TODO("Not yet implemented") + } + + override fun Tensor.sum(): T = asMultik().array.reduceMultiIndexed { _: IntArray, acc: T, t: T -> + elementAlgebra.add(acc, t) + } + + override fun Tensor.sum(dim: Int, keepDim: Boolean): MultikTensor { + TODO("Not yet implemented") + } + + override fun Tensor.min(): T = + asMultik().array.minWith(comparator) ?: error("No elements in tensor") + + override fun Tensor.min(dim: Int, keepDim: Boolean): MultikTensor { + TODO("Not yet implemented") + } + + override fun Tensor.max(): T = + asMultik().array.maxWith(comparator) ?: error("No elements in tensor") + + + override fun Tensor.max(dim: Int, keepDim: Boolean): MultikTensor { + TODO("Not yet implemented") + } + + override fun Tensor.argMax(dim: Int, keepDim: Boolean): MultikTensor { + TODO("Not yet implemented") + } +} \ No newline at end of file From d0354da80a52e56fdb81dadd881a0ab4fa0a0d24 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 17 Oct 2021 11:12:35 +0300 Subject: [PATCH 02/10] Shapeless ND and Buffer algebras --- CHANGELOG.md | 3 + .../kmath/benchmarks/NDFieldBenchmark.kt | 32 ++- .../kmath/benchmarks/ViktorBenchmark.kt | 20 +- .../kmath/benchmarks/ViktorLogBenchmark.kt | 20 +- .../kscience/kmath/operations/complexDemo.kt | 12 +- .../kscience/kmath/structures/NDField.kt | 4 +- .../kmath/structures/StreamDoubleFieldND.kt | 10 +- .../kscience/kmath/structures/buffers.kt | 3 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../kotlin/space/kscience/kmath/ast/parser.kt | 16 +- .../kscience/kmath/ast/rendering/features.kt | 30 +- .../kscience/kmath/ast/rendering/phases.kt | 14 +- .../kscience/kmath/ast/rendering/TestLatex.kt | 4 +- .../kmath/ast/rendering/TestMathML.kt | 4 +- .../kmath/wasm/internal/WasmBuilder.kt | 22 +- .../DerivativeStructureExpression.kt | 4 +- .../space/kscience/kmath/complex/Complex.kt | 3 +- .../kscience/kmath/complex/ComplexFieldND.kt | 134 +++------ .../kscience/kmath/complex/Quaternion.kt | 2 +- .../FunctionalExpressionAlgebra.kt | 8 +- .../kscience/kmath/expressions/MstAlgebra.kt | 24 +- .../kmath/expressions/SimpleAutoDiff.kt | 2 +- .../kmath/linear/BufferedLinearSpace.kt | 29 +- .../kmath/linear/DoubleLinearSpace.kt | 30 +- .../kscience/kmath/linear/LinearSpace.kt | 3 +- .../space/kscience/kmath/nd/AlgebraND.kt | 116 +++----- .../kscience/kmath/nd/BufferAlgebraND.kt | 262 ++++++++++-------- .../space/kscience/kmath/nd/BufferND.kt | 24 +- .../space/kscience/kmath/nd/DoubleFieldND.kt | 132 +++------ .../space/kscience/kmath/nd/ShapeIndex.kt | 120 ++++++++ .../space/kscience/kmath/nd/ShortRingND.kt | 25 +- .../space/kscience/kmath/nd/StructureND.kt | 110 +------- .../kscience/kmath/nd/algebraNDExtentions.kt | 34 +++ .../kscience/kmath/operations/Algebra.kt | 14 +- .../space/kscience/kmath/operations/BigInt.kt | 8 +- .../kmath/operations/BufferAlgebra.kt | 179 +++++++----- .../kmath/operations/DoubleBufferField.kt | 14 +- ...BufferOperations.kt => DoubleBufferOps.kt} | 6 +- .../kmath/operations/NumericAlgebra.kt | 2 +- .../kscience/kmath/operations/numbers.kt | 8 +- .../kscience/kmath/structures/NDFieldTest.kt | 3 +- .../kmath/structures/NumberNDFieldTest.kt | 13 +- .../space/kscience/kmath/chains/flowExtra.kt | 4 +- .../space/kscience/kmath/real/realND.kt | 4 +- .../kmath/histogram/DoubleHistogramSpace.kt | 9 +- .../kmath/histogram/IndexedHistogramSpace.kt | 9 +- .../histogram/MultivariateHistogramTest.kt | 3 +- .../kmath/kotlingrad/scalarsAdapters.kt | 12 +- .../kscience/kmath/nd4j/Nd4jArrayAlgebra.kt | 164 +++++------ .../kmath/nd4j/Nd4jArrayAlgebraTest.kt | 18 +- .../space/kscience/kmath/symja/adapters.kt | 12 +- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 8 +- .../core/internal/TensorLinearStructure.kt | 16 +- .../tensors/core/internal/tensorCastsUtils.kt | 7 +- .../kmath/viktor/ViktorStructureND.kt | 20 +- settings.gradle.kts | 8 +- 56 files changed, 893 insertions(+), 906 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndex.kt create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/algebraNDExtentions.kt rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/{DoubleBufferOperations.kt => DoubleBufferOps.kt} (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05376d425..bb267744e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,9 @@ - Use `Symbol` factory function instead of `StringSymbol` - New discoverability pattern: `.algebra.` - Adjusted commons-math API for linear solvers to match conventions. +- Buffer algebra does not require size anymore +- Operations -> Ops +- Default Buffer and ND algebras are now Ops and lack neutral elements (0, 1) as well as algebra-level shapes. ### Deprecated - Specialized `DoubleBufferAlgebra` diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt index 76fec05d3..950a10b33 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt @@ -9,9 +9,10 @@ import kotlinx.benchmark.Benchmark import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Scope import kotlinx.benchmark.State +import space.kscience.kmath.nd.BufferedFieldOpsND import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.autoNdAlgebra import space.kscience.kmath.nd.ndAlgebra +import space.kscience.kmath.nd.one import space.kscience.kmath.nd4j.nd4j import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.structures.Buffer @@ -23,21 +24,21 @@ import space.kscience.kmath.tensors.core.tensorAlgebra internal class NDFieldBenchmark { @Benchmark fun autoFieldAdd(blackhole: Blackhole) = with(autoField) { - var res: StructureND = one - repeat(n) { res += one } + var res: StructureND = one(shape) + repeat(n) { res += 1.0 } blackhole.consume(res) } @Benchmark fun specializedFieldAdd(blackhole: Blackhole) = with(specializedField) { - var res: StructureND = one + var res: StructureND = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @Benchmark fun boxingFieldAdd(blackhole: Blackhole) = with(genericField) { - var res: StructureND = one + var res: StructureND = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @@ -56,19 +57,20 @@ internal class NDFieldBenchmark { blackhole.consume(res) } -// @Benchmark -// fun nd4jAdd(blackhole: Blackhole) = with(nd4jField) { -// var res: StructureND = one -// repeat(n) { res += 1.0 } -// blackhole.consume(res) -// } + @Benchmark + fun nd4jAdd(blackhole: Blackhole) = with(nd4jField) { + var res: StructureND = one(dim, dim) + repeat(n) { res += 1.0 } + blackhole.consume(res) + } private companion object { private const val dim = 1000 private const val n = 100 - private val autoField = DoubleField.autoNdAlgebra(dim, dim) - private val specializedField = DoubleField.ndAlgebra(dim, dim) - private val genericField = DoubleField.ndAlgebra(Buffer.Companion::boxing, dim, dim) - private val nd4jField = DoubleField.nd4j(dim, dim) + private val shape = intArrayOf(dim, dim) + private val autoField = BufferedFieldOpsND(DoubleField, Buffer.Companion::auto) + private val specializedField = DoubleField.ndAlgebra + private val genericField = BufferedFieldOpsND(DoubleField, Buffer.Companion::boxing) + private val nd4jField = DoubleField.nd4j } } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt index b97a05a52..6b4d5759b 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt @@ -10,18 +10,17 @@ import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Scope import kotlinx.benchmark.State import org.jetbrains.bio.viktor.F64Array -import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.autoNdAlgebra -import space.kscience.kmath.nd.ndAlgebra +import space.kscience.kmath.nd.* import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.viktor.ViktorNDField +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.viktor.ViktorFieldND @State(Scope.Benchmark) internal class ViktorBenchmark { @Benchmark fun automaticFieldAddition(blackhole: Blackhole) { with(autoField) { - var res: StructureND = one + var res: StructureND = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @@ -30,7 +29,7 @@ internal class ViktorBenchmark { @Benchmark fun realFieldAddition(blackhole: Blackhole) { with(realField) { - var res: StructureND = one + var res: StructureND = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @@ -39,7 +38,7 @@ internal class ViktorBenchmark { @Benchmark fun viktorFieldAddition(blackhole: Blackhole) { with(viktorField) { - var res = one + var res = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @@ -56,10 +55,11 @@ internal class ViktorBenchmark { private companion object { private const val dim = 1000 private const val n = 100 + private val shape = Shape(dim, dim) // automatically build context most suited for given type. - private val autoField = DoubleField.autoNdAlgebra(dim, dim) - private val realField = DoubleField.ndAlgebra(dim, dim) - private val viktorField = ViktorNDField(dim, dim) + private val autoField = BufferedFieldOpsND(DoubleField, Buffer.Companion::auto) + private val realField = DoubleField.ndAlgebra + private val viktorField = ViktorFieldND(dim, dim) } } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt index 91e9dcd76..ef2adaad8 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt @@ -10,18 +10,21 @@ import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Scope import kotlinx.benchmark.State import org.jetbrains.bio.viktor.F64Array -import space.kscience.kmath.nd.autoNdAlgebra +import space.kscience.kmath.nd.BufferedFieldOpsND +import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ndAlgebra +import space.kscience.kmath.nd.one import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.structures.Buffer import space.kscience.kmath.viktor.ViktorFieldND @State(Scope.Benchmark) internal class ViktorLogBenchmark { @Benchmark fun realFieldLog(blackhole: Blackhole) { - with(realNdField) { - val fortyTwo = produce { 42.0 } - var res = one + with(realField) { + val fortyTwo = produce(shape) { 42.0 } + var res = one(shape) repeat(n) { res = ln(fortyTwo) } blackhole.consume(res) } @@ -30,7 +33,7 @@ internal class ViktorLogBenchmark { @Benchmark fun viktorFieldLog(blackhole: Blackhole) { with(viktorField) { - val fortyTwo = produce { 42.0 } + val fortyTwo = produce(shape) { 42.0 } var res = one repeat(n) { res = ln(fortyTwo) } blackhole.consume(res) @@ -48,10 +51,11 @@ internal class ViktorLogBenchmark { private companion object { private const val dim = 1000 private const val n = 100 + private val shape = Shape(dim, dim) // automatically build context most suited for given type. - private val autoField = DoubleField.autoNdAlgebra(dim, dim) - private val realNdField = DoubleField.ndAlgebra(dim, dim) - private val viktorField = ViktorFieldND(intArrayOf(dim, dim)) + private val autoField = BufferedFieldOpsND(DoubleField, Buffer.Companion::auto) + private val realField = DoubleField.ndAlgebra + private val viktorField = ViktorFieldND(dim, dim) } } diff --git a/examples/src/main/kotlin/space/kscience/kmath/operations/complexDemo.kt b/examples/src/main/kotlin/space/kscience/kmath/operations/complexDemo.kt index 319221bcc..67d83d77c 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/operations/complexDemo.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/operations/complexDemo.kt @@ -11,27 +11,27 @@ import space.kscience.kmath.complex.bufferAlgebra import space.kscience.kmath.complex.ndAlgebra import space.kscience.kmath.nd.BufferND import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.nd.produce fun main() = Complex.algebra { val complex = 2 + 2 * i println(complex * 8 - 5 * i) //flat buffer - val buffer = bufferAlgebra(8).run { - buffer { Complex(it, -it) }.map { Complex(it.im, it.re) } + val buffer = with(bufferAlgebra){ + buffer(8) { Complex(it, -it) }.map { Complex(it.im, it.re) } } println(buffer) - // 2d element - val element: BufferND = ndAlgebra(2, 2).produce { (i, j) -> + val element: BufferND = ndAlgebra.produce(2, 2) { (i, j) -> Complex(i - j, i + j) } println(element) // 1d element operation - val result: StructureND = ndAlgebra(8).run { - val a = produce { (it) -> i * it - it.toDouble() } + val result: StructureND = ndAlgebra{ + val a = produce(8) { (it) -> i * it - it.toDouble() } val b = 3 val c = Complex(1.0, 1.0) diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt index 5b0e2eb30..7359f3bf6 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt @@ -14,7 +14,7 @@ import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd4j.Nd4jArrayField import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.invoke -import space.kscience.kmath.viktor.ViktorNDField +import space.kscience.kmath.viktor.ViktorFieldND import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.system.measureTimeMillis @@ -41,7 +41,7 @@ fun main() { // Nd4j specialized field. val nd4jField = Nd4jArrayField.real(dim, dim) //viktor field - val viktorField = ViktorNDField(dim, dim) + val viktorField = ViktorFieldND(dim, dim) //parallel processing based on Java Streams val parallelField = DoubleField.ndStreaming(dim, dim) diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt index b1248bd0f..f1f5f2e84 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.structures import space.kscience.kmath.nd.* import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.ExtendedField -import space.kscience.kmath.operations.NumbersAddOperations +import space.kscience.kmath.operations.NumbersAddOps import java.util.* import java.util.stream.IntStream @@ -17,11 +17,11 @@ import java.util.stream.IntStream * execution. */ class StreamDoubleFieldND(override val shape: IntArray) : FieldND, - NumbersAddOperations>, + NumbersAddOps>, ExtendedField> { private val strides = DefaultStrides(shape) - override val elementContext: DoubleField get() = DoubleField + override val elementAlgebra: DoubleField get() = DoubleField override val zero: BufferND by lazy { produce { zero } } override val one: BufferND by lazy { produce { one } } @@ -36,7 +36,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND this.buffer as DoubleBuffer + this is BufferND && this.indexes == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } } @@ -69,7 +69,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND, b: StructureND, transform: DoubleField.(Double, Double) -> Double, diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt index d78141507..889ea99bd 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.structures import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.buffer import space.kscience.kmath.operations.bufferAlgebra +import space.kscience.kmath.operations.withSize inline fun MutableBuffer.Companion.same( n: Int, @@ -16,7 +17,7 @@ inline fun MutableBuffer.Companion.same( fun main() { - with(DoubleField.bufferAlgebra(5)) { + with(DoubleField.bufferAlgebra.withSize(5)) { println(number(2.0) + buffer(1, 2, 3, 4, 5)) } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 05679dc3c..ffed3a254 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt index ef6d51c7b..7f2780548 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt @@ -18,10 +18,10 @@ import com.github.h0tk3y.betterParse.parser.ParseResult import com.github.h0tk3y.betterParse.parser.Parser import space.kscience.kmath.expressions.MST import space.kscience.kmath.expressions.Symbol -import space.kscience.kmath.operations.FieldOperations -import space.kscience.kmath.operations.GroupOperations +import space.kscience.kmath.operations.FieldOps +import space.kscience.kmath.operations.GroupOps import space.kscience.kmath.operations.PowerOperations -import space.kscience.kmath.operations.RingOperations +import space.kscience.kmath.operations.RingOps /** * better-parse implementation of grammar defined in the ArithmeticsEvaluator.g4. @@ -60,7 +60,7 @@ public object ArithmeticsEvaluator : Grammar() { .or(binaryFunction) .or(unaryFunction) .or(singular) - .or(-minus and parser(ArithmeticsEvaluator::term) map { MST.Unary(GroupOperations.MINUS_OPERATION, it) }) + .or(-minus and parser(ArithmeticsEvaluator::term) map { MST.Unary(GroupOps.MINUS_OPERATION, it) }) .or(-lpar and parser(ArithmeticsEvaluator::subSumChain) and -rpar) private val powChain: Parser by leftAssociative(term = term, operator = pow) { a, _, b -> @@ -72,9 +72,9 @@ public object ArithmeticsEvaluator : Grammar() { operator = div or mul use TokenMatch::type ) { a, op, b -> if (op == div) - MST.Binary(FieldOperations.DIV_OPERATION, a, b) + MST.Binary(FieldOps.DIV_OPERATION, a, b) else - MST.Binary(RingOperations.TIMES_OPERATION, a, b) + MST.Binary(RingOps.TIMES_OPERATION, a, b) } private val subSumChain: Parser by leftAssociative( @@ -82,9 +82,9 @@ public object ArithmeticsEvaluator : Grammar() { operator = plus or minus use TokenMatch::type ) { a, op, b -> if (op == plus) - MST.Binary(GroupOperations.PLUS_OPERATION, a, b) + MST.Binary(GroupOps.PLUS_OPERATION, a, b) else - MST.Binary(GroupOperations.MINUS_OPERATION, a, b) + MST.Binary(GroupOps.MINUS_OPERATION, a, b) } override val rootParser: Parser by subSumChain diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/features.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/features.kt index a7a28d87f..8b76b6f19 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/features.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/features.kt @@ -39,7 +39,7 @@ public val PrintNumeric: RenderFeature = RenderFeature { _, node -> @UnstableKMathAPI private fun printSignedNumberString(s: String): MathSyntax = if (s.startsWith('-')) UnaryMinusSyntax( - operation = GroupOperations.MINUS_OPERATION, + operation = GroupOps.MINUS_OPERATION, operand = OperandSyntax( operand = NumberSyntax(string = s.removePrefix("-")), parentheses = true, @@ -72,7 +72,7 @@ public class PrettyPrintFloats(public val types: Set>) : Rend val exponent = afterE.toDouble().toString().removeSuffix(".0") return MultiplicationSyntax( - operation = RingOperations.TIMES_OPERATION, + operation = RingOps.TIMES_OPERATION, left = OperandSyntax(operand = NumberSyntax(significand), parentheses = true), right = OperandSyntax( operand = SuperscriptSyntax( @@ -91,7 +91,7 @@ public class PrettyPrintFloats(public val types: Set>) : Rend if (toString.startsWith('-')) return UnaryMinusSyntax( - operation = GroupOperations.MINUS_OPERATION, + operation = GroupOps.MINUS_OPERATION, operand = OperandSyntax(operand = infty, parentheses = true), ) @@ -211,9 +211,9 @@ public class BinaryPlus(operations: Collection?) : Binary(operations) { public companion object { /** - * The default instance configured with [GroupOperations.PLUS_OPERATION]. + * The default instance configured with [GroupOps.PLUS_OPERATION]. */ - public val Default: BinaryPlus = BinaryPlus(setOf(GroupOperations.PLUS_OPERATION)) + public val Default: BinaryPlus = BinaryPlus(setOf(GroupOps.PLUS_OPERATION)) } } @@ -233,9 +233,9 @@ public class BinaryMinus(operations: Collection?) : Binary(operations) { public companion object { /** - * The default instance configured with [GroupOperations.MINUS_OPERATION]. + * The default instance configured with [GroupOps.MINUS_OPERATION]. */ - public val Default: BinaryMinus = BinaryMinus(setOf(GroupOperations.MINUS_OPERATION)) + public val Default: BinaryMinus = BinaryMinus(setOf(GroupOps.MINUS_OPERATION)) } } @@ -253,9 +253,9 @@ public class UnaryPlus(operations: Collection?) : Unary(operations) { public companion object { /** - * The default instance configured with [GroupOperations.PLUS_OPERATION]. + * The default instance configured with [GroupOps.PLUS_OPERATION]. */ - public val Default: UnaryPlus = UnaryPlus(setOf(GroupOperations.PLUS_OPERATION)) + public val Default: UnaryPlus = UnaryPlus(setOf(GroupOps.PLUS_OPERATION)) } } @@ -273,9 +273,9 @@ public class UnaryMinus(operations: Collection?) : Unary(operations) { public companion object { /** - * The default instance configured with [GroupOperations.MINUS_OPERATION]. + * The default instance configured with [GroupOps.MINUS_OPERATION]. */ - public val Default: UnaryMinus = UnaryMinus(setOf(GroupOperations.MINUS_OPERATION)) + public val Default: UnaryMinus = UnaryMinus(setOf(GroupOps.MINUS_OPERATION)) } } @@ -295,9 +295,9 @@ public class Fraction(operations: Collection?) : Binary(operations) { public companion object { /** - * The default instance configured with [FieldOperations.DIV_OPERATION]. + * The default instance configured with [FieldOps.DIV_OPERATION]. */ - public val Default: Fraction = Fraction(setOf(FieldOperations.DIV_OPERATION)) + public val Default: Fraction = Fraction(setOf(FieldOps.DIV_OPERATION)) } } @@ -422,9 +422,9 @@ public class Multiplication(operations: Collection?) : Binary(operations public companion object { /** - * The default instance configured with [RingOperations.TIMES_OPERATION]. + * The default instance configured with [RingOps.TIMES_OPERATION]. */ - public val Default: Multiplication = Multiplication(setOf(RingOperations.TIMES_OPERATION)) + public val Default: Multiplication = Multiplication(setOf(RingOps.TIMES_OPERATION)) } } diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/phases.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/phases.kt index 3d05e03d6..ecea2d104 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/phases.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/phases.kt @@ -7,10 +7,10 @@ package space.kscience.kmath.ast.rendering import space.kscience.kmath.ast.rendering.FeaturedMathRendererWithPostProcess.PostProcessPhase import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.operations.FieldOperations -import space.kscience.kmath.operations.GroupOperations +import space.kscience.kmath.operations.FieldOps +import space.kscience.kmath.operations.GroupOps import space.kscience.kmath.operations.PowerOperations -import space.kscience.kmath.operations.RingOperations +import space.kscience.kmath.operations.RingOps /** * Removes unnecessary times (×) symbols from [MultiplicationSyntax]. @@ -306,10 +306,10 @@ public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) -> is BinarySyntax -> when (it.operation) { PowerOperations.POW_OPERATION -> 1 - RingOperations.TIMES_OPERATION -> 3 - FieldOperations.DIV_OPERATION -> 3 - GroupOperations.MINUS_OPERATION -> 4 - GroupOperations.PLUS_OPERATION -> 4 + RingOps.TIMES_OPERATION -> 3 + FieldOps.DIV_OPERATION -> 3 + GroupOps.MINUS_OPERATION -> 4 + GroupOps.PLUS_OPERATION -> 4 else -> 0 } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestLatex.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestLatex.kt index d8e432230..aba713c43 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestLatex.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestLatex.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.ast.rendering import space.kscience.kmath.ast.rendering.TestUtils.testLatex import space.kscience.kmath.expressions.MST -import space.kscience.kmath.operations.GroupOperations +import space.kscience.kmath.operations.GroupOps import kotlin.test.Test internal class TestLatex { @@ -36,7 +36,7 @@ internal class TestLatex { fun unaryOperator() = testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)") @Test - fun unaryPlus() = testLatex(MST.Unary(GroupOperations.PLUS_OPERATION, MST.Numeric(1)), "+1") + fun unaryPlus() = testLatex(MST.Unary(GroupOps.PLUS_OPERATION, MST.Numeric(1)), "+1") @Test fun unaryMinus() = testLatex("-x", "-x") diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestMathML.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestMathML.kt index a7fcbc75b..658ecd47a 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestMathML.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestMathML.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.ast.rendering import space.kscience.kmath.ast.rendering.TestUtils.testMathML import space.kscience.kmath.expressions.MST -import space.kscience.kmath.operations.GroupOperations +import space.kscience.kmath.operations.GroupOps import kotlin.test.Test internal class TestMathML { @@ -47,7 +47,7 @@ internal class TestMathML { @Test fun unaryPlus() = - testMathML(MST.Unary(GroupOperations.PLUS_OPERATION, MST.Numeric(1)), "+1") + testMathML(MST.Unary(GroupOps.PLUS_OPERATION, MST.Numeric(1)), "+1") @Test fun unaryMinus() = testMathML("-x", "-x") diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt index 5b6cf65db..b04c4d48f 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt @@ -108,8 +108,8 @@ internal class DoubleWasmBuilder(target: MST) : WasmBuilder(f64, DoubleF override fun visitNumeric(mst: Numeric): ExpressionRef = ctx.f64.const(mst.value) override fun visitUnary(mst: Unary): ExpressionRef = when (mst.operation) { - GroupOperations.MINUS_OPERATION -> ctx.f64.neg(visit(mst.value)) - GroupOperations.PLUS_OPERATION -> visit(mst.value) + GroupOps.MINUS_OPERATION -> ctx.f64.neg(visit(mst.value)) + GroupOps.PLUS_OPERATION -> visit(mst.value) PowerOperations.SQRT_OPERATION -> ctx.f64.sqrt(visit(mst.value)) TrigonometricOperations.SIN_OPERATION -> ctx.call("sin", arrayOf(visit(mst.value)), f64) TrigonometricOperations.COS_OPERATION -> ctx.call("cos", arrayOf(visit(mst.value)), f64) @@ -129,10 +129,10 @@ internal class DoubleWasmBuilder(target: MST) : WasmBuilder(f64, DoubleF } override fun visitBinary(mst: Binary): ExpressionRef = when (mst.operation) { - GroupOperations.PLUS_OPERATION -> ctx.f64.add(visit(mst.left), visit(mst.right)) - GroupOperations.MINUS_OPERATION -> ctx.f64.sub(visit(mst.left), visit(mst.right)) - RingOperations.TIMES_OPERATION -> ctx.f64.mul(visit(mst.left), visit(mst.right)) - FieldOperations.DIV_OPERATION -> ctx.f64.div(visit(mst.left), visit(mst.right)) + GroupOps.PLUS_OPERATION -> ctx.f64.add(visit(mst.left), visit(mst.right)) + GroupOps.MINUS_OPERATION -> ctx.f64.sub(visit(mst.left), visit(mst.right)) + RingOps.TIMES_OPERATION -> ctx.f64.mul(visit(mst.left), visit(mst.right)) + FieldOps.DIV_OPERATION -> ctx.f64.div(visit(mst.left), visit(mst.right)) PowerOperations.POW_OPERATION -> ctx.call("pow", arrayOf(visit(mst.left), visit(mst.right)), f64) else -> super.visitBinary(mst) } @@ -142,15 +142,15 @@ internal class IntWasmBuilder(target: MST) : WasmBuilder(i32, IntRing, targ override fun visitNumeric(mst: Numeric): ExpressionRef = ctx.i32.const(mst.value) override fun visitUnary(mst: Unary): ExpressionRef = when (mst.operation) { - GroupOperations.MINUS_OPERATION -> ctx.i32.sub(ctx.i32.const(0), visit(mst.value)) - GroupOperations.PLUS_OPERATION -> visit(mst.value) + GroupOps.MINUS_OPERATION -> ctx.i32.sub(ctx.i32.const(0), visit(mst.value)) + GroupOps.PLUS_OPERATION -> visit(mst.value) else -> super.visitUnary(mst) } override fun visitBinary(mst: Binary): ExpressionRef = when (mst.operation) { - GroupOperations.PLUS_OPERATION -> ctx.i32.add(visit(mst.left), visit(mst.right)) - GroupOperations.MINUS_OPERATION -> ctx.i32.sub(visit(mst.left), visit(mst.right)) - RingOperations.TIMES_OPERATION -> ctx.i32.mul(visit(mst.left), visit(mst.right)) + GroupOps.PLUS_OPERATION -> ctx.i32.add(visit(mst.left), visit(mst.right)) + GroupOps.MINUS_OPERATION -> ctx.i32.sub(visit(mst.left), visit(mst.right)) + RingOps.TIMES_OPERATION -> ctx.i32.mul(visit(mst.left), visit(mst.right)) else -> super.visitBinary(mst) } } diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt index bc0119ca2..a178e8dd7 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt @@ -9,7 +9,7 @@ import org.apache.commons.math3.analysis.differentiation.DerivativeStructure import space.kscience.kmath.expressions.* import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.ExtendedField -import space.kscience.kmath.operations.NumbersAddOperations +import space.kscience.kmath.operations.NumbersAddOps /** * A field over commons-math [DerivativeStructure]. @@ -22,7 +22,7 @@ public class DerivativeStructureField( public val order: Int, bindings: Map, ) : ExtendedField, ExpressionAlgebra, - NumbersAddOperations { + NumbersAddOps { public val numberOfVariables: Int = bindings.size override val zero: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order) } diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt index 7d948cb61..e61a7bccc 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt @@ -52,7 +52,7 @@ private val PI_DIV_2 = Complex(PI / 2, 0) public object ComplexField : ExtendedField, Norm, - NumbersAddOperations, + NumbersAddOps, ScaleOperations { override val zero: Complex = 0.0.toComplex() @@ -216,7 +216,6 @@ public data class Complex(val re: Double, val im: Double) { public val Complex.Companion.algebra: ComplexField get() = ComplexField - /** * Creates a complex number with real part equal to this real. * diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/ComplexFieldND.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/ComplexFieldND.kt index 29e790d16..3951b5de0 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/ComplexFieldND.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/ComplexFieldND.kt @@ -6,13 +6,8 @@ package space.kscience.kmath.complex import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.nd.BufferND -import space.kscience.kmath.nd.BufferedFieldND -import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.operations.BufferField -import space.kscience.kmath.operations.ExtendedField -import space.kscience.kmath.operations.NumbersAddOperations -import space.kscience.kmath.operations.bufferAlgebra +import space.kscience.kmath.nd.* +import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -22,100 +17,61 @@ import kotlin.contracts.contract * An optimized nd-field for complex numbers */ @OptIn(UnstableKMathAPI::class) -public class ComplexFieldND( - shape: IntArray, -) : BufferedFieldND(shape, ComplexField, Buffer.Companion::complex), - NumbersAddOperations>, - ExtendedField> { +public sealed class ComplexFieldOpsND : BufferedFieldOpsND(ComplexField.bufferAlgebra), + ScaleOperations>, ExtendedFieldOps> { - override val zero: BufferND by lazy { produce { zero } } - override val one: BufferND by lazy { produce { one } } - - override fun number(value: Number): BufferND { - val d = value.toComplex() // minimize conversions - return produce { d } + override fun StructureND.toBufferND(): BufferND = when (this) { + is BufferND -> this + else -> { + val indexer = indexerBuilder(shape) + BufferND(indexer, Buffer.complex(indexer.linearSize) { offset -> get(indexer.index(offset)) }) + } } -// -// @Suppress("OVERRIDE_BY_INLINE") -// override inline fun map( -// arg: AbstractNDBuffer, -// transform: DoubleField.(Double) -> Double, -// ): RealNDElement { -// check(arg) -// val array = RealBuffer(arg.strides.linearSize) { offset -> DoubleField.transform(arg.buffer[offset]) } -// return BufferedNDFieldElement(this, array) -// } -// -// @Suppress("OVERRIDE_BY_INLINE") -// override inline fun produce(initializer: DoubleField.(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: DoubleField.(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: DoubleField.(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) -// } + //TODO do specialization - override fun power(arg: StructureND, pow: Number): BufferND = arg.map { power(it, pow) } + override fun scale(a: StructureND, value: Double): BufferND = + mapInline(a.toBufferND()) { it * value } - override fun exp(arg: StructureND): BufferND = arg.map { exp(it) } + override fun power(arg: StructureND, pow: Number): BufferND = + mapInline(arg.toBufferND()) { power(it, pow) } - override fun ln(arg: StructureND): BufferND = arg.map { ln(it) } + override fun exp(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { exp(it) } + override fun ln(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { ln(it) } - override fun sin(arg: StructureND): BufferND = arg.map { sin(it) } - override fun cos(arg: StructureND): BufferND = arg.map { cos(it) } - override fun tan(arg: StructureND): BufferND = arg.map { tan(it) } - override fun asin(arg: StructureND): BufferND = arg.map { asin(it) } - override fun acos(arg: StructureND): BufferND = arg.map { acos(it) } - override fun atan(arg: StructureND): BufferND = arg.map { atan(it) } + override fun sin(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { sin(it) } + override fun cos(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { cos(it) } + override fun tan(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { tan(it) } + override fun asin(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { asin(it) } + override fun acos(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { acos(it) } + override fun atan(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { atan(it) } - override fun sinh(arg: StructureND): BufferND = arg.map { sinh(it) } - override fun cosh(arg: StructureND): BufferND = arg.map { cosh(it) } - override fun tanh(arg: StructureND): BufferND = arg.map { tanh(it) } - override fun asinh(arg: StructureND): BufferND = arg.map { asinh(it) } - override fun acosh(arg: StructureND): BufferND = arg.map { acosh(it) } - override fun atanh(arg: StructureND): BufferND = arg.map { atanh(it) } -} + override fun sinh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { sinh(it) } + override fun cosh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { cosh(it) } + override fun tanh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { tanh(it) } + override fun asinh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { asinh(it) } + override fun acosh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { acosh(it) } + override fun atanh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { atanh(it) } - -/** - * Fast element production using function inlining - */ -public inline fun BufferedFieldND.produceInline(initializer: ComplexField.(Int) -> Complex): BufferND { - contract { callsInPlace(initializer, InvocationKind.EXACTLY_ONCE) } - val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.initializer(offset) } - return BufferND(strides, buffer) + public companion object : ComplexFieldOpsND() } @UnstableKMathAPI -public fun ComplexField.bufferAlgebra(size: Int): BufferField = - bufferAlgebra(Buffer.Companion::complex, size) +public val ComplexField.bufferAlgebra: BufferFieldOps + get() = bufferAlgebra(Buffer.Companion::complex) + + +@OptIn(UnstableKMathAPI::class) +public class ComplexFieldND(override val shape: Shape) : + ComplexFieldOpsND(), FieldND, NumbersAddOps> { + + override fun number(value: Number): BufferND { + val d = value.toDouble() // minimize conversions + return produce(shape) { d.toComplex() } + } +} + +public val ComplexField.ndAlgebra: ComplexFieldOpsND get() = ComplexFieldOpsND public fun ComplexField.ndAlgebra(vararg shape: Int): ComplexFieldND = ComplexFieldND(shape) diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt index e5d7ebd1e..b0ec10c35 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt @@ -44,7 +44,7 @@ public val Quaternion.r: Double */ @OptIn(UnstableKMathAPI::class) public object QuaternionField : Field, Norm, PowerOperations, - ExponentialOperations, NumbersAddOperations, ScaleOperations { + ExponentialOperations, NumbersAddOps, ScaleOperations { override val zero: Quaternion = 0.toQuaternion() override val one: Quaternion = 1.toQuaternion() diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt index 36ccb96f7..1baca0f55 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -52,13 +52,13 @@ public open class FunctionalExpressionGroup>( override val zero: Expression get() = const(algebra.zero) override fun Expression.unaryMinus(): Expression = - unaryOperation(GroupOperations.MINUS_OPERATION, this) + unaryOperation(GroupOps.MINUS_OPERATION, this) /** * Builds an Expression of addition of two another expressions. */ override fun add(a: Expression, b: Expression): Expression = - binaryOperation(GroupOperations.PLUS_OPERATION, a, b) + binaryOperation(GroupOps.PLUS_OPERATION, a, b) // /** // * Builds an Expression of multiplication of expression by number. @@ -89,7 +89,7 @@ public open class FunctionalExpressionRing>( * Builds an Expression of multiplication of two expressions. */ override fun multiply(a: Expression, b: Expression): Expression = - binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, b) + binaryOperationFunction(RingOps.TIMES_OPERATION)(a, b) public operator fun Expression.times(arg: T): Expression = this * const(arg) public operator fun T.times(arg: Expression): Expression = arg * this @@ -108,7 +108,7 @@ public open class FunctionalExpressionField>( * Builds an Expression of division an expression by another one. */ override fun divide(a: Expression, b: Expression): Expression = - binaryOperationFunction(FieldOperations.DIV_OPERATION)(a, b) + binaryOperationFunction(FieldOps.DIV_OPERATION)(a, b) public operator fun Expression.div(arg: T): Expression = this / const(arg) public operator fun T.div(arg: Expression): Expression = arg / this diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt index bbc74005c..360715408 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt @@ -31,18 +31,18 @@ public object MstGroup : Group, NumericAlgebra, ScaleOperations { override fun number(value: Number): MST.Numeric = MstNumericAlgebra.number(value) override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value) - override fun add(a: MST, b: MST): MST.Binary = binaryOperationFunction(GroupOperations.PLUS_OPERATION)(a, b) + override fun add(a: MST, b: MST): MST.Binary = binaryOperationFunction(GroupOps.PLUS_OPERATION)(a, b) override operator fun MST.unaryPlus(): MST.Unary = - unaryOperationFunction(GroupOperations.PLUS_OPERATION)(this) + unaryOperationFunction(GroupOps.PLUS_OPERATION)(this) override operator fun MST.unaryMinus(): MST.Unary = - unaryOperationFunction(GroupOperations.MINUS_OPERATION)(this) + unaryOperationFunction(GroupOps.MINUS_OPERATION)(this) override operator fun MST.minus(b: MST): MST.Binary = - binaryOperationFunction(GroupOperations.MINUS_OPERATION)(this, b) + binaryOperationFunction(GroupOps.MINUS_OPERATION)(this, b) override fun scale(a: MST, value: Double): MST.Binary = - binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, number(value)) + binaryOperationFunction(RingOps.TIMES_OPERATION)(a, number(value)) override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = MstNumericAlgebra.binaryOperationFunction(operation) @@ -56,7 +56,7 @@ public object MstGroup : Group, NumericAlgebra, ScaleOperations { */ @Suppress("OVERRIDE_BY_INLINE") @OptIn(UnstableKMathAPI::class) -public object MstRing : Ring, NumbersAddOperations, ScaleOperations { +public object MstRing : Ring, NumbersAddOps, ScaleOperations { override inline val zero: MST.Numeric get() = MstGroup.zero override val one: MST.Numeric = number(1.0) @@ -65,10 +65,10 @@ public object MstRing : Ring, NumbersAddOperations, ScaleOperations, NumbersAddOperations, ScaleOperations, NumbersAddOperations, ScaleOperations { +public object MstField : Field, NumbersAddOps, ScaleOperations { override inline val zero: MST.Numeric get() = MstRing.zero override inline val one: MST.Numeric get() = MstRing.one @@ -95,11 +95,11 @@ public object MstField : Field, NumbersAddOperations, ScaleOperations< override fun add(a: MST, b: MST): MST.Binary = MstRing.add(a, b) override fun scale(a: MST, value: Double): MST.Binary = - MstGroup.binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, MstGroup.number(value)) + MstGroup.binaryOperationFunction(RingOps.TIMES_OPERATION)(a, MstGroup.number(value)) override fun multiply(a: MST, b: MST): MST.Binary = MstRing.multiply(a, b) override fun divide(a: MST, b: MST): MST.Binary = - binaryOperationFunction(FieldOperations.DIV_OPERATION)(a, b) + binaryOperationFunction(FieldOps.DIV_OPERATION)(a, b) override operator fun MST.unaryPlus(): MST.Unary = MstRing { +this@unaryPlus } override operator fun MST.unaryMinus(): MST.Unary = MstRing { -this@unaryMinus } @@ -138,7 +138,7 @@ public object MstExtendedField : ExtendedField, NumericAlgebra { override fun sqrt(arg: MST): MST = unaryOperationFunction(PowerOperations.SQRT_OPERATION)(arg) override fun scale(a: MST, value: Double): MST = - binaryOperation(GroupOperations.PLUS_OPERATION, a, number(value)) + binaryOperation(GroupOps.PLUS_OPERATION, a, number(value)) override fun multiply(a: MST, b: MST): MST.Binary = MstField.multiply(a, b) override fun divide(a: MST, b: MST): MST.Binary = MstField.divide(a, b) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt index d5b80da2c..089503dc8 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt @@ -59,7 +59,7 @@ public fun DerivationResult.grad(vararg variables: Symbol): Point>( public val context: F, bindings: Map, -) : Field>, ExpressionAlgebra>, NumbersAddOperations> { +) : Field>, ExpressionAlgebra>, NumbersAddOps> { override val zero: AutoDiffValue get() = const(context.zero) override val one: AutoDiffValue get() = const(context.one) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt index 3d562f26f..39dbe3a81 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt @@ -6,12 +6,10 @@ package space.kscience.kmath.linear import space.kscience.kmath.misc.PerformancePitfall -import space.kscience.kmath.nd.BufferedRingND +import space.kscience.kmath.nd.BufferedRingOpsND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.asND -import space.kscience.kmath.nd.ndAlgebra -import space.kscience.kmath.operations.Ring -import space.kscience.kmath.operations.invoke +import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.VirtualBuffer @@ -19,31 +17,28 @@ import space.kscience.kmath.structures.indices public class BufferedLinearSpace>( - override val elementAlgebra: A, - private val bufferFactory: BufferFactory, + private val bufferAlgebra: BufferAlgebra ) : LinearSpace { + override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra - private fun ndRing( - rows: Int, - cols: Int, - ): BufferedRingND = elementAlgebra.ndAlgebra(bufferFactory, rows, cols) + private val ndAlgebra = BufferedRingOpsND(bufferAlgebra) override fun buildMatrix(rows: Int, columns: Int, initializer: A.(i: Int, j: Int) -> T): Matrix = - ndRing(rows, columns).produce { (i, j) -> elementAlgebra.initializer(i, j) }.as2D() + ndAlgebra.produce(intArrayOf(rows, columns)) { (i, j) -> elementAlgebra.initializer(i, j) }.as2D() override fun buildVector(size: Int, initializer: A.(Int) -> T): Point = - bufferFactory(size) { elementAlgebra.initializer(it) } + bufferAlgebra.buffer(size) { elementAlgebra.initializer(it) } - override fun Matrix.unaryMinus(): Matrix = ndRing(rowNum, colNum).run { + override fun Matrix.unaryMinus(): Matrix = ndAlgebra { asND().map { -it }.as2D() } - override fun Matrix.plus(other: Matrix): Matrix = ndRing(rowNum, colNum).run { + override fun Matrix.plus(other: Matrix): Matrix = ndAlgebra { require(shape.contentEquals(other.shape)) { "Shape mismatch on Matrix::plus. Expected $shape but found ${other.shape}" } asND().plus(other.asND()).as2D() } - override fun Matrix.minus(other: Matrix): Matrix = ndRing(rowNum, colNum).run { + override fun Matrix.minus(other: Matrix): Matrix = ndAlgebra { require(shape.contentEquals(other.shape)) { "Shape mismatch on Matrix::minus. Expected $shape but found ${other.shape}" } asND().minus(other.asND()).as2D() } @@ -88,11 +83,11 @@ public class BufferedLinearSpace>( } } - override fun Matrix.times(value: T): Matrix = ndRing(rowNum, colNum).run { + override fun Matrix.times(value: T): Matrix = ndAlgebra { asND().map { it * value }.as2D() } } public fun > A.linearSpace(bufferFactory: BufferFactory): BufferedLinearSpace = - BufferedLinearSpace(this, bufferFactory) + BufferedLinearSpace(BufferRingOps(this, bufferFactory)) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt index c2f53939f..ec6040af0 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt @@ -6,11 +6,12 @@ package space.kscience.kmath.linear import space.kscience.kmath.misc.PerformancePitfall -import space.kscience.kmath.nd.DoubleFieldND +import space.kscience.kmath.nd.DoubleFieldOpsND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.asND -import space.kscience.kmath.operations.DoubleBufferOperations +import space.kscience.kmath.operations.DoubleBufferOps import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.DoubleBuffer @@ -18,30 +19,27 @@ public object DoubleLinearSpace : LinearSpace { override val elementAlgebra: DoubleField get() = DoubleField - private fun ndRing( - rows: Int, - cols: Int, - ): DoubleFieldND = DoubleFieldND(intArrayOf(rows, cols)) - override fun buildMatrix( rows: Int, columns: Int, initializer: DoubleField.(i: Int, j: Int) -> Double - ): Matrix = ndRing(rows, columns).produce { (i, j) -> DoubleField.initializer(i, j) }.as2D() + ): Matrix = DoubleFieldOpsND.produce(intArrayOf(rows, columns)) { (i, j) -> + DoubleField.initializer(i, j) + }.as2D() override fun buildVector(size: Int, initializer: DoubleField.(Int) -> Double): DoubleBuffer = DoubleBuffer(size) { DoubleField.initializer(it) } - override fun Matrix.unaryMinus(): Matrix = ndRing(rowNum, colNum).run { + override fun Matrix.unaryMinus(): Matrix = DoubleFieldOpsND { asND().map { -it }.as2D() } - override fun Matrix.plus(other: Matrix): Matrix = ndRing(rowNum, colNum).run { + override fun Matrix.plus(other: Matrix): Matrix = DoubleFieldOpsND { require(shape.contentEquals(other.shape)) { "Shape mismatch on Matrix::plus. Expected $shape but found ${other.shape}" } asND().plus(other.asND()).as2D() } - override fun Matrix.minus(other: Matrix): Matrix = ndRing(rowNum, colNum).run { + override fun Matrix.minus(other: Matrix): Matrix = DoubleFieldOpsND { require(shape.contentEquals(other.shape)) { "Shape mismatch on Matrix::minus. Expected $shape but found ${other.shape}" } asND().minus(other.asND()).as2D() } @@ -84,23 +82,23 @@ public object DoubleLinearSpace : LinearSpace { } - override fun Matrix.times(value: Double): Matrix = ndRing(rowNum, colNum).run { + override fun Matrix.times(value: Double): Matrix = DoubleFieldOpsND { asND().map { it * value }.as2D() } - public override fun Point.plus(other: Point): DoubleBuffer = DoubleBufferOperations.run { + public override fun Point.plus(other: Point): DoubleBuffer = DoubleBufferOps.run { this@plus + other } - public override fun Point.minus(other: Point): DoubleBuffer = DoubleBufferOperations.run { + public override fun Point.minus(other: Point): DoubleBuffer = DoubleBufferOps.run { this@minus - other } - public override fun Point.times(value: Double): DoubleBuffer = DoubleBufferOperations.run { + public override fun Point.times(value: Double): DoubleBuffer = DoubleBufferOps.run { scale(this@times, value) } - public operator fun Point.div(value: Double): DoubleBuffer = DoubleBufferOperations.run { + public operator fun Point.div(value: Double): DoubleBuffer = DoubleBufferOps.run { scale(this@div, 1.0 / value) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt index 1d8985b59..5349ad864 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt @@ -10,6 +10,7 @@ import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.StructureFeature import space.kscience.kmath.nd.as1D +import space.kscience.kmath.operations.BufferRingOps import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.invoke @@ -188,7 +189,7 @@ public interface LinearSpace> { public fun > buffered( algebra: A, bufferFactory: BufferFactory = Buffer.Companion::boxing, - ): LinearSpace = BufferedLinearSpace(algebra, bufferFactory) + ): LinearSpace = BufferedLinearSpace(BufferRingOps(algebra, bufferFactory)) @Deprecated("use DoubleField.linearSpace") public val double: LinearSpace = buffered(DoubleField, ::DoubleBuffer) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt index b925c2642..dde04a97a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt @@ -7,7 +7,6 @@ package space.kscience.kmath.nd import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.* -import space.kscience.kmath.structures.* import kotlin.reflect.KClass /** @@ -19,6 +18,14 @@ import kotlin.reflect.KClass public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) : RuntimeException("Shape ${actual.contentToString()} doesn't fit in expected shape ${expected.contentToString()}.") +public typealias Shape = IntArray + +public fun Shape(shapeFirst: Int, vararg shapeRest: Int): Shape = intArrayOf(shapeFirst, *shapeRest) + +public interface WithShape { + public val shape: Shape +} + /** * The base interface for all ND-algebra implementations. * @@ -26,20 +33,15 @@ public class ShapeMismatchException(public val expected: IntArray, public val ac * @param C the type of the element context. */ public interface AlgebraND> { - /** - * The shape of ND-structures this algebra operates on. - */ - public val shape: IntArray - /** * The algebra over elements of ND structure. */ - public val elementContext: C + public val elementAlgebra: C /** * Produces a new NDStructure using given initializer function. */ - public fun produce(initializer: C.(IntArray) -> T): StructureND + public fun produce(shape: Shape, initializer: C.(IntArray) -> T): StructureND /** * Maps elements from one structure to another one by applying [transform] to them. @@ -54,7 +56,7 @@ public interface AlgebraND> { /** * Combines two structures into one. */ - public fun combine(a: StructureND, b: StructureND, transform: C.(T, T) -> T): StructureND + public fun zip(left: StructureND, right: StructureND, transform: C.(T, T) -> T): StructureND /** * Element-wise invocation of function working on [T] on a [StructureND]. @@ -77,7 +79,6 @@ public interface AlgebraND> { public companion object } - /** * Get a feature of the structure in this scope. Structure features take precedence other context features. * @@ -89,37 +90,13 @@ public interface AlgebraND> { public inline fun AlgebraND.getFeature(structure: StructureND): F? = getFeature(structure, F::class) -/** - * Checks if given elements are consistent with this context. - * - * @param structures the structures to check. - * @return the array of valid structures. - */ -internal fun > AlgebraND.checkShape(vararg structures: StructureND): Array> = - structures - .map(StructureND::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 > AlgebraND.checkShape(element: StructureND): StructureND { - if (!element.shape.contentEquals(shape)) throw ShapeMismatchException(shape, element.shape) - return element -} - /** * Space of [StructureND]. * * @param T the type of the element contained in ND structure. - * @param S the type of group over structure elements. + * @param A the type of group over structure elements. */ -public interface GroupND> : Group>, AlgebraND { +public interface GroupOpsND> : GroupOps>, AlgebraND { /** * Element-wise addition. * @@ -128,7 +105,7 @@ public interface GroupND> : Group>, AlgebraND * @return the sum. */ override fun add(a: StructureND, b: StructureND): StructureND = - combine(a, b) { aValue, bValue -> add(aValue, bValue) } + zip(a, b) { aValue, bValue -> add(aValue, bValue) } // TODO move to extensions after KEEP-176 @@ -171,13 +148,17 @@ public interface GroupND> : Group>, AlgebraND public companion object } +public interface GroupND> : Group>, GroupOpsND, WithShape { + override val zero: StructureND get() = produce(shape) { elementAlgebra.zero } +} + /** * Ring of [StructureND]. * * @param T the type of the element contained in ND structure. - * @param R the type of ring over structure elements. + * @param A the type of ring over structure elements. */ -public interface RingND> : Ring>, GroupND { +public interface RingOpsND> : RingOps>, GroupOpsND { /** * Element-wise multiplication. * @@ -186,7 +167,7 @@ public interface RingND> : Ring>, GroupND, b: StructureND): StructureND = - combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } + zip(a, b) { aValue, bValue -> multiply(aValue, bValue) } //TODO move to extensions after KEEP-176 @@ -211,13 +192,19 @@ public interface RingND> : Ring>, GroupND> : Ring>, RingOpsND, GroupND, WithShape { + override val one: StructureND get() = produce(shape) { elementAlgebra.one } +} + + /** * Field of [StructureND]. * * @param T the type of the element contained in ND structure. - * @param F the type field over structure elements. + * @param A the type field over structure elements. */ -public interface FieldND> : Field>, RingND { +public interface FieldOpsND> : FieldOps>, RingOpsND, + ScaleOperations> { /** * Element-wise division. * @@ -226,9 +213,9 @@ public interface FieldND> : Field>, RingND, b: StructureND): StructureND = - combine(a, b) { aValue, bValue -> divide(aValue, bValue) } + zip(a, b) { aValue, bValue -> divide(aValue, bValue) } - //TODO move to extensions after KEEP-176 + //TODO move to extensions after https://github.com/Kotlin/KEEP/blob/master/proposals/context-receivers.md /** * Divides an ND structure by an element of it. * @@ -247,42 +234,9 @@ public interface FieldND> : Field>, RingND): StructureND = arg.map { divide(it, this@div) } - /** - * Element-wise scaling. - * - * @param a the multiplicand. - * @param value the multiplier. - * @return the product. - */ override fun scale(a: StructureND, value: Double): StructureND = a.map { scale(it, value) } - -// @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) -// } -// } } + +public interface FieldND> : Field>, FieldOpsND, RingND, WithShape { + override val one: StructureND get() = produce(shape) { elementAlgebra.one } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt index ae72f3689..06534c5e5 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt @@ -3,145 +3,173 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ +@file:OptIn(UnstableKMathAPI::class) + package space.kscience.kmath.nd import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.* -import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.jvm.JvmName public interface BufferAlgebraND> : AlgebraND { - public val strides: Strides - public val bufferFactory: BufferFactory + public val indexerBuilder: (IntArray) -> ShapeIndex + public val bufferAlgebra: BufferAlgebra + override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra - override fun produce(initializer: A.(IntArray) -> T): BufferND = BufferND( - strides, - bufferFactory(strides.linearSize) { offset -> - elementContext.initializer(strides.index(offset)) + override fun produce(shape: Shape, initializer: A.(IntArray) -> T): BufferND { + val indexer = indexerBuilder(shape) + return BufferND( + indexer, + bufferAlgebra.buffer(indexer.linearSize) { offset -> + elementAlgebra.initializer(indexer.index(offset)) + } + ) + } + + public fun StructureND.toBufferND(): BufferND = when (this) { + is BufferND -> this + else -> { + val indexer = indexerBuilder(shape) + BufferND(indexer, bufferAlgebra.buffer(indexer.linearSize) { offset -> get(indexer.index(offset)) }) + } + } + + override fun StructureND.map(transform: A.(T) -> T): BufferND = mapInline(toBufferND(), transform) + + override fun StructureND.mapIndexed(transform: A.(index: IntArray, T) -> T): BufferND = + mapIndexedInline(toBufferND(), transform) + + override fun zip(left: StructureND, right: StructureND, transform: A.(T, T) -> T): BufferND = + zipInline(left.toBufferND(), right.toBufferND(), transform) + + public companion object { + public val defaultIndexerBuilder: (IntArray) -> ShapeIndex = DefaultStrides.Companion::invoke + } +} + +public inline fun > BufferAlgebraND.mapInline( + arg: BufferND, + crossinline transform: A.(T) -> T +): BufferND { + val indexes = arg.indexes + return BufferND(indexes, bufferAlgebra.mapInline(arg.buffer, transform)) +} + +internal inline fun > BufferAlgebraND.mapIndexedInline( + arg: BufferND, + crossinline transform: A.(index: IntArray, arg: T) -> T +): BufferND { + val indexes = arg.indexes + return BufferND( + indexes, + bufferAlgebra.mapIndexedInline(arg.buffer) { offset, value -> + transform(indexes.index(offset), value) } ) - - public val StructureND.buffer: Buffer - get() = when { - !shape.contentEquals(this@BufferAlgebraND.shape) -> throw ShapeMismatchException( - this@BufferAlgebraND.shape, - shape - ) - this is BufferND && this.strides == this@BufferAlgebraND.strides -> this.buffer - else -> bufferFactory(strides.linearSize) { offset -> get(strides.index(offset)) } - } - - override fun StructureND.map(transform: A.(T) -> T): BufferND { - val buffer = bufferFactory(strides.linearSize) { offset -> - elementContext.transform(buffer[offset]) - } - return BufferND(strides, buffer) - } - - override fun StructureND.mapIndexed(transform: A.(index: IntArray, T) -> T): BufferND { - val buffer = bufferFactory(strides.linearSize) { offset -> - elementContext.transform( - strides.index(offset), - buffer[offset] - ) - } - return BufferND(strides, buffer) - } - - override fun combine(a: StructureND, b: StructureND, transform: A.(T, T) -> T): BufferND { - val buffer = bufferFactory(strides.linearSize) { offset -> - elementContext.transform(a.buffer[offset], b.buffer[offset]) - } - return BufferND(strides, buffer) - } } -public open class BufferedGroupND>( - final override val shape: IntArray, - final override val elementContext: A, - final override val bufferFactory: BufferFactory, -) : GroupND, BufferAlgebraND { - override val strides: Strides = DefaultStrides(shape) - override val zero: BufferND by lazy { produce { zero } } - override fun StructureND.unaryMinus(): StructureND = produce { -get(it) } +internal inline fun > BufferAlgebraND.zipInline( + l: BufferND, + r: BufferND, + crossinline block: A.(l: T, r: T) -> T +): BufferND { + require(l.indexes == r.indexes) + val indexes = l.indexes + return BufferND(indexes, bufferAlgebra.zipInline(l.buffer, r.buffer, block)) } -public open class BufferedRingND>( - shape: IntArray, - elementContext: R, - bufferFactory: BufferFactory, -) : BufferedGroupND(shape, elementContext, bufferFactory), RingND { - override val one: BufferND by lazy { produce { one } } +public open class BufferedGroupNDOps>( + override val bufferAlgebra: BufferAlgebra, + override val indexerBuilder: (IntArray) -> ShapeIndex = BufferAlgebraND.defaultIndexerBuilder +) : GroupOpsND, BufferAlgebraND { + override fun StructureND.unaryMinus(): StructureND = map { -it } } -public open class BufferedFieldND>( - shape: IntArray, - elementContext: R, - bufferFactory: BufferFactory, -) : BufferedRingND(shape, elementContext, bufferFactory), FieldND { +public open class BufferedRingOpsND>( + bufferAlgebra: BufferAlgebra, + indexerBuilder: (IntArray) -> ShapeIndex = BufferAlgebraND.defaultIndexerBuilder +) : BufferedGroupNDOps(bufferAlgebra, indexerBuilder), RingOpsND + +public open class BufferedFieldOpsND>( + bufferAlgebra: BufferAlgebra, + indexerBuilder: (IntArray) -> ShapeIndex = BufferAlgebraND.defaultIndexerBuilder +) : BufferedRingOpsND(bufferAlgebra, indexerBuilder), FieldOpsND { + + public constructor( + elementAlgebra: A, + bufferFactory: BufferFactory, + indexerBuilder: (IntArray) -> ShapeIndex = BufferAlgebraND.defaultIndexerBuilder + ) : this(BufferFieldOps(elementAlgebra, bufferFactory), indexerBuilder) override fun scale(a: StructureND, value: Double): StructureND = a.map { it * value } } -// group factories -public fun > A.ndAlgebra( - bufferFactory: BufferFactory, - vararg shape: Int, -): BufferedGroupND = BufferedGroupND(shape, this, bufferFactory) +public val > BufferAlgebra.nd: BufferedGroupNDOps get() = BufferedGroupNDOps(this) +public val > BufferAlgebra.nd: BufferedRingOpsND get() = BufferedRingOpsND(this) +public val > BufferAlgebra.nd: BufferedFieldOpsND get() = BufferedFieldOpsND(this) -@JvmName("withNdGroup") -public inline fun , R> A.withNdAlgebra( - noinline bufferFactory: BufferFactory, - vararg shape: Int, - action: BufferedGroupND.() -> R, -): R { - contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } - return ndAlgebra(bufferFactory, *shape).run(action) -} -//ring factories -public fun > A.ndAlgebra( - bufferFactory: BufferFactory, +public fun > BufferAlgebraND.produce( vararg shape: Int, -): BufferedRingND = BufferedRingND(shape, this, bufferFactory) + initializer: A.(IntArray) -> T +): BufferND = produce(shape, initializer) -@JvmName("withNdRing") -public inline fun , R> A.withNdAlgebra( - noinline bufferFactory: BufferFactory, - vararg shape: Int, - action: BufferedRingND.() -> R, -): R { - contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } - return ndAlgebra(bufferFactory, *shape).run(action) -} +//// group factories +//public fun > A.ndAlgebra( +// bufferAlgebra: BufferAlgebra, +// vararg shape: Int, +//): BufferedGroupNDOps = BufferedGroupNDOps(bufferAlgebra) +// +//@JvmName("withNdGroup") +//public inline fun , R> A.withNdAlgebra( +// noinline bufferFactory: BufferFactory, +// vararg shape: Int, +// action: BufferedGroupNDOps.() -> R, +//): R { +// contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } +// return ndAlgebra(bufferFactory, *shape).run(action) +//} -//field factories -public fun > A.ndAlgebra( - bufferFactory: BufferFactory, - vararg shape: Int, -): BufferedFieldND = BufferedFieldND(shape, this, bufferFactory) - -/** - * Create a [FieldND] for this [Field] inferring proper buffer factory from the type - */ -@UnstableKMathAPI -@Suppress("UNCHECKED_CAST") -public inline fun > A.autoNdAlgebra( - vararg shape: Int, -): FieldND = when (this) { - DoubleField -> DoubleFieldND(shape) as FieldND - else -> BufferedFieldND(shape, this, Buffer.Companion::auto) -} - -@JvmName("withNdField") -public inline fun , R> A.withNdAlgebra( - noinline bufferFactory: BufferFactory, - vararg shape: Int, - action: BufferedFieldND.() -> R, -): R { - contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } - return ndAlgebra(bufferFactory, *shape).run(action) -} \ No newline at end of file +////ring factories +//public fun > A.ndAlgebra( +// bufferFactory: BufferFactory, +// vararg shape: Int, +//): BufferedRingNDOps = BufferedRingNDOps(shape, this, bufferFactory) +// +//@JvmName("withNdRing") +//public inline fun , R> A.withNdAlgebra( +// noinline bufferFactory: BufferFactory, +// vararg shape: Int, +// action: BufferedRingNDOps.() -> R, +//): R { +// contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } +// return ndAlgebra(bufferFactory, *shape).run(action) +//} +// +////field factories +//public fun > A.ndAlgebra( +// bufferFactory: BufferFactory, +// vararg shape: Int, +//): BufferedFieldNDOps = BufferedFieldNDOps(shape, this, bufferFactory) +// +///** +// * Create a [FieldND] for this [Field] inferring proper buffer factory from the type +// */ +//@UnstableKMathAPI +//@Suppress("UNCHECKED_CAST") +//public inline fun > A.autoNdAlgebra( +// vararg shape: Int, +//): FieldND = when (this) { +// DoubleField -> DoubleFieldND(shape) as FieldND +// else -> BufferedFieldNDOps(shape, this, Buffer.Companion::auto) +//} +// +//@JvmName("withNdField") +//public inline fun , R> A.withNdAlgebra( +// noinline bufferFactory: BufferFactory, +// vararg shape: Int, +// action: BufferedFieldNDOps.() -> R, +//): R { +// contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } +// return ndAlgebra(bufferFactory, *shape).run(action) +//} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt index 694e0ceae..9eba401ab 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt @@ -15,26 +15,20 @@ import space.kscience.kmath.structures.MutableBufferFactory * Represents [StructureND] over [Buffer]. * * @param T the type of items. - * @param strides The strides to access elements of [Buffer] by linear indices. + * @param indexes The strides to access elements of [Buffer] by linear indices. * @param buffer The underlying buffer. */ public open class BufferND( - public val strides: Strides, + public val indexes: ShapeIndex, public val buffer: Buffer, ) : StructureND { - init { - if (strides.linearSize != buffer.size) { - error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}") - } - } + override operator fun get(index: IntArray): T = buffer[indexes.offset(index)] - override operator fun get(index: IntArray): T = buffer[strides.offset(index)] - - override val shape: IntArray get() = strides.shape + override val shape: IntArray get() = indexes.shape @PerformancePitfall - override fun elements(): Sequence> = strides.indices().map { + override fun elements(): Sequence> = indexes.indices().map { it to this[it] } @@ -49,7 +43,7 @@ public inline fun StructureND.mapToBuffer( crossinline transform: (T) -> R, ): BufferND { return if (this is BufferND) - BufferND(this.strides, factory.invoke(strides.linearSize) { transform(buffer[it]) }) + BufferND(this.indexes, factory.invoke(indexes.linearSize) { transform(buffer[it]) }) else { val strides = DefaultStrides(shape) BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) @@ -64,11 +58,11 @@ public inline fun StructureND.mapToBuffer( * @param mutableBuffer The underlying buffer. */ public class MutableBufferND( - strides: Strides, + strides: ShapeIndex, public val mutableBuffer: MutableBuffer, ) : MutableStructureND, BufferND(strides, mutableBuffer) { override fun set(index: IntArray, value: T) { - mutableBuffer[strides.offset(index)] = value + mutableBuffer[indexes.offset(index)] = value } } @@ -80,7 +74,7 @@ public inline fun MutableStructureND.mapToMutableBuffer( crossinline transform: (T) -> R, ): MutableBufferND { return if (this is MutableBufferND) - MutableBufferND(this.strides, factory.invoke(strides.linearSize) { transform(mutableBuffer[it]) }) + MutableBufferND(this.indexes, factory.invoke(indexes.linearSize) { transform(mutableBuffer[it]) }) else { val strides = DefaultStrides(shape) MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt index 0c7d4d5d1..294c44ec4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt @@ -6,108 +6,68 @@ package space.kscience.kmath.nd import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.ExtendedField -import space.kscience.kmath.operations.NumbersAddOperations -import space.kscience.kmath.operations.ScaleOperations +import space.kscience.kmath.operations.* import space.kscience.kmath.structures.DoubleBuffer import kotlin.contracts.InvocationKind import kotlin.contracts.contract -@OptIn(UnstableKMathAPI::class) -public class DoubleFieldND( - shape: IntArray, -) : BufferedFieldND(shape, DoubleField, ::DoubleBuffer), - NumbersAddOperations>, - ScaleOperations>, - ExtendedField> { +public sealed class DoubleFieldOpsND : BufferedFieldOpsND(DoubleField.bufferAlgebra), + ScaleOperations>, ExtendedFieldOps> { - override val zero: BufferND by lazy { produce { zero } } - override val one: BufferND by lazy { produce { one } } + override fun StructureND.toBufferND(): BufferND = when (this) { + is BufferND -> this + else -> { + val indexer = indexerBuilder(shape) + BufferND(indexer, DoubleBuffer(indexer.linearSize) { offset -> get(indexer.index(offset)) }) + } + } + + //TODO do specialization + + override fun scale(a: StructureND, value: Double): BufferND = + mapInline(a.toBufferND()) { it * value } + + override fun power(arg: StructureND, pow: Number): BufferND = + mapInline(arg.toBufferND()) { power(it, pow) } + + override fun exp(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { exp(it) } + override fun ln(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { ln(it) } + + override fun sin(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { sin(it) } + override fun cos(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { cos(it) } + override fun tan(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { tan(it) } + override fun asin(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { asin(it) } + override fun acos(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { acos(it) } + override fun atan(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { atan(it) } + + override fun sinh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { sinh(it) } + override fun cosh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { cosh(it) } + override fun tanh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { tanh(it) } + override fun asinh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { asinh(it) } + override fun acosh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { acosh(it) } + override fun atanh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { atanh(it) } + + public companion object : DoubleFieldOpsND() +} + +@OptIn(UnstableKMathAPI::class) +public class DoubleFieldND(override val shape: Shape) : + DoubleFieldOpsND(), FieldND, NumbersAddOps> { override fun number(value: Number): BufferND { val d = value.toDouble() // minimize conversions - return produce { d } + return produce(shape) { d } } - - override val StructureND.buffer: DoubleBuffer - get() = when { - !shape.contentEquals(this@DoubleFieldND.shape) -> throw ShapeMismatchException( - this@DoubleFieldND.shape, - shape - ) - this is BufferND && this.strides == this@DoubleFieldND.strides -> this.buffer as DoubleBuffer - else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } - } - - @Suppress("OVERRIDE_BY_INLINE") - override inline fun StructureND.map( - transform: DoubleField.(Double) -> Double, - ): BufferND { - val buffer = DoubleBuffer(strides.linearSize) { offset -> DoubleField.transform(buffer.array[offset]) } - return BufferND(strides, buffer) - } - - @Suppress("OVERRIDE_BY_INLINE") - override inline fun produce(initializer: DoubleField.(IntArray) -> Double): BufferND { - val array = DoubleArray(strides.linearSize) { offset -> - val index = strides.index(offset) - DoubleField.initializer(index) - } - return BufferND(strides, DoubleBuffer(array)) - } - - @Suppress("OVERRIDE_BY_INLINE") - override inline fun StructureND.mapIndexed( - transform: DoubleField.(index: IntArray, Double) -> Double, - ): BufferND = BufferND( - strides, - buffer = DoubleBuffer(strides.linearSize) { offset -> - DoubleField.transform( - strides.index(offset), - buffer.array[offset] - ) - }) - - @Suppress("OVERRIDE_BY_INLINE") - override inline fun combine( - a: StructureND, - b: StructureND, - transform: DoubleField.(Double, Double) -> Double, - ): BufferND { - val buffer = DoubleBuffer(strides.linearSize) { offset -> - DoubleField.transform(a.buffer.array[offset], b.buffer.array[offset]) - } - return BufferND(strides, buffer) - } - - override fun scale(a: StructureND, value: Double): StructureND = a.map { it * value } - - override fun power(arg: StructureND, pow: Number): BufferND = arg.map { power(it, pow) } - - override fun exp(arg: StructureND): BufferND = arg.map { exp(it) } - override fun ln(arg: StructureND): BufferND = arg.map { ln(it) } - - override fun sin(arg: StructureND): BufferND = arg.map { sin(it) } - override fun cos(arg: StructureND): BufferND = arg.map { cos(it) } - override fun tan(arg: StructureND): BufferND = arg.map { tan(it) } - override fun asin(arg: StructureND): BufferND = arg.map { asin(it) } - override fun acos(arg: StructureND): BufferND = arg.map { acos(it) } - override fun atan(arg: StructureND): BufferND = arg.map { atan(it) } - - override fun sinh(arg: StructureND): BufferND = arg.map { sinh(it) } - override fun cosh(arg: StructureND): BufferND = arg.map { cosh(it) } - override fun tanh(arg: StructureND): BufferND = arg.map { tanh(it) } - override fun asinh(arg: StructureND): BufferND = arg.map { asinh(it) } - override fun acosh(arg: StructureND): BufferND = arg.map { acosh(it) } - override fun atanh(arg: StructureND): BufferND = arg.map { atanh(it) } } +public val DoubleField.ndAlgebra: DoubleFieldOpsND get() = DoubleFieldOpsND + public fun DoubleField.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleFieldND(shape) /** * Produce a context for n-dimensional operations inside this real field */ +@UnstableKMathAPI public inline fun DoubleField.withNdAlgebra(vararg shape: Int, action: DoubleFieldND.() -> R): R { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } return DoubleFieldND(shape).run(action) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndex.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndex.kt new file mode 100644 index 000000000..bdbae70c2 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndex.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package space.kscience.kmath.nd + +import kotlin.native.concurrent.ThreadLocal + +/** + * A converter from linear index to multivariate index + */ +public interface ShapeIndex{ + public val shape: Shape + + /** + * Get linear index from multidimensional index + */ + public fun offset(index: IntArray): Int + + /** + * Get multidimensional from linear + */ + public fun index(offset: Int): IntArray + + /** + * The size of linear buffer to accommodate all elements of ND-structure corresponding to strides + */ + public val linearSize: Int + + // TODO introduce a fast way to calculate index of the next element? + + /** + * Iterate over ND indices in a natural order + */ + public fun indices(): Sequence + + override fun equals(other: Any?): Boolean + override fun hashCode(): Int +} + +/** + * Linear transformation of indexes + */ +public abstract class Strides: ShapeIndex { + /** + * Array strides + */ + public abstract val strides: IntArray + + public 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]})") + value * strides[i] + }.sum() + + // TODO introduce a fast way to calculate index of the next element? + + /** + * Iterate over ND indices in a natural order + */ + public override fun indices(): Sequence = (0 until linearSize).asSequence().map(::index) +} + +/** + * Simple implementation of [Strides]. + */ +public class DefaultStrides private constructor(override val shape: IntArray) : Strides() { + override val linearSize: Int get() = strides[shape.size] + + /** + * Strides for memory access + */ + override val strides: IntArray by lazy { + sequence { + var current = 1 + yield(1) + + shape.forEach { + current *= it + yield(current) + } + }.toList().toIntArray() + } + + override fun index(offset: Int): IntArray { + val res = IntArray(shape.size) + var current = offset + var strideIndex = strides.size - 2 + + while (strideIndex >= 0) { + res[strideIndex] = (current / strides[strideIndex]) + current %= strides[strideIndex] + strideIndex-- + } + + return res + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is DefaultStrides) return false + if (!shape.contentEquals(other.shape)) return false + return true + } + + override fun hashCode(): Int = shape.contentHashCode() + + @ThreadLocal + public companion object { + //private val defaultStridesCache = HashMap() + + /** + * Cached builder for default strides + */ + public operator fun invoke(shape: IntArray): Strides = DefaultStrides(shape) + //defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } + + //TODO fix cache + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt index b56bef230..65c1f71b4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt @@ -6,34 +6,27 @@ package space.kscience.kmath.nd import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.operations.NumbersAddOperations +import space.kscience.kmath.operations.NumbersAddOps import space.kscience.kmath.operations.ShortRing -import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.ShortBuffer +import space.kscience.kmath.operations.bufferAlgebra import kotlin.contracts.InvocationKind import kotlin.contracts.contract +public sealed class ShortRingOpsND : BufferedRingOpsND(ShortRing.bufferAlgebra) { + public companion object : ShortRingOpsND() +} + @OptIn(UnstableKMathAPI::class) public class ShortRingND( - shape: IntArray, -) : BufferedRingND(shape, ShortRing, Buffer.Companion::auto), - NumbersAddOperations> { - - override val zero: BufferND by lazy { produce { zero } } - override val one: BufferND by lazy { produce { one } } + override val shape: Shape +) : ShortRingOpsND(), RingND, NumbersAddOps> { override fun number(value: Number): BufferND { val d = value.toShort() // minimize conversions - return produce { d } + return produce(shape) { d } } } -/** - * Fast element production using function inlining. - */ -public inline fun BufferedRingND.produceInline(crossinline initializer: ShortRing.(Int) -> Short): BufferND = - BufferND(strides, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) })) - public inline fun ShortRing.withNdAlgebra(vararg shape: Int, action: ShortRingND.() -> R): R { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } return ShortRingND(shape).run(action) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt index 6123336ba..611d2724f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt @@ -15,7 +15,6 @@ import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory import kotlin.jvm.JvmName import kotlin.math.abs -import kotlin.native.concurrent.ThreadLocal import kotlin.reflect.KClass public interface StructureFeature : Feature @@ -72,7 +71,7 @@ public interface StructureND : Featured { if (st1 === st2) return true // fast comparison of buffers if possible - if (st1 is BufferND && st2 is BufferND && st1.strides == st2.strides) + if (st1 is BufferND && st2 is BufferND && st1.indexes == st2.indexes) return Buffer.contentEquals(st1.buffer, st2.buffer) //element by element comparison if it could not be avoided @@ -88,7 +87,7 @@ public interface StructureND : Featured { if (st1 === st2) return true // fast comparison of buffers if possible - if (st1 is BufferND && st2 is BufferND && st1.strides == st2.strides) + if (st1 is BufferND && st2 is BufferND && st1.indexes == st2.indexes) return Buffer.contentEquals(st1.buffer, st2.buffer) //element by element comparison if it could not be avoided @@ -187,11 +186,11 @@ public fun > LinearSpace>.contentEquals( * Indicates whether some [StructureND] is equal to another one with [absoluteTolerance]. */ @PerformancePitfall -public fun > GroupND>.contentEquals( +public fun > GroupOpsND>.contentEquals( st1: StructureND, st2: StructureND, absoluteTolerance: T, -): Boolean = st1.elements().all { (index, value) -> elementContext { (value - st2[index]) } < absoluteTolerance } +): Boolean = st1.elements().all { (index, value) -> elementAlgebra { (value - st2[index]) } < absoluteTolerance } /** * Indicates whether some [StructureND] is equal to another one with [absoluteTolerance]. @@ -231,107 +230,10 @@ public interface MutableStructureND : StructureND { * Transform a structure element-by element in place. */ @OptIn(PerformancePitfall::class) -public inline fun MutableStructureND.mapInPlace(action: (IntArray, T) -> T): Unit = +public inline fun MutableStructureND.mapInPlace(action: (index: IntArray, t: T) -> T): Unit = elements().forEach { (index, oldValue) -> this[index] = action(index, oldValue) } -/** - * A way to convert ND indices to linear one and back. - */ -public interface Strides { - /** - * Shape of NDStructure - */ - public val shape: IntArray - - /** - * Array strides - */ - public val strides: IntArray - - /** - * Get linear index from multidimensional index - */ - public 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]})") - value * strides[i] - }.sum() - - /** - * Get multidimensional from linear - */ - public fun index(offset: Int): IntArray - - /** - * The size of linear buffer to accommodate all elements of ND-structure corresponding to strides - */ - public val linearSize: Int - - // TODO introduce a fast way to calculate index of the next element? - - /** - * Iterate over ND indices in a natural order - */ - public fun indices(): Sequence = (0 until linearSize).asSequence().map(::index) -} - -/** - * Simple implementation of [Strides]. - */ -public class DefaultStrides private constructor(override val shape: IntArray) : Strides { - override val linearSize: Int - get() = strides[shape.size] - - /** - * Strides for memory access - */ - override val strides: IntArray by lazy { - sequence { - var current = 1 - yield(1) - - shape.forEach { - current *= it - yield(current) - } - }.toList().toIntArray() - } - - override fun index(offset: Int): IntArray { - val res = IntArray(shape.size) - var current = offset - var strideIndex = strides.size - 2 - - while (strideIndex >= 0) { - res[strideIndex] = (current / strides[strideIndex]) - current %= strides[strideIndex] - strideIndex-- - } - - return res - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is DefaultStrides) return false - if (!shape.contentEquals(other.shape)) return false - return true - } - - override fun hashCode(): Int = shape.contentHashCode() - - @ThreadLocal - public companion object { - private val defaultStridesCache = HashMap() - - /** - * Cached builder for default strides - */ - public operator fun invoke(shape: IntArray): Strides = - defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } - } -} - -public inline fun StructureND.combine( +public inline fun StructureND.zip( struct: StructureND, crossinline block: (T, T) -> T, ): StructureND { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/algebraNDExtentions.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/algebraNDExtentions.kt new file mode 100644 index 000000000..7bc18a4dd --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/algebraNDExtentions.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package space.kscience.kmath.nd + +import space.kscience.kmath.operations.Algebra +import space.kscience.kmath.operations.Group +import space.kscience.kmath.operations.Ring +import kotlin.jvm.JvmName + + +public fun > AlgebraND.produce( + shapeFirst: Int, + vararg shapeRest: Int, + initializer: A.(IntArray) -> T +): StructureND = produce(Shape(shapeFirst, *shapeRest), initializer) + +public fun > AlgebraND.zero(shape: Shape): StructureND = produce(shape) { zero } + +@JvmName("zeroVarArg") +public fun > AlgebraND.zero( + shapeFirst: Int, + vararg shapeRest: Int, +): StructureND = produce(shapeFirst, *shapeRest) { zero } + +public fun > AlgebraND.one(shape: Shape): StructureND = produce(shape) { one } + +@JvmName("oneVarArg") +public fun > AlgebraND.one( + shapeFirst: Int, + vararg shapeRest: Int, +): StructureND = produce(shapeFirst, *shapeRest) { one } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt index daff58d9a..d731ee4ee 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt @@ -117,7 +117,7 @@ public inline operator fun , R> A.invoke(block: A.() -> R): R = r * * @param T the type of element of this semispace. */ -public interface GroupOperations : Algebra { +public interface GroupOps : Algebra { /** * Addition of two elements. * @@ -162,7 +162,7 @@ public interface GroupOperations : Algebra { * @return the difference. */ public operator fun T.minus(b: T): T = add(this, -b) - + // Dynamic dispatch of operations override fun unaryOperationFunction(operation: String): (arg: T) -> T = when (operation) { PLUS_OPERATION -> { arg -> +arg } MINUS_OPERATION -> { arg -> -arg } @@ -193,7 +193,7 @@ public interface GroupOperations : Algebra { * * @param T the type of element of this semispace. */ -public interface Group : GroupOperations { +public interface Group : GroupOps { /** * The neutral element of addition. */ @@ -206,7 +206,7 @@ public interface Group : GroupOperations { * * @param T the type of element of this semiring. */ -public interface RingOperations : GroupOperations { +public interface RingOps : GroupOps { /** * Multiplies two elements. * @@ -242,7 +242,7 @@ public interface RingOperations : GroupOperations { * * @param T the type of element of this ring. */ -public interface Ring : Group, RingOperations { +public interface Ring : Group, RingOps { /** * The neutral element of multiplication */ @@ -256,7 +256,7 @@ public interface Ring : Group, RingOperations { * * @param T the type of element of this semifield. */ -public interface FieldOperations : RingOperations { +public interface FieldOps : RingOps { /** * Division of two elements. * @@ -295,6 +295,6 @@ public interface FieldOperations : RingOperations { * * @param T the type of element of this field. */ -public interface Field : Ring, FieldOperations, ScaleOperations, NumericAlgebra { +public interface Field : Ring, FieldOps, ScaleOperations, NumericAlgebra { override fun number(value: Number): T = scale(one, value.toDouble()) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt index 82754e43d..97e35e4b0 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.operations import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.nd.BufferedRingND +import space.kscience.kmath.nd.BufferedRingOpsND import space.kscience.kmath.operations.BigInt.Companion.BASE import space.kscience.kmath.operations.BigInt.Companion.BASE_SIZE import space.kscience.kmath.structures.Buffer @@ -26,7 +26,7 @@ private typealias TBase = ULong * @author Peter Klimai */ @OptIn(UnstableKMathAPI::class) -public object BigIntField : Field, NumbersAddOperations, ScaleOperations { +public object BigIntField : Field, NumbersAddOps, ScaleOperations { override val zero: BigInt = BigInt.ZERO override val one: BigInt = BigInt.ONE @@ -542,5 +542,5 @@ public inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) - public inline fun BigInt.mutableBuffer(size: Int, initializer: (Int) -> BigInt): Buffer = Buffer.boxing(size, initializer) -public fun BigIntField.nd(vararg shape: Int): BufferedRingND = - BufferedRingND(shape, BigIntField, BigInt::buffer) +public val BigIntField.nd: BufferedRingOpsND + get() = BufferedRingOpsND(BufferRingOps(BigIntField, BigInt::buffer)) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt index e82b62c1b..5a14e7734 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt @@ -5,32 +5,34 @@ package space.kscience.kmath.operations -import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.ShortBuffer + +public interface WithSize { + public val size: Int +} /** * An algebra over [Buffer] */ -@UnstableKMathAPI -public interface BufferAlgebra> : Algebra> { - public val bufferFactory: BufferFactory +public interface BufferAlgebra> : Algebra> { public val elementAlgebra: A - public val size: Int + public val bufferFactory: BufferFactory - public fun buffer(vararg elements: T): Buffer { + public fun buffer(size: Int, vararg elements: T): Buffer { require(elements.size == size) { "Expected $size elements but found ${elements.size}" } return bufferFactory(size) { elements[it] } } //TODO move to multi-receiver inline extension - public fun Buffer.map(block: (T) -> T): Buffer = bufferFactory(size) { block(get(it)) } + public fun Buffer.map(block: A.(T) -> T): Buffer = mapInline(this, block) - public fun Buffer.zip(other: Buffer, block: (left: T, right: T) -> T): Buffer { - require(size == other.size) { "Incompatible buffer sizes. left: $size, right: ${other.size}" } - return bufferFactory(size) { block(this[it], other[it]) } - } + public fun Buffer.mapIndexed(block: A.(index: Int, arg: T) -> T): Buffer = mapIndexedInline(this, block) + + public fun Buffer.zip(other: Buffer, block: A.(left: T, right: T) -> T): Buffer = + zipInline(this, other, block) override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer { val operationFunction = elementAlgebra.unaryOperationFunction(operation) @@ -45,112 +47,149 @@ public interface BufferAlgebra> : Algebra> { } } -@UnstableKMathAPI -public fun BufferField.buffer(initializer: (Int) -> T): Buffer { +/** + * Inline map + */ +public inline fun > BufferAlgebra.mapInline( + buffer: Buffer, + crossinline block: A.(T) -> T +): Buffer = bufferFactory(buffer.size) { elementAlgebra.block(buffer[it]) } + +/** + * Inline map + */ +public inline fun > BufferAlgebra.mapIndexedInline( + buffer: Buffer, + crossinline block: A.(index: Int, arg: T) -> T +): Buffer = bufferFactory(buffer.size) { elementAlgebra.block(it, buffer[it]) } + +/** + * Inline zip + */ +public inline fun > BufferAlgebra.zipInline( + l: Buffer, + r: Buffer, + crossinline block: A.(l: T, r: T) -> T +): Buffer { + require(l.size == r.size) { "Incompatible buffer sizes. left: ${l.size}, right: ${r.size}" } + return bufferFactory(l.size) { elementAlgebra.block(l[it], r[it]) } +} + +public fun BufferAlgebra.buffer(size: Int, initializer: (Int) -> T): Buffer { + return bufferFactory(size, initializer) +} + +public fun A.buffer(initializer: (Int) -> T): Buffer where A : BufferAlgebra, A : WithSize { return bufferFactory(size, initializer) } -@UnstableKMathAPI public fun > BufferAlgebra.sin(arg: Buffer): Buffer = - arg.map(elementAlgebra::sin) + mapInline(arg) { sin(it) } -@UnstableKMathAPI public fun > BufferAlgebra.cos(arg: Buffer): Buffer = - arg.map(elementAlgebra::cos) + mapInline(arg) { cos(it) } -@UnstableKMathAPI public fun > BufferAlgebra.tan(arg: Buffer): Buffer = - arg.map(elementAlgebra::tan) + mapInline(arg) { tan(it) } -@UnstableKMathAPI public fun > BufferAlgebra.asin(arg: Buffer): Buffer = - arg.map(elementAlgebra::asin) + mapInline(arg) { asin(it) } -@UnstableKMathAPI public fun > BufferAlgebra.acos(arg: Buffer): Buffer = - arg.map(elementAlgebra::acos) + mapInline(arg) { acos(it) } -@UnstableKMathAPI public fun > BufferAlgebra.atan(arg: Buffer): Buffer = - arg.map(elementAlgebra::atan) + mapInline(arg) { atan(it) } -@UnstableKMathAPI public fun > BufferAlgebra.exp(arg: Buffer): Buffer = - arg.map(elementAlgebra::exp) + mapInline(arg) { exp(it) } -@UnstableKMathAPI public fun > BufferAlgebra.ln(arg: Buffer): Buffer = - arg.map(elementAlgebra::ln) + mapInline(arg) { ln(it) } -@UnstableKMathAPI public fun > BufferAlgebra.sinh(arg: Buffer): Buffer = - arg.map(elementAlgebra::sinh) + mapInline(arg) { sinh(it) } -@UnstableKMathAPI public fun > BufferAlgebra.cosh(arg: Buffer): Buffer = - arg.map(elementAlgebra::cosh) + mapInline(arg) { cosh(it) } -@UnstableKMathAPI public fun > BufferAlgebra.tanh(arg: Buffer): Buffer = - arg.map(elementAlgebra::tanh) + mapInline(arg) { tanh(it) } -@UnstableKMathAPI public fun > BufferAlgebra.asinh(arg: Buffer): Buffer = - arg.map(elementAlgebra::asinh) + mapInline(arg) { asinh(it) } -@UnstableKMathAPI public fun > BufferAlgebra.acosh(arg: Buffer): Buffer = - arg.map(elementAlgebra::acosh) + mapInline(arg) { acosh(it) } -@UnstableKMathAPI public fun > BufferAlgebra.atanh(arg: Buffer): Buffer = - arg.map(elementAlgebra::atanh) + mapInline(arg) { atanh(it) } -@UnstableKMathAPI public fun > BufferAlgebra.pow(arg: Buffer, pow: Number): Buffer = - with(elementAlgebra) { arg.map { power(it, pow) } } + mapInline(arg) { power(it, pow) } -@UnstableKMathAPI -public class BufferField>( - override val bufferFactory: BufferFactory, +public open class BufferRingOps>( override val elementAlgebra: A, + override val bufferFactory: BufferFactory, +) : BufferAlgebra, RingOps>{ + + override fun add(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l + r } + override fun multiply(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l * r } + override fun Buffer.unaryMinus(): Buffer = map { -it } + + override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer = + super.unaryOperationFunction(operation) + + override fun binaryOperationFunction(operation: String): (left: Buffer, right: Buffer) -> Buffer = + super.binaryOperationFunction(operation) +} + +public val ShortRing.bufferAlgebra: BufferRingOps + get() = BufferRingOps(ShortRing, ::ShortBuffer) + +public open class BufferFieldOps>( + elementAlgebra: A, + bufferFactory: BufferFactory, +) : BufferRingOps(elementAlgebra, bufferFactory), BufferAlgebra, FieldOps>, ScaleOperations> { + + override fun add(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l + r } + override fun multiply(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l * r } + override fun divide(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l / r } + + override fun scale(a: Buffer, value: Double): Buffer = a.map { scale(it, value) } + override fun Buffer.unaryMinus(): Buffer = map { -it } + + override fun binaryOperationFunction(operation: String): (left: Buffer, right: Buffer) -> Buffer = + super.binaryOperationFunction(operation) +} + +public class BufferField>( + elementAlgebra: A, + bufferFactory: BufferFactory, override val size: Int -) : BufferAlgebra, Field> { +) : BufferFieldOps(elementAlgebra, bufferFactory), Field>, WithSize { override val zero: Buffer = bufferFactory(size) { elementAlgebra.zero } override val one: Buffer = bufferFactory(size) { elementAlgebra.one } - - - override fun add(a: Buffer, b: Buffer): Buffer = a.zip(b, elementAlgebra::add) - override fun multiply(a: Buffer, b: Buffer): Buffer = a.zip(b, elementAlgebra::multiply) - override fun divide(a: Buffer, b: Buffer): Buffer = a.zip(b, elementAlgebra::divide) - - override fun scale(a: Buffer, value: Double): Buffer = with(elementAlgebra) { a.map { scale(it, value) } } - override fun Buffer.unaryMinus(): Buffer = with(elementAlgebra) { map { -it } } - - override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer { - return super.unaryOperationFunction(operation) - } - - override fun binaryOperationFunction(operation: String): (left: Buffer, right: Buffer) -> Buffer { - return super.binaryOperationFunction(operation) - } } +/** + * Generate full buffer field from given buffer operations + */ +public fun > BufferFieldOps.withSize(size: Int): BufferField = + BufferField(elementAlgebra, bufferFactory, size) + //Double buffer specialization -@UnstableKMathAPI public fun BufferField.buffer(vararg elements: Number): Buffer { require(elements.size == size) { "Expected $size elements but found ${elements.size}" } return bufferFactory(size) { elements[it].toDouble() } } -@UnstableKMathAPI -public fun > A.bufferAlgebra(bufferFactory: BufferFactory, size: Int): BufferField = - BufferField(bufferFactory, this, size) +public fun > A.bufferAlgebra(bufferFactory: BufferFactory): BufferFieldOps = + BufferFieldOps(this, bufferFactory) -@UnstableKMathAPI -public fun DoubleField.bufferAlgebra(size: Int): BufferField = - BufferField(::DoubleBuffer, DoubleField, size) +public val DoubleField.bufferAlgebra: BufferFieldOps + get() = BufferFieldOps(DoubleField, ::DoubleBuffer) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferField.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferField.kt index acc2c2dc0..060ea5a7e 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferField.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferField.kt @@ -13,21 +13,21 @@ import space.kscience.kmath.structures.DoubleBuffer * * @property size the size of buffers to operate on. */ -public class DoubleBufferField(public val size: Int) : ExtendedField>, DoubleBufferOperations() { +public class DoubleBufferField(public val size: Int) : ExtendedField>, DoubleBufferOps() { override val zero: Buffer by lazy { DoubleBuffer(size) { 0.0 } } override val one: Buffer by lazy { DoubleBuffer(size) { 1.0 } } - override fun sinh(arg: Buffer): DoubleBuffer = super.sinh(arg) + override fun sinh(arg: Buffer): DoubleBuffer = super.sinh(arg) - override fun cosh(arg: Buffer): DoubleBuffer = super.cosh(arg) + override fun cosh(arg: Buffer): DoubleBuffer = super.cosh(arg) - override fun tanh(arg: Buffer): DoubleBuffer = super.tanh(arg) + override fun tanh(arg: Buffer): DoubleBuffer = super.tanh(arg) - override fun asinh(arg: Buffer): DoubleBuffer = super.asinh(arg) + override fun asinh(arg: Buffer): DoubleBuffer = super.asinh(arg) - override fun acosh(arg: Buffer): DoubleBuffer = super.acosh(arg) + override fun acosh(arg: Buffer): DoubleBuffer = super.acosh(arg) - override fun atanh(arg: Buffer): DoubleBuffer= super.atanh(arg) + override fun atanh(arg: Buffer): DoubleBuffer= super.atanh(arg) // override fun number(value: Number): Buffer = DoubleBuffer(size) { value.toDouble() } // diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOperations.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt similarity index 97% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOperations.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt index 50b821962..2580cd6f8 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOperations.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt @@ -12,9 +12,9 @@ import space.kscience.kmath.structures.DoubleBuffer import kotlin.math.* /** - * [ExtendedFieldOperations] over [DoubleBuffer]. + * [ExtendedFieldOps] over [DoubleBuffer]. */ -public abstract class DoubleBufferOperations : ExtendedFieldOperations>, Norm, Double> { +public abstract class DoubleBufferOps : ExtendedFieldOps>, Norm, Double> { override fun Buffer.unaryMinus(): DoubleBuffer = if (this is DoubleBuffer) { DoubleBuffer(size) { -array[it] } } else { @@ -185,7 +185,7 @@ public abstract class DoubleBufferOperations : ExtendedFieldOperations, Double> { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt index a3d8f5ffe..ca88ad42d 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt @@ -150,7 +150,7 @@ public interface ScaleOperations : Algebra { * TODO to be removed and replaced by extensions after multiple receivers are there */ @UnstableKMathAPI -public interface NumbersAddOperations : Ring, NumericAlgebra { +public interface NumbersAddOps : Ring, NumericAlgebra { /** * Addition of element and scalar. * diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt index 4c0010bf9..c1775296c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt @@ -10,8 +10,8 @@ import kotlin.math.pow as kpow /** * Advanced Number-like semifield that implements basic operations. */ -public interface ExtendedFieldOperations : - FieldOperations, +public interface ExtendedFieldOps : + FieldOps, TrigonometricOperations, PowerOperations, ExponentialOperations, @@ -35,14 +35,14 @@ public interface ExtendedFieldOperations : ExponentialOperations.ACOSH_OPERATION -> ::acosh ExponentialOperations.ASINH_OPERATION -> ::asinh ExponentialOperations.ATANH_OPERATION -> ::atanh - else -> super.unaryOperationFunction(operation) + else -> super.unaryOperationFunction(operation) } } /** * Advanced Number-like field that implements basic operations. */ -public interface ExtendedField : ExtendedFieldOperations, Field, NumericAlgebra{ +public interface ExtendedField : ExtendedFieldOps, Field, NumericAlgebra{ override fun sinh(arg: T): T = (exp(arg) - exp(-arg)) / 2.0 override fun cosh(arg: T): T = (exp(arg) + exp(-arg)) / 2.0 override fun tanh(arg: T): T = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt index 05a67ab09..2009eb64f 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.structures import space.kscience.kmath.nd.get import space.kscience.kmath.nd.ndAlgebra +import space.kscience.kmath.nd.produce import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.invoke import space.kscience.kmath.testutils.FieldVerifier @@ -21,7 +22,7 @@ internal class NDFieldTest { @Test fun testStrides() { - val ndArray = DoubleField.ndAlgebra(10, 10).produce { (it[0] + it[1]).toDouble() } + val ndArray = DoubleField.ndAlgebra.produce(10, 10) { (it[0] + it[1]).toDouble() } assertEquals(ndArray[5, 5], 10.0) } } diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt index e58976f4a..907301a53 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt @@ -7,10 +7,7 @@ package space.kscience.kmath.structures import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.misc.PerformancePitfall -import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.combine -import space.kscience.kmath.nd.get -import space.kscience.kmath.nd.ndAlgebra +import space.kscience.kmath.nd.* import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.Norm import space.kscience.kmath.operations.algebra @@ -22,9 +19,9 @@ import kotlin.test.assertEquals @Suppress("UNUSED_VARIABLE") class NumberNDFieldTest { - val algebra = DoubleField.ndAlgebra(3, 3) - val array1 = algebra.produce { (i, j) -> (i + j).toDouble() } - val array2 = algebra.produce { (i, j) -> (i - j).toDouble() } + val algebra = DoubleField.ndAlgebra + val array1 = algebra.produce(3, 3) { (i, j) -> (i + j).toDouble() } + val array2 = algebra.produce(3, 3) { (i, j) -> (i - j).toDouble() } @Test fun testSum() { @@ -77,7 +74,7 @@ class NumberNDFieldTest { @Test fun combineTest() { - val division = array1.combine(array2, Double::div) + val division = array1.zip(array2, Double::div) } object L2Norm : Norm, Double> { diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/flowExtra.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/flowExtra.kt index 1821daac9..1620f029c 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/flowExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/flowExtra.kt @@ -10,12 +10,12 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.runningReduce import kotlinx.coroutines.flow.scan -import space.kscience.kmath.operations.GroupOperations +import space.kscience.kmath.operations.GroupOps import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.invoke -public fun Flow.cumulativeSum(group: GroupOperations): Flow = +public fun Flow.cumulativeSum(group: GroupOps): Flow = group { runningReduce { sum, element -> sum + element } } @ExperimentalCoroutinesApi diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt index b2c3209c2..0edd51be2 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt @@ -13,8 +13,8 @@ import space.kscience.kmath.structures.DoubleBuffer * Map one [BufferND] using function without indices. */ public inline fun BufferND.mapInline(crossinline transform: DoubleField.(Double) -> Double): BufferND { - val array = DoubleArray(strides.linearSize) { offset -> DoubleField.transform(buffer[offset]) } - return BufferND(strides, DoubleBuffer(array)) + val array = DoubleArray(indexes.linearSize) { offset -> DoubleField.transform(buffer[offset]) } + return BufferND(indexes, DoubleBuffer(array)) } /** diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/DoubleHistogramSpace.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/DoubleHistogramSpace.kt index 5446f05f8..c452edc9c 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/DoubleHistogramSpace.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/DoubleHistogramSpace.kt @@ -28,10 +28,9 @@ public class DoubleHistogramSpace( public val dimension: Int get() = lower.size - private val shape = IntArray(binNums.size) { binNums[it] + 2 } + override val shape: IntArray = IntArray(binNums.size) { binNums[it] + 2 } override val histogramValueSpace: DoubleFieldND = DoubleField.ndAlgebra(*shape) - override val strides: Strides get() = histogramValueSpace.strides private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] } /** @@ -52,7 +51,7 @@ public class DoubleHistogramSpace( val lowerBoundary = index.mapIndexed { axis, i -> when (i) { 0 -> Double.NEGATIVE_INFINITY - strides.shape[axis] - 1 -> upper[axis] + shape[axis] - 1 -> upper[axis] else -> lower[axis] + (i.toDouble()) * binSize[axis] } }.asBuffer() @@ -60,7 +59,7 @@ public class DoubleHistogramSpace( val upperBoundary = index.mapIndexed { axis, i -> when (i) { 0 -> lower[axis] - strides.shape[axis] - 1 -> Double.POSITIVE_INFINITY + shape[axis] - 1 -> Double.POSITIVE_INFINITY else -> lower[axis] + (i.toDouble() + 1) * binSize[axis] } }.asBuffer() @@ -75,7 +74,7 @@ public class DoubleHistogramSpace( } override fun produce(builder: HistogramBuilder.() -> Unit): IndexedHistogram { - val ndCounter = StructureND.auto(strides) { Counter.real() } + val ndCounter = StructureND.auto(shape) { Counter.real() } val hBuilder = HistogramBuilder { point, value -> val index = getIndex(point) ndCounter[index].add(value.toDouble()) diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/IndexedHistogramSpace.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/IndexedHistogramSpace.kt index 44f3072d2..7c719ccd7 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/IndexedHistogramSpace.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/IndexedHistogramSpace.kt @@ -8,8 +8,9 @@ package space.kscience.kmath.histogram import space.kscience.kmath.domains.Domain import space.kscience.kmath.linear.Point import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.nd.DefaultStrides import space.kscience.kmath.nd.FieldND -import space.kscience.kmath.nd.Strides +import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.Group import space.kscience.kmath.operations.ScaleOperations @@ -34,10 +35,10 @@ public class IndexedHistogram, V : Any>( return context.produceBin(index, values[index]) } - override val dimension: Int get() = context.strides.shape.size + override val dimension: Int get() = context.shape.size override val bins: Iterable> - get() = context.strides.indices().map { + get() = DefaultStrides(context.shape).indices().map { context.produceBin(it, values[it]) }.asIterable() @@ -49,7 +50,7 @@ public class IndexedHistogram, V : Any>( public interface IndexedHistogramSpace, V : Any> : Group>, ScaleOperations> { //public val valueSpace: Space - public val strides: Strides + public val shape: Shape public val histogramValueSpace: FieldND //= NDAlgebra.space(valueSpace, Buffer.Companion::boxing, *shape), /** diff --git a/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/MultivariateHistogramTest.kt b/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/MultivariateHistogramTest.kt index c797eb65a..23dd076e1 100644 --- a/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/MultivariateHistogramTest.kt +++ b/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/MultivariateHistogramTest.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.histogram +import space.kscience.kmath.nd.DefaultStrides import space.kscience.kmath.operations.invoke import space.kscience.kmath.real.DoubleVector import kotlin.random.Random @@ -69,7 +70,7 @@ internal class MultivariateHistogramTest { } val res = histogram1 - histogram2 assertTrue { - strides.indices().all { index -> + DefaultStrides(shape).indices().all { index -> res.values[index] <= histogram1.values[index] } } diff --git a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/scalarsAdapters.kt b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/scalarsAdapters.kt index 857824275..11e5853a8 100644 --- a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/scalarsAdapters.kt +++ b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/scalarsAdapters.kt @@ -106,8 +106,8 @@ public fun > MST.toSFun(): SFun = when (this) { is Symbol -> toSVar() is MST.Unary -> when (operation) { - GroupOperations.PLUS_OPERATION -> +value.toSFun() - GroupOperations.MINUS_OPERATION -> -value.toSFun() + GroupOps.PLUS_OPERATION -> +value.toSFun() + GroupOps.MINUS_OPERATION -> -value.toSFun() TrigonometricOperations.SIN_OPERATION -> sin(value.toSFun()) TrigonometricOperations.COS_OPERATION -> cos(value.toSFun()) TrigonometricOperations.TAN_OPERATION -> tan(value.toSFun()) @@ -124,10 +124,10 @@ public fun > MST.toSFun(): SFun = when (this) { } is MST.Binary -> when (operation) { - GroupOperations.PLUS_OPERATION -> left.toSFun() + right.toSFun() - GroupOperations.MINUS_OPERATION -> left.toSFun() - right.toSFun() - RingOperations.TIMES_OPERATION -> left.toSFun() * right.toSFun() - FieldOperations.DIV_OPERATION -> left.toSFun() / right.toSFun() + GroupOps.PLUS_OPERATION -> left.toSFun() + right.toSFun() + GroupOps.MINUS_OPERATION -> left.toSFun() - right.toSFun() + RingOps.TIMES_OPERATION -> left.toSFun() * right.toSFun() + FieldOps.DIV_OPERATION -> left.toSFun() / right.toSFun() PowerOperations.POW_OPERATION -> left.toSFun() pow (right as MST.Numeric).toSConst() else -> error("Binary operation $operation not defined in $this") } diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt index b604bf5f2..d65ad0dc9 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt @@ -15,13 +15,6 @@ import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.* import space.kscience.kmath.operations.* -internal fun AlgebraND<*, *>.checkShape(array: INDArray): INDArray { - val arrayShape = array.shape().toIntArray() - if (!shape.contentEquals(arrayShape)) throw ShapeMismatchException(shape, arrayShape) - return array -} - - /** * Represents [AlgebraND] over [Nd4jArrayAlgebra]. * @@ -39,33 +32,34 @@ public sealed interface Nd4jArrayAlgebra> : AlgebraND.ndArray: INDArray - override fun produce(initializer: C.(IntArray) -> T): Nd4jArrayStructure { + override fun produce(shape: Shape, initializer: C.(IntArray) -> T): Nd4jArrayStructure { val struct = Nd4j.create(*shape)!!.wrap() - struct.indicesIterator().forEach { struct[it] = elementContext.initializer(it) } + struct.indicesIterator().forEach { struct[it] = elementAlgebra.initializer(it) } return struct } override fun StructureND.map(transform: C.(T) -> T): Nd4jArrayStructure { val newStruct = ndArray.dup().wrap() - newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } + newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementAlgebra.transform(value) } return newStruct } override fun StructureND.mapIndexed( transform: C.(index: IntArray, T) -> T, ): Nd4jArrayStructure { - val new = Nd4j.create(*this@Nd4jArrayAlgebra.shape).wrap() - new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(idx, this[idx]) } + val new = Nd4j.create(*shape).wrap() + new.indicesIterator().forEach { idx -> new[idx] = elementAlgebra.transform(idx, this[idx]) } return new } - override fun combine( - a: StructureND, - b: StructureND, + override fun zip( + left: StructureND, + right: StructureND, transform: C.(T, T) -> T, ): Nd4jArrayStructure { - val new = Nd4j.create(*shape).wrap() - new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(a[idx], b[idx]) } + require(left.shape.contentEquals(right.shape)) { "Can't zip tow structures of shape ${left.shape} and ${right.shape}" } + val new = Nd4j.create(*left.shape).wrap() + new.indicesIterator().forEach { idx -> new[idx] = elementAlgebra.transform(left[idx], right[idx]) } return new } } @@ -76,10 +70,7 @@ public sealed interface Nd4jArrayAlgebra> : AlgebraND> : GroupND, Nd4jArrayAlgebra { - - override val zero: Nd4jArrayStructure - get() = Nd4j.zeros(*shape).wrap() +public sealed interface Nd4jArrayGroupOps> : GroupOpsND, Nd4jArrayAlgebra { override fun add(a: StructureND, b: StructureND): Nd4jArrayStructure = a.ndArray.add(b.ndArray).wrap() @@ -101,10 +92,7 @@ public sealed interface Nd4jArrayGroup> : GroupND, Nd4j * @param R the type of ring of structure elements. */ @OptIn(UnstableKMathAPI::class) -public sealed interface Nd4jArrayRing> : RingND, Nd4jArrayGroup { - - override val one: Nd4jArrayStructure - get() = Nd4j.ones(*shape).wrap() +public sealed interface Nd4jArrayRingOps> : RingOpsND, Nd4jArrayGroupOps { override fun multiply(a: StructureND, b: StructureND): Nd4jArrayStructure = a.ndArray.mul(b.ndArray).wrap() @@ -125,21 +113,12 @@ public sealed interface Nd4jArrayRing> : RingND, Nd4jAr // } public companion object { - private val intNd4jArrayRingCache: ThreadLocal> = - ThreadLocal.withInitial(::HashMap) - - /** - * Creates an [RingND] for [Int] values or pull it from cache if it was created previously. - */ - public fun int(vararg shape: Int): Nd4jArrayRing = - intNd4jArrayRingCache.get().getOrPut(shape) { IntNd4jArrayRing(shape) } - /** * Creates a most suitable implementation of [RingND] using reified class. */ @Suppress("UNCHECKED_CAST") - public inline fun auto(vararg shape: Int): Nd4jArrayRing> = when { - T::class == Int::class -> int(*shape) as Nd4jArrayRing> + public inline fun auto(vararg shape: Int): Nd4jArrayRingOps> = when { + T::class == Int::class -> IntRing.nd4j as Nd4jArrayRingOps> else -> throw UnsupportedOperationException("This factory method only supports Long type.") } } @@ -151,38 +130,21 @@ public sealed interface Nd4jArrayRing> : RingND, Nd4jAr * @param T the type of the element contained in ND structure. * @param F the type field of structure elements. */ -public sealed interface Nd4jArrayField> : FieldND, Nd4jArrayRing { +public sealed interface Nd4jArrayField> : FieldOpsND, Nd4jArrayRingOps { + override fun divide(a: StructureND, b: StructureND): Nd4jArrayStructure = a.ndArray.div(b.ndArray).wrap() public operator fun Number.div(b: StructureND): Nd4jArrayStructure = b.ndArray.rdiv(this).wrap() public companion object { - private val floatNd4jArrayFieldCache: ThreadLocal> = - ThreadLocal.withInitial(::HashMap) - - private val doubleNd4JArrayFieldCache: ThreadLocal> = - ThreadLocal.withInitial(::HashMap) - - /** - * Creates an [FieldND] for [Float] values or pull it from cache if it was created previously. - */ - public fun float(vararg shape: Int): Nd4jArrayRing = - floatNd4jArrayFieldCache.get().getOrPut(shape) { FloatNd4jArrayField(shape) } - - /** - * Creates an [FieldND] for [Double] values or pull it from cache if it was created previously. - */ - public fun real(vararg shape: Int): Nd4jArrayRing = - doubleNd4JArrayFieldCache.get().getOrPut(shape) { DoubleNd4jArrayField(shape) } - /** * Creates a most suitable implementation of [FieldND] using reified class. */ @Suppress("UNCHECKED_CAST") public inline fun auto(vararg shape: Int): Nd4jArrayField> = when { - T::class == Float::class -> float(*shape) as Nd4jArrayField> - T::class == Double::class -> real(*shape) as Nd4jArrayField> + T::class == Float::class -> FloatField.nd4j as Nd4jArrayField> + T::class == Double::class -> DoubleField.nd4j as Nd4jArrayField> else -> throw UnsupportedOperationException("This factory method only supports Float and Double types.") } } @@ -191,8 +153,9 @@ public sealed interface Nd4jArrayField> : FieldND, Nd4 /** * Represents intersection of [ExtendedField] and [Field] over [Nd4jArrayStructure]. */ -public sealed interface Nd4jArrayExtendedField> : ExtendedField>, - Nd4jArrayField { +public sealed interface Nd4jArrayExtendedFieldOps> : + ExtendedFieldOps>, Nd4jArrayField { + override fun sin(arg: StructureND): StructureND = Transforms.sin(arg.ndArray).wrap() override fun cos(arg: StructureND): StructureND = Transforms.cos(arg.ndArray).wrap() override fun asin(arg: StructureND): StructureND = Transforms.asin(arg.ndArray).wrap() @@ -221,63 +184,59 @@ public sealed interface Nd4jArrayExtendedField> : Ex /** * Represents [FieldND] over [Nd4jArrayDoubleStructure]. */ -public class DoubleNd4jArrayField(override val shape: IntArray) : Nd4jArrayExtendedField { - override val elementContext: DoubleField get() = DoubleField +public open class DoubleNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps { + override val elementAlgebra: DoubleField get() = DoubleField - override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(this).asDoubleStructure() + override fun INDArray.wrap(): Nd4jArrayStructure = asDoubleStructure() @OptIn(PerformancePitfall::class) override val StructureND.ndArray: INDArray get() = when (this) { - is Nd4jArrayStructure -> checkShape(ndArray) + is Nd4jArrayStructure -> ndArray else -> Nd4j.zeros(*shape).also { elements().forEach { (idx, value) -> it.putScalar(idx, value) } } } - override fun scale(a: StructureND, value: Double): Nd4jArrayStructure { - return a.ndArray.mul(value).wrap() - } + override fun scale(a: StructureND, value: Double): Nd4jArrayStructure = a.ndArray.mul(value).wrap() - override operator fun StructureND.div(arg: Double): Nd4jArrayStructure { - return ndArray.div(arg).wrap() - } + override operator fun StructureND.div(arg: Double): Nd4jArrayStructure = ndArray.div(arg).wrap() - override operator fun StructureND.plus(arg: Double): Nd4jArrayStructure { - return ndArray.add(arg).wrap() - } + override operator fun StructureND.plus(arg: Double): Nd4jArrayStructure = ndArray.add(arg).wrap() - override operator fun StructureND.minus(arg: Double): Nd4jArrayStructure { - return ndArray.sub(arg).wrap() - } + override operator fun StructureND.minus(arg: Double): Nd4jArrayStructure = ndArray.sub(arg).wrap() - override operator fun StructureND.times(arg: Double): Nd4jArrayStructure { - return ndArray.mul(arg).wrap() - } + override operator fun StructureND.times(arg: Double): Nd4jArrayStructure = ndArray.mul(arg).wrap() - override operator fun Double.div(arg: StructureND): Nd4jArrayStructure { - return arg.ndArray.rdiv(this).wrap() - } + override operator fun Double.div(arg: StructureND): Nd4jArrayStructure = + arg.ndArray.rdiv(this).wrap() - override operator fun Double.minus(arg: StructureND): Nd4jArrayStructure { - return arg.ndArray.rsub(this).wrap() - } + override operator fun Double.minus(arg: StructureND): Nd4jArrayStructure = + arg.ndArray.rsub(this).wrap() + + public companion object : DoubleNd4jArrayFieldOps() } -public fun DoubleField.nd4j(vararg shape: Int): DoubleNd4jArrayField = DoubleNd4jArrayField(intArrayOf(*shape)) +public val DoubleField.nd4j: DoubleNd4jArrayFieldOps get() = DoubleNd4jArrayFieldOps + +public class DoubleNd4jArrayField(override val shape: Shape) : DoubleNd4jArrayFieldOps(), FieldND + +public fun DoubleField.nd4j(shapeFirst: Int, vararg shapeRest: Int): DoubleNd4jArrayField = + DoubleNd4jArrayField(intArrayOf(shapeFirst, * shapeRest)) + /** * Represents [FieldND] over [Nd4jArrayStructure] of [Float]. */ -public class FloatNd4jArrayField(override val shape: IntArray) : Nd4jArrayExtendedField { - override val elementContext: FloatField get() = FloatField +public open class FloatNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps { + override val elementAlgebra: FloatField get() = FloatField - override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(this).asFloatStructure() + override fun INDArray.wrap(): Nd4jArrayStructure = asFloatStructure() @OptIn(PerformancePitfall::class) override val StructureND.ndArray: INDArray get() = when (this) { - is Nd4jArrayStructure -> checkShape(ndArray) + is Nd4jArrayStructure -> ndArray else -> Nd4j.zeros(*shape).also { elements().forEach { (idx, value) -> it.putScalar(idx, value) } } @@ -303,21 +262,29 @@ public class FloatNd4jArrayField(override val shape: IntArray) : Nd4jArrayExtend override operator fun Float.minus(arg: StructureND): Nd4jArrayStructure = arg.ndArray.rsub(this).wrap() + + public companion object : FloatNd4jArrayFieldOps() } +public class FloatNd4jArrayField(override val shape: Shape) : FloatNd4jArrayFieldOps(), RingND + +public val FloatField.nd4j: FloatNd4jArrayFieldOps get() = FloatNd4jArrayFieldOps + +public fun FloatField.nd4j(shapeFirst: Int, vararg shapeRest: Int): FloatNd4jArrayField = + FloatNd4jArrayField(intArrayOf(shapeFirst, * shapeRest)) + /** * Represents [RingND] over [Nd4jArrayIntStructure]. */ -public class IntNd4jArrayRing(override val shape: IntArray) : Nd4jArrayRing { - override val elementContext: IntRing - get() = IntRing +public open class IntNd4jArrayRingOps : Nd4jArrayRingOps { + override val elementAlgebra: IntRing get() = IntRing - override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(this).asIntStructure() + override fun INDArray.wrap(): Nd4jArrayStructure = asIntStructure() @OptIn(PerformancePitfall::class) override val StructureND.ndArray: INDArray get() = when (this) { - is Nd4jArrayStructure -> checkShape(ndArray) + is Nd4jArrayStructure -> ndArray else -> Nd4j.zeros(*shape).also { elements().forEach { (idx, value) -> it.putScalar(idx, value) } } @@ -334,4 +301,13 @@ public class IntNd4jArrayRing(override val shape: IntArray) : Nd4jArrayRing): Nd4jArrayStructure = arg.ndArray.rsub(this).wrap() + + public companion object : IntNd4jArrayRingOps() } + +public val IntRing.nd4j: IntNd4jArrayRingOps get() = IntNd4jArrayRingOps + +public class IntNd4jArrayRing(override val shape: Shape) : IntNd4jArrayRingOps(), RingND + +public fun IntRing.nd4j(shapeFirst: Int, vararg shapeRest: Int): IntNd4jArrayRing = + IntNd4jArrayRing(intArrayOf(shapeFirst, * shapeRest)) \ No newline at end of file diff --git a/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt b/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt index a03a7269e..465937fa9 100644 --- a/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt +++ b/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt @@ -8,6 +8,10 @@ package space.kscience.kmath.nd4j import org.nd4j.linalg.factory.Nd4j import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.nd.one +import space.kscience.kmath.nd.produce +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.IntRing import space.kscience.kmath.operations.invoke import kotlin.math.PI import kotlin.test.Test @@ -19,7 +23,7 @@ import kotlin.test.fail internal class Nd4jArrayAlgebraTest { @Test fun testProduce() { - val res = with(DoubleNd4jArrayField(intArrayOf(2, 2))) { produce { it.sum().toDouble() } } + val res = DoubleField.nd4j.produce(2, 2) { it.sum().toDouble() } val expected = (Nd4j.create(2, 2) ?: fail()).asDoubleStructure() expected[intArrayOf(0, 0)] = 0.0 expected[intArrayOf(0, 1)] = 1.0 @@ -30,7 +34,9 @@ internal class Nd4jArrayAlgebraTest { @Test fun testMap() { - val res = with(IntNd4jArrayRing(intArrayOf(2, 2))) { one.map { it + it * 2 } } + val res = IntRing.nd4j { + one(2, 2).map { it + it * 2 } + } val expected = (Nd4j.create(2, 2) ?: fail()).asIntStructure() expected[intArrayOf(0, 0)] = 3 expected[intArrayOf(0, 1)] = 3 @@ -41,7 +47,7 @@ internal class Nd4jArrayAlgebraTest { @Test fun testAdd() { - val res = with(IntNd4jArrayRing(intArrayOf(2, 2))) { one + 25 } + val res = IntRing.nd4j { one(2, 2) + 25 } val expected = (Nd4j.create(2, 2) ?: fail()).asIntStructure() expected[intArrayOf(0, 0)] = 26 expected[intArrayOf(0, 1)] = 26 @@ -51,10 +57,10 @@ internal class Nd4jArrayAlgebraTest { } @Test - fun testSin() = DoubleNd4jArrayField(intArrayOf(2, 2)).invoke { - val initial = produce { (i, j) -> if (i == j) PI / 2 else 0.0 } + fun testSin() = DoubleField.nd4j{ + val initial = produce(2, 2) { (i, j) -> if (i == j) PI / 2 else 0.0 } val transformed = sin(initial) - val expected = produce { (i, j) -> if (i == j) 1.0 else 0.0 } + val expected = produce(2, 2) { (i, j) -> if (i == j) 1.0 else 0.0 } println(transformed) assertTrue { StructureND.contentEquals(transformed, expected) } diff --git a/kmath-symja/src/main/kotlin/space/kscience/kmath/symja/adapters.kt b/kmath-symja/src/main/kotlin/space/kscience/kmath/symja/adapters.kt index 8def1ae83..a7ca298a9 100644 --- a/kmath-symja/src/main/kotlin/space/kscience/kmath/symja/adapters.kt +++ b/kmath-symja/src/main/kotlin/space/kscience/kmath/symja/adapters.kt @@ -64,8 +64,8 @@ public fun MST.toIExpr(): IExpr = when (this) { } is MST.Unary -> when (operation) { - GroupOperations.PLUS_OPERATION -> value.toIExpr() - GroupOperations.MINUS_OPERATION -> F.Negate(value.toIExpr()) + GroupOps.PLUS_OPERATION -> value.toIExpr() + GroupOps.MINUS_OPERATION -> F.Negate(value.toIExpr()) TrigonometricOperations.SIN_OPERATION -> F.Sin(value.toIExpr()) TrigonometricOperations.COS_OPERATION -> F.Cos(value.toIExpr()) TrigonometricOperations.TAN_OPERATION -> F.Tan(value.toIExpr()) @@ -85,10 +85,10 @@ public fun MST.toIExpr(): IExpr = when (this) { } is MST.Binary -> when (operation) { - GroupOperations.PLUS_OPERATION -> left.toIExpr() + right.toIExpr() - GroupOperations.MINUS_OPERATION -> left.toIExpr() - right.toIExpr() - RingOperations.TIMES_OPERATION -> left.toIExpr() * right.toIExpr() - FieldOperations.DIV_OPERATION -> F.Divide(left.toIExpr(), right.toIExpr()) + GroupOps.PLUS_OPERATION -> left.toIExpr() + right.toIExpr() + GroupOps.MINUS_OPERATION -> left.toIExpr() - right.toIExpr() + RingOps.TIMES_OPERATION -> left.toIExpr() * right.toIExpr() + FieldOps.DIV_OPERATION -> F.Divide(left.toIExpr(), right.toIExpr()) PowerOperations.POW_OPERATION -> F.Power(left.toIExpr(), F.symjify((right as MST.Numeric).value)) else -> error("Binary operation $operation not defined in $this") } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 8e39e6cdd..594070cd2 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -373,8 +373,12 @@ public open class DoubleTensorAlgebra : return resTensor } - override fun diagonalEmbedding(diagonalEntries: Tensor, offset: Int, dim1: Int, dim2: Int): - DoubleTensor { + override fun diagonalEmbedding( + diagonalEntries: Tensor, + offset: Int, + dim1: Int, + dim2: Int + ): DoubleTensor { val n = diagonalEntries.shape.size val d1 = minusIndexFrom(n + 1, dim1) val d2 = minusIndexFrom(n + 1, dim2) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/TensorLinearStructure.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/TensorLinearStructure.kt index 817ed60d8..57668722a 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/TensorLinearStructure.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/TensorLinearStructure.kt @@ -44,7 +44,7 @@ internal fun indexFromOffset(offset: Int, strides: IntArray, nDim: Int): IntArra * * @param shape the shape of the tensor. */ -internal class TensorLinearStructure(override val shape: IntArray) : Strides { +internal class TensorLinearStructure(override val shape: IntArray) : Strides() { override val strides: IntArray get() = stridesFromShape(shape) @@ -54,4 +54,18 @@ internal class TensorLinearStructure(override val shape: IntArray) : Strides { override val linearSize: Int get() = shape.reduce(Int::times) + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as TensorLinearStructure + + if (!shape.contentEquals(other.shape)) return false + + return true + } + + override fun hashCode(): Int { + return shape.contentHashCode() + } } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt index 29aa02931..e0451c2eb 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt @@ -26,8 +26,11 @@ internal fun Tensor.copyToBufferedTensor(): BufferedTensor = internal fun Tensor.toBufferedTensor(): BufferedTensor = when (this) { is BufferedTensor -> this - is MutableBufferND -> if (this.strides.strides contentEquals TensorLinearStructure(this.shape).strides) - BufferedTensor(this.shape, this.mutableBuffer, 0) else this.copyToBufferedTensor() + is MutableBufferND -> if (this.indexes == TensorLinearStructure(this.shape)) { + BufferedTensor(this.shape, this.mutableBuffer, 0) + } else { + this.copyToBufferedTensor() + } else -> this.copyToBufferedTensor() } diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt index 682123ddd..fca23bc16 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt @@ -11,7 +11,7 @@ import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.* import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.ExtendedField -import space.kscience.kmath.operations.NumbersAddOperations +import space.kscience.kmath.operations.NumbersAddOps import space.kscience.kmath.operations.ScaleOperations @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @@ -34,7 +34,7 @@ public fun F64Array.asStructure(): ViktorStructureND = ViktorStructureND(this) @OptIn(UnstableKMathAPI::class) @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") public class ViktorFieldND(override val shape: IntArray) : FieldND, - NumbersAddOperations>, ExtendedField>, + NumbersAddOps>, ExtendedField>, ScaleOperations> { public val StructureND.f64Buffer: F64Array @@ -44,7 +44,7 @@ public class ViktorFieldND(override val shape: IntArray) : FieldND this.f64Buffer - else -> produce { this@f64Buffer[it] }.f64Buffer + else -> produce(shape) { this@f64Buffer[it] }.f64Buffer } override val zero: ViktorStructureND by lazy { F64Array.full(init = 0.0, shape = shape).asStructure() } @@ -52,9 +52,9 @@ public class ViktorFieldND(override val shape: IntArray) : FieldND Double): ViktorStructureND = + override fun produce(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND = F64Array(*shape).apply { this@ViktorFieldND.strides.indices().forEach { index -> set(value = DoubleField.initializer(index), indices = index) @@ -78,13 +78,13 @@ public class ViktorFieldND(override val shape: IntArray) : FieldND, - b: StructureND, + override fun zip( + left: StructureND, + right: StructureND, transform: DoubleField.(Double, Double) -> Double, ): ViktorStructureND = F64Array(*shape).apply { this@ViktorFieldND.strides.indices().forEach { index -> - set(value = DoubleField.transform(a[index], b[index]), indices = index) + set(value = DoubleField.transform(left[index], right[index]), indices = index) } }.asStructure() @@ -123,4 +123,4 @@ public class ViktorFieldND(override val shape: IntArray) : FieldND): ViktorStructureND = arg.f64Buffer.log().asStructure() } -public fun ViktorNDField(vararg shape: Int): ViktorFieldND = ViktorFieldND(shape) +public fun ViktorFieldND(vararg shape: Int): ViktorFieldND = ViktorFieldND(shape) diff --git a/settings.gradle.kts b/settings.gradle.kts index 528adb336..dc70cbb9e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,16 +1,18 @@ pluginManagement { repositories { - mavenLocal() maven("https://repo.kotlin.link") mavenCentral() gradlePluginPortal() } - val kotlinVersion = "1.6.0-M1" + val kotlinVersion = "1.6.0-RC" + val toolsVersion = "0.10.5" plugins { id("org.jetbrains.kotlinx.benchmark") version "0.3.1" - id("ru.mipt.npm.gradle.project") version "0.10.5" + id("ru.mipt.npm.gradle.project") version toolsVersion + id("ru.mipt.npm.gradle.jvm") version toolsVersion + id("ru.mipt.npm.gradle.mpp") version toolsVersion kotlin("multiplatform") version kotlinVersion kotlin("plugin.allopen") version kotlinVersion } From 688382eed6e514cf61d00047b75a8669c7599ce8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 17 Oct 2021 12:42:35 +0300 Subject: [PATCH 03/10] Shapeless ND and Buffer algebras --- .../kmath/benchmarks/NDFieldBenchmark.kt | 12 +- .../kmath/functions/matrixIntegration.kt | 3 +- .../kscience/kmath/structures/ComplexND.kt | 1 + .../kscience/kmath/structures/NDField.kt | 26 ++-- .../kmath/structures/StreamDoubleFieldND.kt | 14 +- .../DerivativeStructureExpression.kt | 6 +- .../space/kscience/kmath/complex/Complex.kt | 24 ++-- .../kscience/kmath/complex/Quaternion.kt | 26 ++-- .../FunctionalExpressionAlgebra.kt | 12 +- .../kscience/kmath/expressions/MstAlgebra.kt | 32 ++--- .../kmath/expressions/SimpleAutoDiff.kt | 24 ++-- .../space/kscience/kmath/nd/AlgebraND.kt | 26 ++-- .../kscience/kmath/nd/BufferAlgebraND.kt | 6 +- .../space/kscience/kmath/nd/BufferND.kt | 12 +- .../space/kscience/kmath/nd/DoubleFieldND.kt | 134 +++++++++++++++--- .../kscience/kmath/operations/Algebra.kt | 34 ++--- .../space/kscience/kmath/operations/BigInt.kt | 6 +- .../kmath/operations/BufferAlgebra.kt | 10 +- .../kmath/operations/DoubleBufferOps.kt | 63 ++++---- .../kscience/kmath/operations/numbers.kt | 68 ++++----- .../kscience/kmath/operations/BigNumbers.kt | 14 +- .../kscience/kmath/functions/Polynomial.kt | 6 +- .../kmath/geometry/Euclidean2DSpace.kt | 2 +- .../kmath/geometry/Euclidean3DSpace.kt | 4 +- .../kmath/histogram/IndexedHistogramSpace.kt | 8 +- .../kmath/histogram/TreeHistogramSpace.kt | 12 +- .../kscience/kmath/jafama/KMathJafama.kt | 28 ++-- .../kscience/kmath/nd4j/Nd4jArrayAlgebra.kt | 16 +-- .../kscience/kmath/stat/SamplerAlgebra.kt | 4 +- .../kmath/tensors/api/TensorAlgebra.kt | 16 ++- .../tensors/core/internal/tensorCastsUtils.kt | 2 +- .../kmath/viktor/ViktorStructureND.kt | 12 +- 32 files changed, 382 insertions(+), 281 deletions(-) diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt index 950a10b33..7f7c03412 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt @@ -57,12 +57,12 @@ internal class NDFieldBenchmark { blackhole.consume(res) } - @Benchmark - fun nd4jAdd(blackhole: Blackhole) = with(nd4jField) { - var res: StructureND = one(dim, dim) - repeat(n) { res += 1.0 } - blackhole.consume(res) - } +// @Benchmark +// fun nd4jAdd(blackhole: Blackhole) = with(nd4jField) { +// var res: StructureND = one(dim, dim) +// repeat(n) { res += 1.0 } +// blackhole.consume(res) +// } private companion object { private const val dim = 1000 diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt index 93b5671fe..609afb47e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt @@ -9,6 +9,7 @@ import space.kscience.kmath.integration.gaussIntegrator import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.value import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.nd.produce import space.kscience.kmath.nd.withNdAlgebra import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.invoke @@ -17,7 +18,7 @@ fun main(): Unit = Double.algebra { withNdAlgebra(2, 2) { //Produce a diagonal StructureND - fun diagonal(v: Double) = produce { (i, j) -> + fun diagonal(v: Double) = produce { (i, j) -> if (i == j) v else 0.0 } diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt index d4554b3ba..42636fafb 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt @@ -12,6 +12,7 @@ import space.kscience.kmath.linear.transpose import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.ndAlgebra +import space.kscience.kmath.nd.produce import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.invoke import kotlin.system.measureTimeMillis diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt index 7359f3bf6..cf0721ce7 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt @@ -8,10 +8,8 @@ package space.kscience.kmath.structures import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import org.nd4j.linalg.factory.Nd4j -import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.autoNdAlgebra -import space.kscience.kmath.nd.ndAlgebra -import space.kscience.kmath.nd4j.Nd4jArrayField +import space.kscience.kmath.nd.* +import space.kscience.kmath.nd4j.nd4j import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.invoke import space.kscience.kmath.viktor.ViktorFieldND @@ -31,15 +29,17 @@ fun main() { Nd4j.zeros(0) val dim = 1000 val n = 1000 + val shape = Shape(dim, dim) + // automatically build context most suited for given type. - val autoField = DoubleField.autoNdAlgebra(dim, dim) + val autoField = BufferedFieldOpsND(DoubleField, Buffer.Companion::auto) // specialized nd-field for Double. It works as generic Double field as well. - val realField = DoubleField.ndAlgebra(dim, dim) + val realField = DoubleField.ndAlgebra //A generic boxing field. It should be used for objects, not primitives. - val boxingField = DoubleField.ndAlgebra(Buffer.Companion::boxing, dim, dim) + val boxingField = BufferedFieldOpsND(DoubleField, Buffer.Companion::boxing) // Nd4j specialized field. - val nd4jField = Nd4jArrayField.real(dim, dim) + val nd4jField = DoubleField.nd4j //viktor field val viktorField = ViktorFieldND(dim, dim) //parallel processing based on Java Streams @@ -47,21 +47,21 @@ fun main() { measureAndPrint("Boxing addition") { boxingField { - var res: StructureND = one + var res: StructureND = one(shape) repeat(n) { res += 1.0 } } } measureAndPrint("Specialized addition") { realField { - var res: StructureND = one + var res: StructureND = one(shape) repeat(n) { res += 1.0 } } } measureAndPrint("Nd4j specialized addition") { nd4jField { - var res: StructureND = one + var res: StructureND = one(shape) repeat(n) { res += 1.0 } } } @@ -82,13 +82,13 @@ fun main() { measureAndPrint("Automatic field addition") { autoField { - var res: StructureND = one + var res: StructureND = one(shape) repeat(n) { res += 1.0 } } } measureAndPrint("Lazy addition") { - val res = realField.one.mapAsync(GlobalScope) { + val res = realField.one(shape).mapAsync(GlobalScope) { var c = 0.0 repeat(n) { c += 1.0 diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt index f1f5f2e84..dfd06973e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -22,12 +22,12 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND by lazy { produce { zero } } - override val one: BufferND by lazy { produce { one } } + override val zero: BufferND by lazy { produce(shape) { zero } } + override val one: BufferND by lazy { produce(shape) { one } } override fun number(value: Number): BufferND { val d = value.toDouble() // minimize conversions - return produce { d } + return produce(shape) { d } } private val StructureND.buffer: DoubleBuffer @@ -40,7 +40,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } } - override fun produce(initializer: DoubleField.(IntArray) -> Double): BufferND { + override fun produce(shape: Shape, initializer: DoubleField.(IntArray) -> Double): BufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> val index = strides.index(offset) DoubleField.initializer(index) @@ -70,12 +70,12 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND, - b: StructureND, + left: StructureND, + right: StructureND, transform: DoubleField.(Double, Double) -> Double, ): BufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> - DoubleField.transform(a.buffer.array[offset], b.buffer.array[offset]) + DoubleField.transform(left.buffer.array[offset], right.buffer.array[offset]) }.toArray() return BufferND(strides, array.asBuffer()) } diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt index a178e8dd7..d42e40d1e 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt @@ -70,12 +70,12 @@ public class DerivativeStructureField( override fun DerivativeStructure.unaryMinus(): DerivativeStructure = negate() - override fun add(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.add(b) + override fun add(left: DerivativeStructure, right: DerivativeStructure): DerivativeStructure = left.add(right) override fun scale(a: DerivativeStructure, value: Double): DerivativeStructure = a.multiply(value) - override fun multiply(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.multiply(b) - override fun divide(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.divide(b) + override fun multiply(left: DerivativeStructure, right: DerivativeStructure): DerivativeStructure = left.multiply(right) + override fun divide(left: DerivativeStructure, right: DerivativeStructure): DerivativeStructure = left.divide(right) override fun sin(arg: DerivativeStructure): DerivativeStructure = arg.sin() override fun cos(arg: DerivativeStructure): DerivativeStructure = arg.cos() override fun tan(arg: DerivativeStructure): DerivativeStructure = arg.tan() diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt index e61a7bccc..879cfe94e 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt @@ -77,33 +77,33 @@ public object ComplexField : override fun scale(a: Complex, value: Double): Complex = Complex(a.re * value, a.im * value) - override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im) + override fun add(left: Complex, right: Complex): Complex = Complex(left.re + right.re, left.im + right.im) // override fun multiply(a: Complex, k: Number): Complex = Complex(a.re * k.toDouble(), a.im * k.toDouble()) - override fun multiply(a: Complex, b: Complex): Complex = - Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re) + override fun multiply(left: Complex, right: Complex): Complex = + Complex(left.re * right.re - left.im * right.im, left.re * right.im + left.im * right.re) - override fun divide(a: Complex, b: Complex): Complex = when { - abs(b.im) < abs(b.re) -> { - val wr = b.im / b.re - val wd = b.re + wr * b.im + override fun divide(left: Complex, right: Complex): Complex = when { + abs(right.im) < abs(right.re) -> { + val wr = right.im / right.re + val wd = right.re + wr * right.im if (wd.isNaN() || wd == 0.0) throw ArithmeticException("Division by zero or infinity") else - Complex((a.re + a.im * wr) / wd, (a.im - a.re * wr) / wd) + Complex((left.re + left.im * wr) / wd, (left.im - left.re * wr) / wd) } - b.im == 0.0 -> throw ArithmeticException("Division by zero") + right.im == 0.0 -> throw ArithmeticException("Division by zero") else -> { - val wr = b.re / b.im - val wd = b.im + wr * b.re + val wr = right.re / right.im + val wd = right.im + wr * right.re if (wd.isNaN() || wd == 0.0) throw ArithmeticException("Division by zero or infinity") else - Complex((a.re * wr + a.im) / wd, (a.im * wr - a.re) / wd) + Complex((left.re * wr + left.im) / wd, (left.im * wr - left.re) / wd) } } diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt index b0ec10c35..9fdd60e1f 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt @@ -63,27 +63,27 @@ public object QuaternionField : Field, Norm, */ public val k: Quaternion = Quaternion(0, 0, 0, 1) - override fun add(a: Quaternion, b: Quaternion): Quaternion = - Quaternion(a.w + b.w, a.x + b.x, a.y + b.y, a.z + b.z) + override fun add(left: Quaternion, right: Quaternion): Quaternion = + Quaternion(left.w + right.w, left.x + right.x, left.y + right.y, left.z + right.z) override fun scale(a: Quaternion, value: Double): Quaternion = Quaternion(a.w * value, a.x * value, a.y * value, a.z * value) - override fun multiply(a: Quaternion, b: Quaternion): Quaternion = Quaternion( - a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z, - a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, - a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x, - a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w, + override fun multiply(left: Quaternion, right: Quaternion): Quaternion = Quaternion( + left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z, + left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y, + left.w * right.y - left.x * right.z + left.y * right.w + left.z * right.x, + left.w * right.z + left.x * right.y - left.y * right.x + left.z * right.w, ) - override fun divide(a: Quaternion, b: Quaternion): Quaternion { - val s = b.w * b.w + b.x * b.x + b.y * b.y + b.z * b.z + override fun divide(left: Quaternion, right: Quaternion): Quaternion { + val s = right.w * right.w + right.x * right.x + right.y * right.y + right.z * right.z return Quaternion( - (b.w * a.w + b.x * a.x + b.y * a.y + b.z * a.z) / s, - (b.w * a.x - b.x * a.w - b.y * a.z + b.z * a.y) / s, - (b.w * a.y + b.x * a.z - b.y * a.w - b.z * a.x) / s, - (b.w * a.z - b.x * a.y + b.y * a.x - b.z * a.w) / s, + (right.w * left.w + right.x * left.x + right.y * left.y + right.z * left.z) / s, + (right.w * left.x - right.x * left.w - right.y * left.z + right.z * left.y) / s, + (right.w * left.y + right.x * left.z - right.y * left.w - right.z * left.x) / s, + (right.w * left.z - right.x * left.y + right.y * left.x - right.z * left.w) / s, ) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt index 1baca0f55..661680565 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -57,8 +57,8 @@ public open class FunctionalExpressionGroup>( /** * Builds an Expression of addition of two another expressions. */ - override fun add(a: Expression, b: Expression): Expression = - binaryOperation(GroupOps.PLUS_OPERATION, a, b) + override fun add(left: Expression, right: Expression): Expression = + binaryOperation(GroupOps.PLUS_OPERATION, left, right) // /** // * Builds an Expression of multiplication of expression by number. @@ -88,8 +88,8 @@ public open class FunctionalExpressionRing>( /** * Builds an Expression of multiplication of two expressions. */ - override fun multiply(a: Expression, b: Expression): Expression = - binaryOperationFunction(RingOps.TIMES_OPERATION)(a, b) + override fun multiply(left: Expression, right: Expression): Expression = + binaryOperationFunction(RingOps.TIMES_OPERATION)(left, right) public operator fun Expression.times(arg: T): Expression = this * const(arg) public operator fun T.times(arg: Expression): Expression = arg * this @@ -107,8 +107,8 @@ public open class FunctionalExpressionField>( /** * Builds an Expression of division an expression by another one. */ - override fun divide(a: Expression, b: Expression): Expression = - binaryOperationFunction(FieldOps.DIV_OPERATION)(a, b) + override fun divide(left: Expression, right: Expression): Expression = + binaryOperationFunction(FieldOps.DIV_OPERATION)(left, right) public operator fun Expression.div(arg: T): Expression = this / const(arg) public operator fun T.div(arg: Expression): Expression = arg / this diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt index 360715408..dd3c46207 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt @@ -31,15 +31,15 @@ public object MstGroup : Group, NumericAlgebra, ScaleOperations { override fun number(value: Number): MST.Numeric = MstNumericAlgebra.number(value) override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value) - override fun add(a: MST, b: MST): MST.Binary = binaryOperationFunction(GroupOps.PLUS_OPERATION)(a, b) + override fun add(left: MST, right: MST): MST.Binary = binaryOperationFunction(GroupOps.PLUS_OPERATION)(left, right) override operator fun MST.unaryPlus(): MST.Unary = unaryOperationFunction(GroupOps.PLUS_OPERATION)(this) override operator fun MST.unaryMinus(): MST.Unary = unaryOperationFunction(GroupOps.MINUS_OPERATION)(this) - override operator fun MST.minus(b: MST): MST.Binary = - binaryOperationFunction(GroupOps.MINUS_OPERATION)(this, b) + override operator fun MST.minus(other: MST): MST.Binary = + binaryOperationFunction(GroupOps.MINUS_OPERATION)(this, other) override fun scale(a: MST, value: Double): MST.Binary = binaryOperationFunction(RingOps.TIMES_OPERATION)(a, number(value)) @@ -62,17 +62,17 @@ public object MstRing : Ring, NumbersAddOps, ScaleOperations { override fun number(value: Number): MST.Numeric = MstGroup.number(value) override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value) - override fun add(a: MST, b: MST): MST.Binary = MstGroup.add(a, b) + override fun add(left: MST, right: MST): MST.Binary = MstGroup.add(left, right) override fun scale(a: MST, value: Double): MST.Binary = MstGroup.binaryOperationFunction(RingOps.TIMES_OPERATION)(a, MstGroup.number(value)) - override fun multiply(a: MST, b: MST): MST.Binary = - binaryOperationFunction(RingOps.TIMES_OPERATION)(a, b) + override fun multiply(left: MST, right: MST): MST.Binary = + binaryOperationFunction(RingOps.TIMES_OPERATION)(left, right) override operator fun MST.unaryPlus(): MST.Unary = MstGroup { +this@unaryPlus } override operator fun MST.unaryMinus(): MST.Unary = MstGroup { -this@unaryMinus } - override operator fun MST.minus(b: MST): MST.Binary = MstGroup { this@minus - b } + override operator fun MST.minus(other: MST): MST.Binary = MstGroup { this@minus - other } override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = MstGroup.binaryOperationFunction(operation) @@ -92,18 +92,18 @@ public object MstField : Field, NumbersAddOps, ScaleOperations { override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value) override fun number(value: Number): MST.Numeric = MstRing.number(value) - override fun add(a: MST, b: MST): MST.Binary = MstRing.add(a, b) + override fun add(left: MST, right: MST): MST.Binary = MstRing.add(left, right) override fun scale(a: MST, value: Double): MST.Binary = MstGroup.binaryOperationFunction(RingOps.TIMES_OPERATION)(a, MstGroup.number(value)) - override fun multiply(a: MST, b: MST): MST.Binary = MstRing.multiply(a, b) - override fun divide(a: MST, b: MST): MST.Binary = - binaryOperationFunction(FieldOps.DIV_OPERATION)(a, b) + override fun multiply(left: MST, right: MST): MST.Binary = MstRing.multiply(left, right) + override fun divide(left: MST, right: MST): MST.Binary = + binaryOperationFunction(FieldOps.DIV_OPERATION)(left, right) override operator fun MST.unaryPlus(): MST.Unary = MstRing { +this@unaryPlus } override operator fun MST.unaryMinus(): MST.Unary = MstRing { -this@unaryMinus } - override operator fun MST.minus(b: MST): MST.Binary = MstRing { this@minus - b } + override operator fun MST.minus(other: MST): MST.Binary = MstRing { this@minus - other } override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = MstRing.binaryOperationFunction(operation) @@ -134,17 +134,17 @@ public object MstExtendedField : ExtendedField, NumericAlgebra { override fun asinh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ASINH_OPERATION)(arg) override fun acosh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ACOSH_OPERATION)(arg) override fun atanh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ATANH_OPERATION)(arg) - override fun add(a: MST, b: MST): MST.Binary = MstField.add(a, b) + override fun add(left: MST, right: MST): MST.Binary = MstField.add(left, right) override fun sqrt(arg: MST): MST = unaryOperationFunction(PowerOperations.SQRT_OPERATION)(arg) override fun scale(a: MST, value: Double): MST = binaryOperation(GroupOps.PLUS_OPERATION, a, number(value)) - override fun multiply(a: MST, b: MST): MST.Binary = MstField.multiply(a, b) - override fun divide(a: MST, b: MST): MST.Binary = MstField.divide(a, b) + override fun multiply(left: MST, right: MST): MST.Binary = MstField.multiply(left, right) + override fun divide(left: MST, right: MST): MST.Binary = MstField.divide(left, right) override operator fun MST.unaryPlus(): MST.Unary = MstField { +this@unaryPlus } override operator fun MST.unaryMinus(): MST.Unary = MstField { -this@unaryMinus } - override operator fun MST.minus(b: MST): MST.Binary = MstField { this@minus - b } + override operator fun MST.minus(other: MST): MST.Binary = MstField { this@minus - other } override fun power(arg: MST, pow: Number): MST.Binary = binaryOperationFunction(PowerOperations.POW_OPERATION)(arg, number(pow)) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt index 089503dc8..704c4edd8 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt @@ -168,22 +168,22 @@ public open class SimpleAutoDiffField>( // Basic math (+, -, *, /) - override fun add(a: AutoDiffValue, b: AutoDiffValue): AutoDiffValue = - derive(const { a.value + b.value }) { z -> - a.d += z.d - b.d += z.d + override fun add(left: AutoDiffValue, right: AutoDiffValue): AutoDiffValue = + derive(const { left.value + right.value }) { z -> + left.d += z.d + right.d += z.d } - override fun multiply(a: AutoDiffValue, b: AutoDiffValue): AutoDiffValue = - derive(const { a.value * b.value }) { z -> - a.d += z.d * b.value - b.d += z.d * a.value + override fun multiply(left: AutoDiffValue, right: AutoDiffValue): AutoDiffValue = + derive(const { left.value * right.value }) { z -> + left.d += z.d * right.value + right.d += z.d * left.value } - override fun divide(a: AutoDiffValue, b: AutoDiffValue): AutoDiffValue = - derive(const { a.value / b.value }) { z -> - a.d += z.d / b.value - b.d -= z.d * a.value / (b.value * b.value) + override fun divide(left: AutoDiffValue, right: AutoDiffValue): AutoDiffValue = + derive(const { left.value / right.value }) { z -> + left.d += z.d / right.value + right.d -= z.d * left.value / (right.value * right.value) } override fun scale(a: AutoDiffValue, value: Double): AutoDiffValue = diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt index dde04a97a..3f9b43f03 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt @@ -100,12 +100,12 @@ public interface GroupOpsND> : GroupOps>, /** * Element-wise addition. * - * @param a the augend. - * @param b the addend. + * @param left the augend. + * @param right the addend. * @return the sum. */ - override fun add(a: StructureND, b: StructureND): StructureND = - zip(a, b) { aValue, bValue -> add(aValue, bValue) } + override fun add(left: StructureND, right: StructureND): StructureND = + zip(left, right) { aValue, bValue -> add(aValue, bValue) } // TODO move to extensions after KEEP-176 @@ -134,7 +134,7 @@ public interface GroupOpsND> : GroupOps>, * @param arg the addend. * @return the sum. */ - public operator fun T.plus(arg: StructureND): StructureND = arg.map { value -> add(this@plus, value) } + public operator fun T.plus(arg: StructureND): StructureND = arg + this /** * Subtracts an ND structure from an element of it. @@ -162,12 +162,12 @@ public interface RingOpsND> : RingOps>, Gro /** * Element-wise multiplication. * - * @param a the multiplicand. - * @param b the multiplier. + * @param left the multiplicand. + * @param right the multiplier. * @return the product. */ - override fun multiply(a: StructureND, b: StructureND): StructureND = - zip(a, b) { aValue, bValue -> multiply(aValue, bValue) } + override fun multiply(left: StructureND, right: StructureND): StructureND = + zip(left, right) { aValue, bValue -> multiply(aValue, bValue) } //TODO move to extensions after KEEP-176 @@ -208,12 +208,12 @@ public interface FieldOpsND> : FieldOps>, Rin /** * Element-wise division. * - * @param a the dividend. - * @param b the divisor. + * @param left the dividend. + * @param right the divisor. * @return the quotient. */ - override fun divide(a: StructureND, b: StructureND): StructureND = - zip(a, b) { aValue, bValue -> divide(aValue, bValue) } + override fun divide(left: StructureND, right: StructureND): StructureND = + zip(left, right) { aValue, bValue -> divide(aValue, bValue) } //TODO move to extensions after https://github.com/Kotlin/KEEP/blob/master/proposals/context-receivers.md /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt index 06534c5e5..c94988eef 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt @@ -73,7 +73,7 @@ internal inline fun > BufferAlgebraND.zipInline( r: BufferND, crossinline block: A.(l: T, r: T) -> T ): BufferND { - require(l.indexes == r.indexes) + require(l.indexes == r.indexes) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } val indexes = l.indexes return BufferND(indexes, bufferAlgebra.zipInline(l.buffer, r.buffer, block)) } @@ -114,6 +114,10 @@ public fun > BufferAlgebraND.produce( initializer: A.(IntArray) -> T ): BufferND = produce(shape, initializer) +public fun , A> A.produce( + initializer: EA.(IntArray) -> T +): BufferND where A : BufferAlgebraND, A : WithShape = produce(shape, initializer) + //// group factories //public fun > A.ndAlgebra( // bufferAlgebra: BufferAlgebra, diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt index 9eba401ab..c17632101 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt @@ -20,7 +20,7 @@ import space.kscience.kmath.structures.MutableBufferFactory */ public open class BufferND( public val indexes: ShapeIndex, - public val buffer: Buffer, + public open val buffer: Buffer, ) : StructureND { override operator fun get(index: IntArray): T = buffer[indexes.offset(index)] @@ -55,14 +55,14 @@ public inline fun StructureND.mapToBuffer( * * @param T the type of items. * @param strides The strides to access elements of [MutableBuffer] by linear indices. - * @param mutableBuffer The underlying buffer. + * @param buffer The underlying buffer. */ public class MutableBufferND( strides: ShapeIndex, - public val mutableBuffer: MutableBuffer, -) : MutableStructureND, BufferND(strides, mutableBuffer) { + override val buffer: MutableBuffer, +) : MutableStructureND, BufferND(strides, buffer) { override fun set(index: IntArray, value: T) { - mutableBuffer[indexes.offset(index)] = value + buffer[indexes.offset(index)] = value } } @@ -74,7 +74,7 @@ public inline fun MutableStructureND.mapToMutableBuffer( crossinline transform: (T) -> R, ): MutableBufferND { return if (this is MutableBufferND) - MutableBufferND(this.indexes, factory.invoke(indexes.linearSize) { transform(mutableBuffer[it]) }) + MutableBufferND(this.indexes, factory.invoke(indexes.linearSize) { transform(buffer[it]) }) else { val strides = DefaultStrides(shape) MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt index 294c44ec4..1502a6fd0 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt @@ -10,42 +10,132 @@ import space.kscience.kmath.operations.* import space.kscience.kmath.structures.DoubleBuffer import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.math.pow + +public class DoubleBufferND( + indexes: ShapeIndex, + override val buffer: DoubleBuffer, +) : BufferND(indexes, buffer) + public sealed class DoubleFieldOpsND : BufferedFieldOpsND(DoubleField.bufferAlgebra), ScaleOperations>, ExtendedFieldOps> { - override fun StructureND.toBufferND(): BufferND = when (this) { - is BufferND -> this + override fun StructureND.toBufferND(): DoubleBufferND = when (this) { + is DoubleBufferND -> this else -> { val indexer = indexerBuilder(shape) - BufferND(indexer, DoubleBuffer(indexer.linearSize) { offset -> get(indexer.index(offset)) }) + DoubleBufferND(indexer, DoubleBuffer(indexer.linearSize) { offset -> get(indexer.index(offset)) }) } } - //TODO do specialization + private inline fun mapInline( + arg: DoubleBufferND, + transform: (Double) -> Double + ): DoubleBufferND { + val indexes = arg.indexes + val array = arg.buffer.array + return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { transform(array[it]) }) + } - override fun scale(a: StructureND, value: Double): BufferND = + private inline fun zipInline( + l: DoubleBufferND, + r: DoubleBufferND, + block: (l: Double, r: Double) -> Double + ): DoubleBufferND { + require(l.indexes == r.indexes) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } + val indexes = l.indexes + val lArray = l.buffer.array + val rArray = r.buffer.array + return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { block(lArray[it], rArray[it]) }) + } + + override fun StructureND.map(transform: DoubleField.(Double) -> Double): BufferND = + mapInline(toBufferND()) { DoubleField.transform(it) } + + + override fun zip( + left: StructureND, + right: StructureND, + transform: DoubleField.(Double, Double) -> Double + ): BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> DoubleField.transform(l, r) } + + override fun produce(shape: Shape, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND { + val indexer = indexerBuilder(shape) + return DoubleBufferND( + indexer, + DoubleBuffer(indexer.linearSize) { offset -> + elementAlgebra.initializer(indexer.index(offset)) + } + ) + } + + override fun add(left: StructureND, right: StructureND): DoubleBufferND = + zipInline(left.toBufferND(), right.toBufferND()) { l, r -> l + r } + + override fun multiply(left: StructureND, right: StructureND): DoubleBufferND = + zipInline(left.toBufferND(), right.toBufferND()) { l, r -> l * r } + + override fun StructureND.unaryMinus(): DoubleBufferND = mapInline(toBufferND()) { -it } + + override fun StructureND.div(other: StructureND): DoubleBufferND = + zipInline(toBufferND(), other.toBufferND()) { l, r -> l / r } + + override fun StructureND.plus(arg: Double): DoubleBufferND = mapInline(toBufferND()) { it + arg } + + override fun StructureND.minus(arg: Double): StructureND = mapInline(toBufferND()) { it - arg } + + override fun Double.plus(arg: StructureND): StructureND = arg + this + + override fun Double.minus(arg: StructureND): StructureND = mapInline(arg.toBufferND()) { this - it } + + override fun scale(a: StructureND, value: Double): DoubleBufferND = mapInline(a.toBufferND()) { it * value } - override fun power(arg: StructureND, pow: Number): BufferND = - mapInline(arg.toBufferND()) { power(it, pow) } + override fun power(arg: StructureND, pow: Number): DoubleBufferND = + mapInline(arg.toBufferND()) { it.pow(pow.toDouble()) } - override fun exp(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { exp(it) } - override fun ln(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { ln(it) } + override fun exp(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.exp(it) } - override fun sin(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { sin(it) } - override fun cos(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { cos(it) } - override fun tan(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { tan(it) } - override fun asin(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { asin(it) } - override fun acos(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { acos(it) } - override fun atan(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { atan(it) } + override fun ln(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.ln(it) } - override fun sinh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { sinh(it) } - override fun cosh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { cosh(it) } - override fun tanh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { tanh(it) } - override fun asinh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { asinh(it) } - override fun acosh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { acosh(it) } - override fun atanh(arg: StructureND): BufferND = mapInline(arg.toBufferND()) { atanh(it) } + override fun sin(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.sin(it) } + + override fun cos(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.cos(it) } + + override fun tan(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.tan(it) } + + override fun asin(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.asin(it) } + + override fun acos(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.acos(it) } + + override fun atan(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.atan(it) } + + override fun sinh(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.sinh(it) } + + override fun cosh(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.cosh(it) } + + override fun tanh(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.tanh(it) } + + override fun asinh(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.asinh(it) } + + override fun acosh(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.acosh(it) } + + override fun atanh(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { kotlin.math.atanh(it) } public companion object : DoubleFieldOpsND() } @@ -54,7 +144,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND(D public class DoubleFieldND(override val shape: Shape) : DoubleFieldOpsND(), FieldND, NumbersAddOps> { - override fun number(value: Number): BufferND { + override fun number(value: Number): DoubleBufferND { val d = value.toDouble() // minimize conversions return produce(shape) { d } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt index d731ee4ee..d0b0c0b73 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt @@ -121,11 +121,11 @@ public interface GroupOps : Algebra { /** * Addition of two elements. * - * @param a the augend. - * @param b the addend. + * @param left the augend. + * @param right the addend. * @return the sum. */ - public fun add(a: T, b: T): T + public fun add(left: T, right: T): T // Operations to be performed in this context. Could be moved to extensions in case of KEEP-176. @@ -149,19 +149,19 @@ public interface GroupOps : Algebra { * Addition of two elements. * * @receiver the augend. - * @param b the addend. + * @param other the addend. * @return the sum. */ - public operator fun T.plus(b: T): T = add(this, b) + public operator fun T.plus(other: T): T = add(this, other) /** * Subtraction of two elements. * * @receiver the minuend. - * @param b the subtrahend. + * @param other the subtrahend. * @return the difference. */ - public operator fun T.minus(b: T): T = add(this, -b) + public operator fun T.minus(other: T): T = add(this, -other) // Dynamic dispatch of operations override fun unaryOperationFunction(operation: String): (arg: T) -> T = when (operation) { PLUS_OPERATION -> { arg -> +arg } @@ -210,18 +210,18 @@ public interface RingOps : GroupOps { /** * Multiplies two elements. * - * @param a the multiplier. - * @param b the multiplicand. + * @param left the multiplier. + * @param right the multiplicand. */ - public fun multiply(a: T, b: T): T + public fun multiply(left: T, right: T): T /** * Multiplies this element by scalar. * * @receiver the multiplier. - * @param b the multiplicand. + * @param other the multiplicand. */ - public operator fun T.times(b: T): T = multiply(this, b) + public operator fun T.times(other: T): T = multiply(this, other) override fun binaryOperationFunction(operation: String): (left: T, right: T) -> T = when (operation) { TIMES_OPERATION -> ::multiply @@ -260,20 +260,20 @@ public interface FieldOps : RingOps { /** * Division of two elements. * - * @param a the dividend. - * @param b the divisor. + * @param left the dividend. + * @param right the divisor. * @return the quotient. */ - public fun divide(a: T, b: T): T + public fun divide(left: T, right: T): T /** * Division of two elements. * * @receiver the dividend. - * @param b the divisor. + * @param other the divisor. * @return the quotient. */ - public operator fun T.div(b: T): T = divide(this, b) + public operator fun T.div(other: T): T = divide(this, other) override fun binaryOperationFunction(operation: String): (left: T, right: T) -> T = when (operation) { DIV_OPERATION -> ::divide diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt index 97e35e4b0..5a713049e 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt @@ -34,10 +34,10 @@ public object BigIntField : Field, NumbersAddOps, ScaleOperation @Suppress("EXTENSION_SHADOWED_BY_MEMBER") override fun BigInt.unaryMinus(): BigInt = -this - override fun add(a: BigInt, b: BigInt): BigInt = a.plus(b) + override fun add(left: BigInt, right: BigInt): BigInt = left.plus(right) override fun scale(a: BigInt, value: Double): BigInt = a.times(number(value)) - override fun multiply(a: BigInt, b: BigInt): BigInt = a.times(b) - override fun divide(a: BigInt, b: BigInt): BigInt = a.div(b) + override fun multiply(left: BigInt, right: BigInt): BigInt = left.times(right) + override fun divide(left: BigInt, right: BigInt): BigInt = left.div(right) public operator fun String.unaryPlus(): BigInt = this.parseBigInteger() ?: error("Can't parse $this as big integer") public operator fun String.unaryMinus(): BigInt = diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt index 5a14e7734..bc05f3904 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt @@ -134,8 +134,8 @@ public open class BufferRingOps>( override val bufferFactory: BufferFactory, ) : BufferAlgebra, RingOps>{ - override fun add(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l + r } - override fun multiply(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l * r } + override fun add(left: Buffer, right: Buffer): Buffer = zipInline(left, right) { l, r -> l + r } + override fun multiply(left: Buffer, right: Buffer): Buffer = zipInline(left, right) { l, r -> l * r } override fun Buffer.unaryMinus(): Buffer = map { -it } override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer = @@ -153,9 +153,9 @@ public open class BufferFieldOps>( bufferFactory: BufferFactory, ) : BufferRingOps(elementAlgebra, bufferFactory), BufferAlgebra, FieldOps>, ScaleOperations> { - override fun add(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l + r } - override fun multiply(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l * r } - override fun divide(a: Buffer, b: Buffer): Buffer = zipInline(a, b) { l, r -> l / r } + override fun add(left: Buffer, right: Buffer): Buffer = zipInline(left, right) { l, r -> l + r } + override fun multiply(left: Buffer, right: Buffer): Buffer = zipInline(left, right) { l, r -> l * r } + override fun divide(left: Buffer, right: Buffer): Buffer = zipInline(left, right) { l, r -> l / r } override fun scale(a: Buffer, value: Double): Buffer = a.map { scale(it, value) } override fun Buffer.unaryMinus(): Buffer = map { -it } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt index 2580cd6f8..29b25aae8 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt @@ -15,36 +15,37 @@ import kotlin.math.* * [ExtendedFieldOps] over [DoubleBuffer]. */ public abstract class DoubleBufferOps : ExtendedFieldOps>, Norm, Double> { + override fun Buffer.unaryMinus(): DoubleBuffer = if (this is DoubleBuffer) { DoubleBuffer(size) { -array[it] } } else { DoubleBuffer(size) { -get(it) } } - override fun add(a: Buffer, b: Buffer): DoubleBuffer { - require(b.size == a.size) { - "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " + override fun add(left: Buffer, right: Buffer): DoubleBuffer { + require(right.size == left.size) { + "The size of the first buffer ${left.size} should be the same as for second one: ${right.size} " } - return if (a is DoubleBuffer && b is DoubleBuffer) { - val aArray = a.array - val bArray = b.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] + bArray[it] }) - } else DoubleBuffer(DoubleArray(a.size) { a[it] + b[it] }) + return if (left is DoubleBuffer && right is DoubleBuffer) { + val aArray = left.array + val bArray = right.array + DoubleBuffer(DoubleArray(left.size) { aArray[it] + bArray[it] }) + } else DoubleBuffer(DoubleArray(left.size) { left[it] + right[it] }) } - override fun Buffer.plus(b: Buffer): DoubleBuffer = add(this, b) + override fun Buffer.plus(other: Buffer): DoubleBuffer = add(this, other) - override fun Buffer.minus(b: Buffer): DoubleBuffer { - require(b.size == this.size) { - "The size of the first buffer ${this.size} should be the same as for second one: ${b.size} " + override fun Buffer.minus(other: Buffer): DoubleBuffer { + require(other.size == this.size) { + "The size of the first buffer ${this.size} should be the same as for second one: ${other.size} " } - return if (this is DoubleBuffer && b is DoubleBuffer) { + return if (this is DoubleBuffer && other is DoubleBuffer) { val aArray = this.array - val bArray = b.array + val bArray = other.array DoubleBuffer(DoubleArray(this.size) { aArray[it] - bArray[it] }) - } else DoubleBuffer(DoubleArray(this.size) { this[it] - b[it] }) + } else DoubleBuffer(DoubleArray(this.size) { this[it] - other[it] }) } // @@ -66,29 +67,29 @@ public abstract class DoubleBufferOps : ExtendedFieldOps>, Norm, b: Buffer): DoubleBuffer { - require(b.size == a.size) { - "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " + override fun multiply(left: Buffer, right: Buffer): DoubleBuffer { + require(right.size == left.size) { + "The size of the first buffer ${left.size} should be the same as for second one: ${right.size} " } - return if (a is DoubleBuffer && b is DoubleBuffer) { - val aArray = a.array - val bArray = b.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] * bArray[it] }) + return if (left is DoubleBuffer && right is DoubleBuffer) { + val aArray = left.array + val bArray = right.array + DoubleBuffer(DoubleArray(left.size) { aArray[it] * bArray[it] }) } else - DoubleBuffer(DoubleArray(a.size) { a[it] * b[it] }) + DoubleBuffer(DoubleArray(left.size) { left[it] * right[it] }) } - override fun divide(a: Buffer, b: Buffer): DoubleBuffer { - require(b.size == a.size) { - "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " + override fun divide(left: Buffer, right: Buffer): DoubleBuffer { + require(right.size == left.size) { + "The size of the first buffer ${left.size} should be the same as for second one: ${right.size} " } - return if (a is DoubleBuffer && b is DoubleBuffer) { - val aArray = a.array - val bArray = b.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] / bArray[it] }) - } else DoubleBuffer(DoubleArray(a.size) { a[it] / b[it] }) + return if (left is DoubleBuffer && right is DoubleBuffer) { + val aArray = left.array + val bArray = right.array + DoubleBuffer(DoubleArray(left.size) { aArray[it] / bArray[it] }) + } else DoubleBuffer(DoubleArray(left.size) { left[it] / right[it] }) } override fun sin(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt index c1775296c..1168dc6ba 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt @@ -73,10 +73,10 @@ public object DoubleField : ExtendedField, Norm, ScaleOp else -> super.binaryOperationFunction(operation) } - override inline fun add(a: Double, b: Double): Double = a + b + override inline fun add(left: Double, right: Double): Double = left + right - override inline fun multiply(a: Double, b: Double): Double = a * b - override inline fun divide(a: Double, b: Double): Double = a / b + override inline fun multiply(left: Double, right: Double): Double = left * right + override inline fun divide(left: Double, right: Double): Double = left / right override inline fun scale(a: Double, value: Double): Double = a * value @@ -102,10 +102,10 @@ public object DoubleField : ExtendedField, Norm, ScaleOp override inline fun norm(arg: Double): Double = abs(arg) override inline fun Double.unaryMinus(): Double = -this - override inline fun Double.plus(b: Double): Double = this + b - override inline fun Double.minus(b: Double): Double = this - b - override inline fun Double.times(b: Double): Double = this * b - override inline fun Double.div(b: Double): Double = this / b + override inline fun Double.plus(other: Double): Double = this + other + override inline fun Double.minus(other: Double): Double = this - other + override inline fun Double.times(other: Double): Double = this * other + override inline fun Double.div(other: Double): Double = this / other } public val Double.Companion.algebra: DoubleField get() = DoubleField @@ -126,12 +126,12 @@ public object FloatField : ExtendedField, Norm { else -> super.binaryOperationFunction(operation) } - override inline fun add(a: Float, b: Float): Float = a + b + override inline fun add(left: Float, right: Float): Float = left + right override fun scale(a: Float, value: Double): Float = a * value.toFloat() - override inline fun multiply(a: Float, b: Float): Float = a * b + override inline fun multiply(left: Float, right: Float): Float = left * right - override inline fun divide(a: Float, b: Float): Float = a / b + override inline fun divide(left: Float, right: Float): Float = left / right override inline fun sin(arg: Float): Float = kotlin.math.sin(arg) override inline fun cos(arg: Float): Float = kotlin.math.cos(arg) @@ -155,10 +155,10 @@ public object FloatField : ExtendedField, Norm { override inline fun norm(arg: Float): Float = abs(arg) override inline fun Float.unaryMinus(): Float = -this - override inline fun Float.plus(b: Float): Float = this + b - override inline fun Float.minus(b: Float): Float = this - b - override inline fun Float.times(b: Float): Float = this * b - override inline fun Float.div(b: Float): Float = this / b + override inline fun Float.plus(other: Float): Float = this + other + override inline fun Float.minus(other: Float): Float = this - other + override inline fun Float.times(other: Float): Float = this * other + override inline fun Float.div(other: Float): Float = this / other } public val Float.Companion.algebra: FloatField get() = FloatField @@ -175,14 +175,14 @@ public object IntRing : Ring, Norm, NumericAlgebra { get() = 1 override fun number(value: Number): Int = value.toInt() - override inline fun add(a: Int, b: Int): Int = a + b - override inline fun multiply(a: Int, b: Int): Int = a * b + override inline fun add(left: Int, right: Int): Int = left + right + override inline fun multiply(left: Int, right: Int): Int = left * right override inline fun norm(arg: Int): Int = abs(arg) override inline fun Int.unaryMinus(): Int = -this - override inline fun Int.plus(b: Int): Int = this + b - override inline fun Int.minus(b: Int): Int = this - b - override inline fun Int.times(b: Int): Int = this * b + override inline fun Int.plus(other: Int): Int = this + other + override inline fun Int.minus(other: Int): Int = this - other + override inline fun Int.times(other: Int): Int = this * other } public val Int.Companion.algebra: IntRing get() = IntRing @@ -199,14 +199,14 @@ public object ShortRing : Ring, Norm, NumericAlgebra get() = 1 override fun number(value: Number): Short = value.toShort() - override inline fun add(a: Short, b: Short): Short = (a + b).toShort() - override inline fun multiply(a: Short, b: Short): Short = (a * b).toShort() + override inline fun add(left: Short, right: Short): Short = (left + right).toShort() + override inline fun multiply(left: Short, right: Short): Short = (left * right).toShort() override fun norm(arg: Short): Short = if (arg > 0) arg else (-arg).toShort() override inline fun Short.unaryMinus(): Short = (-this).toShort() - override inline fun Short.plus(b: Short): Short = (this + b).toShort() - override inline fun Short.minus(b: Short): Short = (this - b).toShort() - override inline fun Short.times(b: Short): Short = (this * b).toShort() + override inline fun Short.plus(other: Short): Short = (this + other).toShort() + override inline fun Short.minus(other: Short): Short = (this - other).toShort() + override inline fun Short.times(other: Short): Short = (this * other).toShort() } public val Short.Companion.algebra: ShortRing get() = ShortRing @@ -223,14 +223,14 @@ public object ByteRing : Ring, Norm, NumericAlgebra { get() = 1 override fun number(value: Number): Byte = value.toByte() - override inline fun add(a: Byte, b: Byte): Byte = (a + b).toByte() - override inline fun multiply(a: Byte, b: Byte): Byte = (a * b).toByte() + override inline fun add(left: Byte, right: Byte): Byte = (left + right).toByte() + override inline fun multiply(left: Byte, right: Byte): Byte = (left * right).toByte() override fun norm(arg: Byte): Byte = if (arg > 0) arg else (-arg).toByte() override inline fun Byte.unaryMinus(): Byte = (-this).toByte() - override inline fun Byte.plus(b: Byte): Byte = (this + b).toByte() - override inline fun Byte.minus(b: Byte): Byte = (this - b).toByte() - override inline fun Byte.times(b: Byte): Byte = (this * b).toByte() + override inline fun Byte.plus(other: Byte): Byte = (this + other).toByte() + override inline fun Byte.minus(other: Byte): Byte = (this - other).toByte() + override inline fun Byte.times(other: Byte): Byte = (this * other).toByte() } public val Byte.Companion.algebra: ByteRing get() = ByteRing @@ -247,14 +247,14 @@ public object LongRing : Ring, Norm, NumericAlgebra { get() = 1L override fun number(value: Number): Long = value.toLong() - override inline fun add(a: Long, b: Long): Long = a + b - override inline fun multiply(a: Long, b: Long): Long = a * b + override inline fun add(left: Long, right: Long): Long = left + right + override inline fun multiply(left: Long, right: Long): Long = left * right override fun norm(arg: Long): Long = abs(arg) override inline fun Long.unaryMinus(): Long = (-this) - override inline fun Long.plus(b: Long): Long = (this + b) - override inline fun Long.minus(b: Long): Long = (this - b) - override inline fun Long.times(b: Long): Long = (this * b) + override inline fun Long.plus(other: Long): Long = (this + other) + override inline fun Long.minus(other: Long): Long = (this - other) + override inline fun Long.times(other: Long): Long = (this * other) } public val Long.Companion.algebra: LongRing get() = LongRing diff --git a/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/operations/BigNumbers.kt index 69dd858c4..3a9c242fc 100644 --- a/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/operations/BigNumbers.kt @@ -18,9 +18,9 @@ public object JBigIntegerField : Ring, NumericAlgebra { override val one: BigInteger get() = BigInteger.ONE override fun number(value: Number): BigInteger = BigInteger.valueOf(value.toLong()) - override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) - override operator fun BigInteger.minus(b: BigInteger): BigInteger = subtract(b) - override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) + override fun add(left: BigInteger, right: BigInteger): BigInteger = left.add(right) + override operator fun BigInteger.minus(other: BigInteger): BigInteger = subtract(other) + override fun multiply(left: BigInteger, right: BigInteger): BigInteger = left.multiply(right) override operator fun BigInteger.unaryMinus(): BigInteger = negate() } @@ -39,15 +39,15 @@ public abstract class JBigDecimalFieldBase internal constructor( override val one: BigDecimal get() = BigDecimal.ONE - override fun add(a: BigDecimal, b: BigDecimal): BigDecimal = a.add(b) - override operator fun BigDecimal.minus(b: BigDecimal): BigDecimal = subtract(b) + override fun add(left: BigDecimal, right: BigDecimal): BigDecimal = left.add(right) + override operator fun BigDecimal.minus(other: BigDecimal): BigDecimal = subtract(other) override fun number(value: Number): BigDecimal = BigDecimal.valueOf(value.toDouble()) override fun scale(a: BigDecimal, value: Double): BigDecimal = a.multiply(value.toBigDecimal(mathContext), mathContext) - override fun multiply(a: BigDecimal, b: BigDecimal): BigDecimal = a.multiply(b, mathContext) - override fun divide(a: BigDecimal, b: BigDecimal): BigDecimal = a.divide(b, mathContext) + override fun multiply(left: BigDecimal, right: BigDecimal): BigDecimal = left.multiply(right, mathContext) + override fun divide(left: BigDecimal, right: BigDecimal): BigDecimal = left.divide(right, mathContext) override fun power(arg: BigDecimal, pow: Number): BigDecimal = arg.pow(pow.toInt(), mathContext) override fun sqrt(arg: BigDecimal): BigDecimal = arg.sqrt(mathContext) override operator fun BigDecimal.unaryMinus(): BigDecimal = negate(mathContext) diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt index 54b285a70..e862c0b9d 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt @@ -104,12 +104,12 @@ public class PolynomialSpace( Polynomial(coefficients.map { -it }) } - override fun add(a: Polynomial, b: Polynomial): Polynomial { - val dim = max(a.coefficients.size, b.coefficients.size) + override fun add(left: Polynomial, right: Polynomial): Polynomial { + val dim = max(left.coefficients.size, right.coefficients.size) return ring { Polynomial(List(dim) { index -> - a.coefficients.getOrElse(index) { zero } + b.coefficients.getOrElse(index) { zero } + left.coefficients.getOrElse(index) { zero } + right.coefficients.getOrElse(index) { zero } }) } } diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt index e8b1ce95b..5e3cbff83 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt @@ -47,7 +47,7 @@ public object Euclidean2DSpace : GeometrySpace, ScaleOperations, ScaleOperations, V : Any> public fun produce(builder: HistogramBuilder.() -> Unit): IndexedHistogram - override fun add(a: IndexedHistogram, b: IndexedHistogram): IndexedHistogram { - require(a.context == this) { "Can't operate on a histogram produced by external space" } - require(b.context == this) { "Can't operate on a histogram produced by external space" } - return IndexedHistogram(this, histogramValueSpace { a.values + b.values }) + override fun add(left: IndexedHistogram, right: IndexedHistogram): IndexedHistogram { + require(left.context == this) { "Can't operate on a histogram produced by external space" } + require(right.context == this) { "Can't operate on a histogram produced by external space" } + return IndexedHistogram(this, histogramValueSpace { left.values + right.values }) } override fun scale(a: IndexedHistogram, value: Double): IndexedHistogram { diff --git a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramSpace.kt b/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramSpace.kt index 96f945f6a..cc54d7e1a 100644 --- a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramSpace.kt +++ b/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramSpace.kt @@ -88,20 +88,20 @@ public class TreeHistogramSpace( TreeHistogramBuilder(binFactory).apply(block).build() override fun add( - a: UnivariateHistogram, - b: UnivariateHistogram, + left: UnivariateHistogram, + right: UnivariateHistogram, ): UnivariateHistogram { // require(a.context == this) { "Histogram $a does not belong to this context" } // require(b.context == this) { "Histogram $b does not belong to this context" } val bins = TreeMap().apply { - (a.bins.map { it.domain } union b.bins.map { it.domain }).forEach { def -> + (left.bins.map { it.domain } union right.bins.map { it.domain }).forEach { def -> put( def.center, UnivariateBin( def, - value = (a[def.center]?.value ?: 0.0) + (b[def.center]?.value ?: 0.0), - standardDeviation = (a[def.center]?.standardDeviation - ?: 0.0) + (b[def.center]?.standardDeviation ?: 0.0) + value = (left[def.center]?.value ?: 0.0) + (right[def.center]?.value ?: 0.0), + standardDeviation = (left[def.center]?.standardDeviation + ?: 0.0) + (right[def.center]?.standardDeviation ?: 0.0) ) ) } diff --git a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt index 1a6e3325b..645a14e30 100644 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt @@ -28,10 +28,10 @@ public object JafamaDoubleField : ExtendedField, Norm, S else -> super.binaryOperationFunction(operation) } - override inline fun add(a: Double, b: Double): Double = a + b + override inline fun add(left: Double, right: Double): Double = left + right - override inline fun multiply(a: Double, b: Double): Double = a * b - override inline fun divide(a: Double, b: Double): Double = a / b + override inline fun multiply(left: Double, right: Double): Double = left * right + override inline fun divide(left: Double, right: Double): Double = left / right override inline fun scale(a: Double, value: Double): Double = a * value @@ -57,10 +57,10 @@ public object JafamaDoubleField : ExtendedField, Norm, S override inline fun norm(arg: Double): Double = FastMath.abs(arg) override inline fun Double.unaryMinus(): Double = -this - override inline fun Double.plus(b: Double): Double = this + b - override inline fun Double.minus(b: Double): Double = this - b - override inline fun Double.times(b: Double): Double = this * b - override inline fun Double.div(b: Double): Double = this / b + override inline fun Double.plus(other: Double): Double = this + other + override inline fun Double.minus(other: Double): Double = this - other + override inline fun Double.times(other: Double): Double = this * other + override inline fun Double.div(other: Double): Double = this / other } /** @@ -79,10 +79,10 @@ public object StrictJafamaDoubleField : ExtendedField, Norm super.binaryOperationFunction(operation) } - override inline fun add(a: Double, b: Double): Double = a + b + override inline fun add(left: Double, right: Double): Double = left + right - override inline fun multiply(a: Double, b: Double): Double = a * b - override inline fun divide(a: Double, b: Double): Double = a / b + override inline fun multiply(left: Double, right: Double): Double = left * right + override inline fun divide(left: Double, right: Double): Double = left / right override inline fun scale(a: Double, value: Double): Double = a * value @@ -108,8 +108,8 @@ public object StrictJafamaDoubleField : ExtendedField, Norm> : AlgebraND> : GroupOpsND, Nd4jArrayAlgebra { - override fun add(a: StructureND, b: StructureND): Nd4jArrayStructure = - a.ndArray.add(b.ndArray).wrap() + override fun add(left: StructureND, right: StructureND): Nd4jArrayStructure = + left.ndArray.add(right.ndArray).wrap() - override operator fun StructureND.minus(b: StructureND): Nd4jArrayStructure = - ndArray.sub(b.ndArray).wrap() + override operator fun StructureND.minus(other: StructureND): Nd4jArrayStructure = + ndArray.sub(other.ndArray).wrap() override operator fun StructureND.unaryMinus(): Nd4jArrayStructure = ndArray.neg().wrap() @@ -94,8 +94,8 @@ public sealed interface Nd4jArrayGroupOps> : GroupOpsND @OptIn(UnstableKMathAPI::class) public sealed interface Nd4jArrayRingOps> : RingOpsND, Nd4jArrayGroupOps { - override fun multiply(a: StructureND, b: StructureND): Nd4jArrayStructure = - a.ndArray.mul(b.ndArray).wrap() + override fun multiply(left: StructureND, right: StructureND): Nd4jArrayStructure = + left.ndArray.mul(right.ndArray).wrap() // // override operator fun Nd4jArrayStructure.minus(b: Number): Nd4jArrayStructure { // check(this) @@ -132,8 +132,8 @@ public sealed interface Nd4jArrayRingOps> : RingOpsND, */ public sealed interface Nd4jArrayField> : FieldOpsND, Nd4jArrayRingOps { - override fun divide(a: StructureND, b: StructureND): Nd4jArrayStructure = - a.ndArray.div(b.ndArray).wrap() + override fun divide(left: StructureND, right: StructureND): Nd4jArrayStructure = + left.ndArray.div(right.ndArray).wrap() public operator fun Number.div(b: StructureND): Nd4jArrayStructure = b.ndArray.rdiv(this).wrap() diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt index c1bbace86..e0be72d4b 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt @@ -41,8 +41,8 @@ public class SamplerSpace(public val algebra: S) : Group = ConstantSampler(algebra.zero) - override fun add(a: Sampler, b: Sampler): Sampler = BasicSampler { generator -> - a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> algebra { aValue + bValue } } + override fun add(left: Sampler, right: Sampler): Sampler = BasicSampler { generator -> + left.sample(generator).zip(right.sample(generator)) { aValue, bValue -> algebra { aValue + bValue } } } override fun scale(a: Sampler, value: Double): Sampler = BasicSampler { generator -> diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt index 6076748d9..810ebe777 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt @@ -5,7 +5,7 @@ package space.kscience.kmath.tensors.api -import space.kscience.kmath.operations.Algebra +import space.kscience.kmath.operations.RingOps /** * Algebra over a ring on [Tensor]. @@ -13,7 +13,7 @@ import space.kscience.kmath.operations.Algebra * * @param T the type of items in the tensors. */ -public interface TensorAlgebra : Algebra> { +public interface TensorAlgebra : RingOps> { /** * Returns a single tensor value of unit dimension if tensor shape equals to [1]. * @@ -53,7 +53,7 @@ public interface TensorAlgebra : Algebra> { * @param other tensor to be added. * @return the sum of this tensor and [other]. */ - public operator fun Tensor.plus(other: Tensor): Tensor + override fun Tensor.plus(other: Tensor): Tensor /** * Adds the scalar [value] to each element of this tensor. @@ -93,7 +93,7 @@ public interface TensorAlgebra : Algebra> { * @param other tensor to be subtracted. * @return the difference between this tensor and [other]. */ - public operator fun Tensor.minus(other: Tensor): Tensor + override fun Tensor.minus(other: Tensor): Tensor /** * Subtracts the scalar [value] from each element of this tensor. @@ -134,7 +134,7 @@ public interface TensorAlgebra : Algebra> { * @param other tensor to be multiplied. * @return the product of this tensor and [other]. */ - public operator fun Tensor.times(other: Tensor): Tensor + override fun Tensor.times(other: Tensor): Tensor /** * Multiplies the scalar [value] by each element of this tensor. @@ -155,7 +155,7 @@ public interface TensorAlgebra : Algebra> { * * @return tensor negation of the original tensor. */ - public operator fun Tensor.unaryMinus(): Tensor + override fun Tensor.unaryMinus(): Tensor /** * Returns the tensor at index i @@ -323,4 +323,8 @@ public interface TensorAlgebra : Algebra> { * @return the index of maximum value of each row of the input tensor in the given dimension [dim]. */ public fun Tensor.argMax(dim: Int, keepDim: Boolean): Tensor + + override fun add(left: Tensor, right: Tensor): Tensor = left + right + + override fun multiply(left: Tensor, right: Tensor): Tensor = left * right } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt index e0451c2eb..1f5778d5e 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt @@ -27,7 +27,7 @@ internal fun Tensor.copyToBufferedTensor(): BufferedTensor = internal fun Tensor.toBufferedTensor(): BufferedTensor = when (this) { is BufferedTensor -> this is MutableBufferND -> if (this.indexes == TensorLinearStructure(this.shape)) { - BufferedTensor(this.shape, this.mutableBuffer, 0) + BufferedTensor(this.shape, this.buffer, 0) } else { this.copyToBufferedTensor() } diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt index fca23bc16..a914542bc 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt @@ -88,17 +88,17 @@ public class ViktorFieldND(override val shape: IntArray) : FieldND, b: StructureND): ViktorStructureND = - (a.f64Buffer + b.f64Buffer).asStructure() + override fun add(left: StructureND, right: StructureND): ViktorStructureND = + (left.f64Buffer + right.f64Buffer).asStructure() override fun scale(a: StructureND, value: Double): ViktorStructureND = (a.f64Buffer * value).asStructure() - override inline fun StructureND.plus(b: StructureND): ViktorStructureND = - (f64Buffer + b.f64Buffer).asStructure() + override inline fun StructureND.plus(other: StructureND): ViktorStructureND = + (f64Buffer + other.f64Buffer).asStructure() - override inline fun StructureND.minus(b: StructureND): ViktorStructureND = - (f64Buffer - b.f64Buffer).asStructure() + override inline fun StructureND.minus(other: StructureND): ViktorStructureND = + (f64Buffer - other.f64Buffer).asStructure() override inline fun StructureND.times(k: Number): ViktorStructureND = (f64Buffer * k.toDouble()).asStructure() From 4513b06e87d4bb2a16266eb5cba5e96167c0e0cd Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 17 Oct 2021 13:09:21 +0300 Subject: [PATCH 04/10] Shapeless ND and Buffer algebras --- .idea/copyright/kmath.xml | 6 - .idea/copyright/profiles_settings.xml | 21 --- .idea/scopes/Apply_copyright.xml | 4 - .../space/kscience/kmath/nd/AlgebraND.kt | 4 +- .../kmath/operations/NumericAlgebra.kt | 2 +- .../kscience/kmath/viktor/ViktorFieldOpsND.kt | 124 ++++++++++++++++++ .../kmath/viktor/ViktorStructureND.kt | 100 +------------- 7 files changed, 130 insertions(+), 131 deletions(-) delete mode 100644 .idea/copyright/kmath.xml delete mode 100644 .idea/copyright/profiles_settings.xml delete mode 100644 .idea/scopes/Apply_copyright.xml create mode 100644 kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt diff --git a/.idea/copyright/kmath.xml b/.idea/copyright/kmath.xml deleted file mode 100644 index 1070e5d33..000000000 --- a/.idea/copyright/kmath.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index b538bdf41..000000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/scopes/Apply_copyright.xml b/.idea/scopes/Apply_copyright.xml deleted file mode 100644 index 0eb589133..000000000 --- a/.idea/scopes/Apply_copyright.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt index 3f9b43f03..b4e8b7487 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt @@ -203,7 +203,9 @@ public interface RingND> : Ring>, RingOpsND> : FieldOps>, RingOpsND, +public interface FieldOpsND> : + FieldOps>, + RingOpsND, ScaleOperations> { /** * Element-wise division. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt index ca88ad42d..5f6848211 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt @@ -150,7 +150,7 @@ public interface ScaleOperations : Algebra { * TODO to be removed and replaced by extensions after multiple receivers are there */ @UnstableKMathAPI -public interface NumbersAddOps : Ring, NumericAlgebra { +public interface NumbersAddOps : RingOps, NumericAlgebra { /** * Addition of element and scalar. * diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt new file mode 100644 index 000000000..c72553a64 --- /dev/null +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package space.kscience.kmath.viktor + +import org.jetbrains.bio.viktor.F64Array +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.nd.* +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.ExtendedFieldOps +import space.kscience.kmath.operations.NumbersAddOps + +@OptIn(UnstableKMathAPI::class) +@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public open class ViktorFieldOpsND : + FieldOpsND, + ExtendedFieldOps> { + + public val StructureND.f64Buffer: F64Array + get() = when (this) { + is ViktorStructureND -> this.f64Buffer + else -> produce(shape) { this@f64Buffer[it] }.f64Buffer + } + + override val elementAlgebra: DoubleField get() = DoubleField + + override fun produce(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND = + F64Array(*shape).apply { + DefaultStrides(shape).indices().forEach { index -> + set(value = DoubleField.initializer(index), indices = index) + } + }.asStructure() + + override fun StructureND.unaryMinus(): StructureND = -1 * this + + override fun StructureND.map(transform: DoubleField.(Double) -> Double): ViktorStructureND = + F64Array(*shape).apply { + DefaultStrides(shape).indices().forEach { index -> + set(value = DoubleField.transform(this@map[index]), indices = index) + } + }.asStructure() + + override fun StructureND.mapIndexed( + transform: DoubleField.(index: IntArray, Double) -> Double, + ): ViktorStructureND = F64Array(*shape).apply { + DefaultStrides(shape).indices().forEach { index -> + set(value = DoubleField.transform(index, this@mapIndexed[index]), indices = index) + } + }.asStructure() + + override fun zip( + left: StructureND, + right: StructureND, + transform: DoubleField.(Double, Double) -> Double, + ): ViktorStructureND { + require(left.shape.contentEquals(right.shape)) + return F64Array(*left.shape).apply { + DefaultStrides(left.shape).indices().forEach { index -> + set(value = DoubleField.transform(left[index], right[index]), indices = index) + } + }.asStructure() + } + + override fun add(left: StructureND, right: StructureND): ViktorStructureND = + (left.f64Buffer + right.f64Buffer).asStructure() + + override fun scale(a: StructureND, value: Double): ViktorStructureND = + (a.f64Buffer * value).asStructure() + + override fun StructureND.plus(other: StructureND): ViktorStructureND = + (f64Buffer + other.f64Buffer).asStructure() + + override fun StructureND.minus(other: StructureND): ViktorStructureND = + (f64Buffer - other.f64Buffer).asStructure() + + override fun StructureND.times(k: Number): ViktorStructureND = + (f64Buffer * k.toDouble()).asStructure() + + override fun StructureND.plus(arg: Double): ViktorStructureND = + (f64Buffer.plus(arg)).asStructure() + + override fun sin(arg: StructureND): ViktorStructureND = arg.map { sin(it) } + override fun cos(arg: StructureND): ViktorStructureND = arg.map { cos(it) } + override fun tan(arg: StructureND): ViktorStructureND = arg.map { tan(it) } + override fun asin(arg: StructureND): ViktorStructureND = arg.map { asin(it) } + override fun acos(arg: StructureND): ViktorStructureND = arg.map { acos(it) } + override fun atan(arg: StructureND): ViktorStructureND = arg.map { atan(it) } + + override fun power(arg: StructureND, pow: Number): ViktorStructureND = arg.map { it.pow(pow) } + + override fun exp(arg: StructureND): ViktorStructureND = arg.f64Buffer.exp().asStructure() + + override fun ln(arg: StructureND): ViktorStructureND = arg.f64Buffer.log().asStructure() + + override fun sinh(arg: StructureND): ViktorStructureND = arg.map { sinh(it) } + + override fun cosh(arg: StructureND): ViktorStructureND = arg.map { cosh(it) } + + override fun asinh(arg: StructureND): ViktorStructureND = arg.map { asinh(it) } + + override fun acosh(arg: StructureND): ViktorStructureND = arg.map { acosh(it) } + + override fun atanh(arg: StructureND): ViktorStructureND = arg.map { atanh(it) } + + public companion object : ViktorFieldOpsND() +} + +public val DoubleField.viktorAlgebra: ViktorFieldOpsND get() = ViktorFieldOpsND + +public open class ViktorFieldND( + override val shape: Shape +) : ViktorFieldOpsND(), FieldND, NumbersAddOps> { + override val zero: ViktorStructureND by lazy { F64Array.full(init = 0.0, shape = shape).asStructure() } + override val one: ViktorStructureND by lazy { F64Array.full(init = 1.0, shape = shape).asStructure() } + + override fun number(value: Number): ViktorStructureND = + F64Array.full(init = value.toDouble(), shape = shape).asStructure() +} + +public fun DoubleField.viktorAlgebra(vararg shape: Int): ViktorFieldND = ViktorFieldND(shape) + +public fun ViktorFieldND(vararg shape: Int): ViktorFieldND = ViktorFieldND(shape) \ No newline at end of file diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt index a914542bc..0d29983f9 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt @@ -7,12 +7,8 @@ package space.kscience.kmath.viktor import org.jetbrains.bio.viktor.F64Array import space.kscience.kmath.misc.PerformancePitfall -import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.nd.* -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.ExtendedField -import space.kscience.kmath.operations.NumbersAddOps -import space.kscience.kmath.operations.ScaleOperations +import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.MutableStructureND @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") public class ViktorStructureND(public val f64Buffer: F64Array) : MutableStructureND { @@ -31,96 +27,4 @@ public class ViktorStructureND(public val f64Buffer: F64Array) : MutableStructur public fun F64Array.asStructure(): ViktorStructureND = ViktorStructureND(this) -@OptIn(UnstableKMathAPI::class) -@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -public class ViktorFieldND(override val shape: IntArray) : FieldND, - NumbersAddOps>, ExtendedField>, - ScaleOperations> { - public val StructureND.f64Buffer: F64Array - get() = when { - !shape.contentEquals(this@ViktorFieldND.shape) -> throw ShapeMismatchException( - this@ViktorFieldND.shape, - shape - ) - this is ViktorStructureND && this.f64Buffer.shape.contentEquals(this@ViktorFieldND.shape) -> this.f64Buffer - else -> produce(shape) { this@f64Buffer[it] }.f64Buffer - } - - override val zero: ViktorStructureND by lazy { F64Array.full(init = 0.0, shape = shape).asStructure() } - override val one: ViktorStructureND by lazy { F64Array.full(init = 1.0, shape = shape).asStructure() } - - private val strides: Strides = DefaultStrides(shape) - - override val elementAlgebra: DoubleField get() = DoubleField - - override fun produce(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND = - F64Array(*shape).apply { - this@ViktorFieldND.strides.indices().forEach { index -> - set(value = DoubleField.initializer(index), indices = index) - } - }.asStructure() - - override fun StructureND.unaryMinus(): StructureND = -1 * this - - override fun StructureND.map(transform: DoubleField.(Double) -> Double): ViktorStructureND = - F64Array(*this@ViktorFieldND.shape).apply { - this@ViktorFieldND.strides.indices().forEach { index -> - set(value = DoubleField.transform(this@map[index]), indices = index) - } - }.asStructure() - - override fun StructureND.mapIndexed( - transform: DoubleField.(index: IntArray, Double) -> Double, - ): ViktorStructureND = F64Array(*this@ViktorFieldND.shape).apply { - this@ViktorFieldND.strides.indices().forEach { index -> - set(value = DoubleField.transform(index, this@mapIndexed[index]), indices = index) - } - }.asStructure() - - override fun zip( - left: StructureND, - right: StructureND, - transform: DoubleField.(Double, Double) -> Double, - ): ViktorStructureND = F64Array(*shape).apply { - this@ViktorFieldND.strides.indices().forEach { index -> - set(value = DoubleField.transform(left[index], right[index]), indices = index) - } - }.asStructure() - - override fun add(left: StructureND, right: StructureND): ViktorStructureND = - (left.f64Buffer + right.f64Buffer).asStructure() - - override fun scale(a: StructureND, value: Double): ViktorStructureND = - (a.f64Buffer * value).asStructure() - - override inline fun StructureND.plus(other: StructureND): ViktorStructureND = - (f64Buffer + other.f64Buffer).asStructure() - - override inline fun StructureND.minus(other: StructureND): ViktorStructureND = - (f64Buffer - other.f64Buffer).asStructure() - - override inline fun StructureND.times(k: Number): ViktorStructureND = - (f64Buffer * k.toDouble()).asStructure() - - override inline fun StructureND.plus(arg: Double): ViktorStructureND = - (f64Buffer.plus(arg)).asStructure() - - override fun number(value: Number): ViktorStructureND = - F64Array.full(init = value.toDouble(), shape = shape).asStructure() - - override fun sin(arg: StructureND): ViktorStructureND = arg.map { sin(it) } - override fun cos(arg: StructureND): ViktorStructureND = arg.map { cos(it) } - override fun tan(arg: StructureND): ViktorStructureND = arg.map { tan(it) } - override fun asin(arg: StructureND): ViktorStructureND = arg.map { asin(it) } - override fun acos(arg: StructureND): ViktorStructureND = arg.map { acos(it) } - override fun atan(arg: StructureND): ViktorStructureND = arg.map { atan(it) } - - override fun power(arg: StructureND, pow: Number): ViktorStructureND = arg.map { it.pow(pow) } - - override fun exp(arg: StructureND): ViktorStructureND = arg.f64Buffer.exp().asStructure() - - override fun ln(arg: StructureND): ViktorStructureND = arg.f64Buffer.log().asStructure() -} - -public fun ViktorFieldND(vararg shape: Int): ViktorFieldND = ViktorFieldND(shape) From 6de43ee4bae0f224c44b798e82e00a60921d9b74 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 17 Oct 2021 13:52:40 +0300 Subject: [PATCH 05/10] update multik module --- .../src/main/kotlin/space/kscience/kmath/tensors/multik.kt | 3 +++ settings.gradle.kts | 1 + 2 files changed, 4 insertions(+) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt index 820a793ac..fc6d7dd56 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.tensors import org.jetbrains.kotlinx.multik.api.Multik import org.jetbrains.kotlinx.multik.api.linalg.dot +import org.jetbrains.kotlinx.multik.api.math.exp import org.jetbrains.kotlinx.multik.api.ndarray import org.jetbrains.kotlinx.multik.ndarray.operations.minus import org.jetbrains.kotlinx.multik.ndarray.operations.plus @@ -18,4 +19,6 @@ fun main() { 2 + (-a) - 2 a dot a + + a.exp() } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index dc70cbb9e..e73381bf2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,6 +32,7 @@ include( ":kmath-histograms", ":kmath-commons", ":kmath-viktor", + ":kmath-multik", ":kmath-optimization", ":kmath-stat", ":kmath-nd4j", From 9b9fe07044ff25b8ebc8bb25b57d951bae02dd36 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 17 Oct 2021 16:42:46 +0300 Subject: [PATCH 06/10] Specialized operations for ND algebra --- .../kmath/operations/mixedNDOperations.kt | 24 ++++++++++++++++ .../DerivativeStructureExpression.kt | 8 +++--- .../kscience/kmath/complex/Quaternion.kt | 14 +++++----- .../space/kscience/kmath/nd/DoubleFieldND.kt | 28 +++++++++++++++++++ .../kmath/operations/NumericAlgebra.kt | 20 ++++++------- 5 files changed, 73 insertions(+), 21 deletions(-) create mode 100644 examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt diff --git a/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt new file mode 100644 index 000000000..628344ceb --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt @@ -0,0 +1,24 @@ +package space.kscience.kmath.operations + +import space.kscience.kmath.commons.linear.CMLinearSpace +import space.kscience.kmath.linear.matrix +import space.kscience.kmath.nd.DoubleBufferND +import space.kscience.kmath.nd.Shape +import space.kscience.kmath.nd.Structure2D +import space.kscience.kmath.nd.ndAlgebra +import space.kscience.kmath.viktor.ViktorStructureND +import space.kscience.kmath.viktor.viktorAlgebra + +fun main() { + val viktorStructure: ViktorStructureND = DoubleField.viktorAlgebra.produce(Shape(2, 2)) { (i, j) -> + if (i == j) 2.0 else 0.0 + } + + val cmMatrix: Structure2D = CMLinearSpace.matrix(2, 2)(0.0, 1.0, 0.0, 3.0) + + val res: DoubleBufferND = DoubleField.ndAlgebra { + exp(viktorStructure) + 2.0 * cmMatrix + } + + println(res) +} \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt index d42e40d1e..4d2bd6237 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt @@ -99,10 +99,10 @@ public class DerivativeStructureField( override fun exp(arg: DerivativeStructure): DerivativeStructure = arg.exp() override fun ln(arg: DerivativeStructure): DerivativeStructure = arg.log() - override operator fun DerivativeStructure.plus(b: Number): DerivativeStructure = add(b.toDouble()) - override operator fun DerivativeStructure.minus(b: Number): DerivativeStructure = subtract(b.toDouble()) - override operator fun Number.plus(b: DerivativeStructure): DerivativeStructure = b + this - override operator fun Number.minus(b: DerivativeStructure): DerivativeStructure = b - this + override operator fun DerivativeStructure.plus(other: Number): DerivativeStructure = add(other.toDouble()) + override operator fun DerivativeStructure.minus(other: Number): DerivativeStructure = subtract(other.toDouble()) + override operator fun Number.plus(other: DerivativeStructure): DerivativeStructure = other + this + override operator fun Number.minus(other: DerivativeStructure): DerivativeStructure = other - this } /** diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt index 9fdd60e1f..47cc61313 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt @@ -158,16 +158,16 @@ public object QuaternionField : Field, Norm, return Quaternion(ln(n), th * arg.x, th * arg.y, th * arg.z) } - override operator fun Number.plus(b: Quaternion): Quaternion = Quaternion(toDouble() + b.w, b.x, b.y, b.z) + override operator fun Number.plus(other: Quaternion): Quaternion = Quaternion(toDouble() + other.w, other.x, other.y, other.z) - override operator fun Number.minus(b: Quaternion): Quaternion = - Quaternion(toDouble() - b.w, -b.x, -b.y, -b.z) + override operator fun Number.minus(other: Quaternion): Quaternion = + Quaternion(toDouble() - other.w, -other.x, -other.y, -other.z) - override operator fun Quaternion.plus(b: Number): Quaternion = Quaternion(w + b.toDouble(), x, y, z) - override operator fun Quaternion.minus(b: Number): Quaternion = Quaternion(w - b.toDouble(), x, y, z) + override operator fun Quaternion.plus(other: Number): Quaternion = Quaternion(w + other.toDouble(), x, y, z) + override operator fun Quaternion.minus(other: Number): Quaternion = Quaternion(w - other.toDouble(), x, y, z) - override operator fun Number.times(b: Quaternion): Quaternion = - Quaternion(toDouble() * b.w, toDouble() * b.x, toDouble() * b.y, toDouble() * b.z) + override operator fun Number.times(other: Quaternion): Quaternion = + Quaternion(toDouble() * other.w, toDouble() * other.x, toDouble() * other.y, toDouble() * other.z) override fun Quaternion.unaryMinus(): Quaternion = Quaternion(-w, -x, -y, -z) override fun norm(arg: Quaternion): Quaternion = sqrt(arg.conjugate * arg) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt index 1502a6fd0..6235971a2 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt @@ -81,6 +81,34 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND(D override fun StructureND.div(other: StructureND): DoubleBufferND = zipInline(toBufferND(), other.toBufferND()) { l, r -> l / r } + override fun divide(left: StructureND, right: StructureND): DoubleBufferND = + zipInline(left.toBufferND(), right.toBufferND()) { l: Double, r: Double -> l / r } + + override fun StructureND.div(arg: Double): DoubleBufferND = + mapInline(toBufferND()) { it / arg } + + override fun Double.div(arg: StructureND): DoubleBufferND = + mapInline(arg.toBufferND()) { this / it } + + override fun StructureND.unaryPlus(): DoubleBufferND = toBufferND() + + override fun StructureND.plus(other: StructureND): DoubleBufferND = + zipInline(toBufferND(), other.toBufferND()) { l: Double, r: Double -> l + r } + + override fun StructureND.minus(other: StructureND): DoubleBufferND = + zipInline(toBufferND(), other.toBufferND()) { l: Double, r: Double -> l - r } + + override fun StructureND.times(other: StructureND): DoubleBufferND = + zipInline(toBufferND(), other.toBufferND()) { l: Double, r: Double -> l * r } + + override fun StructureND.times(k: Number): DoubleBufferND = + mapInline(toBufferND()) { it * k.toDouble() } + + override fun StructureND.div(k: Number): DoubleBufferND = + mapInline(toBufferND()) { it / k.toDouble() } + + override fun Number.times(other: StructureND): DoubleBufferND = other * this + override fun StructureND.plus(arg: Double): DoubleBufferND = mapInline(toBufferND()) { it + arg } override fun StructureND.minus(arg: Double): StructureND = mapInline(toBufferND()) { it - arg } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt index 5f6848211..6be3449f9 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt @@ -139,10 +139,10 @@ public interface ScaleOperations : Algebra { * Multiplication of this number by element. * * @receiver the multiplier. - * @param b the multiplicand. + * @param other the multiplicand. * @return the product. */ - public operator fun Number.times(b: T): T = b * this + public operator fun Number.times(other: T): T = other * this } /** @@ -155,33 +155,33 @@ public interface NumbersAddOps : RingOps, NumericAlgebra { * Addition of element and scalar. * * @receiver the augend. - * @param b the addend. + * @param other the addend. */ - public operator fun T.plus(b: Number): T = this + number(b) + public operator fun T.plus(other: Number): T = this + number(other) /** * Addition of scalar and element. * * @receiver the augend. - * @param b the addend. + * @param other the addend. */ - public operator fun Number.plus(b: T): T = b + this + public operator fun Number.plus(other: T): T = other + this /** * Subtraction of element from number. * * @receiver the minuend. - * @param b the subtrahend. + * @param other the subtrahend. * @receiver the difference. */ - public operator fun T.minus(b: Number): T = this - number(b) + public operator fun T.minus(other: Number): T = this - number(other) /** * Subtraction of number from element. * * @receiver the minuend. - * @param b the subtrahend. + * @param other the subtrahend. * @receiver the difference. */ - public operator fun Number.minus(b: T): T = -b + this + public operator fun Number.minus(other: T): T = -other + this } \ No newline at end of file From 827f115a9257235e8e36ee43af06937e2488eb75 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 17 Oct 2021 21:12:14 +0300 Subject: [PATCH 07/10] Multik wrapper --- benchmarks/build.gradle.kts | 1 + .../kmath/benchmarks/NDFieldBenchmark.kt | 24 +++- .../space/kscience/kmath/nd/ShapeIndex.kt | 15 +- .../kscience/kmath/multik/MultikOpsND.kt | 135 ++++++++++++++++++ .../kmath/multik/MultikTensorAlgebra.kt | 30 ++-- .../kscience/kmath/multik/MultikNDTest.kt | 13 ++ .../kscience/kmath/nd4j/Nd4jArrayAlgebra.kt | 5 +- .../tensors/core/tensorAlgebraExtensions.kt | 10 +- 8 files changed, 204 insertions(+), 29 deletions(-) create mode 100644 kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt create mode 100644 kmath-multik/src/test/kotlin/space/kscience/kmath/multik/MultikNDTest.kt diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 686c491b9..cca3d312d 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -48,6 +48,7 @@ kotlin { implementation(project(":kmath-nd4j")) implementation(project(":kmath-kotlingrad")) implementation(project(":kmath-viktor")) + implementation(projects.kmathMultik) implementation("org.nd4j:nd4j-native:1.0.0-M1") // uncomment if your system supports AVX2 // val os = System.getProperty("os.name") diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt index 7f7c03412..672aa4307 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt @@ -9,6 +9,7 @@ import kotlinx.benchmark.Benchmark import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Scope import kotlinx.benchmark.State +import space.kscience.kmath.multik.multikND import space.kscience.kmath.nd.BufferedFieldOpsND import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.ndAlgebra @@ -17,8 +18,9 @@ import space.kscience.kmath.nd4j.nd4j import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.structures.Buffer import space.kscience.kmath.tensors.core.DoubleTensor -import space.kscience.kmath.tensors.core.ones +import space.kscience.kmath.tensors.core.one import space.kscience.kmath.tensors.core.tensorAlgebra +import space.kscience.kmath.viktor.viktorAlgebra @State(Scope.Benchmark) internal class NDFieldBenchmark { @@ -43,16 +45,30 @@ internal class NDFieldBenchmark { blackhole.consume(res) } + @Benchmark + fun multikAdd(blackhole: Blackhole) = with(multikField) { + var res: StructureND = one(shape) + repeat(n) { res += 1.0 } + blackhole.consume(res) + } + + @Benchmark + fun viktorAdd(blackhole: Blackhole) = with(viktorField) { + var res: StructureND = one(shape) + repeat(n) { res += 1.0 } + blackhole.consume(res) + } + @Benchmark fun tensorAdd(blackhole: Blackhole) = with(Double.tensorAlgebra) { - var res: DoubleTensor = ones(dim, dim) + var res: DoubleTensor = one(shape) repeat(n) { res = res + 1.0 } blackhole.consume(res) } @Benchmark fun tensorInPlaceAdd(blackhole: Blackhole) = with(Double.tensorAlgebra) { - val res: DoubleTensor = ones(dim, dim) + val res: DoubleTensor = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @@ -72,5 +88,7 @@ internal class NDFieldBenchmark { private val specializedField = DoubleField.ndAlgebra private val genericField = BufferedFieldOpsND(DoubleField, Buffer.Companion::boxing) private val nd4jField = DoubleField.nd4j + private val multikField = DoubleField.multikND + private val viktorField = DoubleField.viktorAlgebra } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndex.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndex.kt index bdbae70c2..a78bcfa70 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndex.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndex.kt @@ -105,16 +105,15 @@ public class DefaultStrides private constructor(override val shape: IntArray) : override fun hashCode(): Int = shape.contentHashCode() - @ThreadLocal - public companion object { - //private val defaultStridesCache = HashMap() + public companion object { /** * Cached builder for default strides */ - public operator fun invoke(shape: IntArray): Strides = DefaultStrides(shape) - //defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } - - //TODO fix cache + public operator fun invoke(shape: IntArray): Strides = + defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } } -} \ No newline at end of file +} + +@ThreadLocal +private val defaultStridesCache = HashMap() \ No newline at end of file diff --git a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt new file mode 100644 index 000000000..d6996d427 --- /dev/null +++ b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt @@ -0,0 +1,135 @@ +package space.kscience.kmath.multik + +import org.jetbrains.kotlinx.multik.api.mk +import org.jetbrains.kotlinx.multik.api.zeros +import org.jetbrains.kotlinx.multik.ndarray.data.* +import org.jetbrains.kotlinx.multik.ndarray.operations.* +import space.kscience.kmath.nd.FieldOpsND +import space.kscience.kmath.nd.RingOpsND +import space.kscience.kmath.nd.Shape +import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.operations.* + +/** + * A ring algebra for Multik operations + */ +public open class MultikRingOpsND> internal constructor( + public val type: DataType, + override val elementAlgebra: A +) : RingOpsND { + + protected fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) + + override fun produce(shape: Shape, initializer: A.(IntArray) -> T): MultikTensor { + val res = mk.zeros(shape, type).asDNArray() + for (index in res.multiIndices) { + res[index] = elementAlgebra.initializer(index) + } + return res.wrap() + } + + protected fun StructureND.asMultik(): MultikTensor = if (this is MultikTensor) { + this + } else { + produce(shape) { get(it) } + } + + override fun StructureND.map(transform: A.(T) -> T): MultikTensor { + //taken directly from Multik sources + val array = asMultik().array + val data = initMemoryView(array.size, type) + var count = 0 + for (el in array) data[count++] = elementAlgebra.transform(el) + return NDArray(data, shape = array.shape, dim = array.dim).wrap() + } + + override fun StructureND.mapIndexed(transform: A.(index: IntArray, T) -> T): MultikTensor { + //taken directly from Multik sources + val array = asMultik().array + val data = initMemoryView(array.size, type) + val indexIter = array.multiIndices.iterator() + var index = 0 + for (item in array) { + if (indexIter.hasNext()) { + data[index++] = elementAlgebra.transform(indexIter.next(), item) + } else { + throw ArithmeticException("Index overflow has happened.") + } + } + return NDArray(data, shape = array.shape, dim = array.dim).wrap() + } + + override fun zip(left: StructureND, right: StructureND, transform: A.(T, T) -> T): MultikTensor { + require(left.shape.contentEquals(right.shape)) { "ND array shape mismatch" } //TODO replace by ShapeMismatchException + val leftArray = left.asMultik().array + val rightArray = right.asMultik().array + val data = initMemoryView(leftArray.size, type) + var counter = 0 + val leftIterator = leftArray.iterator() + val rightIterator = rightArray.iterator() + //iterating them together + while (leftIterator.hasNext()) { + data[counter++] = elementAlgebra.transform(leftIterator.next(), rightIterator.next()) + } + return NDArray(data, shape = leftArray.shape, dim = leftArray.dim).wrap() + } + + override fun StructureND.unaryMinus(): MultikTensor = asMultik().array.unaryMinus().wrap() + + override fun add(left: StructureND, right: StructureND): MultikTensor = + (left.asMultik().array + right.asMultik().array).wrap() + + override fun StructureND.plus(arg: T): MultikTensor = + asMultik().array.plus(arg).wrap() + + override fun StructureND.minus(arg: T): MultikTensor = asMultik().array.minus(arg).wrap() + + override fun T.plus(arg: StructureND): MultikTensor = arg + this + + override fun T.minus(arg: StructureND): MultikTensor = arg.map { this@minus - it } + + override fun multiply(left: StructureND, right: StructureND): MultikTensor = + left.asMultik().array.times(right.asMultik().array).wrap() + + override fun StructureND.times(arg: T): MultikTensor = + asMultik().array.times(arg).wrap() + + override fun T.times(arg: StructureND): MultikTensor = arg * this + + override fun StructureND.unaryPlus(): MultikTensor = asMultik() + + override fun StructureND.plus(other: StructureND): MultikTensor = + asMultik().array.plus(other.asMultik().array).wrap() + + override fun StructureND.minus(other: StructureND): MultikTensor = + asMultik().array.minus(other.asMultik().array).wrap() + + override fun StructureND.times(other: StructureND): MultikTensor = + asMultik().array.times(other.asMultik().array).wrap() +} + +/** + * A field algebra for multik operations + */ +public class MultikFieldOpsND> internal constructor( + type: DataType, + elementAlgebra: A +) : MultikRingOpsND(type, elementAlgebra), FieldOpsND { + override fun StructureND.div(other: StructureND): StructureND = + asMultik().array.div(other.asMultik().array).wrap() +} + +public val DoubleField.multikND: MultikFieldOpsND + get() = MultikFieldOpsND(DataType.DoubleDataType, DoubleField) + +public val FloatField.multikND: MultikFieldOpsND + get() = MultikFieldOpsND(DataType.FloatDataType, FloatField) + +public val ShortRing.multikND: MultikRingOpsND + get() = MultikRingOpsND(DataType.ShortDataType, ShortRing) + +public val IntRing.multikND: MultikRingOpsND + get() = MultikRingOpsND(DataType.IntDataType, IntRing) + +public val LongRing.multikND: MultikRingOpsND + get() = MultikRingOpsND(DataType.LongDataType, LongRing) \ No newline at end of file diff --git a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt index b25d3ef56..e9e56e06e 100644 --- a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt +++ b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt @@ -5,6 +5,8 @@ package space.kscience.kmath.multik +import org.jetbrains.kotlinx.multik.api.mk +import org.jetbrains.kotlinx.multik.api.zeros import org.jetbrains.kotlinx.multik.ndarray.data.* import org.jetbrains.kotlinx.multik.ndarray.operations.* import space.kscience.kmath.misc.PerformancePitfall @@ -30,6 +32,7 @@ public value class MultikTensor(public val array: MutableMultiArray) : public abstract class MultikTensorAlgebra( + public val type: DataType, public val elementAlgebra: Ring, public val comparator: Comparator ) : TensorAlgebra { @@ -38,15 +41,19 @@ public abstract class MultikTensorAlgebra( * Convert a tensor to [MultikTensor] if necessary. If tensor is converted, changes on the resulting tensor * are not reflected back onto the source */ - public fun Tensor.asMultik(): MultikTensor { + private fun Tensor.asMultik(): MultikTensor { return if (this is MultikTensor) { this } else { - TODO() + val res = mk.zeros(shape, type).asDNArray() + for (index in res.multiIndices) { + res[index] = this[index] + } + res.wrap() } } - public fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) + private fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) override fun Tensor.valueOrNull(): T? = if (shape contentEquals intArrayOf(1)) { get(intArrayOf(0)) @@ -77,8 +84,7 @@ public abstract class MultikTensorAlgebra( } } - //TODO avoid additional copy - override fun T.minus(other: Tensor): MultikTensor = -(other - this) + override fun T.minus(other: Tensor): MultikTensor = (-(other.asMultik().array - this)).wrap() override fun Tensor.minus(value: T): MultikTensor = asMultik().array.deepCopy().apply { minusAssign(value) }.wrap() @@ -130,13 +136,9 @@ public abstract class MultikTensorAlgebra( override fun Tensor.unaryMinus(): MultikTensor = asMultik().array.unaryMinus().wrap() - override fun Tensor.get(i: Int): MultikTensor { - TODO("Not yet implemented") - } + override fun Tensor.get(i: Int): MultikTensor = asMultik().array.mutableView(i).wrap() - override fun Tensor.transpose(i: Int, j: Int): MultikTensor { - TODO("Not yet implemented") - } + override fun Tensor.transpose(i: Int, j: Int): MultikTensor = asMultik().array.transpose(i, j).wrap() override fun Tensor.view(shape: IntArray): MultikTensor { require(shape.all { it > 0 }) @@ -158,16 +160,14 @@ public abstract class MultikTensorAlgebra( }.wrap() } - override fun Tensor.viewAs(other: Tensor): MultikTensor { - TODO("Not yet implemented") - } + override fun Tensor.viewAs(other: Tensor): MultikTensor = view(other.shape) override fun Tensor.dot(other: Tensor): MultikTensor { TODO("Not yet implemented") } override fun diagonalEmbedding(diagonalEntries: Tensor, offset: Int, dim1: Int, dim2: Int): MultikTensor { - TODO("Not yet implemented") + TODO("Diagonal embedding not implemented") } override fun Tensor.sum(): T = asMultik().array.reduceMultiIndexed { _: IntArray, acc: T, t: T -> diff --git a/kmath-multik/src/test/kotlin/space/kscience/kmath/multik/MultikNDTest.kt b/kmath-multik/src/test/kotlin/space/kscience/kmath/multik/MultikNDTest.kt new file mode 100644 index 000000000..9dd9854c7 --- /dev/null +++ b/kmath-multik/src/test/kotlin/space/kscience/kmath/multik/MultikNDTest.kt @@ -0,0 +1,13 @@ +package space.kscience.kmath.multik + +import org.junit.jupiter.api.Test +import space.kscience.kmath.nd.one +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.invoke + +internal class MultikNDTest { + @Test + fun basicAlgebra(): Unit = DoubleField.multikND{ + one(2,2) + 1.0 + } +} \ No newline at end of file diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt index 792890a2d..259b1a934 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt @@ -38,6 +38,7 @@ public sealed interface Nd4jArrayAlgebra> : AlgebraND.map(transform: C.(T) -> T): Nd4jArrayStructure { val newStruct = ndArray.dup().wrap() newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementAlgebra.transform(value) } @@ -117,7 +118,7 @@ public sealed interface Nd4jArrayRingOps> : RingOpsND, * Creates a most suitable implementation of [RingND] using reified class. */ @Suppress("UNCHECKED_CAST") - public inline fun auto(vararg shape: Int): Nd4jArrayRingOps> = when { + public inline fun auto(): Nd4jArrayRingOps> = when { T::class == Int::class -> IntRing.nd4j as Nd4jArrayRingOps> else -> throw UnsupportedOperationException("This factory method only supports Long type.") } @@ -142,7 +143,7 @@ public sealed interface Nd4jArrayField> : FieldOpsND, * Creates a most suitable implementation of [FieldND] using reified class. */ @Suppress("UNCHECKED_CAST") - public inline fun auto(vararg shape: Int): Nd4jArrayField> = when { + public inline fun auto(): Nd4jArrayField> = when { T::class == Float::class -> FloatField.nd4j as Nd4jArrayField> T::class == Double::class -> DoubleField.nd4j as Nd4jArrayField> else -> throw UnsupportedOperationException("This factory method only supports Float and Double types.") diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorAlgebraExtensions.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorAlgebraExtensions.kt index b73f95054..916388ba9 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorAlgebraExtensions.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorAlgebraExtensions.kt @@ -5,4 +5,12 @@ package space.kscience.kmath.tensors.core -public fun DoubleTensorAlgebra.ones(vararg shape: Int): DoubleTensor = ones(intArrayOf(*shape)) \ No newline at end of file +import space.kscience.kmath.nd.Shape +import kotlin.jvm.JvmName + +@JvmName("varArgOne") +public fun DoubleTensorAlgebra.one(vararg shape: Int): DoubleTensor = ones(intArrayOf(*shape)) +public fun DoubleTensorAlgebra.one(shape: Shape): DoubleTensor = ones(shape) +@JvmName("varArgZero") +public fun DoubleTensorAlgebra.zero(vararg shape: Int): DoubleTensor = zeros(intArrayOf(*shape)) +public fun DoubleTensorAlgebra.zero(shape: Shape): DoubleTensor = zeros(shape) \ No newline at end of file From a81ab474f73c2a6ce1ef3819d3c39df34c139065 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 18 Oct 2021 11:35:09 +0300 Subject: [PATCH 08/10] Add multik tensor factories and benchmarks --- .../kmath/benchmarks/NDFieldBenchmark.kt | 12 +++++++++ .../kmath/benchmarks/ViktorLogBenchmark.kt | 4 +-- .../kmath/functions/matrixIntegration.kt | 4 +-- .../kscience/kmath/operations/complexDemo.kt | 6 ++--- .../kmath/operations/mixedNDOperations.kt | 2 +- .../kscience/kmath/structures/ComplexND.kt | 4 +-- .../kmath/structures/StreamDoubleFieldND.kt | 8 +++--- .../kscience/kmath/complex/ComplexFieldND.kt | 2 +- .../kmath/linear/BufferedLinearSpace.kt | 2 +- .../kmath/linear/DoubleLinearSpace.kt | 2 +- .../space/kscience/kmath/nd/AlgebraND.kt | 10 ++++---- .../kscience/kmath/nd/BufferAlgebraND.kt | 10 ++++---- .../space/kscience/kmath/nd/DoubleFieldND.kt | 4 +-- .../space/kscience/kmath/nd/ShortRingND.kt | 2 +- .../kscience/kmath/nd/algebraNDExtentions.kt | 12 ++++----- .../kscience/kmath/structures/NDFieldTest.kt | 4 +-- .../kmath/structures/NumberNDFieldTest.kt | 4 +-- .../kscience/kmath/multik/MultikOpsND.kt | 8 +++--- .../kmath/multik/MultikTensorAlgebra.kt | 25 +++++++++++++++---- .../kscience/kmath/nd4j/Nd4jArrayAlgebra.kt | 2 +- .../kmath/nd4j/Nd4jArrayAlgebraTest.kt | 8 +++--- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 2 +- .../kscience/kmath/viktor/ViktorFieldOpsND.kt | 4 +-- 23 files changed, 84 insertions(+), 57 deletions(-) diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt index 672aa4307..8f9a3e2b8 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt @@ -9,7 +9,12 @@ import kotlinx.benchmark.Benchmark import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Scope import kotlinx.benchmark.State +import org.jetbrains.kotlinx.multik.api.Multik +import org.jetbrains.kotlinx.multik.api.ones +import org.jetbrains.kotlinx.multik.ndarray.data.DN +import org.jetbrains.kotlinx.multik.ndarray.data.DataType import space.kscience.kmath.multik.multikND +import space.kscience.kmath.multik.multikTensorAlgebra import space.kscience.kmath.nd.BufferedFieldOpsND import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.ndAlgebra @@ -73,6 +78,13 @@ internal class NDFieldBenchmark { blackhole.consume(res) } + @Benchmark + fun multikInPlaceAdd(blackhole: Blackhole) = with(DoubleField.multikTensorAlgebra) { + val res = Multik.ones(shape, DataType.DoubleDataType).wrap() + repeat(n) { res += 1.0 } + blackhole.consume(res) + } + // @Benchmark // fun nd4jAdd(blackhole: Blackhole) = with(nd4jField) { // var res: StructureND = one(dim, dim) diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt index ef2adaad8..a9d1e68fc 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt @@ -23,7 +23,7 @@ internal class ViktorLogBenchmark { @Benchmark fun realFieldLog(blackhole: Blackhole) { with(realField) { - val fortyTwo = produce(shape) { 42.0 } + val fortyTwo = structureND(shape) { 42.0 } var res = one(shape) repeat(n) { res = ln(fortyTwo) } blackhole.consume(res) @@ -33,7 +33,7 @@ internal class ViktorLogBenchmark { @Benchmark fun viktorFieldLog(blackhole: Blackhole) { with(viktorField) { - val fortyTwo = produce(shape) { 42.0 } + val fortyTwo = structureND(shape) { 42.0 } var res = one repeat(n) { res = ln(fortyTwo) } blackhole.consume(res) diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt index 609afb47e..4b6ac475c 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.integration.gaussIntegrator import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.value import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.produce +import space.kscience.kmath.nd.structureND import space.kscience.kmath.nd.withNdAlgebra import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.invoke @@ -18,7 +18,7 @@ fun main(): Unit = Double.algebra { withNdAlgebra(2, 2) { //Produce a diagonal StructureND - fun diagonal(v: Double) = produce { (i, j) -> + fun diagonal(v: Double) = structureND { (i, j) -> if (i == j) v else 0.0 } diff --git a/examples/src/main/kotlin/space/kscience/kmath/operations/complexDemo.kt b/examples/src/main/kotlin/space/kscience/kmath/operations/complexDemo.kt index 67d83d77c..3b9c32f4b 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/operations/complexDemo.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/operations/complexDemo.kt @@ -11,7 +11,7 @@ import space.kscience.kmath.complex.bufferAlgebra import space.kscience.kmath.complex.ndAlgebra import space.kscience.kmath.nd.BufferND import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.produce +import space.kscience.kmath.nd.structureND fun main() = Complex.algebra { val complex = 2 + 2 * i @@ -24,14 +24,14 @@ fun main() = Complex.algebra { println(buffer) // 2d element - val element: BufferND = ndAlgebra.produce(2, 2) { (i, j) -> + val element: BufferND = ndAlgebra.structureND(2, 2) { (i, j) -> Complex(i - j, i + j) } println(element) // 1d element operation val result: StructureND = ndAlgebra{ - val a = produce(8) { (it) -> i * it - it.toDouble() } + val a = structureND(8) { (it) -> i * it - it.toDouble() } val b = 3 val c = Complex(1.0, 1.0) diff --git a/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt index 628344ceb..f517046ee 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt @@ -10,7 +10,7 @@ import space.kscience.kmath.viktor.ViktorStructureND import space.kscience.kmath.viktor.viktorAlgebra fun main() { - val viktorStructure: ViktorStructureND = DoubleField.viktorAlgebra.produce(Shape(2, 2)) { (i, j) -> + val viktorStructure: ViktorStructureND = DoubleField.viktorAlgebra.structureND(Shape(2, 2)) { (i, j) -> if (i == j) 2.0 else 0.0 } diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt index 42636fafb..61df3d065 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt @@ -12,7 +12,7 @@ import space.kscience.kmath.linear.transpose import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.ndAlgebra -import space.kscience.kmath.nd.produce +import space.kscience.kmath.nd.structureND import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.invoke import kotlin.system.measureTimeMillis @@ -55,7 +55,7 @@ fun complexExample() { val x = one * 2.5 operator fun Number.plus(other: Complex) = Complex(this.toDouble() + other.re, other.im) //a structure generator specific to this context - val matrix = produce { (k, l) -> k + l * i } + val matrix = structureND { (k, l) -> k + l * i } //Perform sum val sum = matrix + x + 1.0 diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt index dfd06973e..2b3e72136 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -22,12 +22,12 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND by lazy { produce(shape) { zero } } - override val one: BufferND by lazy { produce(shape) { one } } + override val zero: BufferND by lazy { structureND(shape) { zero } } + override val one: BufferND by lazy { structureND(shape) { one } } override fun number(value: Number): BufferND { val d = value.toDouble() // minimize conversions - return produce(shape) { d } + return structureND(shape) { d } } private val StructureND.buffer: DoubleBuffer @@ -40,7 +40,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } } - override fun produce(shape: Shape, initializer: DoubleField.(IntArray) -> Double): BufferND { + override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): BufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> val index = strides.index(offset) DoubleField.initializer(index) diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/ComplexFieldND.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/ComplexFieldND.kt index 3951b5de0..9d5b1cddd 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/ComplexFieldND.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/ComplexFieldND.kt @@ -67,7 +67,7 @@ public class ComplexFieldND(override val shape: Shape) : override fun number(value: Number): BufferND { val d = value.toDouble() // minimize conversions - return produce(shape) { d.toComplex() } + return structureND(shape) { d.toComplex() } } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt index 39dbe3a81..f72b9bd81 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt @@ -24,7 +24,7 @@ public class BufferedLinearSpace>( private val ndAlgebra = BufferedRingOpsND(bufferAlgebra) override fun buildMatrix(rows: Int, columns: Int, initializer: A.(i: Int, j: Int) -> T): Matrix = - ndAlgebra.produce(intArrayOf(rows, columns)) { (i, j) -> elementAlgebra.initializer(i, j) }.as2D() + ndAlgebra.structureND(intArrayOf(rows, columns)) { (i, j) -> elementAlgebra.initializer(i, j) }.as2D() override fun buildVector(size: Int, initializer: A.(Int) -> T): Point = bufferAlgebra.buffer(size) { elementAlgebra.initializer(it) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt index ec6040af0..91db33bce 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt @@ -23,7 +23,7 @@ public object DoubleLinearSpace : LinearSpace { rows: Int, columns: Int, initializer: DoubleField.(i: Int, j: Int) -> Double - ): Matrix = DoubleFieldOpsND.produce(intArrayOf(rows, columns)) { (i, j) -> + ): Matrix = DoubleFieldOpsND.structureND(intArrayOf(rows, columns)) { (i, j) -> DoubleField.initializer(i, j) }.as2D() diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt index b4e8b7487..30cb01146 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt @@ -39,9 +39,9 @@ public interface AlgebraND> { public val elementAlgebra: C /** - * Produces a new NDStructure using given initializer function. + * Produces a new [StructureND] using given initializer function. */ - public fun produce(shape: Shape, initializer: C.(IntArray) -> T): StructureND + public fun structureND(shape: Shape, initializer: C.(IntArray) -> T): StructureND /** * Maps elements from one structure to another one by applying [transform] to them. @@ -149,7 +149,7 @@ public interface GroupOpsND> : GroupOps>, } public interface GroupND> : Group>, GroupOpsND, WithShape { - override val zero: StructureND get() = produce(shape) { elementAlgebra.zero } + override val zero: StructureND get() = structureND(shape) { elementAlgebra.zero } } /** @@ -193,7 +193,7 @@ public interface RingOpsND> : RingOps>, Gro } public interface RingND> : Ring>, RingOpsND, GroupND, WithShape { - override val one: StructureND get() = produce(shape) { elementAlgebra.one } + override val one: StructureND get() = structureND(shape) { elementAlgebra.one } } @@ -240,5 +240,5 @@ public interface FieldOpsND> : } public interface FieldND> : Field>, FieldOpsND, RingND, WithShape { - override val one: StructureND get() = produce(shape) { elementAlgebra.one } + override val one: StructureND get() = structureND(shape) { elementAlgebra.one } } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt index c94988eef..1f231ce9f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt @@ -16,7 +16,7 @@ public interface BufferAlgebraND> : AlgebraND { public val bufferAlgebra: BufferAlgebra override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra - override fun produce(shape: Shape, initializer: A.(IntArray) -> T): BufferND { + override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): BufferND { val indexer = indexerBuilder(shape) return BufferND( indexer, @@ -109,14 +109,14 @@ public val > BufferAlgebra.nd: BufferedRingOpsND get( public val > BufferAlgebra.nd: BufferedFieldOpsND get() = BufferedFieldOpsND(this) -public fun > BufferAlgebraND.produce( +public fun > BufferAlgebraND.structureND( vararg shape: Int, initializer: A.(IntArray) -> T -): BufferND = produce(shape, initializer) +): BufferND = structureND(shape, initializer) -public fun , A> A.produce( +public fun , A> A.structureND( initializer: EA.(IntArray) -> T -): BufferND where A : BufferAlgebraND, A : WithShape = produce(shape, initializer) +): BufferND where A : BufferAlgebraND, A : WithShape = structureND(shape, initializer) //// group factories //public fun > A.ndAlgebra( diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt index 6235971a2..961f5869a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt @@ -60,7 +60,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND(D transform: DoubleField.(Double, Double) -> Double ): BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> DoubleField.transform(l, r) } - override fun produce(shape: Shape, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND { + override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND { val indexer = indexerBuilder(shape) return DoubleBufferND( indexer, @@ -174,7 +174,7 @@ public class DoubleFieldND(override val shape: Shape) : override fun number(value: Number): DoubleBufferND { val d = value.toDouble() // minimize conversions - return produce(shape) { d } + return structureND(shape) { d } } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt index 65c1f71b4..827f0e21e 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt @@ -23,7 +23,7 @@ public class ShortRingND( override fun number(value: Number): BufferND { val d = value.toShort() // minimize conversions - return produce(shape) { d } + return structureND(shape) { d } } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/algebraNDExtentions.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/algebraNDExtentions.kt index 7bc18a4dd..0e694bcb3 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/algebraNDExtentions.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/algebraNDExtentions.kt @@ -11,24 +11,24 @@ import space.kscience.kmath.operations.Ring import kotlin.jvm.JvmName -public fun > AlgebraND.produce( +public fun > AlgebraND.structureND( shapeFirst: Int, vararg shapeRest: Int, initializer: A.(IntArray) -> T -): StructureND = produce(Shape(shapeFirst, *shapeRest), initializer) +): StructureND = structureND(Shape(shapeFirst, *shapeRest), initializer) -public fun > AlgebraND.zero(shape: Shape): StructureND = produce(shape) { zero } +public fun > AlgebraND.zero(shape: Shape): StructureND = structureND(shape) { zero } @JvmName("zeroVarArg") public fun > AlgebraND.zero( shapeFirst: Int, vararg shapeRest: Int, -): StructureND = produce(shapeFirst, *shapeRest) { zero } +): StructureND = structureND(shapeFirst, *shapeRest) { zero } -public fun > AlgebraND.one(shape: Shape): StructureND = produce(shape) { one } +public fun > AlgebraND.one(shape: Shape): StructureND = structureND(shape) { one } @JvmName("oneVarArg") public fun > AlgebraND.one( shapeFirst: Int, vararg shapeRest: Int, -): StructureND = produce(shapeFirst, *shapeRest) { one } \ No newline at end of file +): StructureND = structureND(shapeFirst, *shapeRest) { one } \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt index 2009eb64f..82172af62 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.structures import space.kscience.kmath.nd.get import space.kscience.kmath.nd.ndAlgebra -import space.kscience.kmath.nd.produce +import space.kscience.kmath.nd.structureND import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.invoke import space.kscience.kmath.testutils.FieldVerifier @@ -22,7 +22,7 @@ internal class NDFieldTest { @Test fun testStrides() { - val ndArray = DoubleField.ndAlgebra.produce(10, 10) { (it[0] + it[1]).toDouble() } + val ndArray = DoubleField.ndAlgebra.structureND(10, 10) { (it[0] + it[1]).toDouble() } assertEquals(ndArray[5, 5], 10.0) } } diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt index 907301a53..61eb6acc8 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt @@ -20,8 +20,8 @@ import kotlin.test.assertEquals @Suppress("UNUSED_VARIABLE") class NumberNDFieldTest { val algebra = DoubleField.ndAlgebra - val array1 = algebra.produce(3, 3) { (i, j) -> (i + j).toDouble() } - val array2 = algebra.produce(3, 3) { (i, j) -> (i - j).toDouble() } + val array1 = algebra.structureND(3, 3) { (i, j) -> (i + j).toDouble() } + val array2 = algebra.structureND(3, 3) { (i, j) -> (i - j).toDouble() } @Test fun testSum() { diff --git a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt index d6996d427..9ee5087e9 100644 --- a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt +++ b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt @@ -18,9 +18,9 @@ public open class MultikRingOpsND> internal constructor( override val elementAlgebra: A ) : RingOpsND { - protected fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) + public fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) - override fun produce(shape: Shape, initializer: A.(IntArray) -> T): MultikTensor { + override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): MultikTensor { val res = mk.zeros(shape, type).asDNArray() for (index in res.multiIndices) { res[index] = elementAlgebra.initializer(index) @@ -28,10 +28,10 @@ public open class MultikRingOpsND> internal constructor( return res.wrap() } - protected fun StructureND.asMultik(): MultikTensor = if (this is MultikTensor) { + public fun StructureND.asMultik(): MultikTensor = if (this is MultikTensor) { this } else { - produce(shape) { get(it) } + structureND(shape) { get(it) } } override fun StructureND.map(transform: A.(T) -> T): MultikTensor { diff --git a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt index e9e56e06e..c229a44bf 100644 --- a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt +++ b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt @@ -11,7 +11,7 @@ import org.jetbrains.kotlinx.multik.ndarray.data.* import org.jetbrains.kotlinx.multik.ndarray.operations.* import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.nd.mapInPlace -import space.kscience.kmath.operations.Ring +import space.kscience.kmath.operations.* import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.api.TensorAlgebra @@ -31,7 +31,7 @@ public value class MultikTensor(public val array: MutableMultiArray) : } -public abstract class MultikTensorAlgebra( +public class MultikTensorAlgebra internal constructor( public val type: DataType, public val elementAlgebra: Ring, public val comparator: Comparator @@ -41,7 +41,7 @@ public abstract class MultikTensorAlgebra( * Convert a tensor to [MultikTensor] if necessary. If tensor is converted, changes on the resulting tensor * are not reflected back onto the source */ - private fun Tensor.asMultik(): MultikTensor { + public fun Tensor.asMultik(): MultikTensor { return if (this is MultikTensor) { this } else { @@ -53,7 +53,7 @@ public abstract class MultikTensorAlgebra( } } - private fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) + public fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) override fun Tensor.valueOrNull(): T? = if (shape contentEquals intArrayOf(1)) { get(intArrayOf(0)) @@ -196,4 +196,19 @@ public abstract class MultikTensorAlgebra( override fun Tensor.argMax(dim: Int, keepDim: Boolean): MultikTensor { TODO("Not yet implemented") } -} \ No newline at end of file +} + +public val DoubleField.multikTensorAlgebra: MultikTensorAlgebra + get() = MultikTensorAlgebra(DataType.DoubleDataType, DoubleField) { o1, o2 -> o1.compareTo(o2) } + +public val FloatField.multikTensorAlgebra: MultikTensorAlgebra + get() = MultikTensorAlgebra(DataType.FloatDataType, FloatField) { o1, o2 -> o1.compareTo(o2) } + +public val ShortRing.multikTensorAlgebra: MultikTensorAlgebra + get() = MultikTensorAlgebra(DataType.ShortDataType, ShortRing) { o1, o2 -> o1.compareTo(o2) } + +public val IntRing.multikTensorAlgebra: MultikTensorAlgebra + get() = MultikTensorAlgebra(DataType.IntDataType, IntRing) { o1, o2 -> o1.compareTo(o2) } + +public val LongRing.multikTensorAlgebra: MultikTensorAlgebra + get() = MultikTensorAlgebra(DataType.LongDataType, LongRing) { o1, o2 -> o1.compareTo(o2) } \ No newline at end of file diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt index 259b1a934..1f312849a 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt @@ -32,7 +32,7 @@ public sealed interface Nd4jArrayAlgebra> : AlgebraND.ndArray: INDArray - override fun produce(shape: Shape, initializer: C.(IntArray) -> T): Nd4jArrayStructure { + override fun structureND(shape: Shape, initializer: C.(IntArray) -> T): Nd4jArrayStructure { val struct = Nd4j.create(*shape)!!.wrap() struct.indicesIterator().forEach { struct[it] = elementAlgebra.initializer(it) } return struct diff --git a/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt b/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt index 465937fa9..103416120 100644 --- a/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt +++ b/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt @@ -9,7 +9,7 @@ import org.nd4j.linalg.factory.Nd4j import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.one -import space.kscience.kmath.nd.produce +import space.kscience.kmath.nd.structureND import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.IntRing import space.kscience.kmath.operations.invoke @@ -23,7 +23,7 @@ import kotlin.test.fail internal class Nd4jArrayAlgebraTest { @Test fun testProduce() { - val res = DoubleField.nd4j.produce(2, 2) { it.sum().toDouble() } + val res = DoubleField.nd4j.structureND(2, 2) { it.sum().toDouble() } val expected = (Nd4j.create(2, 2) ?: fail()).asDoubleStructure() expected[intArrayOf(0, 0)] = 0.0 expected[intArrayOf(0, 1)] = 1.0 @@ -58,9 +58,9 @@ internal class Nd4jArrayAlgebraTest { @Test fun testSin() = DoubleField.nd4j{ - val initial = produce(2, 2) { (i, j) -> if (i == j) PI / 2 else 0.0 } + val initial = structureND(2, 2) { (i, j) -> if (i == j) PI / 2 else 0.0 } val transformed = sin(initial) - val expected = produce(2, 2) { (i, j) -> if (i == j) 1.0 else 0.0 } + val expected = structureND(2, 2) { (i, j) -> if (i == j) 1.0 else 0.0 } println(transformed) assertTrue { StructureND.contentEquals(transformed, expected) } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 594070cd2..16ed4b834 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -22,7 +22,7 @@ import kotlin.math.* public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra, AnalyticTensorAlgebra, - LinearOpsTensorAlgebra { + LinearOpsTensorAlgebra{ public companion object : DoubleTensorAlgebra() diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt index c72553a64..aaa113e56 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt @@ -21,12 +21,12 @@ public open class ViktorFieldOpsND : public val StructureND.f64Buffer: F64Array get() = when (this) { is ViktorStructureND -> this.f64Buffer - else -> produce(shape) { this@f64Buffer[it] }.f64Buffer + else -> structureND(shape) { this@f64Buffer[it] }.f64Buffer } override val elementAlgebra: DoubleField get() = DoubleField - override fun produce(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND = + override fun structureND(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND = F64Array(*shape).apply { DefaultStrides(shape).indices().forEach { index -> set(value = DoubleField.initializer(index), indices = index) From dccc92bf2fd9f979e70386a25d1ff1db231d368f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 18 Oct 2021 12:52:24 +0300 Subject: [PATCH 09/10] Switch to multik-default --- .../kotlin/space/kscience/kmath/tensors/multik.kt | 14 +++++++------- kmath-multik/build.gradle.kts | 2 +- .../space/kscience/kmath/multik/MultikOpsND.kt | 4 +++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt index fc6d7dd56..5383b5c3e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.tensors +import edu.mcgill.kaliningraph.power import org.jetbrains.kotlinx.multik.api.Multik import org.jetbrains.kotlinx.multik.api.linalg.dot import org.jetbrains.kotlinx.multik.api.math.exp @@ -12,13 +13,12 @@ import org.jetbrains.kotlinx.multik.api.ndarray import org.jetbrains.kotlinx.multik.ndarray.operations.minus import org.jetbrains.kotlinx.multik.ndarray.operations.plus import org.jetbrains.kotlinx.multik.ndarray.operations.unaryMinus +import space.kscience.kmath.multik.multikND +import space.kscience.kmath.nd.one +import space.kscience.kmath.operations.DoubleField -fun main() { - val a = Multik.ndarray(intArrayOf(1, 2, 3)) +fun main(): Unit = with(DoubleField.multikND) { + val a = Multik.ndarray(intArrayOf(1, 2, 3)).asType().wrap() val b = Multik.ndarray(doubleArrayOf(1.0, 2.0, 3.0)) - 2 + (-a) - 2 - - a dot a - - a.exp() + one(a.shape) - a + power() } \ No newline at end of file diff --git a/kmath-multik/build.gradle.kts b/kmath-multik/build.gradle.kts index 16a7ab652..df2292f2e 100644 --- a/kmath-multik/build.gradle.kts +++ b/kmath-multik/build.gradle.kts @@ -6,7 +6,7 @@ description = "JetBrains Multik connector" dependencies { api(project(":kmath-tensors")) - api("org.jetbrains.kotlinx:multik-api:0.1.0") + api("org.jetbrains.kotlinx:multik-default:0.1.0") } readme { diff --git a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt index 9ee5087e9..4068ba9b9 100644 --- a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt +++ b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikOpsND.kt @@ -1,5 +1,7 @@ package space.kscience.kmath.multik +import org.jetbrains.kotlinx.multik.api.math.cos +import org.jetbrains.kotlinx.multik.api.math.sin import org.jetbrains.kotlinx.multik.api.mk import org.jetbrains.kotlinx.multik.api.zeros import org.jetbrains.kotlinx.multik.ndarray.data.* @@ -18,7 +20,7 @@ public open class MultikRingOpsND> internal constructor( override val elementAlgebra: A ) : RingOpsND { - public fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) + public fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this.asDNArray()) override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): MultikTensor { val res = mk.zeros(shape, type).asDNArray() From bfc6cbe5d8fdda11db1ffd8dd6da73271a184eaa Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 18 Oct 2021 13:04:36 +0300 Subject: [PATCH 10/10] Update multik.kt --- .../src/main/kotlin/space/kscience/kmath/tensors/multik.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt index 5383b5c3e..b1cf435d4 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt @@ -20,5 +20,5 @@ import space.kscience.kmath.operations.DoubleField fun main(): Unit = with(DoubleField.multikND) { val a = Multik.ndarray(intArrayOf(1, 2, 3)).asType().wrap() val b = Multik.ndarray(doubleArrayOf(1.0, 2.0, 3.0)) - one(a.shape) - a + power() -} \ No newline at end of file + one(a.shape) - a +}