From f364060acfa20539b5c4ff7b89a89c3b2236efce Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 12:16:22 +0700 Subject: [PATCH 01/28] Add project stub --- kmath-nd4j/build.gradle.kts | 3 +++ settings.gradle.kts | 1 + 2 files changed, 4 insertions(+) create mode 100644 kmath-nd4j/build.gradle.kts diff --git a/kmath-nd4j/build.gradle.kts b/kmath-nd4j/build.gradle.kts new file mode 100644 index 000000000..5da7d66b7 --- /dev/null +++ b/kmath-nd4j/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("scientifik.jvm") +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 57173250b..afb5598b4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -39,6 +39,7 @@ include( ":kmath-commons", ":kmath-viktor", ":kmath-koma", + ":kmath-nd4j", ":kmath-prob", ":kmath-io", ":kmath-dimensions", From 3df9892de53c0aa719091ad3d0c4aa0405081655 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 14:10:39 +0700 Subject: [PATCH 02/28] Implement the ND4J module for scalars --- build.gradle.kts | 1 + kmath-nd4j/build.gradle.kts | 10 ++++++++-- .../INDArrayScalarsIterator.kt | 19 +++++++++++++++++++ .../scientifik.kmath.nd4j/ND4JStructure.kt | 14 ++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayScalarsIterator.kt create mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt diff --git a/build.gradle.kts b/build.gradle.kts index 6d102a77a..10e030520 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ allprojects { repositories { jcenter() maven("https://dl.bintray.com/kotlin/kotlinx") + mavenCentral() } group = "scientifik" diff --git a/kmath-nd4j/build.gradle.kts b/kmath-nd4j/build.gradle.kts index 5da7d66b7..59354a8f9 100644 --- a/kmath-nd4j/build.gradle.kts +++ b/kmath-nd4j/build.gradle.kts @@ -1,3 +1,9 @@ -plugins { - id("scientifik.jvm") +plugins { id("scientifik.jvm") } + +dependencies { + api(project(":kmath-core")) + api("org.nd4j:nd4j-api:1.0.0-beta7") + testImplementation("org.deeplearning4j:deeplearning4j-core:1.0.0-beta7") + testImplementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") + testImplementation("org.slf4j:slf4j-simple:1.7.30") } diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayScalarsIterator.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayScalarsIterator.kt new file mode 100644 index 000000000..2c2dc970f --- /dev/null +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayScalarsIterator.kt @@ -0,0 +1,19 @@ +package scientifik.kmath.nd4j + +import org.nd4j.linalg.api.ndarray.INDArray +import org.nd4j.linalg.api.shape.Shape + +internal class INDArrayScalarsIterator(private val iterateOver: INDArray) : Iterator> { + private var i: Int = 0 + + override fun hasNext(): Boolean = i < iterateOver.length() + + override fun next(): Pair { + val idx = if (iterateOver.ordering() == 'c') + Shape.ind2subC(iterateOver, i++.toLong())!! + else + Shape.ind2sub(iterateOver, i++.toLong())!! + + return narrowToIntArray(idx) to iterateOver.getScalar(*idx) + } +} diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt new file mode 100644 index 000000000..eb9f9b80c --- /dev/null +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt @@ -0,0 +1,14 @@ +package scientifik.kmath.nd4j + +import org.nd4j.linalg.api.ndarray.INDArray +import scientifik.kmath.structures.NDStructure + +internal fun narrowToIntArray(la: LongArray): IntArray = IntArray(la.size) { la[it].toInt() } + +data class ND4JStructure(val ndArray: INDArray) : NDStructure { + override val shape: IntArray + get() = narrowToIntArray(ndArray.shape()) + + override fun get(index: IntArray): INDArray = ndArray.getScalar(*index) + override fun elements(): Sequence> = Sequence { INDArrayScalarsIterator(ndArray) } +} From 9a4dd315072e6cb27c430c799074b1dd36a06f94 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 14:17:46 +0700 Subject: [PATCH 03/28] Move narrowToIntArray to new file --- kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt | 3 +++ .../src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt new file mode 100644 index 000000000..5d341dd68 --- /dev/null +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt @@ -0,0 +1,3 @@ +package scientifik.kmath.nd4j + +internal fun narrowToIntArray(la: LongArray): IntArray = IntArray(la.size) { la[it].toInt() } \ No newline at end of file diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt index eb9f9b80c..1d0301ff9 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt @@ -3,8 +3,6 @@ package scientifik.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray import scientifik.kmath.structures.NDStructure -internal fun narrowToIntArray(la: LongArray): IntArray = IntArray(la.size) { la[it].toInt() } - data class ND4JStructure(val ndArray: INDArray) : NDStructure { override val shape: IntArray get() = narrowToIntArray(ndArray.shape()) From d0cc75098bdc523c1c5f945c5a83a460efa00af8 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 14:36:19 +0700 Subject: [PATCH 04/28] Rework with specialized NDStructure implementations --- .../kotlin/scientifik.kmath.nd4j/Arrays.kt | 3 +- .../INDArrayScalarsIterator.kt | 19 ---------- .../scientifik.kmath.nd4j/ND4JStructure.kt | 12 ------ .../scientifik.kmath.nd4j/NDArrayIterators.kt | 37 +++++++++++++++++++ .../ScalarsND4JStructure.kt | 34 +++++++++++++++++ 5 files changed, 73 insertions(+), 32 deletions(-) delete mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayScalarsIterator.kt delete mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt create mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/NDArrayIterators.kt create mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt index 5d341dd68..3d5062a4f 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt @@ -1,3 +1,4 @@ package scientifik.kmath.nd4j -internal fun narrowToIntArray(la: LongArray): IntArray = IntArray(la.size) { la[it].toInt() } \ No newline at end of file +internal fun widenToLongArray(ia: IntArray): LongArray = LongArray(ia.size) { ia[it].toLong() } +internal fun narrowToIntArray(la: LongArray): IntArray = IntArray(la.size) { la[it].toInt() } diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayScalarsIterator.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayScalarsIterator.kt deleted file mode 100644 index 2c2dc970f..000000000 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayScalarsIterator.kt +++ /dev/null @@ -1,19 +0,0 @@ -package scientifik.kmath.nd4j - -import org.nd4j.linalg.api.ndarray.INDArray -import org.nd4j.linalg.api.shape.Shape - -internal class INDArrayScalarsIterator(private val iterateOver: INDArray) : Iterator> { - private var i: Int = 0 - - override fun hasNext(): Boolean = i < iterateOver.length() - - override fun next(): Pair { - val idx = if (iterateOver.ordering() == 'c') - Shape.ind2subC(iterateOver, i++.toLong())!! - else - Shape.ind2sub(iterateOver, i++.toLong())!! - - return narrowToIntArray(idx) to iterateOver.getScalar(*idx) - } -} diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt deleted file mode 100644 index 1d0301ff9..000000000 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ND4JStructure.kt +++ /dev/null @@ -1,12 +0,0 @@ -package scientifik.kmath.nd4j - -import org.nd4j.linalg.api.ndarray.INDArray -import scientifik.kmath.structures.NDStructure - -data class ND4JStructure(val ndArray: INDArray) : NDStructure { - override val shape: IntArray - get() = narrowToIntArray(ndArray.shape()) - - override fun get(index: IntArray): INDArray = ndArray.getScalar(*index) - override fun elements(): Sequence> = Sequence { INDArrayScalarsIterator(ndArray) } -} diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/NDArrayIterators.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/NDArrayIterators.kt new file mode 100644 index 000000000..426b1ec2d --- /dev/null +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/NDArrayIterators.kt @@ -0,0 +1,37 @@ +package scientifik.kmath.nd4j + +import org.nd4j.linalg.api.ndarray.INDArray +import org.nd4j.linalg.api.shape.Shape + +internal sealed class INDArrayIteratorBase(protected val iterateOver: INDArray) : Iterator> { + private var i: Int = 0 + + override fun hasNext(): Boolean = i < iterateOver.length() + + abstract fun getSingle(indices: LongArray): T + + final override fun next(): Pair { + val la = if (iterateOver.ordering() == 'c') + Shape.ind2subC(iterateOver, i++.toLong())!! + else + Shape.ind2sub(iterateOver, i++.toLong())!! + + return narrowToIntArray(la) to getSingle(la) + } +} + +internal class INDArrayDoubleIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { + override fun getSingle(indices: LongArray): Double = iterateOver.getDouble(*indices) +} + +internal class INDArrayLongIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { + override fun getSingle(indices: LongArray) = iterateOver.getLong(*indices) +} + +internal class INDArrayIntIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { + override fun getSingle(indices: LongArray) = iterateOver.getInt(*narrowToIntArray(indices)) +} + +internal class INDArrayFloatIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { + override fun getSingle(indices: LongArray) = iterateOver.getFloat(*indices) +} diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt new file mode 100644 index 000000000..ef8c3ec2e --- /dev/null +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt @@ -0,0 +1,34 @@ +package scientifik.kmath.nd4j + +import org.nd4j.linalg.api.ndarray.INDArray +import scientifik.kmath.structures.NDStructure + +interface INDArrayStructureBase : NDStructure { + val ndArray: INDArray + + override val shape: IntArray + get() = narrowToIntArray(ndArray.shape()) + + fun elementsIterator(): Iterator> + override fun elements(): Sequence> = Sequence { elementsIterator() } +} + +data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructureBase { + override fun elementsIterator(): Iterator> = INDArrayIntIterator(ndArray) + override fun get(index: IntArray): Int = ndArray.getInt(*index) +} + +data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructureBase { + override fun elementsIterator(): Iterator> = INDArrayLongIterator(ndArray) + override fun get(index: IntArray): Long = ndArray.getLong(*widenToLongArray(index)) +} + +data class INDArrayDoubleStructure(override val ndArray: INDArray) : INDArrayStructureBase { + override fun elementsIterator(): Iterator> = INDArrayDoubleIterator(ndArray) + override fun get(index: IntArray): Double = ndArray.getDouble(*index) +} + +data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructureBase { + override fun elementsIterator(): Iterator> = INDArrayFloatIterator(ndArray) + override fun get(index: IntArray): Float = ndArray.getFloat(*index) +} From bac6451443f5cf9995453fc780cd2a8d037e60cd Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 27 Jun 2020 21:17:40 +0700 Subject: [PATCH 05/28] Add tests --- .../kmath/nd4j/INDArrayStructureTest.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt new file mode 100644 index 000000000..e851f2e80 --- /dev/null +++ b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt @@ -0,0 +1,38 @@ +package scientifik.kmath.nd4j + +import org.nd4j.linalg.factory.Nd4j +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class INDArrayStructureTest { + @Test + fun testElements() { + val nd = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! + val struct = INDArrayDoubleStructure(nd) + val res = struct.elements().map { it.second }.toList() + assertEquals(listOf(1.0, 2.0, 3.0), res) + } + + @Test + fun testShape() { + val nd = Nd4j.rand(10, 2, 3, 6)!! + val struct = INDArrayIntStructure(nd) + assertEquals(intArrayOf(10, 2, 3, 6).toList(), struct.shape.toList()) + } + + @Test + fun testEquals() { + val nd1 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! + val struct1 = INDArrayDoubleStructure(nd1) + val nd2 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! + val struct2 = INDArrayDoubleStructure(nd2) + assertEquals(struct1, struct2) + } + + @Test + fun testDimension() { + val nd = Nd4j.rand(8, 16, 3, 7, 1)!! + val struct = INDArrayIntStructure(nd) + assertEquals(5, struct.dimension) + } +} From b6bf741dbe72afdcbc311d6ac89ac8700ac81cb4 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 27 Jun 2020 21:19:19 +0700 Subject: [PATCH 06/28] Replace lambdas with references --- .../main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt | 2 +- .../test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt index ef8c3ec2e..3ffcc110d 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt @@ -10,7 +10,7 @@ interface INDArrayStructureBase : NDStructure { get() = narrowToIntArray(ndArray.shape()) fun elementsIterator(): Iterator> - override fun elements(): Sequence> = Sequence { elementsIterator() } + override fun elements(): Sequence> = Sequence(::elementsIterator) } data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructureBase { diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt index e851f2e80..235b65556 100644 --- a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt +++ b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt @@ -9,7 +9,7 @@ internal class INDArrayStructureTest { fun testElements() { val nd = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! val struct = INDArrayDoubleStructure(nd) - val res = struct.elements().map { it.second }.toList() + val res = struct.elements().map(Pair::second).toList() assertEquals(listOf(1.0, 2.0, 3.0), res) } From e466f4bdf2448ed028c8895e5d6bb7b147fa0777 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 27 Jun 2020 21:21:16 +0700 Subject: [PATCH 07/28] Add test for get --- .../kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt index 235b65556..239289262 100644 --- a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt +++ b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt @@ -3,6 +3,7 @@ package scientifik.kmath.nd4j import org.nd4j.linalg.factory.Nd4j import kotlin.test.Test import kotlin.test.assertEquals +import scientifik.kmath.structures.get internal class INDArrayStructureTest { @Test @@ -35,4 +36,11 @@ internal class INDArrayStructureTest { val struct = INDArrayIntStructure(nd) assertEquals(5, struct.dimension) } + + @Test + fun testGet() { + val nd = Nd4j.rand(10, 2, 3, 6)!! + val struct = INDArrayIntStructure(nd) + assertEquals(nd.getInt(0, 0, 0, 0), struct[0, 0, 0, 0]) + } } From fefa0db86ed15a7583f442aa1d2e958dcdfee5c3 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 17:29:00 +0700 Subject: [PATCH 08/28] Rename files --- .../{NDArrayIterators.kt => INDArrayIterators.kt} | 0 .../{ScalarsND4JStructure.kt => INDArrayStructures.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/{NDArrayIterators.kt => INDArrayIterators.kt} (100%) rename kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/{ScalarsND4JStructure.kt => INDArrayStructures.kt} (100%) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/NDArrayIterators.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt similarity index 100% rename from kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/NDArrayIterators.kt rename to kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt similarity index 100% rename from kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/ScalarsND4JStructure.kt rename to kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt From 5cc56b6ab01dff7c412e93a43feea400f70bbc96 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 17:30:09 +0700 Subject: [PATCH 09/28] Remove Base suffix from class name --- .../kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt index 3ffcc110d..b444def3d 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt @@ -3,7 +3,7 @@ package scientifik.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray import scientifik.kmath.structures.NDStructure -interface INDArrayStructureBase : NDStructure { +interface INDArrayStructure : NDStructure { val ndArray: INDArray override val shape: IntArray @@ -13,22 +13,22 @@ interface INDArrayStructureBase : NDStructure { override fun elements(): Sequence> = Sequence(::elementsIterator) } -data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructureBase { +data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure { override fun elementsIterator(): Iterator> = INDArrayIntIterator(ndArray) override fun get(index: IntArray): Int = ndArray.getInt(*index) } -data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructureBase { +data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure { override fun elementsIterator(): Iterator> = INDArrayLongIterator(ndArray) override fun get(index: IntArray): Long = ndArray.getLong(*widenToLongArray(index)) } -data class INDArrayDoubleStructure(override val ndArray: INDArray) : INDArrayStructureBase { +data class INDArrayDoubleStructure(override val ndArray: INDArray) : INDArrayStructure { override fun elementsIterator(): Iterator> = INDArrayDoubleIterator(ndArray) override fun get(index: IntArray): Double = ndArray.getDouble(*index) } -data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructureBase { +data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure { override fun elementsIterator(): Iterator> = INDArrayFloatIterator(ndArray) override fun get(index: IntArray): Float = ndArray.getFloat(*index) } From f49c3e4f4d2158123640c442dd7ebe6447ec2474 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 17:33:09 +0700 Subject: [PATCH 10/28] Add final modifier --- .../src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt index 426b1ec2d..f6efdc0ba 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt @@ -6,7 +6,7 @@ import org.nd4j.linalg.api.shape.Shape internal sealed class INDArrayIteratorBase(protected val iterateOver: INDArray) : Iterator> { private var i: Int = 0 - override fun hasNext(): Boolean = i < iterateOver.length() + final override fun hasNext(): Boolean = i < iterateOver.length() abstract fun getSingle(indices: LongArray): T From b41a9588bc6c98842126e89650d6a942f4369bdf Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 18:21:27 +0700 Subject: [PATCH 11/28] Rename file --- .../main/kotlin/scientifik.kmath.nd4j/{Arrays.kt => arrays.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/{Arrays.kt => arrays.kt} (100%) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/arrays.kt similarity index 100% rename from kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/Arrays.kt rename to kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/arrays.kt From 05120929b0d18bb8a2bf7937a6f679e95ba5dc9b Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 19:08:44 +0700 Subject: [PATCH 12/28] Encapsulate classOfT property of AsmBuilder --- .../kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index e550bc563..5531fd5dc 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -16,13 +16,13 @@ import kotlin.reflect.KClass * ASM Builder is a structure that abstracts building a class designated to unwrap [MST] to plain Java expression. * This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new class. * - * @param T the type of AsmExpression to unwrap. - * @param algebra the algebra the applied AsmExpressions use. - * @param className the unique class name of new loaded class. - * @param invokeLabel0Visitor the function to apply to this object when generating invoke method, label 0. + * @property T the type of AsmExpression to unwrap. + * @property algebra the algebra the applied AsmExpressions use. + * @property className the unique class name of new loaded class. + * @property invokeLabel0Visitor the function to apply to this object when generating invoke method, label 0. */ internal class AsmBuilder internal constructor( - internal val classOfT: KClass<*>, + private val classOfT: KClass<*>, private val algebra: Algebra, private val className: String, private val invokeLabel0Visitor: AsmBuilder.() -> Unit From 3b18000f1edcd9d99e15037230885140b64b8001 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 29 Jun 2020 00:14:01 +0700 Subject: [PATCH 13/28] Make several NDStructures mutable --- .../scientifik.kmath.nd4j/INDArrayStructures.kt | 10 +++++++--- .../scientifik/kmath/nd4j/INDArrayStructureTest.kt | 14 +++++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt index b444def3d..f39d84716 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt @@ -1,6 +1,7 @@ package scientifik.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray +import scientifik.kmath.structures.MutableNDStructure import scientifik.kmath.structures.NDStructure interface INDArrayStructure : NDStructure { @@ -13,9 +14,10 @@ interface INDArrayStructure : NDStructure { override fun elements(): Sequence> = Sequence(::elementsIterator) } -data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure { +data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { override fun elementsIterator(): Iterator> = INDArrayIntIterator(ndArray) override fun get(index: IntArray): Int = ndArray.getInt(*index) + override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) } } data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure { @@ -23,12 +25,14 @@ data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStruc override fun get(index: IntArray): Long = ndArray.getLong(*widenToLongArray(index)) } -data class INDArrayDoubleStructure(override val ndArray: INDArray) : INDArrayStructure { +data class INDArrayDoubleStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { override fun elementsIterator(): Iterator> = INDArrayDoubleIterator(ndArray) override fun get(index: IntArray): Double = ndArray.getDouble(*index) + override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) } } -data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure { +data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { override fun elementsIterator(): Iterator> = INDArrayFloatIterator(ndArray) override fun get(index: IntArray): Float = ndArray.getFloat(*index) + override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) } } diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt index 239289262..77565856a 100644 --- a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt +++ b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt @@ -1,9 +1,9 @@ package scientifik.kmath.nd4j import org.nd4j.linalg.factory.Nd4j +import scientifik.kmath.structures.get import kotlin.test.Test import kotlin.test.assertEquals -import scientifik.kmath.structures.get internal class INDArrayStructureTest { @Test @@ -17,7 +17,7 @@ internal class INDArrayStructureTest { @Test fun testShape() { val nd = Nd4j.rand(10, 2, 3, 6)!! - val struct = INDArrayIntStructure(nd) + val struct = INDArrayLongStructure(nd) assertEquals(intArrayOf(10, 2, 3, 6).toList(), struct.shape.toList()) } @@ -33,7 +33,7 @@ internal class INDArrayStructureTest { @Test fun testDimension() { val nd = Nd4j.rand(8, 16, 3, 7, 1)!! - val struct = INDArrayIntStructure(nd) + val struct = INDArrayFloatStructure(nd) assertEquals(5, struct.dimension) } @@ -43,4 +43,12 @@ internal class INDArrayStructureTest { val struct = INDArrayIntStructure(nd) assertEquals(nd.getInt(0, 0, 0, 0), struct[0, 0, 0, 0]) } + + @Test + fun testSet() { + val nd = Nd4j.rand(17, 12, 4, 8)!! + val struct = INDArrayIntStructure(nd) + struct[intArrayOf(1, 2, 3, 4)] = 777 + assertEquals(777, struct[1, 2, 3, 4]) + } } From eb9d40fd2aed7342a5584609fdfb83718093d400 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 29 Jun 2020 00:29:43 +0700 Subject: [PATCH 14/28] Convert INDArray NDStructures implementations to inline classes, add tests to verify equals and hashCode --- .../scientifik.kmath.nd4j/INDArrayStructures.kt | 13 +++++++++---- .../kmath/nd4j/INDArrayStructureTest.kt | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt index f39d84716..66aa00fac 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt @@ -14,25 +14,30 @@ interface INDArrayStructure : NDStructure { override fun elements(): Sequence> = Sequence(::elementsIterator) } -data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { +inline class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { override fun elementsIterator(): Iterator> = INDArrayIntIterator(ndArray) override fun get(index: IntArray): Int = ndArray.getInt(*index) override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) } + override fun toString(): String = "INDArrayIntStructure(ndArray=$ndArray)" } -data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure { +inline class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure { override fun elementsIterator(): Iterator> = INDArrayLongIterator(ndArray) override fun get(index: IntArray): Long = ndArray.getLong(*widenToLongArray(index)) + override fun toString(): String = "INDArrayLongStructure(ndArray=$ndArray)" + } -data class INDArrayDoubleStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { +inline class INDArrayDoubleStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { override fun elementsIterator(): Iterator> = INDArrayDoubleIterator(ndArray) override fun get(index: IntArray): Double = ndArray.getDouble(*index) override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) } + override fun toString(): String = "INDArrayDoubleStructure(ndArray=$ndArray)" } -data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { +inline class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { override fun elementsIterator(): Iterator> = INDArrayFloatIterator(ndArray) override fun get(index: IntArray): Float = ndArray.getFloat(*index) override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) } + override fun toString(): String = "INDArrayFloatStructure(ndArray=$ndArray)" } diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt index 77565856a..ad1cbb585 100644 --- a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt +++ b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt @@ -4,6 +4,7 @@ import org.nd4j.linalg.factory.Nd4j import scientifik.kmath.structures.get import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotEquals internal class INDArrayStructureTest { @Test @@ -25,9 +26,25 @@ internal class INDArrayStructureTest { fun testEquals() { val nd1 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! val struct1 = INDArrayDoubleStructure(nd1) + assertEquals(struct1, struct1) + assertNotEquals(struct1, null as INDArrayDoubleStructure?) val nd2 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! val struct2 = INDArrayDoubleStructure(nd2) assertEquals(struct1, struct2) + assertEquals(struct2, struct1) + val nd3 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! + val struct3 = INDArrayDoubleStructure(nd3) + assertEquals(struct2, struct3) + assertEquals(struct1, struct3) + } + + @Test + fun testHashCode() { + val nd1 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! + val struct1 = INDArrayDoubleStructure(nd1) + val nd2 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! + val struct2 = INDArrayDoubleStructure(nd2) + assertEquals(struct1.hashCode(), struct2.hashCode()) } @Test From 783087982fbe93f60a2c20e7a3d50627b69b8b58 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 29 Jun 2020 02:50:34 +0700 Subject: [PATCH 15/28] Rollback making Structures inline, implement Algebras for NDArrayStructure --- .../scientifik.kmath.nd4j/INDArrayAlgebra.kt | 89 +++++++++++++++++++ .../INDArrayIterators.kt | 7 +- .../INDArrayStructures.kt | 25 +++--- .../kmath/nd4j/INDArrayAlgebraTest.kt | 30 +++++++ .../kmath/nd4j/INDArrayStructureTest.kt | 14 +-- 5 files changed, 147 insertions(+), 18 deletions(-) create mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt create mode 100644 kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt new file mode 100644 index 000000000..760e3d03e --- /dev/null +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt @@ -0,0 +1,89 @@ +package scientifik.kmath.nd4j + +import org.nd4j.linalg.api.ndarray.INDArray +import org.nd4j.linalg.factory.Nd4j +import scientifik.kmath.operations.* +import scientifik.kmath.structures.MutableNDStructure +import scientifik.kmath.structures.NDField +import scientifik.kmath.structures.NDRing + +interface INDArrayRing : + NDRing where F : Ring, N : INDArrayStructure, N : MutableNDStructure { + fun INDArray.wrap(): N + + override val zero: N + get() = Nd4j.zeros(*shape).wrap() + + override val one: N + get() = Nd4j.ones(*shape).wrap() + + override fun produce(initializer: F.(IntArray) -> T): N { + val struct = Nd4j.create(*shape).wrap() + struct.elements().map(Pair::first).forEach { struct[it] = elementContext.initializer(it) } + return struct + } + + override fun map(arg: N, transform: F.(T) -> T): N { + val new = Nd4j.create(*shape) + Nd4j.copy(arg.ndArray, new) + val newStruct = new.wrap() + newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } + return newStruct + } + + override fun mapIndexed(arg: N, transform: F.(index: IntArray, T) -> T): N { + val new = Nd4j.create(*shape).wrap() + new.elements().forEach { (idx, _) -> new[idx] = elementContext.transform(idx, arg[idx]) } + return new + } + + override fun combine(a: N, b: N, transform: F.(T, T) -> T): N { + val new = Nd4j.create(*shape).wrap() + new.elements().forEach { (idx, _) -> new[idx] = elementContext.transform(a[idx], b[idx]) } + return new + } + + override fun add(a: N, b: N): N = a.ndArray.addi(b.ndArray).wrap() + override fun N.minus(b: N): N = ndArray.subi(b.ndArray).wrap() + override fun N.unaryMinus(): N = ndArray.negi().wrap() + override fun multiply(a: N, b: N): N = a.ndArray.muli(b.ndArray).wrap() + override fun multiply(a: N, k: Number): N = a.ndArray.muli(k).wrap() + override fun N.div(k: Number): N = ndArray.divi(k).wrap() + override fun N.minus(b: Number): N = ndArray.subi(b).wrap() + override fun N.plus(b: Number): N = ndArray.addi(b).wrap() + override fun N.times(k: Number): N = ndArray.muli(k).wrap() +} + +interface INDArrayField : NDField, + INDArrayRing where F : Field, N : INDArrayStructure, N : MutableNDStructure { + override fun divide(a: N, b: N): N = a.ndArray.divi(b.ndArray).wrap() +} + +class RealINDArrayField(override val shape: IntArray, override val elementContext: Field = RealField) : + INDArrayField, INDArrayRealStructure> { + override fun INDArray.wrap(): INDArrayRealStructure = asRealStructure() + override fun INDArrayRealStructure.div(arg: Double): INDArrayRealStructure = ndArray.divi(arg).wrap() + override fun INDArrayRealStructure.plus(arg: Double): INDArrayRealStructure = ndArray.addi(arg).wrap() + override fun INDArrayRealStructure.div(k: Number): INDArrayRealStructure = ndArray.divi(k).wrap() + override fun INDArrayRealStructure.minus(arg: Double): INDArrayRealStructure = ndArray.subi(arg).wrap() + override fun INDArrayRealStructure.times(arg: Double): INDArrayRealStructure = ndArray.muli(arg).wrap() +} + +class FloatINDArrayField(override val shape: IntArray, override val elementContext: Field = FloatField) : + INDArrayField, INDArrayFloatStructure> { + override fun INDArray.wrap(): INDArrayFloatStructure = asFloatStructure() + override fun INDArrayFloatStructure.div(arg: Float): INDArrayFloatStructure = ndArray.divi(arg).wrap() + override fun INDArrayFloatStructure.plus(arg: Float): INDArrayFloatStructure = ndArray.addi(arg).wrap() + override fun INDArrayFloatStructure.div(k: Number): INDArrayFloatStructure = ndArray.divi(k).wrap() + override fun INDArrayFloatStructure.minus(arg: Float): INDArrayFloatStructure = ndArray.subi(arg).wrap() + override fun INDArrayFloatStructure.times(arg: Float): INDArrayFloatStructure = ndArray.muli(arg).wrap() +} + +class IntINDArrayRing(override val shape: IntArray, override val elementContext: Ring = IntRing) : + INDArrayRing, INDArrayIntStructure> { + override fun INDArray.wrap(): INDArrayIntStructure = asIntStructure() + override fun INDArrayIntStructure.plus(arg: Int): INDArrayIntStructure = ndArray.addi(arg).wrap() + override fun INDArrayIntStructure.div(k: Number): INDArrayIntStructure = ndArray.divi(k).wrap() + override fun INDArrayIntStructure.minus(arg: Int): INDArrayIntStructure = ndArray.subi(arg).wrap() + override fun INDArrayIntStructure.times(arg: Int): INDArrayIntStructure = ndArray.muli(arg).wrap() +} diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt index f6efdc0ba..115c78cb9 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt @@ -20,14 +20,19 @@ internal sealed class INDArrayIteratorBase(protected val iterateOver: INDArra } } -internal class INDArrayDoubleIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { +internal class INDArrayRealIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray): Double = iterateOver.getDouble(*indices) } +internal fun INDArray.realIterator(): INDArrayRealIterator = INDArrayRealIterator(this) + internal class INDArrayLongIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray) = iterateOver.getLong(*indices) } +// TODO +//internal fun INDArray.longI + internal class INDArrayIntIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray) = iterateOver.getInt(*narrowToIntArray(indices)) } diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt index 66aa00fac..351110485 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt @@ -14,30 +14,35 @@ interface INDArrayStructure : NDStructure { override fun elements(): Sequence> = Sequence(::elementsIterator) } -inline class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { +data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { override fun elementsIterator(): Iterator> = INDArrayIntIterator(ndArray) override fun get(index: IntArray): Int = ndArray.getInt(*index) override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) } - override fun toString(): String = "INDArrayIntStructure(ndArray=$ndArray)" } -inline class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure { +fun INDArray.asIntStructure(): INDArrayIntStructure = INDArrayIntStructure(this) + +data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure { override fun elementsIterator(): Iterator> = INDArrayLongIterator(ndArray) override fun get(index: IntArray): Long = ndArray.getLong(*widenToLongArray(index)) - override fun toString(): String = "INDArrayLongStructure(ndArray=$ndArray)" - } -inline class INDArrayDoubleStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { - override fun elementsIterator(): Iterator> = INDArrayDoubleIterator(ndArray) +fun INDArray.asLongStructure(): INDArrayLongStructure = INDArrayLongStructure(this) + +data class INDArrayRealStructure(override val ndArray: INDArray) : INDArrayStructure, + MutableNDStructure { + override fun elementsIterator(): Iterator> = INDArrayRealIterator(ndArray) override fun get(index: IntArray): Double = ndArray.getDouble(*index) override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) } - override fun toString(): String = "INDArrayDoubleStructure(ndArray=$ndArray)" } -inline class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { +fun INDArray.asRealStructure(): INDArrayRealStructure = INDArrayRealStructure(this) + +data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure, + MutableNDStructure { override fun elementsIterator(): Iterator> = INDArrayFloatIterator(ndArray) override fun get(index: IntArray): Float = ndArray.getFloat(*index) override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) } - override fun toString(): String = "INDArrayFloatStructure(ndArray=$ndArray)" } + +fun INDArray.asFloatStructure(): INDArrayFloatStructure = INDArrayFloatStructure(this) diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt new file mode 100644 index 000000000..f971e7871 --- /dev/null +++ b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt @@ -0,0 +1,30 @@ +package scientifik.kmath.nd4j + +import org.nd4j.linalg.factory.Nd4j +import scientifik.kmath.operations.invoke +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class INDArrayAlgebraTest { + @Test + fun testProduce() { + val res = (RealINDArrayField(intArrayOf(2, 2))) { produce { it.sum().toDouble() } } + val expected = Nd4j.create(2, 2)!!.asRealStructure() + expected[intArrayOf(0, 0)] = 0.0 + expected[intArrayOf(0, 1)] = 1.0 + expected[intArrayOf(1, 0)] = 1.0 + expected[intArrayOf(1, 1)] = 2.0 + assertEquals(expected, res) + } + + @Test + fun testMap() { + val res = (IntINDArrayRing(intArrayOf(2, 2))) { map(one) { it + it * 2 } } + val expected = Nd4j.create(2, 2)!!.asIntStructure() + expected[intArrayOf(0, 0)] = 3 + expected[intArrayOf(0, 1)] = 3 + expected[intArrayOf(1, 0)] = 3 + expected[intArrayOf(1, 1)] = 3 + assertEquals(expected, res) + } +} diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt index ad1cbb585..dfede6d32 100644 --- a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt +++ b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt @@ -10,7 +10,7 @@ internal class INDArrayStructureTest { @Test fun testElements() { val nd = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct = INDArrayDoubleStructure(nd) + val struct = INDArrayRealStructure(nd) val res = struct.elements().map(Pair::second).toList() assertEquals(listOf(1.0, 2.0, 3.0), res) } @@ -25,15 +25,15 @@ internal class INDArrayStructureTest { @Test fun testEquals() { val nd1 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct1 = INDArrayDoubleStructure(nd1) + val struct1 = INDArrayRealStructure(nd1) assertEquals(struct1, struct1) - assertNotEquals(struct1, null as INDArrayDoubleStructure?) + assertNotEquals(struct1, null as INDArrayRealStructure?) val nd2 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct2 = INDArrayDoubleStructure(nd2) + val struct2 = INDArrayRealStructure(nd2) assertEquals(struct1, struct2) assertEquals(struct2, struct1) val nd3 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct3 = INDArrayDoubleStructure(nd3) + val struct3 = INDArrayRealStructure(nd3) assertEquals(struct2, struct3) assertEquals(struct1, struct3) } @@ -41,9 +41,9 @@ internal class INDArrayStructureTest { @Test fun testHashCode() { val nd1 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct1 = INDArrayDoubleStructure(nd1) + val struct1 = INDArrayRealStructure(nd1) val nd2 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct2 = INDArrayDoubleStructure(nd2) + val struct2 = INDArrayRealStructure(nd2) assertEquals(struct1.hashCode(), struct2.hashCode()) } From d7949fdb01ac96a617bf7bf8e3c2579de481b946 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 29 Jun 2020 03:39:37 +0700 Subject: [PATCH 16/28] Remove duplicated code --- .../src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt index 760e3d03e..a4ecd09e5 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt @@ -64,7 +64,6 @@ class RealINDArrayField(override val shape: IntArray, override val elementContex override fun INDArray.wrap(): INDArrayRealStructure = asRealStructure() override fun INDArrayRealStructure.div(arg: Double): INDArrayRealStructure = ndArray.divi(arg).wrap() override fun INDArrayRealStructure.plus(arg: Double): INDArrayRealStructure = ndArray.addi(arg).wrap() - override fun INDArrayRealStructure.div(k: Number): INDArrayRealStructure = ndArray.divi(k).wrap() override fun INDArrayRealStructure.minus(arg: Double): INDArrayRealStructure = ndArray.subi(arg).wrap() override fun INDArrayRealStructure.times(arg: Double): INDArrayRealStructure = ndArray.muli(arg).wrap() } @@ -74,7 +73,6 @@ class FloatINDArrayField(override val shape: IntArray, override val elementConte override fun INDArray.wrap(): INDArrayFloatStructure = asFloatStructure() override fun INDArrayFloatStructure.div(arg: Float): INDArrayFloatStructure = ndArray.divi(arg).wrap() override fun INDArrayFloatStructure.plus(arg: Float): INDArrayFloatStructure = ndArray.addi(arg).wrap() - override fun INDArrayFloatStructure.div(k: Number): INDArrayFloatStructure = ndArray.divi(k).wrap() override fun INDArrayFloatStructure.minus(arg: Float): INDArrayFloatStructure = ndArray.subi(arg).wrap() override fun INDArrayFloatStructure.times(arg: Float): INDArrayFloatStructure = ndArray.muli(arg).wrap() } @@ -83,7 +81,6 @@ class IntINDArrayRing(override val shape: IntArray, override val elementContext: INDArrayRing, INDArrayIntStructure> { override fun INDArray.wrap(): INDArrayIntStructure = asIntStructure() override fun INDArrayIntStructure.plus(arg: Int): INDArrayIntStructure = ndArray.addi(arg).wrap() - override fun INDArrayIntStructure.div(k: Number): INDArrayIntStructure = ndArray.divi(k).wrap() override fun INDArrayIntStructure.minus(arg: Int): INDArrayIntStructure = ndArray.subi(arg).wrap() override fun INDArrayIntStructure.times(arg: Int): INDArrayIntStructure = ndArray.muli(arg).wrap() } From 8a8b314d0a60c027f6abcfcff8d0d924613cd0d1 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 29 Jun 2020 03:48:11 +0700 Subject: [PATCH 17/28] Optimize reverse division for FP INDArrayAlgebra --- .../scientifik.kmath.nd4j/INDArrayAlgebra.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt index a4ecd09e5..44d8f6611 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt @@ -7,8 +7,8 @@ import scientifik.kmath.structures.MutableNDStructure import scientifik.kmath.structures.NDField import scientifik.kmath.structures.NDRing -interface INDArrayRing : - NDRing where F : Ring, N : INDArrayStructure, N : MutableNDStructure { +interface INDArrayRing : + NDRing where R : Ring, N : INDArrayStructure, N : MutableNDStructure { fun INDArray.wrap(): N override val zero: N @@ -17,13 +17,13 @@ interface INDArrayRing : override val one: N get() = Nd4j.ones(*shape).wrap() - override fun produce(initializer: F.(IntArray) -> T): N { + override fun produce(initializer: R.(IntArray) -> T): N { val struct = Nd4j.create(*shape).wrap() struct.elements().map(Pair::first).forEach { struct[it] = elementContext.initializer(it) } return struct } - override fun map(arg: N, transform: F.(T) -> T): N { + override fun map(arg: N, transform: R.(T) -> T): N { val new = Nd4j.create(*shape) Nd4j.copy(arg.ndArray, new) val newStruct = new.wrap() @@ -31,13 +31,13 @@ interface INDArrayRing : return newStruct } - override fun mapIndexed(arg: N, transform: F.(index: IntArray, T) -> T): N { + override fun mapIndexed(arg: N, transform: R.(index: IntArray, T) -> T): N { val new = Nd4j.create(*shape).wrap() new.elements().forEach { (idx, _) -> new[idx] = elementContext.transform(idx, arg[idx]) } return new } - override fun combine(a: N, b: N, transform: F.(T, T) -> T): N { + override fun combine(a: N, b: N, transform: R.(T, T) -> T): N { val new = Nd4j.create(*shape).wrap() new.elements().forEach { (idx, _) -> new[idx] = elementContext.transform(a[idx], b[idx]) } return new @@ -66,6 +66,7 @@ class RealINDArrayField(override val shape: IntArray, override val elementContex override fun INDArrayRealStructure.plus(arg: Double): INDArrayRealStructure = ndArray.addi(arg).wrap() override fun INDArrayRealStructure.minus(arg: Double): INDArrayRealStructure = ndArray.subi(arg).wrap() override fun INDArrayRealStructure.times(arg: Double): INDArrayRealStructure = ndArray.muli(arg).wrap() + override fun Double.div(arg: INDArrayRealStructure): INDArrayRealStructure = arg.ndArray.rdivi(this).wrap() } class FloatINDArrayField(override val shape: IntArray, override val elementContext: Field = FloatField) : @@ -75,6 +76,7 @@ class FloatINDArrayField(override val shape: IntArray, override val elementConte override fun INDArrayFloatStructure.plus(arg: Float): INDArrayFloatStructure = ndArray.addi(arg).wrap() override fun INDArrayFloatStructure.minus(arg: Float): INDArrayFloatStructure = ndArray.subi(arg).wrap() override fun INDArrayFloatStructure.times(arg: Float): INDArrayFloatStructure = ndArray.muli(arg).wrap() + override fun Float.div(arg: INDArrayFloatStructure): INDArrayFloatStructure = arg.ndArray.rdivi(this).wrap() } class IntINDArrayRing(override val shape: IntArray, override val elementContext: Ring = IntRing) : From 23b2ba9950cb4fab33f0b53565ae762cfa2f7a45 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 29 Jun 2020 03:49:29 +0700 Subject: [PATCH 18/28] Optimize reverse division for FP INDArrayAlgebra --- .../src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt index 44d8f6611..f476af0d5 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt @@ -57,6 +57,7 @@ interface INDArrayRing : interface INDArrayField : NDField, INDArrayRing where F : Field, N : INDArrayStructure, N : MutableNDStructure { override fun divide(a: N, b: N): N = a.ndArray.divi(b.ndArray).wrap() + override fun Number.div(b: N): N = b.ndArray.rdivi(this).wrap() } class RealINDArrayField(override val shape: IntArray, override val elementContext: Field = RealField) : From d87dd3e717cc18851b7922d1e31b7ac81698c082 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 29 Jun 2020 21:31:08 +0700 Subject: [PATCH 19/28] Refactor array functions --- .../kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt | 11 +++++++---- .../scientifik.kmath.nd4j/INDArrayStructures.kt | 4 ++-- .../src/main/kotlin/scientifik.kmath.nd4j/arrays.kt | 4 ++-- .../scientifik/kmath/nd4j/INDArrayAlgebraTest.kt | 11 +++++++++++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt index 115c78cb9..bba5089a1 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt @@ -16,7 +16,7 @@ internal sealed class INDArrayIteratorBase(protected val iterateOver: INDArra else Shape.ind2sub(iterateOver, i++.toLong())!! - return narrowToIntArray(la) to getSingle(la) + return la.toIntArray() to getSingle(la) } } @@ -30,13 +30,16 @@ internal class INDArrayLongIterator(iterateOver: INDArray) : INDArrayIteratorBas override fun getSingle(indices: LongArray) = iterateOver.getLong(*indices) } -// TODO -//internal fun INDArray.longI +internal fun INDArray.longIterator(): INDArrayLongIterator = INDArrayLongIterator(this) internal class INDArrayIntIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { - override fun getSingle(indices: LongArray) = iterateOver.getInt(*narrowToIntArray(indices)) + override fun getSingle(indices: LongArray) = iterateOver.getInt(*indices.toIntArray()) } +internal fun INDArray.intIterator(): INDArrayIntIterator = INDArrayIntIterator(this) + internal class INDArrayFloatIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray) = iterateOver.getFloat(*indices) } + +internal fun INDArray.floatIterator() = INDArrayFloatIterator(this) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt index 351110485..ef7436285 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt @@ -8,7 +8,7 @@ interface INDArrayStructure : NDStructure { val ndArray: INDArray override val shape: IntArray - get() = narrowToIntArray(ndArray.shape()) + get() = ndArray.shape().toIntArray() fun elementsIterator(): Iterator> override fun elements(): Sequence> = Sequence(::elementsIterator) @@ -24,7 +24,7 @@ fun INDArray.asIntStructure(): INDArrayIntStructure = INDArrayIntStructure(this) data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure { override fun elementsIterator(): Iterator> = INDArrayLongIterator(ndArray) - override fun get(index: IntArray): Long = ndArray.getLong(*widenToLongArray(index)) + override fun get(index: IntArray): Long = ndArray.getLong(*index.toLongArray()) } fun INDArray.asLongStructure(): INDArrayLongStructure = INDArrayLongStructure(this) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/arrays.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/arrays.kt index 3d5062a4f..269fc89c2 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/arrays.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/arrays.kt @@ -1,4 +1,4 @@ package scientifik.kmath.nd4j -internal fun widenToLongArray(ia: IntArray): LongArray = LongArray(ia.size) { ia[it].toLong() } -internal fun narrowToIntArray(la: LongArray): IntArray = IntArray(la.size) { la[it].toInt() } +internal fun IntArray.toLongArray(): LongArray = LongArray(size) { this[it].toLong() } +internal fun LongArray.toIntArray(): IntArray = IntArray(size) { this[it].toInt() } diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt index f971e7871..4aa40c233 100644 --- a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt +++ b/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt @@ -27,4 +27,15 @@ internal class INDArrayAlgebraTest { expected[intArrayOf(1, 1)] = 3 assertEquals(expected, res) } + + @Test + fun testAdd() { + val res = (IntINDArrayRing(intArrayOf(2, 2))) { one + 25 } + val expected = Nd4j.create(2, 2)!!.asIntStructure() + expected[intArrayOf(0, 0)] = 26 + expected[intArrayOf(0, 1)] = 26 + expected[intArrayOf(1, 0)] = 26 + expected[intArrayOf(1, 1)] = 26 + assertEquals(expected, res) + } } From f54e5679cf261dceeff58531e929d3fd0bd04549 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 29 Jun 2020 22:06:13 +0700 Subject: [PATCH 20/28] Add README.md for kmath-nd4j --- kmath-nd4j/README.md | 73 +++++++++++++++++++ .../INDArrayStructures.kt | 8 +- 2 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 kmath-nd4j/README.md diff --git a/kmath-nd4j/README.md b/kmath-nd4j/README.md new file mode 100644 index 000000000..63687a880 --- /dev/null +++ b/kmath-nd4j/README.md @@ -0,0 +1,73 @@ +# ND4J NDStructure implementation (`kmath-nd4j`) + +This subproject implements the following features: + +- NDStructure wrapper for INDArray. +- Optimized NDRing implementation for INDArray storing Ints. +- Optimized NDField implementation for INDArray storing Floats and Doubles. + +> #### Artifact: +> This module is distributed in the artifact `scientifik:kmath-nd4j:0.1.4-dev-8`. +> +> **Gradle:** +> +> ```gradle +> repositories { +> maven { url 'https://dl.bintray.com/mipt-npm/scientifik' } +> maven { url 'https://dl.bintray.com/mipt-npm/dev' } +> } +> +> dependencies { +> implementation 'scientifik:kmath-nd4j:0.1.4-dev-8' +> } +> ``` +> **Gradle Kotlin DSL:** +> +> ```kotlin +> repositories { +> maven("https://dl.bintray.com/mipt-npm/scientifik") +> maven("https://dl.bintray.com/mipt-npm/dev") +> } +> +> dependencies { +> implementation("scientifik:kmath-nd4j:0.1.4-dev-8") +> } +> ``` +> + +## Examples + +NDStructure wrapper for INDArray: + +```kotlin +import org.nd4j.linalg.factory.* +import scientifik.kmath.nd4j.* +import scientifik.kmath.structures.* + +val array = Nd4j.ones(2, 2)!!.asRealStructure() +println(array[0, 0]) // 1.0 +array[intArrayOf(0, 0)] = 24.0 +println(array[0, 0]) // 24.0 +``` + +Fast element-wise arithmetics for INDArray: + +```kotlin +import org.nd4j.linalg.factory.* +import scientifik.kmath.nd4j.* +import scientifik.kmath.operations.* + +val field = RealINDArrayField(intArrayOf(2, 2)) +val array = Nd4j.rand(2, 2)!!.asRealStructure() + +val res = field { + (25.0 / array + 20) * 4 +} + +println(res.ndArray) +// [[ 250.6449, 428.5840], +// [ 269.7913, 202.2077]] +``` + + +Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt index ef7436285..06b0354d8 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt @@ -15,7 +15,7 @@ interface INDArrayStructure : NDStructure { } data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { - override fun elementsIterator(): Iterator> = INDArrayIntIterator(ndArray) + override fun elementsIterator(): Iterator> = ndArray.intIterator() override fun get(index: IntArray): Int = ndArray.getInt(*index) override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) } } @@ -23,7 +23,7 @@ data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStruct fun INDArray.asIntStructure(): INDArrayIntStructure = INDArrayIntStructure(this) data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure { - override fun elementsIterator(): Iterator> = INDArrayLongIterator(ndArray) + override fun elementsIterator(): Iterator> = ndArray.longIterator() override fun get(index: IntArray): Long = ndArray.getLong(*index.toLongArray()) } @@ -31,7 +31,7 @@ fun INDArray.asLongStructure(): INDArrayLongStructure = INDArrayLongStructure(th data class INDArrayRealStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { - override fun elementsIterator(): Iterator> = INDArrayRealIterator(ndArray) + override fun elementsIterator(): Iterator> = ndArray.realIterator() override fun get(index: IntArray): Double = ndArray.getDouble(*index) override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) } } @@ -40,7 +40,7 @@ fun INDArray.asRealStructure(): INDArrayRealStructure = INDArrayRealStructure(th data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { - override fun elementsIterator(): Iterator> = INDArrayFloatIterator(ndArray) + override fun elementsIterator(): Iterator> = ndArray.floatIterator() override fun get(index: IntArray): Float = ndArray.getFloat(*index) override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) } } From bf071bcdc1f1040bf644fe9638827c3ffe36491e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 29 Jun 2020 22:30:08 +0700 Subject: [PATCH 21/28] Minor refactor --- kmath-nd4j/README.md | 2 +- .../scientifik.kmath.nd4j/INDArrayAlgebra.kt | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/kmath-nd4j/README.md b/kmath-nd4j/README.md index 63687a880..ad76799c3 100644 --- a/kmath-nd4j/README.md +++ b/kmath-nd4j/README.md @@ -50,7 +50,7 @@ array[intArrayOf(0, 0)] = 24.0 println(array[0, 0]) // 24.0 ``` -Fast element-wise arithmetics for INDArray: +Fast element-wise and in-place arithmetics for INDArray: ```kotlin import org.nd4j.linalg.factory.* diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt index f476af0d5..14fe202c3 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt @@ -9,24 +9,22 @@ import scientifik.kmath.structures.NDRing interface INDArrayRing : NDRing where R : Ring, N : INDArrayStructure, N : MutableNDStructure { - fun INDArray.wrap(): N - override val zero: N get() = Nd4j.zeros(*shape).wrap() override val one: N get() = Nd4j.ones(*shape).wrap() + fun INDArray.wrap(): N + override fun produce(initializer: R.(IntArray) -> T): N { - val struct = Nd4j.create(*shape).wrap() + val struct = Nd4j.create(*shape)!!.wrap() struct.elements().map(Pair::first).forEach { struct[it] = elementContext.initializer(it) } return struct } override fun map(arg: N, transform: R.(T) -> T): N { - val new = Nd4j.create(*shape) - Nd4j.copy(arg.ndArray, new) - val newStruct = new.wrap() + val newStruct = arg.ndArray.dup().wrap() newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } return newStruct } @@ -52,6 +50,7 @@ interface INDArrayRing : override fun N.minus(b: Number): N = ndArray.subi(b).wrap() override fun N.plus(b: Number): N = ndArray.addi(b).wrap() override fun N.times(k: Number): N = ndArray.muli(k).wrap() + override fun Number.minus(b: N): N = b.ndArray.rsubi(this).wrap() } interface INDArrayField : NDField, @@ -61,13 +60,14 @@ interface INDArrayField : NDField, } class RealINDArrayField(override val shape: IntArray, override val elementContext: Field = RealField) : - INDArrayField, INDArrayRealStructure> { + INDArrayField, INDArrayRealStructure> { override fun INDArray.wrap(): INDArrayRealStructure = asRealStructure() override fun INDArrayRealStructure.div(arg: Double): INDArrayRealStructure = ndArray.divi(arg).wrap() override fun INDArrayRealStructure.plus(arg: Double): INDArrayRealStructure = ndArray.addi(arg).wrap() override fun INDArrayRealStructure.minus(arg: Double): INDArrayRealStructure = ndArray.subi(arg).wrap() override fun INDArrayRealStructure.times(arg: Double): INDArrayRealStructure = ndArray.muli(arg).wrap() override fun Double.div(arg: INDArrayRealStructure): INDArrayRealStructure = arg.ndArray.rdivi(this).wrap() + override fun Double.minus(arg: INDArrayRealStructure): INDArrayRealStructure = arg.ndArray.rsubi(this).wrap() } class FloatINDArrayField(override val shape: IntArray, override val elementContext: Field = FloatField) : @@ -78,6 +78,7 @@ class FloatINDArrayField(override val shape: IntArray, override val elementConte override fun INDArrayFloatStructure.minus(arg: Float): INDArrayFloatStructure = ndArray.subi(arg).wrap() override fun INDArrayFloatStructure.times(arg: Float): INDArrayFloatStructure = ndArray.muli(arg).wrap() override fun Float.div(arg: INDArrayFloatStructure): INDArrayFloatStructure = arg.ndArray.rdivi(this).wrap() + override fun Float.minus(arg: INDArrayFloatStructure): INDArrayFloatStructure = arg.ndArray.rsubi(this).wrap() } class IntINDArrayRing(override val shape: IntArray, override val elementContext: Ring = IntRing) : @@ -86,4 +87,5 @@ class IntINDArrayRing(override val shape: IntArray, override val elementContext: override fun INDArrayIntStructure.plus(arg: Int): INDArrayIntStructure = ndArray.addi(arg).wrap() override fun INDArrayIntStructure.minus(arg: Int): INDArrayIntStructure = ndArray.subi(arg).wrap() override fun INDArrayIntStructure.times(arg: Int): INDArrayIntStructure = ndArray.muli(arg).wrap() + override fun Int.minus(arg: INDArrayIntStructure): INDArrayIntStructure = arg.ndArray.rsubi(this).wrap() } From 7157878485236ee88688053b6313b710891c0c03 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 15 Aug 2020 18:35:16 +0700 Subject: [PATCH 22/28] Update changelog, document kmath-nd4j, refactor iterators, correct algebra mistakes, separate INDArrayStructureRing to Space, Ring and Algebra --- CHANGELOG.md | 1 + .../kmath/operations/AlgebraElements.kt | 6 +- .../kmath/structures/BufferedNDElement.kt | 6 +- .../scientifik/kmath/structures/NDAlgebra.kt | 170 ++++++++--- kmath-nd4j/README.md | 8 +- .../scientifik.kmath.nd4j/INDArrayAlgebra.kt | 286 ++++++++++++++---- .../INDArrayIterators.kt | 35 ++- .../INDArrayStructures.kt | 50 ++- 8 files changed, 448 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26f9e33ec..e9afb6c26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Blocking chains in `kmath-coroutines` - Full hyperbolic functions support and default implementations within `ExtendedField` - Norm support for `Complex` +- ND4J support module submitting `NDStructure` and `NDAlgebra` over `INDArray`. ### Changed - BigInteger and BigDecimal algebra: JBigDecimalField has companion object with default math context; minor optimizations diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt index 197897c14..e1d50c4f0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt @@ -74,9 +74,9 @@ interface SpaceElement, S : Space> : MathElement /** * The element of [Ring]. * - * @param T the type of space operation results. + * @param T the type of ring operation results. * @param I self type of the element. Needed for static type checking. - * @param R the type of space. + * @param R the type of ring. */ interface RingElement, R : Ring> : SpaceElement { /** @@ -91,7 +91,7 @@ interface RingElement, R : Ring> : SpaceElement>( override val context: BufferedNDField, override val buffer: Buffer ) : BufferedNDElement(), FieldElement, BufferedNDFieldElement, BufferedNDField> { - override fun unwrap(): NDBuffer = this override fun NDBuffer.wrap(): BufferedNDFieldElement { @@ -56,8 +55,9 @@ class BufferedNDFieldElement>( /** * Element by element application of any operation on elements to the whole array. Just like in numpy. */ -operator fun > Function1.invoke(ndElement: BufferedNDElement): MathElement> = - ndElement.context.run { map(ndElement) { invoke(it) }.toElement() } +operator fun > Function1.invoke( + ndElement: BufferedNDElement +): MathElement> = ndElement.context.run { map(ndElement) { invoke(it) }.toElement() } /* plus and minus */ diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt index f09db3c72..14c23a81a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -5,56 +5,78 @@ import scientifik.kmath.operations.Field import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space - /** - * An exception is thrown when the expected ans actual shape of NDArray differs + * An exception is thrown when the expected ans actual shape of NDArray differs. + * + * @property expected the expected shape. + * @property actual the actual shape. */ -class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : RuntimeException() - +class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : + RuntimeException("Shape ${actual.contentToString()} doesn't fit in expected shape ${expected.contentToString()}.") /** - * The base interface for all nd-algebra implementations - * @param T the type of nd-structure element - * @param C the type of the element context - * @param N the type of the structure + * The base interface for all ND-algebra implementations. + * + * @param T the type of ND-structure element. + * @param C the type of the element context. + * @param N the type of the structure. */ interface NDAlgebra> { + /** + * The shape of ND-structures this algebra operates on. + */ val shape: IntArray + + /** + * The algebra over elements of ND structure. + */ val elementContext: C /** - * Produce a new [N] structure using given initializer function + * Produces a new [N] structure using given initializer function. */ fun produce(initializer: C.(IntArray) -> T): N /** - * Map elements from one structure to another one + * Maps elements from one structure to another one by applying [transform] to them. */ fun map(arg: N, transform: C.(T) -> T): N /** - * Map indexed elements + * Maps elements from one structure to another one by applying [transform] to them alongside with their indices. */ fun mapIndexed(arg: N, transform: C.(index: IntArray, T) -> T): N /** - * Combine two structures into one + * Combines two structures into one. */ fun combine(a: N, b: N, transform: C.(T, T) -> T): N /** - * Check if given elements are consistent with this context + * Checks if given element is consistent with this context. + * + * @param element the structure to check. + * @return the valid structure. */ - fun check(vararg elements: N) { - elements.forEach { - if (!shape.contentEquals(it.shape)) { - throw ShapeMismatchException(shape, it.shape) - } - } + fun check(element: N): N { + if (!element.shape.contentEquals(shape)) throw ShapeMismatchException(shape, element.shape) + return element } /** - * element-by-element invoke a function working on [T] on a [NDStructure] + * Checks if given elements are consistent with this context. + * + * @param elements the structures to check. + * @return the array of valid structures. + */ + fun check(vararg elements: N): Array = elements + .map(NDStructure::shape) + .singleOrNull { !shape.contentEquals(it) } + ?.let { throw ShapeMismatchException(shape, it) } + ?: elements + + /** + * Element-wise invocation of function working on [T] on a [NDStructure]. */ operator fun Function1.invoke(structure: N): N = map(structure) { value -> this@invoke(value) } @@ -62,43 +84,107 @@ interface NDAlgebra> { } /** - * An nd-space over element space + * Space of [NDStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param S the type of space of structure elements. */ interface NDSpace, N : NDStructure> : Space, NDAlgebra { /** - * Element-by-element addition + * Element-wise addition. + * + * @param a the addend. + * @param b the augend. + * @return the sum. */ override fun add(a: N, b: N): N = combine(a, b) { aValue, bValue -> add(aValue, bValue) } /** - * Multiply all elements by constant + * Element-wise multiplication by scalar. + * + * @param a the multiplicand. + * @param k the multiplier. + * @return the product. */ override fun multiply(a: N, k: Number): N = map(a) { multiply(it, k) } - //TODO move to extensions after KEEP-176 + // TODO move to extensions after KEEP-176 + + /** + * Adds an ND structure to an element of it. + * + * @receiver the addend. + * @param arg the augend. + * @return the sum. + */ operator fun N.plus(arg: T): N = map(this) { value -> add(arg, value) } + /** + * Subtracts an element from ND structure of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ operator fun N.minus(arg: T): N = map(this) { value -> add(arg, -value) } + /** + * Adds an element to ND structure of it. + * + * @receiver the addend. + * @param arg the augend. + * @return the sum. + */ operator fun T.plus(arg: N): N = map(arg) { value -> add(this@plus, value) } + + /** + * Subtracts an ND structure from an element of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ operator fun T.minus(arg: N): N = map(arg) { value -> add(-this@minus, value) } companion object } /** - * An nd-ring over element ring + * Ring of [NDStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param R the type of ring of structure elements. */ interface NDRing, N : NDStructure> : Ring, NDSpace { - /** - * Element-by-element multiplication + * Element-wise multiplication. + * + * @param a the multiplicand. + * @param b the multiplier. + * @return the product. */ override fun multiply(a: N, b: N): N = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } //TODO move to extensions after KEEP-176 + + /** + * Multiplies an ND structure by an element of it. + * + * @receiver the multiplicand. + * @param arg the multiplier. + * @return the product. + */ operator fun N.times(arg: T): N = map(this) { value -> multiply(arg, value) } + /** + * Multiplies an element by a ND structure of it. + * + * @receiver the multiplicand. + * @param arg the multiplier. + * @return the product. + */ operator fun T.times(arg: N): N = map(arg) { value -> multiply(this@times, value) } companion object @@ -109,31 +195,47 @@ interface NDRing, N : NDStructure> : Ring, NDSpace * * @param T the type of the element contained in ND structure. * @param N the type of ND structure. - * @param F field of structure elements. + * @param F the type field of structure elements. */ interface NDField, N : NDStructure> : Field, NDRing { - /** - * Element-by-element division + * Element-wise division. + * + * @param a the dividend. + * @param b the divisor. + * @return the quotient. */ override fun divide(a: N, b: N): N = combine(a, b) { aValue, bValue -> divide(aValue, bValue) } //TODO move to extensions after KEEP-176 + /** + * Divides an ND structure by an element of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ operator fun N.div(arg: T): N = map(this) { value -> divide(arg, value) } + /** + * Divides an element by an ND structure of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ operator fun T.div(arg: N): N = map(arg) { divide(it, this@div) } companion object { - - private val realNDFieldCache = HashMap() + private val realNDFieldCache: MutableMap = hashMapOf() /** - * Create a nd-field for [Double] values or pull it from cache if it was created previously + * Create a nd-field for [Double] values or pull it from cache if it was created previously. */ fun real(vararg shape: Int): RealNDField = realNDFieldCache.getOrPut(shape) { RealNDField(shape) } /** - * Create a nd-field with boxing generic buffer + * Create a ND field with boxing generic buffer. */ fun > boxing( field: F, diff --git a/kmath-nd4j/README.md b/kmath-nd4j/README.md index ad76799c3..f8ca5eed2 100644 --- a/kmath-nd4j/README.md +++ b/kmath-nd4j/README.md @@ -3,8 +3,8 @@ This subproject implements the following features: - NDStructure wrapper for INDArray. -- Optimized NDRing implementation for INDArray storing Ints. -- Optimized NDField implementation for INDArray storing Floats and Doubles. +- Optimized NDRing implementations for INDArray storing Ints and Longs. +- Optimized NDField implementations for INDArray storing Floats and Doubles. > #### Artifact: > This module is distributed in the artifact `scientifik:kmath-nd4j:0.1.4-dev-8`. @@ -44,7 +44,7 @@ import org.nd4j.linalg.factory.* import scientifik.kmath.nd4j.* import scientifik.kmath.structures.* -val array = Nd4j.ones(2, 2)!!.asRealStructure() +val array = Nd4j.ones(2, 2).asRealStructure() println(array[0, 0]) // 1.0 array[intArrayOf(0, 0)] = 24.0 println(array[0, 0]) // 24.0 @@ -58,7 +58,7 @@ import scientifik.kmath.nd4j.* import scientifik.kmath.operations.* val field = RealINDArrayField(intArrayOf(2, 2)) -val array = Nd4j.rand(2, 2)!!.asRealStructure() +val array = Nd4j.rand(2, 2).asRealStructure() val res = field { (25.0 / array + 20) * 4 diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt index 14fe202c3..c24e2ece6 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt @@ -3,89 +3,271 @@ package scientifik.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray import org.nd4j.linalg.factory.Nd4j import scientifik.kmath.operations.* -import scientifik.kmath.structures.MutableNDStructure -import scientifik.kmath.structures.NDField -import scientifik.kmath.structures.NDRing - -interface INDArrayRing : - NDRing where R : Ring, N : INDArrayStructure, N : MutableNDStructure { - override val zero: N - get() = Nd4j.zeros(*shape).wrap() - - override val one: N - get() = Nd4j.ones(*shape).wrap() +import scientifik.kmath.structures.* +/** + * Represents [NDAlgebra] over [INDArrayAlgebra]. + * + * @param T the type of ND-structure element. + * @param C the type of the element context. + * @param N the type of the structure. + */ +interface INDArrayAlgebra : NDAlgebra where N : INDArrayStructure, N : MutableNDStructure { + /** + * Wraps [INDArray] to [N]. + */ fun INDArray.wrap(): N - override fun produce(initializer: R.(IntArray) -> T): N { + override fun produce(initializer: C.(IntArray) -> T): N { val struct = Nd4j.create(*shape)!!.wrap() - struct.elements().map(Pair::first).forEach { struct[it] = elementContext.initializer(it) } + struct.indicesIterator().forEach { struct[it] = elementContext.initializer(it) } return struct } - override fun map(arg: N, transform: R.(T) -> T): N { + override fun map(arg: N, transform: C.(T) -> T): N { + check(arg) val newStruct = arg.ndArray.dup().wrap() newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } return newStruct } - override fun mapIndexed(arg: N, transform: R.(index: IntArray, T) -> T): N { + override fun mapIndexed(arg: N, transform: C.(index: IntArray, T) -> T): N { + check(arg) val new = Nd4j.create(*shape).wrap() - new.elements().forEach { (idx, _) -> new[idx] = elementContext.transform(idx, arg[idx]) } + new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(idx, arg[idx]) } return new } - override fun combine(a: N, b: N, transform: R.(T, T) -> T): N { + override fun combine(a: N, b: N, transform: C.(T, T) -> T): N { + check(a, b) val new = Nd4j.create(*shape).wrap() - new.elements().forEach { (idx, _) -> new[idx] = elementContext.transform(a[idx], b[idx]) } + new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(a[idx], b[idx]) } return new } - - override fun add(a: N, b: N): N = a.ndArray.addi(b.ndArray).wrap() - override fun N.minus(b: N): N = ndArray.subi(b.ndArray).wrap() - override fun N.unaryMinus(): N = ndArray.negi().wrap() - override fun multiply(a: N, b: N): N = a.ndArray.muli(b.ndArray).wrap() - override fun multiply(a: N, k: Number): N = a.ndArray.muli(k).wrap() - override fun N.div(k: Number): N = ndArray.divi(k).wrap() - override fun N.minus(b: Number): N = ndArray.subi(b).wrap() - override fun N.plus(b: Number): N = ndArray.addi(b).wrap() - override fun N.times(k: Number): N = ndArray.muli(k).wrap() - override fun Number.minus(b: N): N = b.ndArray.rsubi(this).wrap() } -interface INDArrayField : NDField, - INDArrayRing where F : Field, N : INDArrayStructure, N : MutableNDStructure { - override fun divide(a: N, b: N): N = a.ndArray.divi(b.ndArray).wrap() - override fun Number.div(b: N): N = b.ndArray.rdivi(this).wrap() +/** + * Represents [NDSpace] over [INDArrayStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param S the type of space of structure elements. + */ +interface INDArraySpace : NDSpace, INDArrayAlgebra + where S : Space, N : INDArrayStructure, N : MutableNDStructure { + + override val zero: N + get() = Nd4j.zeros(*shape).wrap() + + override fun add(a: N, b: N): N { + check(a, b) + return a.ndArray.add(b.ndArray).wrap() + } + + override operator fun N.minus(b: N): N { + check(this, b) + return ndArray.sub(b.ndArray).wrap() + } + + override operator fun N.unaryMinus(): N { + check(this) + return ndArray.neg().wrap() + } + + override fun multiply(a: N, k: Number): N { + check(a) + return a.ndArray.mul(k).wrap() + } + + override operator fun N.div(k: Number): N { + check(this) + return ndArray.div(k).wrap() + } + + override operator fun N.times(k: Number): N { + check(this) + return ndArray.mul(k).wrap() + } } +/** + * Represents [NDRing] over [INDArrayStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param R the type of ring of structure elements. + */ +interface INDArrayRing : NDRing, INDArraySpace + where R : Ring, N : INDArrayStructure, N : MutableNDStructure { + + override val one: N + get() = Nd4j.ones(*shape).wrap() + + override fun multiply(a: N, b: N): N { + check(a, b) + return a.ndArray.mul(b.ndArray).wrap() + } + + override operator fun N.minus(b: Number): N { + check(this) + return ndArray.sub(b).wrap() + } + + override operator fun N.plus(b: Number): N { + check(this) + return ndArray.add(b).wrap() + } + + override operator fun Number.minus(b: N): N { + check(b) + return b.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDField] over [INDArrayStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param F the type field of structure elements. + */ +interface INDArrayField : NDField, INDArrayRing + where F : Field, N : INDArrayStructure, N : MutableNDStructure { + override fun divide(a: N, b: N): N { + check(a, b) + return a.ndArray.div(b.ndArray).wrap() + } + + override operator fun Number.div(b: N): N { + check(b) + return b.ndArray.rdiv(this).wrap() + } +} + +/** + * Represents [NDField] over [INDArrayRealStructure]. + */ class RealINDArrayField(override val shape: IntArray, override val elementContext: Field = RealField) : INDArrayField, INDArrayRealStructure> { - override fun INDArray.wrap(): INDArrayRealStructure = asRealStructure() - override fun INDArrayRealStructure.div(arg: Double): INDArrayRealStructure = ndArray.divi(arg).wrap() - override fun INDArrayRealStructure.plus(arg: Double): INDArrayRealStructure = ndArray.addi(arg).wrap() - override fun INDArrayRealStructure.minus(arg: Double): INDArrayRealStructure = ndArray.subi(arg).wrap() - override fun INDArrayRealStructure.times(arg: Double): INDArrayRealStructure = ndArray.muli(arg).wrap() - override fun Double.div(arg: INDArrayRealStructure): INDArrayRealStructure = arg.ndArray.rdivi(this).wrap() - override fun Double.minus(arg: INDArrayRealStructure): INDArrayRealStructure = arg.ndArray.rsubi(this).wrap() + override fun INDArray.wrap(): INDArrayRealStructure = check(asRealStructure()) + override operator fun INDArrayRealStructure.div(arg: Double): INDArrayRealStructure { + check(this) + return ndArray.div(arg).wrap() + } + + override operator fun INDArrayRealStructure.plus(arg: Double): INDArrayRealStructure { + check(this) + return ndArray.add(arg).wrap() + } + + override operator fun INDArrayRealStructure.minus(arg: Double): INDArrayRealStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + override operator fun INDArrayRealStructure.times(arg: Double): INDArrayRealStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + override operator fun Double.div(arg: INDArrayRealStructure): INDArrayRealStructure { + check(arg) + return arg.ndArray.rdiv(this).wrap() + } + + override operator fun Double.minus(arg: INDArrayRealStructure): INDArrayRealStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } } +/** + * Represents [NDField] over [INDArrayFloatStructure]. + */ class FloatINDArrayField(override val shape: IntArray, override val elementContext: Field = FloatField) : INDArrayField, INDArrayFloatStructure> { - override fun INDArray.wrap(): INDArrayFloatStructure = asFloatStructure() - override fun INDArrayFloatStructure.div(arg: Float): INDArrayFloatStructure = ndArray.divi(arg).wrap() - override fun INDArrayFloatStructure.plus(arg: Float): INDArrayFloatStructure = ndArray.addi(arg).wrap() - override fun INDArrayFloatStructure.minus(arg: Float): INDArrayFloatStructure = ndArray.subi(arg).wrap() - override fun INDArrayFloatStructure.times(arg: Float): INDArrayFloatStructure = ndArray.muli(arg).wrap() - override fun Float.div(arg: INDArrayFloatStructure): INDArrayFloatStructure = arg.ndArray.rdivi(this).wrap() - override fun Float.minus(arg: INDArrayFloatStructure): INDArrayFloatStructure = arg.ndArray.rsubi(this).wrap() + override fun INDArray.wrap(): INDArrayFloatStructure = check(asFloatStructure()) + override operator fun INDArrayFloatStructure.div(arg: Float): INDArrayFloatStructure { + check(this) + return ndArray.div(arg).wrap() + } + + override operator fun INDArrayFloatStructure.plus(arg: Float): INDArrayFloatStructure { + check(this) + return ndArray.add(arg).wrap() + } + + override operator fun INDArrayFloatStructure.minus(arg: Float): INDArrayFloatStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + override operator fun INDArrayFloatStructure.times(arg: Float): INDArrayFloatStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + override operator fun Float.div(arg: INDArrayFloatStructure): INDArrayFloatStructure { + check(arg) + return arg.ndArray.rdiv(this).wrap() + } + + override operator fun Float.minus(arg: INDArrayFloatStructure): INDArrayFloatStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } } +/** + * Represents [NDRing] over [INDArrayIntStructure]. + */ class IntINDArrayRing(override val shape: IntArray, override val elementContext: Ring = IntRing) : INDArrayRing, INDArrayIntStructure> { - override fun INDArray.wrap(): INDArrayIntStructure = asIntStructure() - override fun INDArrayIntStructure.plus(arg: Int): INDArrayIntStructure = ndArray.addi(arg).wrap() - override fun INDArrayIntStructure.minus(arg: Int): INDArrayIntStructure = ndArray.subi(arg).wrap() - override fun INDArrayIntStructure.times(arg: Int): INDArrayIntStructure = ndArray.muli(arg).wrap() - override fun Int.minus(arg: INDArrayIntStructure): INDArrayIntStructure = arg.ndArray.rsubi(this).wrap() + override fun INDArray.wrap(): INDArrayIntStructure = check(asIntStructure()) + override operator fun INDArrayIntStructure.plus(arg: Int): INDArrayIntStructure { + check(this) + return ndArray.add(arg).wrap() + } + + override operator fun INDArrayIntStructure.minus(arg: Int): INDArrayIntStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + override operator fun INDArrayIntStructure.times(arg: Int): INDArrayIntStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + override operator fun Int.minus(arg: INDArrayIntStructure): INDArrayIntStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDRing] over [INDArrayLongStructure]. + */ +class LongINDArrayRing(override val shape: IntArray, override val elementContext: Ring = LongRing) : + INDArrayRing, INDArrayLongStructure> { + override fun INDArray.wrap(): INDArrayLongStructure = check(asLongStructure()) + override operator fun INDArrayLongStructure.plus(arg: Long): INDArrayLongStructure { + check(this) + return ndArray.add(arg).wrap() + } + + override operator fun INDArrayLongStructure.minus(arg: Long): INDArrayLongStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + override operator fun INDArrayLongStructure.times(arg: Long): INDArrayLongStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + override operator fun Long.minus(arg: INDArrayLongStructure): INDArrayLongStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } } diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt index bba5089a1..2759f9fdb 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt @@ -3,7 +3,24 @@ package scientifik.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray import org.nd4j.linalg.api.shape.Shape -internal sealed class INDArrayIteratorBase(protected val iterateOver: INDArray) : Iterator> { +private class INDArrayIndicesIterator(private val iterateOver: INDArray) : Iterator { + private var i: Int = 0 + + override fun hasNext(): Boolean = i < iterateOver.length() + + override fun next(): IntArray { + val la = if (iterateOver.ordering() == 'c') + Shape.ind2subC(iterateOver, i++.toLong())!! + else + Shape.ind2sub(iterateOver, i++.toLong())!! + + return la.toIntArray() + } +} + +internal fun INDArray.indicesIterator(): Iterator = INDArrayIndicesIterator(this) + +private sealed class INDArrayIteratorBase(protected val iterateOver: INDArray) : Iterator> { private var i: Int = 0 final override fun hasNext(): Boolean = i < iterateOver.length() @@ -20,26 +37,26 @@ internal sealed class INDArrayIteratorBase(protected val iterateOver: INDArra } } -internal class INDArrayRealIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { +private class INDArrayRealIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray): Double = iterateOver.getDouble(*indices) } -internal fun INDArray.realIterator(): INDArrayRealIterator = INDArrayRealIterator(this) +internal fun INDArray.realIterator(): Iterator> = INDArrayRealIterator(this) -internal class INDArrayLongIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { +private class INDArrayLongIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray) = iterateOver.getLong(*indices) } -internal fun INDArray.longIterator(): INDArrayLongIterator = INDArrayLongIterator(this) +internal fun INDArray.longIterator(): Iterator> = INDArrayLongIterator(this) -internal class INDArrayIntIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { +private class INDArrayIntIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray) = iterateOver.getInt(*indices.toIntArray()) } -internal fun INDArray.intIterator(): INDArrayIntIterator = INDArrayIntIterator(this) +internal fun INDArray.intIterator(): Iterator> = INDArrayIntIterator(this) -internal class INDArrayFloatIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { +private class INDArrayFloatIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray) = iterateOver.getFloat(*indices) } -internal fun INDArray.floatIterator() = INDArrayFloatIterator(this) +internal fun INDArray.floatIterator(): Iterator> = INDArrayFloatIterator(this) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt index 06b0354d8..39cefee3d 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt +++ b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt @@ -4,45 +4,77 @@ import org.nd4j.linalg.api.ndarray.INDArray import scientifik.kmath.structures.MutableNDStructure import scientifik.kmath.structures.NDStructure -interface INDArrayStructure : NDStructure { - val ndArray: INDArray +/** + * Represents a [NDStructure] wrapping an [INDArray] object. + * + * @param T the type of items. + */ +sealed class INDArrayStructure : MutableNDStructure { + /** + * The wrapped [INDArray]. + */ + abstract val ndArray: INDArray override val shape: IntArray get() = ndArray.shape().toIntArray() - fun elementsIterator(): Iterator> + internal abstract fun elementsIterator(): Iterator> + internal fun indicesIterator(): Iterator = ndArray.indicesIterator() override fun elements(): Sequence> = Sequence(::elementsIterator) } -data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure, MutableNDStructure { +/** + * Represents a [NDStructure] over [INDArray] elements of which are accessed as ints. + */ +data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure() { override fun elementsIterator(): Iterator> = ndArray.intIterator() override fun get(index: IntArray): Int = ndArray.getInt(*index) override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) } } +/** + * Wraps this [INDArray] to [INDArrayIntStructure]. + */ fun INDArray.asIntStructure(): INDArrayIntStructure = INDArrayIntStructure(this) -data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure { +/** + * Represents a [NDStructure] over [INDArray] elements of which are accessed as longs. + */ +data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure() { override fun elementsIterator(): Iterator> = ndArray.longIterator() override fun get(index: IntArray): Long = ndArray.getLong(*index.toLongArray()) + override fun set(index: IntArray, value: Long): Unit = run { ndArray.putScalar(index, value.toDouble()) } } +/** + * Wraps this [INDArray] to [INDArrayLongStructure]. + */ fun INDArray.asLongStructure(): INDArrayLongStructure = INDArrayLongStructure(this) -data class INDArrayRealStructure(override val ndArray: INDArray) : INDArrayStructure, - MutableNDStructure { +/** + * Represents a [NDStructure] over [INDArray] elements of which are accessed as reals. + */ +data class INDArrayRealStructure(override val ndArray: INDArray) : INDArrayStructure() { override fun elementsIterator(): Iterator> = ndArray.realIterator() override fun get(index: IntArray): Double = ndArray.getDouble(*index) override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) } } +/** + * Wraps this [INDArray] to [INDArrayRealStructure]. + */ fun INDArray.asRealStructure(): INDArrayRealStructure = INDArrayRealStructure(this) -data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure, - MutableNDStructure { +/** + * Represents a [NDStructure] over [INDArray] elements of which are accessed as floats. + */ +data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure() { override fun elementsIterator(): Iterator> = ndArray.floatIterator() override fun get(index: IntArray): Float = ndArray.getFloat(*index) override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) } } +/** + * Wraps this [INDArray] to [INDArrayFloatStructure]. + */ fun INDArray.asFloatStructure(): INDArrayFloatStructure = INDArrayFloatStructure(this) From 2bc62356d6da378f2aae87cc830a119f13f03af5 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 27 Aug 2020 16:44:58 +0700 Subject: [PATCH 23/28] Fix compilation issues --- .../kotlin/scientifik/kmath/structures/BoxingNDField.kt | 3 ++- .../kotlin/scientifik/kmath/structures/BoxingNDRing.kt | 3 ++- .../kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt index 4cbb565c1..29e0ba276 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt @@ -14,8 +14,9 @@ class BoxingNDField>( fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) - override fun check(vararg elements: NDBuffer) { + override fun check(vararg elements: NDBuffer): Array> { if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") + return elements } override val zero: BufferedNDFieldElement by lazy { produce { zero } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt index f7be95736..8aabe169a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt @@ -14,8 +14,9 @@ class BoxingNDRing>( fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) - override fun check(vararg elements: NDBuffer) { + override fun check(vararg elements: NDBuffer): Array> { if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") + return elements } override val zero: BufferedNDRingElement by lazy { produce { zero } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt index 06922c56f..de8a150c6 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt @@ -5,8 +5,9 @@ import scientifik.kmath.operations.* interface BufferedNDAlgebra : NDAlgebra> { val strides: Strides - override fun check(vararg elements: NDBuffer) { + override fun check(vararg elements: NDBuffer): Array> { if (!elements.all { it.strides == this.strides }) error("Strides mismatch") + return elements } /** From d54e7c3e97e02090ef5abae2cfe1aca32c6d8e0a Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 15 Sep 2020 17:48:43 +0700 Subject: [PATCH 24/28] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 015f0deeb..bf0316c9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] ### Added +- ND4J support module submitting `NDStructure` and `NDAlgebra` over `INDArray`. ### Changed @@ -30,7 +31,6 @@ - Blocking chains in `kmath-coroutines` - Full hyperbolic functions support and default implementations within `ExtendedField` - Norm support for `Complex` -- ND4J support module submitting `NDStructure` and `NDAlgebra` over `INDArray`. ### Changed - `readAsMemory` now has `throws IOException` in JVM signature. From 4e5c7ab366e41fe5ec8f392b4bf36d0ba9be6769 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 20 Sep 2020 16:45:26 +0700 Subject: [PATCH 25/28] Make one-liner not a one-liner --- kmath-nd4j/build.gradle.kts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kmath-nd4j/build.gradle.kts b/kmath-nd4j/build.gradle.kts index 59354a8f9..110a2ac30 100644 --- a/kmath-nd4j/build.gradle.kts +++ b/kmath-nd4j/build.gradle.kts @@ -1,4 +1,6 @@ -plugins { id("scientifik.jvm") } +plugins { + id("scientifik.jvm") +} dependencies { api(project(":kmath-core")) From 2ee5d0f325fb7189462b4190497be9efefe420eb Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 21 Sep 2020 20:53:31 +0700 Subject: [PATCH 26/28] Change package name, simplify exposed API types, update build snippet, minor refactor --- examples/build.gradle.kts | 6 +- .../kmath/structures/BoxingNDField.kt | 5 +- .../kmath/structures/BufferedNDAlgebra.kt | 4 +- .../kscience/kmath/structures/NDAlgebra.kt | 2 +- kmath-nd4j/README.md | 7 + kmath-nd4j/build.gradle.kts | 2 +- .../kscience.kmath.nd4j/INDArrayAlgebra.kt | 284 ++++++++++++++++++ .../INDArrayIterators.kt | 4 +- .../kscience.kmath.nd4j/INDArrayStructures.kt | 68 +++++ .../arrays.kt | 2 +- .../scientifik.kmath.nd4j/INDArrayAlgebra.kt | 273 ----------------- .../INDArrayStructures.kt | 80 ----- .../kmath/nd4j/INDArrayAlgebraTest.kt | 11 +- .../kmath/nd4j/INDArrayStructureTest.kt | 41 +-- 14 files changed, 402 insertions(+), 387 deletions(-) create mode 100644 kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayAlgebra.kt rename kmath-nd4j/src/main/kotlin/{scientifik.kmath.nd4j => kscience.kmath.nd4j}/INDArrayIterators.kt (94%) create mode 100644 kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayStructures.kt rename kmath-nd4j/src/main/kotlin/{scientifik.kmath.nd4j => kscience.kmath.nd4j}/arrays.kt (85%) delete mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt delete mode 100644 kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt rename kmath-nd4j/src/test/kotlin/{scientifik => kscience}/kmath/nd4j/INDArrayAlgebraTest.kt (78%) rename kmath-nd4j/src/test/kotlin/{scientifik => kscience}/kmath/nd4j/INDArrayStructureTest.kt (55%) diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 3d193efce..f0161afbb 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -26,9 +26,13 @@ dependencies { implementation(project(":kmath-prob")) implementation(project(":kmath-viktor")) implementation(project(":kmath-dimensions")) + implementation(project(":kmath-nd4j")) implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6") implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20") - "benchmarksCompile"(sourceSets.main.get().output + sourceSets.main.get().compileClasspath) //sourceSets.main.output + sourceSets.main.runtimeClasspath + implementation("org.slf4j:slf4j-simple:1.7.30") + implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") + "benchmarksImplementation"("org.jetbrains.kotlinx:kotlinx.benchmark.runtime-jvm:0.2.0-dev-8") + "benchmarksImplementation"(sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath) } // Configure benchmark diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt index ddec7bd25..dc65b12c4 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt @@ -15,8 +15,9 @@ public class BoxingNDField>( public fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) - public override fun check(vararg elements: NDBuffer) { - check(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } + public override fun check(vararg elements: NDBuffer): Array> { + require(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } + return elements } public override fun produce(initializer: F.(IntArray) -> T): BufferedNDFieldElement = diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt index 66b4f19e1..251b1bcb5 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt @@ -5,8 +5,10 @@ import kscience.kmath.operations.* public interface BufferedNDAlgebra : NDAlgebra> { public val strides: Strides - public override fun check(vararg elements: NDBuffer): Unit = + public override fun check(vararg elements: NDBuffer): Array> { require(elements.all { it.strides == strides }) { ("Strides mismatch") } + return elements + } /** * Convert any [NDStructure] to buffered structure using strides from this context. diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt index 35a65c487..4315f0423 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt @@ -11,7 +11,7 @@ import kscience.kmath.operations.Space * @property expected the expected shape. * @property actual the actual shape. */ -public class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : +public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) : RuntimeException("Shape ${actual.contentToString()} doesn't fit in expected shape ${expected.contentToString()}.") /** diff --git a/kmath-nd4j/README.md b/kmath-nd4j/README.md index f8ca5eed2..fac24504a 100644 --- a/kmath-nd4j/README.md +++ b/kmath-nd4j/README.md @@ -13,26 +13,33 @@ This subproject implements the following features: > > ```gradle > repositories { +> mavenCentral() > maven { url 'https://dl.bintray.com/mipt-npm/scientifik' } > maven { url 'https://dl.bintray.com/mipt-npm/dev' } > } > > dependencies { > implementation 'scientifik:kmath-nd4j:0.1.4-dev-8' +> implementation 'org.nd4j:nd4j-native-platform:1.0.0-beta7' > } > ``` > **Gradle Kotlin DSL:** > > ```kotlin > repositories { +> mavenCentral() > maven("https://dl.bintray.com/mipt-npm/scientifik") > maven("https://dl.bintray.com/mipt-npm/dev") > } > > dependencies { > implementation("scientifik:kmath-nd4j:0.1.4-dev-8") +> implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") > } > ``` +> +> This distribution also needs an implementation of ND4J API. The ND4J Native Platform is usually the fastest one, so +> it is included to the snippet. > ## Examples diff --git a/kmath-nd4j/build.gradle.kts b/kmath-nd4j/build.gradle.kts index 110a2ac30..67569b870 100644 --- a/kmath-nd4j/build.gradle.kts +++ b/kmath-nd4j/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("scientifik.jvm") + id("ru.mipt.npm.jvm") } dependencies { diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayAlgebra.kt new file mode 100644 index 000000000..728ce3773 --- /dev/null +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayAlgebra.kt @@ -0,0 +1,284 @@ +package kscience.kmath.nd4j + +import org.nd4j.linalg.api.ndarray.INDArray +import org.nd4j.linalg.factory.Nd4j +import kscience.kmath.operations.* +import kscience.kmath.structures.* + +/** + * Represents [NDAlgebra] over [INDArrayAlgebra]. + * + * @param T the type of ND-structure element. + * @param C the type of the element context. + */ +public interface INDArrayAlgebra : NDAlgebra> { + /** + * Wraps [INDArray] to [N]. + */ + public fun INDArray.wrap(): INDArrayStructure + + public override fun produce(initializer: C.(IntArray) -> T): INDArrayStructure { + val struct = Nd4j.create(*shape)!!.wrap() + struct.indicesIterator().forEach { struct[it] = elementContext.initializer(it) } + return struct + } + + public override fun map(arg: INDArrayStructure, transform: C.(T) -> T): INDArrayStructure { + check(arg) + val newStruct = arg.ndArray.dup().wrap() + newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } + return newStruct + } + + public override fun mapIndexed( + arg: INDArrayStructure, + transform: C.(index: IntArray, T) -> T + ): INDArrayStructure { + check(arg) + val new = Nd4j.create(*shape).wrap() + new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(idx, arg[idx]) } + return new + } + + public override fun combine( + a: INDArrayStructure, + b: INDArrayStructure, + transform: C.(T, T) -> T + ): INDArrayStructure { + check(a, b) + val new = Nd4j.create(*shape).wrap() + new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(a[idx], b[idx]) } + return new + } +} + +/** + * Represents [NDSpace] over [INDArrayStructure]. + * + * @param T the type of the element contained in ND structure. + * @param S the type of space of structure elements. + */ +public interface INDArraySpace : NDSpace>, INDArrayAlgebra where S : Space { + public override val zero: INDArrayStructure + get() = Nd4j.zeros(*shape).wrap() + + public override fun add(a: INDArrayStructure, b: INDArrayStructure): INDArrayStructure { + check(a, b) + return a.ndArray.add(b.ndArray).wrap() + } + + public override operator fun INDArrayStructure.minus(b: INDArrayStructure): INDArrayStructure { + check(this, b) + return ndArray.sub(b.ndArray).wrap() + } + + public override operator fun INDArrayStructure.unaryMinus(): INDArrayStructure { + check(this) + return ndArray.neg().wrap() + } + + public override fun multiply(a: INDArrayStructure, k: Number): INDArrayStructure { + check(a) + return a.ndArray.mul(k).wrap() + } + + public override operator fun INDArrayStructure.div(k: Number): INDArrayStructure { + check(this) + return ndArray.div(k).wrap() + } + + public override operator fun INDArrayStructure.times(k: Number): INDArrayStructure { + check(this) + return ndArray.mul(k).wrap() + } +} + +/** + * Represents [NDRing] over [INDArrayStructure]. + * + * @param T the type of the element contained in ND structure. + * @param R the type of ring of structure elements. + */ +public interface INDArrayRing : NDRing>, INDArraySpace where R : Ring { + public override val one: INDArrayStructure + get() = Nd4j.ones(*shape).wrap() + + public override fun multiply(a: INDArrayStructure, b: INDArrayStructure): INDArrayStructure { + check(a, b) + return a.ndArray.mul(b.ndArray).wrap() + } + + public override operator fun INDArrayStructure.minus(b: Number): INDArrayStructure { + check(this) + return ndArray.sub(b).wrap() + } + + public override operator fun INDArrayStructure.plus(b: Number): INDArrayStructure { + check(this) + return ndArray.add(b).wrap() + } + + public override operator fun Number.minus(b: INDArrayStructure): INDArrayStructure { + check(b) + return b.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDField] over [INDArrayStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param F the type field of structure elements. + */ +public interface INDArrayField : NDField>, INDArrayRing where F : Field { + public override fun divide(a: INDArrayStructure, b: INDArrayStructure): INDArrayStructure { + check(a, b) + return a.ndArray.div(b.ndArray).wrap() + } + + public override operator fun Number.div(b: INDArrayStructure): INDArrayStructure { + check(b) + return b.ndArray.rdiv(this).wrap() + } +} + +/** + * Represents [NDField] over [INDArrayRealStructure]. + */ +public class RealINDArrayField(public override val shape: IntArray) : INDArrayField { + public override val elementContext: RealField + get() = RealField + + public override fun INDArray.wrap(): INDArrayStructure = check(asRealStructure()) + + public override operator fun INDArrayStructure.div(arg: Double): INDArrayStructure { + check(this) + return ndArray.div(arg).wrap() + } + + public override operator fun INDArrayStructure.plus(arg: Double): INDArrayStructure { + check(this) + return ndArray.add(arg).wrap() + } + + public override operator fun INDArrayStructure.minus(arg: Double): INDArrayStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + public override operator fun INDArrayStructure.times(arg: Double): INDArrayStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + public override operator fun Double.div(arg: INDArrayStructure): INDArrayStructure { + check(arg) + return arg.ndArray.rdiv(this).wrap() + } + + public override operator fun Double.minus(arg: INDArrayStructure): INDArrayStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDField] over [INDArrayStructure] of [Float]. + */ +public class FloatINDArrayField(public override val shape: IntArray) : INDArrayField { + public override val elementContext: FloatField + get() = FloatField + + public override fun INDArray.wrap(): INDArrayStructure = check(asFloatStructure()) + + public override operator fun INDArrayStructure.div(arg: Float): INDArrayStructure { + check(this) + return ndArray.div(arg).wrap() + } + + public override operator fun INDArrayStructure.plus(arg: Float): INDArrayStructure { + check(this) + return ndArray.add(arg).wrap() + } + + public override operator fun INDArrayStructure.minus(arg: Float): INDArrayStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + public override operator fun INDArrayStructure.times(arg: Float): INDArrayStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + public override operator fun Float.div(arg: INDArrayStructure): INDArrayStructure { + check(arg) + return arg.ndArray.rdiv(this).wrap() + } + + public override operator fun Float.minus(arg: INDArrayStructure): INDArrayStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDRing] over [INDArrayIntStructure]. + */ +public class IntINDArrayRing(public override val shape: IntArray) : INDArrayRing { + public override val elementContext: IntRing + get() = IntRing + + public override fun INDArray.wrap(): INDArrayStructure = check(asIntStructure()) + + public override operator fun INDArrayStructure.plus(arg: Int): INDArrayStructure { + check(this) + return ndArray.add(arg).wrap() + } + + public override operator fun INDArrayStructure.minus(arg: Int): INDArrayStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + public override operator fun INDArrayStructure.times(arg: Int): INDArrayStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + public override operator fun Int.minus(arg: INDArrayStructure): INDArrayStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDRing] over [INDArrayStructure] of [Long]. + */ +public class LongINDArrayRing(public override val shape: IntArray) : INDArrayRing { + public override val elementContext: LongRing + get() = LongRing + + public override fun INDArray.wrap(): INDArrayStructure = check(asLongStructure()) + + public override operator fun INDArrayStructure.plus(arg: Long): INDArrayStructure { + check(this) + return ndArray.add(arg).wrap() + } + + public override operator fun INDArrayStructure.minus(arg: Long): INDArrayStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + public override operator fun INDArrayStructure.times(arg: Long): INDArrayStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + public override operator fun Long.minus(arg: INDArrayStructure): INDArrayStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } +} diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayIterators.kt similarity index 94% rename from kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt rename to kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayIterators.kt index 2759f9fdb..9e7ef9e16 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayIterators.kt +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayIterators.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.nd4j +package kscience.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray import org.nd4j.linalg.api.shape.Shape @@ -18,7 +18,7 @@ private class INDArrayIndicesIterator(private val iterateOver: INDArray) : Itera } } -internal fun INDArray.indicesIterator(): Iterator = INDArrayIndicesIterator(this) +internal fun INDArray.indicesIterator(): Iterator = INDArrayIndicesIterator(this) private sealed class INDArrayIteratorBase(protected val iterateOver: INDArray) : Iterator> { private var i: Int = 0 diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayStructures.kt new file mode 100644 index 000000000..5d4e1a979 --- /dev/null +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayStructures.kt @@ -0,0 +1,68 @@ +package kscience.kmath.nd4j + +import org.nd4j.linalg.api.ndarray.INDArray +import kscience.kmath.structures.MutableNDStructure +import kscience.kmath.structures.NDStructure + +/** + * Represents a [NDStructure] wrapping an [INDArray] object. + * + * @param T the type of items. + */ +public sealed class INDArrayStructure : MutableNDStructure { + /** + * The wrapped [INDArray]. + */ + public abstract val ndArray: INDArray + + public override val shape: IntArray + get() = ndArray.shape().toIntArray() + + internal abstract fun elementsIterator(): Iterator> + internal fun indicesIterator(): Iterator = ndArray.indicesIterator() + public override fun elements(): Sequence> = Sequence(::elementsIterator) +} + +private data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure() { + override fun elementsIterator(): Iterator> = ndArray.intIterator() + override fun get(index: IntArray): Int = ndArray.getInt(*index) + override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) } +} + +/** + * Wraps this [INDArray] to [INDArrayStructure]. + */ +public fun INDArray.asIntStructure(): INDArrayStructure = INDArrayIntStructure(this) + +private data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure() { + override fun elementsIterator(): Iterator> = ndArray.longIterator() + override fun get(index: IntArray): Long = ndArray.getLong(*index.toLongArray()) + override fun set(index: IntArray, value: Long): Unit = run { ndArray.putScalar(index, value.toDouble()) } +} + +/** + * Wraps this [INDArray] to [INDArrayStructure]. + */ +public fun INDArray.asLongStructure(): INDArrayStructure = INDArrayLongStructure(this) + +private data class INDArrayRealStructure(override val ndArray: INDArray) : INDArrayStructure() { + override fun elementsIterator(): Iterator> = ndArray.realIterator() + override fun get(index: IntArray): Double = ndArray.getDouble(*index) + override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) } +} + +/** + * Wraps this [INDArray] to [INDArrayStructure]. + */ +public fun INDArray.asRealStructure(): INDArrayStructure = INDArrayRealStructure(this) + +private data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure() { + override fun elementsIterator(): Iterator> = ndArray.floatIterator() + override fun get(index: IntArray): Float = ndArray.getFloat(*index) + override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) } +} + +/** + * Wraps this [INDArray] to [INDArrayStructure]. + */ +public fun INDArray.asFloatStructure(): INDArrayStructure = INDArrayFloatStructure(this) diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/arrays.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/arrays.kt similarity index 85% rename from kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/arrays.kt rename to kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/arrays.kt index 269fc89c2..798f81c35 100644 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/arrays.kt +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/arrays.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.nd4j +package kscience.kmath.nd4j internal fun IntArray.toLongArray(): LongArray = LongArray(size) { this[it].toLong() } internal fun LongArray.toIntArray(): IntArray = IntArray(size) { this[it].toInt() } diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt deleted file mode 100644 index c24e2ece6..000000000 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayAlgebra.kt +++ /dev/null @@ -1,273 +0,0 @@ -package scientifik.kmath.nd4j - -import org.nd4j.linalg.api.ndarray.INDArray -import org.nd4j.linalg.factory.Nd4j -import scientifik.kmath.operations.* -import scientifik.kmath.structures.* - -/** - * Represents [NDAlgebra] over [INDArrayAlgebra]. - * - * @param T the type of ND-structure element. - * @param C the type of the element context. - * @param N the type of the structure. - */ -interface INDArrayAlgebra : NDAlgebra where N : INDArrayStructure, N : MutableNDStructure { - /** - * Wraps [INDArray] to [N]. - */ - fun INDArray.wrap(): N - - override fun produce(initializer: C.(IntArray) -> T): N { - val struct = Nd4j.create(*shape)!!.wrap() - struct.indicesIterator().forEach { struct[it] = elementContext.initializer(it) } - return struct - } - - override fun map(arg: N, transform: C.(T) -> T): N { - check(arg) - val newStruct = arg.ndArray.dup().wrap() - newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } - return newStruct - } - - override fun mapIndexed(arg: N, transform: C.(index: IntArray, T) -> T): N { - check(arg) - val new = Nd4j.create(*shape).wrap() - new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(idx, arg[idx]) } - return new - } - - override fun combine(a: N, b: N, transform: C.(T, T) -> T): N { - check(a, b) - val new = Nd4j.create(*shape).wrap() - new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(a[idx], b[idx]) } - return new - } -} - -/** - * Represents [NDSpace] over [INDArrayStructure]. - * - * @param T the type of the element contained in ND structure. - * @param N the type of ND structure. - * @param S the type of space of structure elements. - */ -interface INDArraySpace : NDSpace, INDArrayAlgebra - where S : Space, N : INDArrayStructure, N : MutableNDStructure { - - override val zero: N - get() = Nd4j.zeros(*shape).wrap() - - override fun add(a: N, b: N): N { - check(a, b) - return a.ndArray.add(b.ndArray).wrap() - } - - override operator fun N.minus(b: N): N { - check(this, b) - return ndArray.sub(b.ndArray).wrap() - } - - override operator fun N.unaryMinus(): N { - check(this) - return ndArray.neg().wrap() - } - - override fun multiply(a: N, k: Number): N { - check(a) - return a.ndArray.mul(k).wrap() - } - - override operator fun N.div(k: Number): N { - check(this) - return ndArray.div(k).wrap() - } - - override operator fun N.times(k: Number): N { - check(this) - return ndArray.mul(k).wrap() - } -} - -/** - * Represents [NDRing] over [INDArrayStructure]. - * - * @param T the type of the element contained in ND structure. - * @param N the type of ND structure. - * @param R the type of ring of structure elements. - */ -interface INDArrayRing : NDRing, INDArraySpace - where R : Ring, N : INDArrayStructure, N : MutableNDStructure { - - override val one: N - get() = Nd4j.ones(*shape).wrap() - - override fun multiply(a: N, b: N): N { - check(a, b) - return a.ndArray.mul(b.ndArray).wrap() - } - - override operator fun N.minus(b: Number): N { - check(this) - return ndArray.sub(b).wrap() - } - - override operator fun N.plus(b: Number): N { - check(this) - return ndArray.add(b).wrap() - } - - override operator fun Number.minus(b: N): N { - check(b) - return b.ndArray.rsub(this).wrap() - } -} - -/** - * Represents [NDField] over [INDArrayStructure]. - * - * @param T the type of the element contained in ND structure. - * @param N the type of ND structure. - * @param F the type field of structure elements. - */ -interface INDArrayField : NDField, INDArrayRing - where F : Field, N : INDArrayStructure, N : MutableNDStructure { - override fun divide(a: N, b: N): N { - check(a, b) - return a.ndArray.div(b.ndArray).wrap() - } - - override operator fun Number.div(b: N): N { - check(b) - return b.ndArray.rdiv(this).wrap() - } -} - -/** - * Represents [NDField] over [INDArrayRealStructure]. - */ -class RealINDArrayField(override val shape: IntArray, override val elementContext: Field = RealField) : - INDArrayField, INDArrayRealStructure> { - override fun INDArray.wrap(): INDArrayRealStructure = check(asRealStructure()) - override operator fun INDArrayRealStructure.div(arg: Double): INDArrayRealStructure { - check(this) - return ndArray.div(arg).wrap() - } - - override operator fun INDArrayRealStructure.plus(arg: Double): INDArrayRealStructure { - check(this) - return ndArray.add(arg).wrap() - } - - override operator fun INDArrayRealStructure.minus(arg: Double): INDArrayRealStructure { - check(this) - return ndArray.sub(arg).wrap() - } - - override operator fun INDArrayRealStructure.times(arg: Double): INDArrayRealStructure { - check(this) - return ndArray.mul(arg).wrap() - } - - override operator fun Double.div(arg: INDArrayRealStructure): INDArrayRealStructure { - check(arg) - return arg.ndArray.rdiv(this).wrap() - } - - override operator fun Double.minus(arg: INDArrayRealStructure): INDArrayRealStructure { - check(arg) - return arg.ndArray.rsub(this).wrap() - } -} - -/** - * Represents [NDField] over [INDArrayFloatStructure]. - */ -class FloatINDArrayField(override val shape: IntArray, override val elementContext: Field = FloatField) : - INDArrayField, INDArrayFloatStructure> { - override fun INDArray.wrap(): INDArrayFloatStructure = check(asFloatStructure()) - override operator fun INDArrayFloatStructure.div(arg: Float): INDArrayFloatStructure { - check(this) - return ndArray.div(arg).wrap() - } - - override operator fun INDArrayFloatStructure.plus(arg: Float): INDArrayFloatStructure { - check(this) - return ndArray.add(arg).wrap() - } - - override operator fun INDArrayFloatStructure.minus(arg: Float): INDArrayFloatStructure { - check(this) - return ndArray.sub(arg).wrap() - } - - override operator fun INDArrayFloatStructure.times(arg: Float): INDArrayFloatStructure { - check(this) - return ndArray.mul(arg).wrap() - } - - override operator fun Float.div(arg: INDArrayFloatStructure): INDArrayFloatStructure { - check(arg) - return arg.ndArray.rdiv(this).wrap() - } - - override operator fun Float.minus(arg: INDArrayFloatStructure): INDArrayFloatStructure { - check(arg) - return arg.ndArray.rsub(this).wrap() - } -} - -/** - * Represents [NDRing] over [INDArrayIntStructure]. - */ -class IntINDArrayRing(override val shape: IntArray, override val elementContext: Ring = IntRing) : - INDArrayRing, INDArrayIntStructure> { - override fun INDArray.wrap(): INDArrayIntStructure = check(asIntStructure()) - override operator fun INDArrayIntStructure.plus(arg: Int): INDArrayIntStructure { - check(this) - return ndArray.add(arg).wrap() - } - - override operator fun INDArrayIntStructure.minus(arg: Int): INDArrayIntStructure { - check(this) - return ndArray.sub(arg).wrap() - } - - override operator fun INDArrayIntStructure.times(arg: Int): INDArrayIntStructure { - check(this) - return ndArray.mul(arg).wrap() - } - - override operator fun Int.minus(arg: INDArrayIntStructure): INDArrayIntStructure { - check(arg) - return arg.ndArray.rsub(this).wrap() - } -} - -/** - * Represents [NDRing] over [INDArrayLongStructure]. - */ -class LongINDArrayRing(override val shape: IntArray, override val elementContext: Ring = LongRing) : - INDArrayRing, INDArrayLongStructure> { - override fun INDArray.wrap(): INDArrayLongStructure = check(asLongStructure()) - override operator fun INDArrayLongStructure.plus(arg: Long): INDArrayLongStructure { - check(this) - return ndArray.add(arg).wrap() - } - - override operator fun INDArrayLongStructure.minus(arg: Long): INDArrayLongStructure { - check(this) - return ndArray.sub(arg).wrap() - } - - override operator fun INDArrayLongStructure.times(arg: Long): INDArrayLongStructure { - check(this) - return ndArray.mul(arg).wrap() - } - - override operator fun Long.minus(arg: INDArrayLongStructure): INDArrayLongStructure { - check(arg) - return arg.ndArray.rsub(this).wrap() - } -} diff --git a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt deleted file mode 100644 index 39cefee3d..000000000 --- a/kmath-nd4j/src/main/kotlin/scientifik.kmath.nd4j/INDArrayStructures.kt +++ /dev/null @@ -1,80 +0,0 @@ -package scientifik.kmath.nd4j - -import org.nd4j.linalg.api.ndarray.INDArray -import scientifik.kmath.structures.MutableNDStructure -import scientifik.kmath.structures.NDStructure - -/** - * Represents a [NDStructure] wrapping an [INDArray] object. - * - * @param T the type of items. - */ -sealed class INDArrayStructure : MutableNDStructure { - /** - * The wrapped [INDArray]. - */ - abstract val ndArray: INDArray - - override val shape: IntArray - get() = ndArray.shape().toIntArray() - - internal abstract fun elementsIterator(): Iterator> - internal fun indicesIterator(): Iterator = ndArray.indicesIterator() - override fun elements(): Sequence> = Sequence(::elementsIterator) -} - -/** - * Represents a [NDStructure] over [INDArray] elements of which are accessed as ints. - */ -data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure() { - override fun elementsIterator(): Iterator> = ndArray.intIterator() - override fun get(index: IntArray): Int = ndArray.getInt(*index) - override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) } -} - -/** - * Wraps this [INDArray] to [INDArrayIntStructure]. - */ -fun INDArray.asIntStructure(): INDArrayIntStructure = INDArrayIntStructure(this) - -/** - * Represents a [NDStructure] over [INDArray] elements of which are accessed as longs. - */ -data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure() { - override fun elementsIterator(): Iterator> = ndArray.longIterator() - override fun get(index: IntArray): Long = ndArray.getLong(*index.toLongArray()) - override fun set(index: IntArray, value: Long): Unit = run { ndArray.putScalar(index, value.toDouble()) } -} - -/** - * Wraps this [INDArray] to [INDArrayLongStructure]. - */ -fun INDArray.asLongStructure(): INDArrayLongStructure = INDArrayLongStructure(this) - -/** - * Represents a [NDStructure] over [INDArray] elements of which are accessed as reals. - */ -data class INDArrayRealStructure(override val ndArray: INDArray) : INDArrayStructure() { - override fun elementsIterator(): Iterator> = ndArray.realIterator() - override fun get(index: IntArray): Double = ndArray.getDouble(*index) - override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) } -} - -/** - * Wraps this [INDArray] to [INDArrayRealStructure]. - */ -fun INDArray.asRealStructure(): INDArrayRealStructure = INDArrayRealStructure(this) - -/** - * Represents a [NDStructure] over [INDArray] elements of which are accessed as floats. - */ -data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure() { - override fun elementsIterator(): Iterator> = ndArray.floatIterator() - override fun get(index: IntArray): Float = ndArray.getFloat(*index) - override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) } -} - -/** - * Wraps this [INDArray] to [INDArrayFloatStructure]. - */ -fun INDArray.asFloatStructure(): INDArrayFloatStructure = INDArrayFloatStructure(this) diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayAlgebraTest.kt similarity index 78% rename from kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt rename to kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayAlgebraTest.kt index 4aa40c233..1a4f4c9f3 100644 --- a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayAlgebraTest.kt +++ b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayAlgebraTest.kt @@ -1,15 +1,16 @@ -package scientifik.kmath.nd4j +package kscience.kmath.nd4j import org.nd4j.linalg.factory.Nd4j -import scientifik.kmath.operations.invoke +import kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.fail internal class INDArrayAlgebraTest { @Test fun testProduce() { val res = (RealINDArrayField(intArrayOf(2, 2))) { produce { it.sum().toDouble() } } - val expected = Nd4j.create(2, 2)!!.asRealStructure() + val expected = (Nd4j.create(2, 2) ?: fail()).asRealStructure() expected[intArrayOf(0, 0)] = 0.0 expected[intArrayOf(0, 1)] = 1.0 expected[intArrayOf(1, 0)] = 1.0 @@ -20,7 +21,7 @@ internal class INDArrayAlgebraTest { @Test fun testMap() { val res = (IntINDArrayRing(intArrayOf(2, 2))) { map(one) { it + it * 2 } } - val expected = Nd4j.create(2, 2)!!.asIntStructure() + val expected = (Nd4j.create(2, 2) ?: fail()).asIntStructure() expected[intArrayOf(0, 0)] = 3 expected[intArrayOf(0, 1)] = 3 expected[intArrayOf(1, 0)] = 3 @@ -31,7 +32,7 @@ internal class INDArrayAlgebraTest { @Test fun testAdd() { val res = (IntINDArrayRing(intArrayOf(2, 2))) { one + 25 } - val expected = Nd4j.create(2, 2)!!.asIntStructure() + val expected = (Nd4j.create(2, 2) ?: fail()).asIntStructure() expected[intArrayOf(0, 0)] = 26 expected[intArrayOf(0, 1)] = 26 expected[intArrayOf(1, 0)] = 26 diff --git a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayStructureTest.kt similarity index 55% rename from kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt rename to kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayStructureTest.kt index dfede6d32..63426d7f9 100644 --- a/kmath-nd4j/src/test/kotlin/scientifik/kmath/nd4j/INDArrayStructureTest.kt +++ b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayStructureTest.kt @@ -1,70 +1,71 @@ -package scientifik.kmath.nd4j +package kscience.kmath.nd4j +import kscience.kmath.structures.get import org.nd4j.linalg.factory.Nd4j -import scientifik.kmath.structures.get import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals +import kotlin.test.fail internal class INDArrayStructureTest { @Test fun testElements() { val nd = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct = INDArrayRealStructure(nd) + val struct = nd.asRealStructure() val res = struct.elements().map(Pair::second).toList() assertEquals(listOf(1.0, 2.0, 3.0), res) } @Test fun testShape() { - val nd = Nd4j.rand(10, 2, 3, 6)!! - val struct = INDArrayLongStructure(nd) + val nd = Nd4j.rand(10, 2, 3, 6) ?: fail() + val struct = nd.asRealStructure() assertEquals(intArrayOf(10, 2, 3, 6).toList(), struct.shape.toList()) } @Test fun testEquals() { - val nd1 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct1 = INDArrayRealStructure(nd1) + val nd1 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0)) ?: fail() + val struct1 = nd1.asRealStructure() assertEquals(struct1, struct1) - assertNotEquals(struct1, null as INDArrayRealStructure?) - val nd2 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct2 = INDArrayRealStructure(nd2) + assertNotEquals(struct1 as Any?, null) + val nd2 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0)) ?: fail() + val struct2 = nd2.asRealStructure() assertEquals(struct1, struct2) assertEquals(struct2, struct1) - val nd3 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct3 = INDArrayRealStructure(nd3) + val nd3 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0)) ?: fail() + val struct3 = nd3.asRealStructure() assertEquals(struct2, struct3) assertEquals(struct1, struct3) } @Test fun testHashCode() { - val nd1 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct1 = INDArrayRealStructure(nd1) - val nd2 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! - val struct2 = INDArrayRealStructure(nd2) + val nd1 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))?:fail() + val struct1 = nd1.asRealStructure() + val nd2 = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))?:fail() + val struct2 = nd2.asRealStructure() assertEquals(struct1.hashCode(), struct2.hashCode()) } @Test fun testDimension() { val nd = Nd4j.rand(8, 16, 3, 7, 1)!! - val struct = INDArrayFloatStructure(nd) + val struct = nd.asFloatStructure() assertEquals(5, struct.dimension) } @Test fun testGet() { - val nd = Nd4j.rand(10, 2, 3, 6)!! - val struct = INDArrayIntStructure(nd) + val nd = Nd4j.rand(10, 2, 3, 6)?:fail() + val struct = nd.asIntStructure() assertEquals(nd.getInt(0, 0, 0, 0), struct[0, 0, 0, 0]) } @Test fun testSet() { val nd = Nd4j.rand(17, 12, 4, 8)!! - val struct = INDArrayIntStructure(nd) + val struct = nd.asLongStructure() struct[intArrayOf(1, 2, 3, 4)] = 777 assertEquals(777, struct[1, 2, 3, 4]) } From 57910f617ab71493a4299a62b4134505a07b4aab Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 29 Oct 2020 15:39:53 +0700 Subject: [PATCH 27/28] Rename API classes, update readme files --- README.md | 92 ++++-- build.gradle.kts | 11 +- docs/templates/README-TEMPLATE.md | 78 +++-- kmath-core/README.md | 6 +- kmath-core/build.gradle.kts | 12 +- .../kscience/kmath/expressions/Expression.kt | 20 +- .../kscience/kmath/structures/NDAlgebra.kt | 2 +- kmath-nd4j/build.gradle.kts | 26 ++ kmath-nd4j/docs/README-TEMPLATE.md | 43 +++ .../kscience.kmath.nd4j/INDArrayAlgebra.kt | 284 ----------------- .../kscience.kmath.nd4j/Nd4jArrayAlgebra.kt | 288 ++++++++++++++++++ ...ArrayIterators.kt => Nd4jArrayIterator.kt} | 22 +- ...rayStructures.kt => Nd4jArrayStructure.kt} | 28 +- ...AlgebraTest.kt => Nd4jArrayAlgebraTest.kt} | 8 +- ...ctureTest.kt => Nd4jArrayStructureTest.kt} | 2 +- 15 files changed, 534 insertions(+), 388 deletions(-) create mode 100644 kmath-nd4j/docs/README-TEMPLATE.md delete mode 100644 kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayAlgebra.kt create mode 100644 kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt rename kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/{INDArrayIterators.kt => Nd4jArrayIterator.kt} (63%) rename kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/{INDArrayStructures.kt => Nd4jArrayStructure.kt} (63%) rename kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/{INDArrayAlgebraTest.kt => Nd4jArrayAlgebraTest.kt} (79%) rename kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/{INDArrayStructureTest.kt => Nd4jArrayStructureTest.kt} (98%) diff --git a/README.md b/README.md index afab32dcf..2df9d3246 100644 --- a/README.md +++ b/README.md @@ -8,41 +8,50 @@ Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion) # KMath -Could be pronounced as `key-math`. -The Kotlin MATHematics library was initially intended as a Kotlin-based analog to Python's `numpy` library. Later we found that kotlin is much more flexible language and allows superior architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could be achieved with [kmath-for-real](/kmath-for-real) extension module. + +Could be pronounced as `key-math`. The Kotlin MATHematics library was initially intended as a Kotlin-based analog to +Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior architecture +designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could +be achieved with [kmath-for-real](/kmath-for-real) extension module. ## Publications and talks + * [A conceptual article about context-oriented design](https://proandroiddev.com/an-introduction-context-oriented-programming-in-kotlin-2e79d316b0a2) * [Another article about context-oriented design](https://proandroiddev.com/diving-deeper-into-context-oriented-programming-in-kotlin-3ecb4ec38814) * [ACAT 2019 conference paper](https://aip.scitation.org/doi/abs/10.1063/1.5130103) # Goal -* Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM and JS for now and Native in future). + +* Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS and Native). * Provide basic multiplatform implementations for those abstractions (without significant performance optimization). * Provide bindings and wrappers with those abstractions for popular optimized platform libraries. ## Non-goals -* Be like Numpy. It was the idea at the beginning, but we decided that we can do better in terms of API. -* Provide best performance out of the box. We have specialized libraries for that. Need only API wrappers for them. + +* Be like NumPy. It was the idea at the beginning, but we decided that we can do better in terms of API. +* Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them. * Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually. -* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like for `Double` in the core. For that we will have specialization modules like `for-real`, which will give better experience for those, who want to work with specific types. +* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like +for `Double` in the core. For that we will have specialization modules like `for-real`, which will give better +experience for those, who want to work with specific types. ## Features -Actual feature list is [here](/docs/features.md) +Current feature list is [here](/docs/features.md) * **Algebra** - * Algebraic structures like rings, spaces and field (**TODO** add example to wiki) + * Algebraic structures like rings, spaces and fields (**TODO** add example to wiki) * Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. - * Complex numbers backed by the `Field` API (meaning that they will be usable in any structure like vectors and N-dimensional arrays). + * Complex numbers backed by the `Field` API (meaning they will be usable in any structure like vectors and + N-dimensional arrays). * Advanced linear algebra operations like matrix inversion and LU decomposition. * **Array-like structures** Full support of many-dimensional array-like structures including mixed arithmetic operations and function operations over arrays and numbers (with the added benefit of static type checking). -* **Expressions** By writing a single mathematical expression -once, users will be able to apply different types of objects to the expression by providing a context. Expressions -can be used for a wide variety of purposes from high performance calculations to code generation. +* **Expressions** By writing a single mathematical expression once, users will be able to apply different types of +objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high +performance calculations to code generation. * **Histograms** Fast multi-dimensional histograms. @@ -50,9 +59,10 @@ can be used for a wide variety of purposes from high performance calculations to * **Type-safe dimensions** Type-safe dimensions for matrix operations. -* **Commons-math wrapper** It is planned to gradually wrap most parts of [Apache commons-math](http://commons.apache.org/proper/commons-math/) - library in Kotlin code and maybe rewrite some parts to better suit the Kotlin programming paradigm, however there is no fixed roadmap for that. Feel free - to submit a feature request if you want something to be done first. +* **Commons-math wrapper** It is planned to gradually wrap most parts of +[Apache commons-math](http://commons.apache.org/proper/commons-math/) library in Kotlin code and maybe rewrite some +parts to better suit the Kotlin programming paradigm, however there is no established roadmap for that. Feel free to +submit a feature request if you want something to be implemented first. ## Planned features @@ -151,6 +161,18 @@ can be used for a wide variety of purposes from high performance calculations to > **Maturity**: EXPERIMENTAL
+* ### [kmath-nd4j](kmath-nd4j) +> ND4J NDStructure implementation and according NDAlgebra classes +> +> **Maturity**: EXPERIMENTAL +> +> **Features:** +> - [nd4jarraystrucure](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : NDStructure wrapper for INDArray +> - [nd4jarrayrings](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Rings over Nd4jArrayStructure of Int and Long +> - [nd4jarrayfields](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt) : Fields over Nd4jArrayStructure of Float and Double + +
+ * ### [kmath-stat](kmath-stat) > > @@ -166,39 +188,53 @@ can be used for a wide variety of purposes from high performance calculations to ## Multi-platform support -KMath is developed as a multi-platform library, which means that most of the interfaces are declared in the [common module](/kmath-core/src/commonMain). Implementation is also done in the common module wherever possible. In some cases, features are delegated to platform-specific implementations even if they could be done in the common module for performance reasons. Currently, the JVM is the main focus of development, however Kotlin/Native and Kotlin/JS contributions are also welcome. +KMath is developed as a multi-platform library, which means that most of the interfaces are declared in the +[common source sets](/kmath-core/src/commonMain) and implemented there wherever it is possible. In some cases, features +are delegated to platform-specific implementations even if they could be provided in the common module for performance +reasons. Currently, the Kotlin/JVM is the primary platform, however Kotlin/Native and Kotlin/JS contributions and +feedback are also welcome. ## Performance -Calculation performance is one of major goals of KMath in the future, but in some cases it is not possible to achieve both performance and flexibility. We expect to focus on creating convenient universal API first and then work on increasing performance for specific cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be better than SciPy. +Calculation performance is one of major goals of KMath in the future, but in some cases it is impossible to achieve +both performance and flexibility. -### Dependency +We expect to focus on creating convenient universal API first and then work on increasing performance for specific +cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized +native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be +better than SciPy. -Release artifacts are accessible from bintray with following configuration (see documentation for [kotlin-multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) form more details): +### Repositories + +Release artifacts are accessible from bintray with following configuration (see documentation of +[Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details): ```kotlin -repositories{ +repositories { maven("https://dl.bintray.com/mipt-npm/kscience") } -dependencies{ - api("kscience.kmath:kmath-core:0.2.0-dev-2") - //api("kscience.kmath:kmath-core-jvm:0.2.0-dev-2") for jvm-specific version +dependencies { + api("kscience.kmath:kmath-core:0.2.0-dev-3") + // api("kscience.kmath:kmath-core-jvm:0.2.0-dev-3") for jvm-specific version } ``` Gradle `6.0+` is required for multiplatform artifacts. -### Development +#### Development + +Development builds are uploaded to the separate repository: -Development builds are accessible from the reposirtory ```kotlin -repositories{ +repositories { maven("https://dl.bintray.com/mipt-npm/dev") } ``` -with the same artifact names. ## Contributing -The project requires a lot of additional work. The most important thing we need is a feedback about what features are required the most. Feel free to open feature issues with requests. We are also welcome to code contributions, especially in issues marked as [waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero). \ No newline at end of file +The project requires a lot of additional work. The most important thing we need is a feedback about what features are +required the most. Feel free to create feature requests. We are also welcome to code contributions, +especially in issues marked with +[waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero) label. diff --git a/build.gradle.kts b/build.gradle.kts index b03c03ab8..de0714543 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,16 +2,15 @@ plugins { id("ru.mipt.npm.project") } -val kmathVersion: String by extra("0.2.0-dev-3") -val bintrayRepo: String by extra("kscience") -val githubProject: String by extra("kmath") +internal val kmathVersion: String by extra("0.2.0-dev-3") +internal val bintrayRepo: String by extra("kscience") +internal val githubProject: String by extra("kmath") allprojects { repositories { jcenter() maven("https://dl.bintray.com/kotlin/kotlin-eap") maven("https://dl.bintray.com/kotlin/kotlinx") - mavenCentral() maven("https://dl.bintray.com/hotkeytlt/maven") } @@ -27,6 +26,6 @@ readme { readmeTemplate = file("docs/templates/README-TEMPLATE.md") } -apiValidation{ +apiValidation { validationDisabled = true -} \ No newline at end of file +} diff --git a/docs/templates/README-TEMPLATE.md b/docs/templates/README-TEMPLATE.md index 5117e0694..ee1df818c 100644 --- a/docs/templates/README-TEMPLATE.md +++ b/docs/templates/README-TEMPLATE.md @@ -8,41 +8,50 @@ Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion) # KMath -Could be pronounced as `key-math`. -The Kotlin MATHematics library was initially intended as a Kotlin-based analog to Python's `numpy` library. Later we found that kotlin is much more flexible language and allows superior architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could be achieved with [kmath-for-real](/kmath-for-real) extension module. + +Could be pronounced as `key-math`. The Kotlin MATHematics library was initially intended as a Kotlin-based analog to +Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior architecture +designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could +be achieved with [kmath-for-real](/kmath-for-real) extension module. ## Publications and talks + * [A conceptual article about context-oriented design](https://proandroiddev.com/an-introduction-context-oriented-programming-in-kotlin-2e79d316b0a2) * [Another article about context-oriented design](https://proandroiddev.com/diving-deeper-into-context-oriented-programming-in-kotlin-3ecb4ec38814) * [ACAT 2019 conference paper](https://aip.scitation.org/doi/abs/10.1063/1.5130103) # Goal -* Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM and JS for now and Native in future). + +* Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS and Native). * Provide basic multiplatform implementations for those abstractions (without significant performance optimization). * Provide bindings and wrappers with those abstractions for popular optimized platform libraries. ## Non-goals -* Be like Numpy. It was the idea at the beginning, but we decided that we can do better in terms of API. -* Provide best performance out of the box. We have specialized libraries for that. Need only API wrappers for them. + +* Be like NumPy. It was the idea at the beginning, but we decided that we can do better in terms of API. +* Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them. * Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually. -* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like for `Double` in the core. For that we will have specialization modules like `for-real`, which will give better experience for those, who want to work with specific types. +* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like +for `Double` in the core. For that we will have specialization modules like `for-real`, which will give better +experience for those, who want to work with specific types. ## Features -Actual feature list is [here](/docs/features.md) +Current feature list is [here](/docs/features.md) * **Algebra** - * Algebraic structures like rings, spaces and field (**TODO** add example to wiki) + * Algebraic structures like rings, spaces and fields (**TODO** add example to wiki) * Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. - * Complex numbers backed by the `Field` API (meaning that they will be usable in any structure like vectors and N-dimensional arrays). + * Complex numbers backed by the `Field` API (meaning they will be usable in any structure like vectors and + N-dimensional arrays). * Advanced linear algebra operations like matrix inversion and LU decomposition. * **Array-like structures** Full support of many-dimensional array-like structures including mixed arithmetic operations and function operations over arrays and numbers (with the added benefit of static type checking). -* **Expressions** By writing a single mathematical expression -once, users will be able to apply different types of objects to the expression by providing a context. Expressions -can be used for a wide variety of purposes from high performance calculations to code generation. +* **Expressions** By writing a single mathematical expression once, users will be able to apply different types of +objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high +performance calculations to code generation. * **Histograms** Fast multi-dimensional histograms. @@ -50,9 +59,10 @@ can be used for a wide variety of purposes from high performance calculations to * **Type-safe dimensions** Type-safe dimensions for matrix operations. -* **Commons-math wrapper** It is planned to gradually wrap most parts of [Apache commons-math](http://commons.apache.org/proper/commons-math/) - library in Kotlin code and maybe rewrite some parts to better suit the Kotlin programming paradigm, however there is no fixed roadmap for that. Feel free - to submit a feature request if you want something to be done first. +* **Commons-math wrapper** It is planned to gradually wrap most parts of +[Apache commons-math](http://commons.apache.org/proper/commons-math/) library in Kotlin code and maybe rewrite some +parts to better suit the Kotlin programming paradigm, however there is no established roadmap for that. Feel free to +submit a feature request if you want something to be implemented first. ## Planned features @@ -72,39 +82,53 @@ $modules ## Multi-platform support -KMath is developed as a multi-platform library, which means that most of the interfaces are declared in the [common module](/kmath-core/src/commonMain). Implementation is also done in the common module wherever possible. In some cases, features are delegated to platform-specific implementations even if they could be done in the common module for performance reasons. Currently, the JVM is the main focus of development, however Kotlin/Native and Kotlin/JS contributions are also welcome. +KMath is developed as a multi-platform library, which means that most of the interfaces are declared in the +[common source sets](/kmath-core/src/commonMain) and implemented there wherever it is possible. In some cases, features +are delegated to platform-specific implementations even if they could be provided in the common module for performance +reasons. Currently, the Kotlin/JVM is the primary platform, however Kotlin/Native and Kotlin/JS contributions and +feedback are also welcome. ## Performance -Calculation performance is one of major goals of KMath in the future, but in some cases it is not possible to achieve both performance and flexibility. We expect to focus on creating convenient universal API first and then work on increasing performance for specific cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be better than SciPy. +Calculation performance is one of major goals of KMath in the future, but in some cases it is impossible to achieve +both performance and flexibility. -### Dependency +We expect to focus on creating convenient universal API first and then work on increasing performance for specific +cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized +native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be +better than SciPy. -Release artifacts are accessible from bintray with following configuration (see documentation for [kotlin-multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) form more details): +### Repositories + +Release artifacts are accessible from bintray with following configuration (see documentation of +[Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details): ```kotlin -repositories{ +repositories { maven("https://dl.bintray.com/mipt-npm/kscience") } -dependencies{ +dependencies { api("kscience.kmath:kmath-core:$version") - //api("kscience.kmath:kmath-core-jvm:$version") for jvm-specific version + // api("kscience.kmath:kmath-core-jvm:$version") for jvm-specific version } ``` Gradle `6.0+` is required for multiplatform artifacts. -### Development +#### Development + +Development builds are uploaded to the separate repository: -Development builds are accessible from the reposirtory ```kotlin -repositories{ +repositories { maven("https://dl.bintray.com/mipt-npm/dev") } ``` -with the same artifact names. ## Contributing -The project requires a lot of additional work. The most important thing we need is a feedback about what features are required the most. Feel free to open feature issues with requests. We are also welcome to code contributions, especially in issues marked as [waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero). \ No newline at end of file +The project requires a lot of additional work. The most important thing we need is a feedback about what features are +required the most. Feel free to create feature requests. We are also welcome to code contributions, +especially in issues marked with +[waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero) label. diff --git a/kmath-core/README.md b/kmath-core/README.md index 5501b1d7a..42a513a10 100644 --- a/kmath-core/README.md +++ b/kmath-core/README.md @@ -12,7 +12,7 @@ The core features of KMath: > #### Artifact: > -> This module artifact: `kscience.kmath:kmath-core:0.2.0-dev-2`. +> This module artifact: `kscience.kmath:kmath-core:0.2.0-dev-3`. > > Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-core/_latestVersion) > @@ -30,7 +30,7 @@ The core features of KMath: > } > > dependencies { -> implementation 'kscience.kmath:kmath-core:0.2.0-dev-2' +> implementation 'kscience.kmath:kmath-core:0.2.0-dev-3' > } > ``` > **Gradle Kotlin DSL:** @@ -44,6 +44,6 @@ The core features of KMath: > } > > dependencies { -> implementation("kscience.kmath:kmath-core:0.2.0-dev-2") +> implementation("kscience.kmath:kmath-core:0.2.0-dev-3") > } > ``` diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index b0849eca5..7f889d9b4 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -1,3 +1,5 @@ +import ru.mipt.npm.gradle.Maturity + plugins { id("ru.mipt.npm.mpp") id("ru.mipt.npm.native") @@ -11,36 +13,42 @@ kotlin.sourceSets.commonMain { readme { description = "Core classes, algebra definitions, basic linear algebra" - maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT + maturity = Maturity.DEVELOPMENT propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) + feature( id = "algebras", description = "Algebraic structures: contexts and elements", ref = "src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt" ) + feature( id = "nd", description = "Many-dimensional structures", ref = "src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt" ) + feature( id = "buffers", description = "One-dimensional structure", ref = "src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt" ) + feature( id = "expressions", description = "Functional Expressions", ref = "src/commonMain/kotlin/kscience/kmath/expressions" ) + feature( id = "domains", description = "Domains", ref = "src/commonMain/kotlin/kscience/kmath/domains" ) + feature( id = "autodif", description = "Automatic differentiation", ref = "src/commonMain/kotlin/kscience/kmath/expressions/SimpleAutoDiff.kt" ) -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt index ab9ff0e72..568de255e 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt @@ -35,20 +35,27 @@ public fun interface Expression { } /** - * Invoke an expression without parameters + * Calls this expression without providing any arguments. + * + * @return a value. */ public operator fun Expression.invoke(): T = invoke(emptyMap()) -//This method exists to avoid resolution ambiguity of vararg methods /** * Calls this expression from arguments. * - * @param pairs the pair of arguments' names to values. - * @return the value. + * @param pairs the pairs of arguments to values. + * @return a value. */ @JvmName("callBySymbol") public operator fun Expression.invoke(vararg pairs: Pair): T = invoke(mapOf(*pairs)) +/** + * Calls this expression from arguments. + * + * @param pairs the pairs of arguments' names to values. + * @return a value. + */ @JvmName("callByString") public operator fun Expression.invoke(vararg pairs: Pair): T = invoke(mapOf(*pairs).mapKeys { StringSymbol(it.key) }) @@ -61,7 +68,6 @@ public operator fun Expression.invoke(vararg pairs: Pair): T = * @param E type of the actual expression state */ public interface ExpressionAlgebra : Algebra { - /** * Bind a given [Symbol] to this context variable and produce context-specific object. Return null if symbol could not be bound in current context. */ @@ -87,7 +93,7 @@ public fun ExpressionAlgebra.bind(symbol: Symbol): E = /** * A delegate to create a symbol with a string identity in this scope */ -public val symbol: ReadOnlyProperty = ReadOnlyProperty { thisRef, property -> +public val symbol: ReadOnlyProperty = ReadOnlyProperty { _, property -> StringSymbol(property.name) } @@ -96,4 +102,4 @@ public val symbol: ReadOnlyProperty = ReadOnlyProperty { */ public fun ExpressionAlgebra.binding(): ReadOnlyProperty = ReadOnlyProperty { _, property -> bind(StringSymbol(property.name)) ?: error("A variable with name ${property.name} does not exist") -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt index c1cfcbe49..d7b019c65 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt @@ -73,7 +73,7 @@ public interface NDAlgebra> { public fun check(vararg elements: N): Array = elements .map(NDStructure::shape) .singleOrNull { !shape.contentEquals(it) } - ?.let { throw ShapeMismatchException(shape, it) } + ?.let> { throw ShapeMismatchException(shape, it) } ?: elements /** diff --git a/kmath-nd4j/build.gradle.kts b/kmath-nd4j/build.gradle.kts index 67569b870..953530b01 100644 --- a/kmath-nd4j/build.gradle.kts +++ b/kmath-nd4j/build.gradle.kts @@ -1,3 +1,5 @@ +import ru.mipt.npm.gradle.Maturity + plugins { id("ru.mipt.npm.jvm") } @@ -9,3 +11,27 @@ dependencies { testImplementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") testImplementation("org.slf4j:slf4j-simple:1.7.30") } + +readme { + description = "ND4J NDStructure implementation and according NDAlgebra classes" + maturity = Maturity.EXPERIMENTAL + propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) + + feature( + id = "nd4jarraystrucure", + description = "NDStructure wrapper for INDArray", + ref = "src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt" + ) + + feature( + id = "nd4jarrayrings", + description = "Rings over Nd4jArrayStructure of Int and Long", + ref = "src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt" + ) + + feature( + id = "nd4jarrayfields", + description = "Fields over Nd4jArrayStructure of Float and Double", + ref = "src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt" + ) +} diff --git a/kmath-nd4j/docs/README-TEMPLATE.md b/kmath-nd4j/docs/README-TEMPLATE.md new file mode 100644 index 000000000..76ce8c9a7 --- /dev/null +++ b/kmath-nd4j/docs/README-TEMPLATE.md @@ -0,0 +1,43 @@ +# ND4J NDStructure implementation (`kmath-nd4j`) + +This subproject implements the following features: + +${features} + +${artifact} + +## Examples + +NDStructure wrapper for INDArray: + +```kotlin +import org.nd4j.linalg.factory.* +import scientifik.kmath.nd4j.* +import scientifik.kmath.structures.* + +val array = Nd4j.ones(2, 2).asRealStructure() +println(array[0, 0]) // 1.0 +array[intArrayOf(0, 0)] = 24.0 +println(array[0, 0]) // 24.0 +``` + +Fast element-wise and in-place arithmetics for INDArray: + +```kotlin +import org.nd4j.linalg.factory.* +import scientifik.kmath.nd4j.* +import scientifik.kmath.operations.* + +val field = RealNd4jArrayField(intArrayOf(2, 2)) +val array = Nd4j.rand(2, 2).asRealStructure() + +val res = field { + (25.0 / array + 20) * 4 +} + +println(res.ndArray) +// [[ 250.6449, 428.5840], +// [ 269.7913, 202.2077]] +``` + +Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayAlgebra.kt deleted file mode 100644 index 728ce3773..000000000 --- a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayAlgebra.kt +++ /dev/null @@ -1,284 +0,0 @@ -package kscience.kmath.nd4j - -import org.nd4j.linalg.api.ndarray.INDArray -import org.nd4j.linalg.factory.Nd4j -import kscience.kmath.operations.* -import kscience.kmath.structures.* - -/** - * Represents [NDAlgebra] over [INDArrayAlgebra]. - * - * @param T the type of ND-structure element. - * @param C the type of the element context. - */ -public interface INDArrayAlgebra : NDAlgebra> { - /** - * Wraps [INDArray] to [N]. - */ - public fun INDArray.wrap(): INDArrayStructure - - public override fun produce(initializer: C.(IntArray) -> T): INDArrayStructure { - val struct = Nd4j.create(*shape)!!.wrap() - struct.indicesIterator().forEach { struct[it] = elementContext.initializer(it) } - return struct - } - - public override fun map(arg: INDArrayStructure, transform: C.(T) -> T): INDArrayStructure { - check(arg) - val newStruct = arg.ndArray.dup().wrap() - newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } - return newStruct - } - - public override fun mapIndexed( - arg: INDArrayStructure, - transform: C.(index: IntArray, T) -> T - ): INDArrayStructure { - check(arg) - val new = Nd4j.create(*shape).wrap() - new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(idx, arg[idx]) } - return new - } - - public override fun combine( - a: INDArrayStructure, - b: INDArrayStructure, - transform: C.(T, T) -> T - ): INDArrayStructure { - check(a, b) - val new = Nd4j.create(*shape).wrap() - new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(a[idx], b[idx]) } - return new - } -} - -/** - * Represents [NDSpace] over [INDArrayStructure]. - * - * @param T the type of the element contained in ND structure. - * @param S the type of space of structure elements. - */ -public interface INDArraySpace : NDSpace>, INDArrayAlgebra where S : Space { - public override val zero: INDArrayStructure - get() = Nd4j.zeros(*shape).wrap() - - public override fun add(a: INDArrayStructure, b: INDArrayStructure): INDArrayStructure { - check(a, b) - return a.ndArray.add(b.ndArray).wrap() - } - - public override operator fun INDArrayStructure.minus(b: INDArrayStructure): INDArrayStructure { - check(this, b) - return ndArray.sub(b.ndArray).wrap() - } - - public override operator fun INDArrayStructure.unaryMinus(): INDArrayStructure { - check(this) - return ndArray.neg().wrap() - } - - public override fun multiply(a: INDArrayStructure, k: Number): INDArrayStructure { - check(a) - return a.ndArray.mul(k).wrap() - } - - public override operator fun INDArrayStructure.div(k: Number): INDArrayStructure { - check(this) - return ndArray.div(k).wrap() - } - - public override operator fun INDArrayStructure.times(k: Number): INDArrayStructure { - check(this) - return ndArray.mul(k).wrap() - } -} - -/** - * Represents [NDRing] over [INDArrayStructure]. - * - * @param T the type of the element contained in ND structure. - * @param R the type of ring of structure elements. - */ -public interface INDArrayRing : NDRing>, INDArraySpace where R : Ring { - public override val one: INDArrayStructure - get() = Nd4j.ones(*shape).wrap() - - public override fun multiply(a: INDArrayStructure, b: INDArrayStructure): INDArrayStructure { - check(a, b) - return a.ndArray.mul(b.ndArray).wrap() - } - - public override operator fun INDArrayStructure.minus(b: Number): INDArrayStructure { - check(this) - return ndArray.sub(b).wrap() - } - - public override operator fun INDArrayStructure.plus(b: Number): INDArrayStructure { - check(this) - return ndArray.add(b).wrap() - } - - public override operator fun Number.minus(b: INDArrayStructure): INDArrayStructure { - check(b) - return b.ndArray.rsub(this).wrap() - } -} - -/** - * Represents [NDField] over [INDArrayStructure]. - * - * @param T the type of the element contained in ND structure. - * @param N the type of ND structure. - * @param F the type field of structure elements. - */ -public interface INDArrayField : NDField>, INDArrayRing where F : Field { - public override fun divide(a: INDArrayStructure, b: INDArrayStructure): INDArrayStructure { - check(a, b) - return a.ndArray.div(b.ndArray).wrap() - } - - public override operator fun Number.div(b: INDArrayStructure): INDArrayStructure { - check(b) - return b.ndArray.rdiv(this).wrap() - } -} - -/** - * Represents [NDField] over [INDArrayRealStructure]. - */ -public class RealINDArrayField(public override val shape: IntArray) : INDArrayField { - public override val elementContext: RealField - get() = RealField - - public override fun INDArray.wrap(): INDArrayStructure = check(asRealStructure()) - - public override operator fun INDArrayStructure.div(arg: Double): INDArrayStructure { - check(this) - return ndArray.div(arg).wrap() - } - - public override operator fun INDArrayStructure.plus(arg: Double): INDArrayStructure { - check(this) - return ndArray.add(arg).wrap() - } - - public override operator fun INDArrayStructure.minus(arg: Double): INDArrayStructure { - check(this) - return ndArray.sub(arg).wrap() - } - - public override operator fun INDArrayStructure.times(arg: Double): INDArrayStructure { - check(this) - return ndArray.mul(arg).wrap() - } - - public override operator fun Double.div(arg: INDArrayStructure): INDArrayStructure { - check(arg) - return arg.ndArray.rdiv(this).wrap() - } - - public override operator fun Double.minus(arg: INDArrayStructure): INDArrayStructure { - check(arg) - return arg.ndArray.rsub(this).wrap() - } -} - -/** - * Represents [NDField] over [INDArrayStructure] of [Float]. - */ -public class FloatINDArrayField(public override val shape: IntArray) : INDArrayField { - public override val elementContext: FloatField - get() = FloatField - - public override fun INDArray.wrap(): INDArrayStructure = check(asFloatStructure()) - - public override operator fun INDArrayStructure.div(arg: Float): INDArrayStructure { - check(this) - return ndArray.div(arg).wrap() - } - - public override operator fun INDArrayStructure.plus(arg: Float): INDArrayStructure { - check(this) - return ndArray.add(arg).wrap() - } - - public override operator fun INDArrayStructure.minus(arg: Float): INDArrayStructure { - check(this) - return ndArray.sub(arg).wrap() - } - - public override operator fun INDArrayStructure.times(arg: Float): INDArrayStructure { - check(this) - return ndArray.mul(arg).wrap() - } - - public override operator fun Float.div(arg: INDArrayStructure): INDArrayStructure { - check(arg) - return arg.ndArray.rdiv(this).wrap() - } - - public override operator fun Float.minus(arg: INDArrayStructure): INDArrayStructure { - check(arg) - return arg.ndArray.rsub(this).wrap() - } -} - -/** - * Represents [NDRing] over [INDArrayIntStructure]. - */ -public class IntINDArrayRing(public override val shape: IntArray) : INDArrayRing { - public override val elementContext: IntRing - get() = IntRing - - public override fun INDArray.wrap(): INDArrayStructure = check(asIntStructure()) - - public override operator fun INDArrayStructure.plus(arg: Int): INDArrayStructure { - check(this) - return ndArray.add(arg).wrap() - } - - public override operator fun INDArrayStructure.minus(arg: Int): INDArrayStructure { - check(this) - return ndArray.sub(arg).wrap() - } - - public override operator fun INDArrayStructure.times(arg: Int): INDArrayStructure { - check(this) - return ndArray.mul(arg).wrap() - } - - public override operator fun Int.minus(arg: INDArrayStructure): INDArrayStructure { - check(arg) - return arg.ndArray.rsub(this).wrap() - } -} - -/** - * Represents [NDRing] over [INDArrayStructure] of [Long]. - */ -public class LongINDArrayRing(public override val shape: IntArray) : INDArrayRing { - public override val elementContext: LongRing - get() = LongRing - - public override fun INDArray.wrap(): INDArrayStructure = check(asLongStructure()) - - public override operator fun INDArrayStructure.plus(arg: Long): INDArrayStructure { - check(this) - return ndArray.add(arg).wrap() - } - - public override operator fun INDArrayStructure.minus(arg: Long): INDArrayStructure { - check(this) - return ndArray.sub(arg).wrap() - } - - public override operator fun INDArrayStructure.times(arg: Long): INDArrayStructure { - check(this) - return ndArray.mul(arg).wrap() - } - - public override operator fun Long.minus(arg: INDArrayStructure): INDArrayStructure { - check(arg) - return arg.ndArray.rsub(this).wrap() - } -} diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt new file mode 100644 index 000000000..2093a3cb3 --- /dev/null +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt @@ -0,0 +1,288 @@ +package kscience.kmath.nd4j + +import kscience.kmath.operations.* +import kscience.kmath.structures.NDAlgebra +import kscience.kmath.structures.NDField +import kscience.kmath.structures.NDRing +import kscience.kmath.structures.NDSpace +import org.nd4j.linalg.api.ndarray.INDArray +import org.nd4j.linalg.factory.Nd4j + +/** + * Represents [NDAlgebra] over [Nd4jArrayAlgebra]. + * + * @param T the type of ND-structure element. + * @param C the type of the element context. + */ +public interface Nd4jArrayAlgebra : NDAlgebra> { + /** + * Wraps [INDArray] to [N]. + */ + public fun INDArray.wrap(): Nd4jArrayStructure + + public override fun produce(initializer: C.(IntArray) -> T): Nd4jArrayStructure { + val struct = Nd4j.create(*shape)!!.wrap() + struct.indicesIterator().forEach { struct[it] = elementContext.initializer(it) } + return struct + } + + public override fun map(arg: Nd4jArrayStructure, transform: C.(T) -> T): Nd4jArrayStructure { + check(arg) + val newStruct = arg.ndArray.dup().wrap() + newStruct.elements().forEach { (idx, value) -> newStruct[idx] = elementContext.transform(value) } + return newStruct + } + + public override fun mapIndexed( + arg: Nd4jArrayStructure, + transform: C.(index: IntArray, T) -> T + ): Nd4jArrayStructure { + check(arg) + val new = Nd4j.create(*shape).wrap() + new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(idx, arg[idx]) } + return new + } + + public override fun combine( + a: Nd4jArrayStructure, + b: Nd4jArrayStructure, + transform: C.(T, T) -> T + ): Nd4jArrayStructure { + check(a, b) + val new = Nd4j.create(*shape).wrap() + new.indicesIterator().forEach { idx -> new[idx] = elementContext.transform(a[idx], b[idx]) } + return new + } +} + +/** + * Represents [NDSpace] over [Nd4jArrayStructure]. + * + * @param T the type of the element contained in ND structure. + * @param S the type of space of structure elements. + */ +public interface Nd4jArraySpace : NDSpace>, + Nd4jArrayAlgebra where S : Space { + public override val zero: Nd4jArrayStructure + get() = Nd4j.zeros(*shape).wrap() + + public override fun add(a: Nd4jArrayStructure, b: Nd4jArrayStructure): Nd4jArrayStructure { + check(a, b) + return a.ndArray.add(b.ndArray).wrap() + } + + public override operator fun Nd4jArrayStructure.minus(b: Nd4jArrayStructure): Nd4jArrayStructure { + check(this, b) + return ndArray.sub(b.ndArray).wrap() + } + + public override operator fun Nd4jArrayStructure.unaryMinus(): Nd4jArrayStructure { + check(this) + return ndArray.neg().wrap() + } + + public override fun multiply(a: Nd4jArrayStructure, k: Number): Nd4jArrayStructure { + check(a) + return a.ndArray.mul(k).wrap() + } + + public override operator fun Nd4jArrayStructure.div(k: Number): Nd4jArrayStructure { + check(this) + return ndArray.div(k).wrap() + } + + public override operator fun Nd4jArrayStructure.times(k: Number): Nd4jArrayStructure { + check(this) + return ndArray.mul(k).wrap() + } +} + +/** + * Represents [NDRing] over [Nd4jArrayStructure]. + * + * @param T the type of the element contained in ND structure. + * @param R the type of ring of structure elements. + */ +public interface Nd4jArrayRing : NDRing>, Nd4jArraySpace where R : Ring { + public override val one: Nd4jArrayStructure + get() = Nd4j.ones(*shape).wrap() + + public override fun multiply(a: Nd4jArrayStructure, b: Nd4jArrayStructure): Nd4jArrayStructure { + check(a, b) + return a.ndArray.mul(b.ndArray).wrap() + } + + public override operator fun Nd4jArrayStructure.minus(b: Number): Nd4jArrayStructure { + check(this) + return ndArray.sub(b).wrap() + } + + public override operator fun Nd4jArrayStructure.plus(b: Number): Nd4jArrayStructure { + check(this) + return ndArray.add(b).wrap() + } + + public override operator fun Number.minus(b: Nd4jArrayStructure): Nd4jArrayStructure { + check(b) + return b.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDField] over [Nd4jArrayStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param F the type field of structure elements. + */ +public interface Nd4jArrayField : NDField>, Nd4jArrayRing where F : Field { + public override fun divide(a: Nd4jArrayStructure, b: Nd4jArrayStructure): Nd4jArrayStructure { + check(a, b) + return a.ndArray.div(b.ndArray).wrap() + } + + public override operator fun Number.div(b: Nd4jArrayStructure): Nd4jArrayStructure { + check(b) + return b.ndArray.rdiv(this).wrap() + } +} + +/** + * Represents [NDField] over [Nd4jArrayRealStructure]. + */ +public class RealNd4jArrayField(public override val shape: IntArray) : Nd4jArrayField { + public override val elementContext: RealField + get() = RealField + + public override fun INDArray.wrap(): Nd4jArrayStructure = check(asRealStructure()) + + public override operator fun Nd4jArrayStructure.div(arg: Double): Nd4jArrayStructure { + check(this) + return ndArray.div(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.plus(arg: Double): Nd4jArrayStructure { + check(this) + return ndArray.add(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.minus(arg: Double): Nd4jArrayStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.times(arg: Double): Nd4jArrayStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + public override operator fun Double.div(arg: Nd4jArrayStructure): Nd4jArrayStructure { + check(arg) + return arg.ndArray.rdiv(this).wrap() + } + + public override operator fun Double.minus(arg: Nd4jArrayStructure): Nd4jArrayStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDField] over [Nd4jArrayStructure] of [Float]. + */ +public class FloatNd4jArrayField(public override val shape: IntArray) : Nd4jArrayField { + public override val elementContext: FloatField + get() = FloatField + + public override fun INDArray.wrap(): Nd4jArrayStructure = check(asFloatStructure()) + + public override operator fun Nd4jArrayStructure.div(arg: Float): Nd4jArrayStructure { + check(this) + return ndArray.div(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.plus(arg: Float): Nd4jArrayStructure { + check(this) + return ndArray.add(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.minus(arg: Float): Nd4jArrayStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.times(arg: Float): Nd4jArrayStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + public override operator fun Float.div(arg: Nd4jArrayStructure): Nd4jArrayStructure { + check(arg) + return arg.ndArray.rdiv(this).wrap() + } + + public override operator fun Float.minus(arg: Nd4jArrayStructure): Nd4jArrayStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDRing] over [Nd4jArrayIntStructure]. + */ +public class IntNd4jArrayRing(public override val shape: IntArray) : Nd4jArrayRing { + public override val elementContext: IntRing + get() = IntRing + + public override fun INDArray.wrap(): Nd4jArrayStructure = check(asIntStructure()) + + public override operator fun Nd4jArrayStructure.plus(arg: Int): Nd4jArrayStructure { + check(this) + return ndArray.add(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.minus(arg: Int): Nd4jArrayStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.times(arg: Int): Nd4jArrayStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + public override operator fun Int.minus(arg: Nd4jArrayStructure): Nd4jArrayStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } +} + +/** + * Represents [NDRing] over [Nd4jArrayStructure] of [Long]. + */ +public class LongNd4jArrayRing(public override val shape: IntArray) : Nd4jArrayRing { + public override val elementContext: LongRing + get() = LongRing + + public override fun INDArray.wrap(): Nd4jArrayStructure = check(asLongStructure()) + + public override operator fun Nd4jArrayStructure.plus(arg: Long): Nd4jArrayStructure { + check(this) + return ndArray.add(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.minus(arg: Long): Nd4jArrayStructure { + check(this) + return ndArray.sub(arg).wrap() + } + + public override operator fun Nd4jArrayStructure.times(arg: Long): Nd4jArrayStructure { + check(this) + return ndArray.mul(arg).wrap() + } + + public override operator fun Long.minus(arg: Nd4jArrayStructure): Nd4jArrayStructure { + check(arg) + return arg.ndArray.rsub(this).wrap() + } +} diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayIterators.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayIterator.kt similarity index 63% rename from kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayIterators.kt rename to kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayIterator.kt index 9e7ef9e16..1463a92fe 100644 --- a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayIterators.kt +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayIterator.kt @@ -3,7 +3,7 @@ package kscience.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray import org.nd4j.linalg.api.shape.Shape -private class INDArrayIndicesIterator(private val iterateOver: INDArray) : Iterator { +private class Nd4jArrayIndicesIterator(private val iterateOver: INDArray) : Iterator { private var i: Int = 0 override fun hasNext(): Boolean = i < iterateOver.length() @@ -18,9 +18,9 @@ private class INDArrayIndicesIterator(private val iterateOver: INDArray) : Itera } } -internal fun INDArray.indicesIterator(): Iterator = INDArrayIndicesIterator(this) +internal fun INDArray.indicesIterator(): Iterator = Nd4jArrayIndicesIterator(this) -private sealed class INDArrayIteratorBase(protected val iterateOver: INDArray) : Iterator> { +private sealed class Nd4jArrayIteratorBase(protected val iterateOver: INDArray) : Iterator> { private var i: Int = 0 final override fun hasNext(): Boolean = i < iterateOver.length() @@ -37,26 +37,26 @@ private sealed class INDArrayIteratorBase(protected val iterateOver: INDArray } } -private class INDArrayRealIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { +private class Nd4jArrayRealIterator(iterateOver: INDArray) : Nd4jArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray): Double = iterateOver.getDouble(*indices) } -internal fun INDArray.realIterator(): Iterator> = INDArrayRealIterator(this) +internal fun INDArray.realIterator(): Iterator> = Nd4jArrayRealIterator(this) -private class INDArrayLongIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { +private class Nd4jArrayLongIterator(iterateOver: INDArray) : Nd4jArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray) = iterateOver.getLong(*indices) } -internal fun INDArray.longIterator(): Iterator> = INDArrayLongIterator(this) +internal fun INDArray.longIterator(): Iterator> = Nd4jArrayLongIterator(this) -private class INDArrayIntIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { +private class Nd4jArrayIntIterator(iterateOver: INDArray) : Nd4jArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray) = iterateOver.getInt(*indices.toIntArray()) } -internal fun INDArray.intIterator(): Iterator> = INDArrayIntIterator(this) +internal fun INDArray.intIterator(): Iterator> = Nd4jArrayIntIterator(this) -private class INDArrayFloatIterator(iterateOver: INDArray) : INDArrayIteratorBase(iterateOver) { +private class Nd4jArrayFloatIterator(iterateOver: INDArray) : Nd4jArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray) = iterateOver.getFloat(*indices) } -internal fun INDArray.floatIterator(): Iterator> = INDArrayFloatIterator(this) +internal fun INDArray.floatIterator(): Iterator> = Nd4jArrayFloatIterator(this) diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayStructures.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayStructure.kt similarity index 63% rename from kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayStructures.kt rename to kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayStructure.kt index 5d4e1a979..d47a293c3 100644 --- a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/INDArrayStructures.kt +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayStructure.kt @@ -1,15 +1,15 @@ package kscience.kmath.nd4j -import org.nd4j.linalg.api.ndarray.INDArray import kscience.kmath.structures.MutableNDStructure import kscience.kmath.structures.NDStructure +import org.nd4j.linalg.api.ndarray.INDArray /** * Represents a [NDStructure] wrapping an [INDArray] object. * * @param T the type of items. */ -public sealed class INDArrayStructure : MutableNDStructure { +public sealed class Nd4jArrayStructure : MutableNDStructure { /** * The wrapped [INDArray]. */ @@ -23,46 +23,46 @@ public sealed class INDArrayStructure : MutableNDStructure { public override fun elements(): Sequence> = Sequence(::elementsIterator) } -private data class INDArrayIntStructure(override val ndArray: INDArray) : INDArrayStructure() { +private data class Nd4jArrayIntStructure(override val ndArray: INDArray) : Nd4jArrayStructure() { override fun elementsIterator(): Iterator> = ndArray.intIterator() override fun get(index: IntArray): Int = ndArray.getInt(*index) override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) } } /** - * Wraps this [INDArray] to [INDArrayStructure]. + * Wraps this [INDArray] to [Nd4jArrayStructure]. */ -public fun INDArray.asIntStructure(): INDArrayStructure = INDArrayIntStructure(this) +public fun INDArray.asIntStructure(): Nd4jArrayStructure = Nd4jArrayIntStructure(this) -private data class INDArrayLongStructure(override val ndArray: INDArray) : INDArrayStructure() { +private data class Nd4jArrayLongStructure(override val ndArray: INDArray) : Nd4jArrayStructure() { override fun elementsIterator(): Iterator> = ndArray.longIterator() override fun get(index: IntArray): Long = ndArray.getLong(*index.toLongArray()) override fun set(index: IntArray, value: Long): Unit = run { ndArray.putScalar(index, value.toDouble()) } } /** - * Wraps this [INDArray] to [INDArrayStructure]. + * Wraps this [INDArray] to [Nd4jArrayStructure]. */ -public fun INDArray.asLongStructure(): INDArrayStructure = INDArrayLongStructure(this) +public fun INDArray.asLongStructure(): Nd4jArrayStructure = Nd4jArrayLongStructure(this) -private data class INDArrayRealStructure(override val ndArray: INDArray) : INDArrayStructure() { +private data class Nd4jArrayRealStructure(override val ndArray: INDArray) : Nd4jArrayStructure() { override fun elementsIterator(): Iterator> = ndArray.realIterator() override fun get(index: IntArray): Double = ndArray.getDouble(*index) override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) } } /** - * Wraps this [INDArray] to [INDArrayStructure]. + * Wraps this [INDArray] to [Nd4jArrayStructure]. */ -public fun INDArray.asRealStructure(): INDArrayStructure = INDArrayRealStructure(this) +public fun INDArray.asRealStructure(): Nd4jArrayStructure = Nd4jArrayRealStructure(this) -private data class INDArrayFloatStructure(override val ndArray: INDArray) : INDArrayStructure() { +private data class Nd4jArrayFloatStructure(override val ndArray: INDArray) : Nd4jArrayStructure() { override fun elementsIterator(): Iterator> = ndArray.floatIterator() override fun get(index: IntArray): Float = ndArray.getFloat(*index) override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) } } /** - * Wraps this [INDArray] to [INDArrayStructure]. + * Wraps this [INDArray] to [Nd4jArrayStructure]. */ -public fun INDArray.asFloatStructure(): INDArrayStructure = INDArrayFloatStructure(this) +public fun INDArray.asFloatStructure(): Nd4jArrayStructure = Nd4jArrayFloatStructure(this) diff --git a/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayAlgebraTest.kt b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt similarity index 79% rename from kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayAlgebraTest.kt rename to kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt index 1a4f4c9f3..650d5670c 100644 --- a/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayAlgebraTest.kt +++ b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt @@ -6,10 +6,10 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.fail -internal class INDArrayAlgebraTest { +internal class Nd4jArrayAlgebraTest { @Test fun testProduce() { - val res = (RealINDArrayField(intArrayOf(2, 2))) { produce { it.sum().toDouble() } } + val res = (RealNd4jArrayField(intArrayOf(2, 2))) { produce { it.sum().toDouble() } } val expected = (Nd4j.create(2, 2) ?: fail()).asRealStructure() expected[intArrayOf(0, 0)] = 0.0 expected[intArrayOf(0, 1)] = 1.0 @@ -20,7 +20,7 @@ internal class INDArrayAlgebraTest { @Test fun testMap() { - val res = (IntINDArrayRing(intArrayOf(2, 2))) { map(one) { it + it * 2 } } + val res = (IntNd4jArrayRing(intArrayOf(2, 2))) { map(one) { it + it * 2 } } val expected = (Nd4j.create(2, 2) ?: fail()).asIntStructure() expected[intArrayOf(0, 0)] = 3 expected[intArrayOf(0, 1)] = 3 @@ -31,7 +31,7 @@ internal class INDArrayAlgebraTest { @Test fun testAdd() { - val res = (IntINDArrayRing(intArrayOf(2, 2))) { one + 25 } + val res = (IntNd4jArrayRing(intArrayOf(2, 2))) { one + 25 } val expected = (Nd4j.create(2, 2) ?: fail()).asIntStructure() expected[intArrayOf(0, 0)] = 26 expected[intArrayOf(0, 1)] = 26 diff --git a/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayStructureTest.kt b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayStructureTest.kt similarity index 98% rename from kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayStructureTest.kt rename to kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayStructureTest.kt index 63426d7f9..7e46211c1 100644 --- a/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/INDArrayStructureTest.kt +++ b/kmath-nd4j/src/test/kotlin/kscience/kmath/nd4j/Nd4jArrayStructureTest.kt @@ -7,7 +7,7 @@ import kotlin.test.assertEquals import kotlin.test.assertNotEquals import kotlin.test.fail -internal class INDArrayStructureTest { +internal class Nd4jArrayStructureTest { @Test fun testElements() { val nd = Nd4j.create(doubleArrayOf(1.0, 2.0, 3.0))!! From 022b8f0fa347aff5bcba3aae6eabae52c60aa390 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 29 Oct 2020 15:44:30 +0700 Subject: [PATCH 28/28] Regenerate readme --- kmath-nd4j/README.md | 40 +++++++++++++++++++------------------ kmath-nd4j/build.gradle.kts | 2 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/kmath-nd4j/README.md b/kmath-nd4j/README.md index fac24504a..b071df499 100644 --- a/kmath-nd4j/README.md +++ b/kmath-nd4j/README.md @@ -2,45 +2,48 @@ This subproject implements the following features: -- NDStructure wrapper for INDArray. -- Optimized NDRing implementations for INDArray storing Ints and Longs. -- Optimized NDField implementations for INDArray storing Floats and Doubles. + - [nd4jarraystrucure](src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : NDStructure wrapper for INDArray + - [nd4jarrayrings](src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Rings over Nd4jArrayStructure of Int and Long + - [nd4jarrayfields](src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt) : Fields over Nd4jArrayStructure of Float and Double + > #### Artifact: -> This module is distributed in the artifact `scientifik:kmath-nd4j:0.1.4-dev-8`. -> +> +> This module artifact: `kscience.kmath:kmath-nd4j:0.2.0-dev-3`. +> +> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-nd4j/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-nd4j/_latestVersion) +> +> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-nd4j/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-nd4j/_latestVersion) +> > **Gradle:** > > ```gradle > repositories { -> mavenCentral() -> maven { url 'https://dl.bintray.com/mipt-npm/scientifik' } +> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } +> maven { url 'https://dl.bintray.com/mipt-npm/kscience' } > maven { url 'https://dl.bintray.com/mipt-npm/dev' } +> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + > } > > dependencies { -> implementation 'scientifik:kmath-nd4j:0.1.4-dev-8' -> implementation 'org.nd4j:nd4j-native-platform:1.0.0-beta7' +> implementation 'kscience.kmath:kmath-nd4j:0.2.0-dev-3' > } > ``` > **Gradle Kotlin DSL:** > > ```kotlin > repositories { -> mavenCentral() -> maven("https://dl.bintray.com/mipt-npm/scientifik") +> maven("https://dl.bintray.com/kotlin/kotlin-eap") +> maven("https://dl.bintray.com/mipt-npm/kscience") > maven("https://dl.bintray.com/mipt-npm/dev") +> maven("https://dl.bintray.com/hotkeytlt/maven") > } > > dependencies { -> implementation("scientifik:kmath-nd4j:0.1.4-dev-8") -> implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") +> implementation("kscience.kmath:kmath-nd4j:0.2.0-dev-3") > } > ``` -> -> This distribution also needs an implementation of ND4J API. The ND4J Native Platform is usually the fastest one, so -> it is included to the snippet. -> ## Examples @@ -64,7 +67,7 @@ import org.nd4j.linalg.factory.* import scientifik.kmath.nd4j.* import scientifik.kmath.operations.* -val field = RealINDArrayField(intArrayOf(2, 2)) +val field = RealNd4jArrayField(intArrayOf(2, 2)) val array = Nd4j.rand(2, 2).asRealStructure() val res = field { @@ -76,5 +79,4 @@ println(res.ndArray) // [ 269.7913, 202.2077]] ``` - Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). diff --git a/kmath-nd4j/build.gradle.kts b/kmath-nd4j/build.gradle.kts index 953530b01..391727c45 100644 --- a/kmath-nd4j/build.gradle.kts +++ b/kmath-nd4j/build.gradle.kts @@ -18,7 +18,7 @@ readme { propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) feature( - id = "nd4jarraystrucure", + id = "nd4jarraystructure", description = "NDStructure wrapper for INDArray", ref = "src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt" )