From 0ac5363acf7c6717275989ec6c3376a05002b953 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 16 Oct 2021 11:10:34 +0300 Subject: [PATCH] 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