From b602066f4880037f60a49db7b4d417cd0626765c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 27 Sep 2022 16:57:06 +0300 Subject: [PATCH] Change the default strides and unify strides processing --- .../space/kscience/kmath/series/analyzeDif.kt | 5 +- .../kmath/structures/StreamDoubleFieldND.kt | 2 +- .../structures/StructureReadBenchmark.kt | 4 +- .../space/kscience/kmath/nd/BufferND.kt | 4 +- .../kotlin/space/kscience/kmath/nd/Shape.kt | 2 +- .../space/kscience/kmath/nd/ShapeIndices.kt | 73 +++++++++++++--- .../space/kscience/kmath/nd/Structure2D.kt | 9 +- .../space/kscience/kmath/nd/StructureND.kt | 8 +- .../kmath/structures/BufferAccessor2D.kt | 4 +- .../kscience/kmath/structures/BufferView.kt | 33 ++++---- .../kmath/structures/bufferPrimitiveAccess.kt | 4 +- .../space/kscience/kmath/nd/StridesTest.kt | 38 +++++++++ .../kmath/structures/BufferExpandedTest.kt | 2 +- .../kmath/structures/LazyStructureND.kt | 4 +- .../kscience/kmath/histogram/HistogramND.kt | 4 +- .../histogram/MultivariateHistogramTest.kt | 4 +- .../kmath/multik/MultikTensorAlgebra.kt | 2 +- .../kscience/kmath/nd4j/Nd4jTensorAlgebra.kt | 4 +- .../kscience/kmath/series/SeriesAlgebra.kt | 6 +- .../tensorflow/DoubleTensorFlowAlgebra.kt | 4 +- .../kmath/tensors/core/BufferedTensor.kt | 5 +- .../kmath/tensors/core/DoubleTensor.kt | 83 +++++++++++++++++-- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 5 +- .../kmath/tensors/core/IntTensorAlgebra.kt | 2 +- .../tensors/core/TensorLinearStructure.kt | 74 ----------------- .../core/internal/doubleTensorHelpers.kt | 13 +-- .../tensors/core/internal/intTensorHelpers.kt | 19 ++--- .../kmath/tensors/core/internal/utils.kt | 8 +- .../kmath/tensors/core/tensorTransform.kt | 9 +- .../kmath/tensors/core/TestDoubleTensor.kt | 2 +- .../kscience/kmath/viktor/ViktorFieldOpsND.kt | 8 +- .../kmath/viktor/ViktorStructureND.kt | 4 +- 32 files changed, 268 insertions(+), 180 deletions(-) create mode 100644 kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/StridesTest.kt delete mode 100644 kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/TensorLinearStructure.kt diff --git a/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt b/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt index 308345ded..c1b0c056e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt @@ -7,6 +7,7 @@ import space.kscience.kmath.operations.DoubleBufferOps import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.bufferAlgebra import space.kscience.kmath.operations.toList +import space.kscience.kmath.stat.KMComparisonResult import space.kscience.kmath.stat.ksComparisonStatistic import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.slice @@ -31,12 +32,12 @@ fun main() = with(Double.algebra.bufferAlgebra.seriesAlgebra()) { val s1 = series(100) { sin(2 * PI * it / 100) + 1.0 } - val s2 = s1.slice(20U..50U).moveTo(40) + val s2 = s1.slice(20..50).moveTo(40) val s3: Buffer = s1.zip(s2) { l, r -> l + r } //s1 + s2 val s4 = DoubleBufferOps.ln(s3) - val kmTest = ksComparisonStatistic(s1, s2) + val kmTest: KMComparisonResult = ksComparisonStatistic(s1, s2) Plotly.page { h1 { +"This is my plot" } diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt index 1061112eb..f97e98973 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -21,7 +21,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND>, ExtendedField> { - private val strides = DefaultStrides(shape) + private val strides = ColumnStrides(shape) override val elementAlgebra: DoubleField get() = DoubleField override val zero: BufferND by lazy { structureND(shape) { zero } } override val one: BufferND by lazy { structureND(shape) { one } } diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StructureReadBenchmark.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StructureReadBenchmark.kt index a64ac7280..ae7693f03 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StructureReadBenchmark.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StructureReadBenchmark.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.structures import space.kscience.kmath.nd.BufferND -import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.ColumnStrides import kotlin.system.measureTimeMillis @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") @@ -14,7 +14,7 @@ fun main() { val n = 6000 val array = DoubleArray(n * n) { 1.0 } val buffer = DoubleBuffer(array) - val strides = DefaultStrides(intArrayOf(n, n)) + val strides = ColumnStrides(intArrayOf(n, n)) val structure = BufferND(strides, buffer) measureTimeMillis { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt index 1eb08fa8c..7efc785fb 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt @@ -38,7 +38,7 @@ public inline fun StructureND.mapToBuffer( ): BufferND = if (this is BufferND) BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) }) else { - val strides = DefaultStrides(shape) + val strides = ColumnStrides(shape) BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) } @@ -75,7 +75,7 @@ public inline fun MutableStructureND.mapToMutableBuffer( return if (this is MutableBufferND) MutableBufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) }) else { - val strides = DefaultStrides(shape) + val strides = ColumnStrides(shape) MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) } } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Shape.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Shape.kt index c5d04c9de..fc0b4b6ea 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Shape.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Shape.kt @@ -24,7 +24,7 @@ public fun Shape(shapeFirst: Int, vararg shapeRest: Int): Shape = intArrayOf(sha public interface WithShape { public val shape: Shape - public val indices: ShapeIndexer get() = DefaultStrides(shape) + public val indices: ShapeIndexer get() = ColumnStrides(shape) } internal fun requireIndexInShape(index: IntArray, shape: Shape) { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndices.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndices.kt index 2dce407ca..4b31e3fc5 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndices.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndices.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.nd +import kotlin.math.max import kotlin.native.concurrent.ThreadLocal /** @@ -61,12 +62,16 @@ public abstract class Strides : ShapeIndexer { * Iterate over ND indices in a natural order */ public override fun asSequence(): Sequence = (0 until linearSize).asSequence().map(::index) + + public companion object{ + public fun linearSizeOf(shape: IntArray): Int = shape.reduce(Int::times) + } } /** - * Simple implementation of [Strides]. + * Column-first [Strides]. Columns are represented as continuous arrays */ -public class DefaultStrides(override val shape: IntArray) : Strides() { +public class ColumnStrides(override val shape: IntArray) : Strides() { override val linearSize: Int get() = strides[shape.size] /** @@ -100,22 +105,64 @@ public class DefaultStrides(override val shape: IntArray) : Strides() { override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is DefaultStrides) return false - if (!shape.contentEquals(other.shape)) return false - return true + if (other !is ColumnStrides) return false + return shape.contentEquals(other.shape) } override fun hashCode(): Int = shape.contentHashCode() - public companion object { - /** - * Cached builder for default strides - */ - @Deprecated("Replace by Strides(shape)") - public operator fun invoke(shape: IntArray): Strides = - defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } + public companion object +} + +/** + * This [Strides] implementation follows the last dimension first convention + * For more information: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.strides.html + * + * @param shape the shape of the tensor. + */ +public class RowStrides(override val shape: IntArray) : Strides() { + + override val strides: IntArray by lazy { + val nDim = shape.size + val res = IntArray(nDim) + if (nDim == 0) return@lazy res + + var current = nDim - 1 + res[current] = 1 + + while (current > 0) { + res[current - 1] = max(1, shape[current]) * res[current] + current-- + } + res } + + override fun index(offset: Int): IntArray { + val res = IntArray(shape.size) + var current = offset + var strideIndex = 0 + + while (strideIndex < shape.size) { + res[strideIndex] = (current / strides[strideIndex]) + current %= strides[strideIndex] + strideIndex++ + } + return res + } + + override val linearSize: Int get() = linearSizeOf(shape) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is RowStrides) return false + return shape.contentEquals(other.shape) + } + + override fun hashCode(): Int = shape.contentHashCode() + + public companion object + } @ThreadLocal @@ -124,4 +171,4 @@ private val defaultStridesCache = HashMap() /** * Cached builder for default strides */ -public fun Strides(shape: IntArray): Strides = defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } \ No newline at end of file +public fun Strides(shape: IntArray): Strides = defaultStridesCache.getOrPut(shape) { RowStrides(shape) } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt index d8fd2031c..2822a74d4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.nd import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableListBuffer import space.kscience.kmath.structures.VirtualBuffer import kotlin.jvm.JvmInline @@ -84,15 +85,15 @@ public interface MutableStructure2D : Structure2D, MutableStructureND { * The buffer of rows of this structure. It gets elements from the structure dynamically. */ @PerformancePitfall - override val rows: List> - get() = List(rowNum) { i -> MutableBuffer1DWrapper(MutableListBuffer(colNum) { j -> get(i, j) }) } + override val rows: List> + get() = List(rowNum) { i -> MutableListBuffer(colNum) { j -> get(i, j) } } /** * The buffer of columns of this structure. It gets elements from the structure dynamically. */ @PerformancePitfall - override val columns: List> - get() = List(colNum) { j -> MutableBuffer1DWrapper(MutableListBuffer(rowNum) { i -> get(i, j) }) } + override val columns: List> + get() = List(colNum) { j -> MutableListBuffer(rowNum) { i -> get(i, j) } } } /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt index b2da083bb..4e1cc1ff4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt @@ -148,25 +148,25 @@ public interface StructureND : Featured, WithShape { shape: IntArray, bufferFactory: BufferFactory = BufferFactory.boxing(), initializer: (IntArray) -> T, - ): BufferND = buffered(DefaultStrides(shape), bufferFactory, initializer) + ): BufferND = buffered(ColumnStrides(shape), bufferFactory, initializer) public inline fun auto( shape: IntArray, crossinline initializer: (IntArray) -> T, - ): BufferND = auto(DefaultStrides(shape), initializer) + ): BufferND = auto(ColumnStrides(shape), initializer) @JvmName("autoVarArg") public inline fun auto( vararg shape: Int, crossinline initializer: (IntArray) -> T, ): BufferND = - auto(DefaultStrides(shape), initializer) + auto(ColumnStrides(shape), initializer) public inline fun auto( type: KClass, vararg shape: Int, crossinline initializer: (IntArray) -> T, - ): BufferND = auto(type, DefaultStrides(shape), initializer) + ): BufferND = auto(type, ColumnStrides(shape), initializer) } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferAccessor2D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferAccessor2D.kt index 07355d396..f61a0a623 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferAccessor2D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferAccessor2D.kt @@ -5,7 +5,7 @@ package space.kscience.kmath.structures -import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.as2D @@ -31,7 +31,7 @@ internal class BufferAccessor2D( //TODO optimize wrapper fun MutableBuffer.collect(): Structure2D = StructureND.buffered( - DefaultStrides(intArrayOf(rowNum, colNum)), + ColumnStrides(intArrayOf(rowNum, colNum)), factory ) { (i, j) -> get(i, j) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt index 8dc0d63ef..e8ab4f0ba 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt @@ -15,6 +15,9 @@ public interface BufferView : Buffer { */ @UnstableKMathAPI public fun originIndex(index: Int): Int + + @OptIn(UnstableKMathAPI::class) + override fun get(index: Int): T = origin[originIndex(index)] } /** @@ -22,40 +25,40 @@ public interface BufferView : Buffer { */ public class BufferSlice( override val origin: Buffer, - public val offset: UInt = 0U, + public val offset: Int = 0, override val size: Int, ) : BufferView { init { require(size > 0) { "Size must be positive" } - require(offset + size.toUInt() <= origin.size.toUInt()) { - "End of buffer ${offset + size.toUInt()} is beyond the end of origin buffer size ${origin.size}" + require(offset + size <= origin.size) { + "End of buffer ${offset + size} is beyond the end of origin buffer size ${origin.size}" } } override fun get(index: Int): T = if (index >= size) { throw IndexOutOfBoundsException("$index is out of ${0 until size} rage") } else { - origin[index.toUInt() + offset] + origin[index + offset] } override fun iterator(): Iterator = - (offset until (offset + size.toUInt())).asSequence().map { origin[it] }.iterator() + (offset until (offset + size)).asSequence().map { origin[it] }.iterator() @UnstableKMathAPI - override fun originIndex(index: Int): Int = if (index >= size) -1 else index - offset.toInt() + override fun originIndex(index: Int): Int = if (index >= size) -1 else index - offset - override fun toString(): String = "$origin[$offset..${offset + size.toUInt()}" + override fun toString(): String = "$origin[$offset..${offset + size}" } /** - * An expanded buffer that could include the whole initial buffer ot its part and fills all space beyond it borders with [defaultValue]. + * An expanded buffer that could include the whole initial buffer or its part and fills all space beyond it borders with [defaultValue]. * * The [offset] parameter shows the shift of expanded buffer start relative to origin start and could be both positive and negative. */ public class BufferExpanded( override val origin: Buffer, - public val defaultValue: T, + private val defaultValue: T, public val offset: Int = 0, override val size: Int = origin.size, ) : BufferView { @@ -79,17 +82,17 @@ public class BufferExpanded( /** * Zero-copy select a slice inside the original buffer */ -public fun Buffer.slice(range: UIntRange): BufferView = if (this is BufferSlice) { +public fun Buffer.slice(range: IntRange): BufferView = if (this is BufferSlice) { BufferSlice( origin, this.offset + range.first, - (range.last - range.first).toInt() + 1 + (range.last - range.first) + 1 ) } else { BufferSlice( this, range.first, - (range.last - range.first).toInt() + 1 + (range.last - range.first) + 1 ) } @@ -103,7 +106,7 @@ public fun Buffer.expand( ): BufferView = if (range.first >= 0 && range.last < size) { BufferSlice( this, - range.first.toUInt(), + range.first, (range.last - range.first) + 1 ) } else { @@ -118,7 +121,7 @@ public fun Buffer.expand( /** * A [BufferView] that overrides indexing of the original buffer */ -public class PermutatedBuffer( +public class PermutedBuffer( override val origin: Buffer, private val permutations: IntArray, ) : BufferView { @@ -145,4 +148,4 @@ public class PermutatedBuffer( /** * Created a permuted view of given buffer using provided [indices] */ -public fun Buffer.permute(indices: IntArray): PermutatedBuffer = PermutatedBuffer(this, indices) \ No newline at end of file +public fun Buffer.permute(indices: IntArray): PermutedBuffer = PermutedBuffer(this, indices) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt index d361cb013..2f2cc80b3 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.misc.UnstableKMathAPI @UnstableKMathAPI public fun Buffer.getDouble(index: Int): Double = if (this is BufferView) { val originIndex = originIndex(index) - if( originIndex>=0) { + if (originIndex >= 0) { origin.getDouble(originIndex) } else { get(index) @@ -26,7 +26,7 @@ public fun Buffer.getDouble(index: Int): Double = if (this is BufferView @UnstableKMathAPI public fun Buffer.getInt(index: Int): Int = if (this is BufferView) { val originIndex = originIndex(index) - if( originIndex>=0) { + if (originIndex >= 0) { origin.getInt(originIndex) } else { get(index) diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/StridesTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/StridesTest.kt new file mode 100644 index 000000000..eac4f17e1 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/StridesTest.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2018-2022 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.nd + +import kotlin.test.Test + +class StridesTest { + @Test + fun checkRowBasedStrides() { + val strides = RowStrides(intArrayOf(3, 3)) + var counter = 0 + for(i in 0..2){ + for(j in 0..2){ +// print(strides.offset(intArrayOf(i,j)).toString() + "\t") + require(strides.offset(intArrayOf(i,j)) == counter) + counter++ + } + println() + } + } + + @Test + fun checkColumnBasedStrides() { + val strides = ColumnStrides(intArrayOf(3, 3)) + var counter = 0 + for(i in 0..2){ + for(j in 0..2){ +// print(strides.offset(intArrayOf(i,j)).toString() + "\t") + require(strides.offset(intArrayOf(j,i)) == counter) + counter++ + } + println() + } + } +} \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/BufferExpandedTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/BufferExpandedTest.kt index 992080fb0..04671e040 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/BufferExpandedTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/BufferExpandedTest.kt @@ -9,7 +9,7 @@ internal class BufferExpandedTest { @Test fun shrink(){ - val view = buffer.slice(20U..30U) + val view = buffer.slice(20..30) assertEquals(20, view[0]) assertEquals(30, view[10]) assertFails { view[11] } diff --git a/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt b/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt index 786d32190..c217c3a26 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.structures import kotlinx.coroutines.* import space.kscience.kmath.coroutines.Math import space.kscience.kmath.misc.PerformancePitfall -import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.StructureND public class LazyStructureND( @@ -27,7 +27,7 @@ public class LazyStructureND( @OptIn(PerformancePitfall::class) override fun elements(): Sequence> { - val strides = DefaultStrides(shape) + val strides = ColumnStrides(shape) val res = runBlocking { strides.asSequence().toList().map { index -> index to await(index) } } return res.asSequence() } diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/HistogramND.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/HistogramND.kt index 2cefb33a5..1c9f00838 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/HistogramND.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/HistogramND.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.histogram import space.kscience.kmath.domains.Domain import space.kscience.kmath.linear.Point -import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.FieldOpsND import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.StructureND @@ -32,7 +32,7 @@ public class HistogramND, D : Domain, V : Any>( override val dimension: Int get() = group.shape.size override val bins: Iterable> - get() = DefaultStrides(group.shape).asSequence().map { + get() = ColumnStrides(group.shape).asSequence().map { group.produceBin(it, values[it]) }.asIterable() } diff --git a/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/MultivariateHistogramTest.kt b/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/MultivariateHistogramTest.kt index 717f9d7a1..7e11c9a2f 100644 --- a/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/MultivariateHistogramTest.kt +++ b/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/MultivariateHistogramTest.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.histogram import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.operations.invoke import space.kscience.kmath.real.DoubleVector import kotlin.random.Random @@ -73,7 +73,7 @@ internal class MultivariateHistogramTest { } val res = histogram1 - histogram2 assertTrue { - DefaultStrides(shape).asSequence().all { index -> + ColumnStrides(shape).asSequence().all { index -> res.values[index] <= histogram1.values[index] } } diff --git a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt index 194c5fcee..dd0249fbc 100644 --- a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt +++ b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt @@ -31,7 +31,7 @@ public abstract class MultikTensorAlgebra>( protected val multikStat: Statistics = multikEngine.getStatistics() override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): MultikTensor { - val strides = DefaultStrides(shape) + val strides = ColumnStrides(shape) val memoryView = initMemoryView(strides.linearSize, type) strides.asSequence().forEachIndexed { linearIndex, tensorIndex -> memoryView[linearIndex] = elementAlgebra.initializer(tensorIndex) diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt index c1772683f..b0fce8dcf 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt @@ -13,7 +13,7 @@ import org.nd4j.linalg.factory.Nd4j import org.nd4j.linalg.factory.ops.NDBase import org.nd4j.linalg.ops.transforms.Transforms import space.kscience.kmath.misc.PerformancePitfall -import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.DoubleField @@ -178,7 +178,7 @@ public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra { override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): Nd4jArrayStructure { val array: INDArray = Nd4j.zeros(*shape) - val indices = DefaultStrides(shape) + val indices = ColumnStrides(shape) indices.asSequence().forEach { index -> array.putScalar(index, elementAlgebra.initializer(index)) } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt index 96ad4daf7..3cd2212f6 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt @@ -38,7 +38,7 @@ public val Series.absoluteIndices: IntRange get() = position until positi /** * A [BufferView] with index offset (both positive and negative) and possible size change */ -private class OffsetBufer( +private class SeriesImpl( override val origin: Buffer, override val position: Int, override val size: Int = origin.size, @@ -86,9 +86,9 @@ public class SeriesAlgebra, out BA : BufferAlgebra, L>( * Create an offset series with index starting point at [index] */ public fun Buffer.moveTo(index: Int): Series = if (this is Series) { - OffsetBufer(origin, index, size) + SeriesImpl(origin, index, size) } else { - OffsetBufer(this, index, size) + SeriesImpl(this, index, size) } public val Buffer.offset: Int get() = if (this is Series) position else 0 diff --git a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt index c4d192792..953076680 100644 --- a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt +++ b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt @@ -13,7 +13,7 @@ import org.tensorflow.types.TFloat64 import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.DoubleField @@ -39,7 +39,7 @@ public class DoubleTensorFlowAlgebra internal constructor( initializer: DoubleField.(IntArray) -> Double, ): StructureND { val res = TFloat64.tensorOf(org.tensorflow.ndarray.Shape.of(*shape.toLongArray())) { array -> - DefaultStrides(shape).forEach { index -> + ColumnStrides(shape).forEach { index -> array.setDouble(elementAlgebra.initializer(index), *index.toLongArray()) } } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt index 0b043db56..53f77195c 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.misc.PerformancePitfall +import space.kscience.kmath.nd.RowStrides import space.kscience.kmath.nd.Strides import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.tensors.api.Tensor @@ -20,9 +21,9 @@ public abstract class BufferedTensor( public abstract val source: MutableBuffer /** - * Buffer strides based on [TensorLinearStructure] implementation + * Buffer strides based on [RowStrides] implementation */ - override val indices: Strides get() = TensorLinearStructure(shape) + override val indices: Strides get() = RowStrides(shape) /** * Number of elements in tensor diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt index d3308a69f..05d2b0feb 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt @@ -5,32 +5,38 @@ package space.kscience.kmath.tensors.core +import space.kscience.kmath.misc.PerformancePitfall +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.nd.MutableStructure2D +import space.kscience.kmath.nd.MutableStructureND +import space.kscience.kmath.nd.Shape import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.core.internal.toPrettyString +import kotlin.jvm.JvmInline public class OffsetDoubleBuffer( - private val source: DoubleBuffer, + override val origin: DoubleBuffer, private val offset: Int, override val size: Int, -) : MutableBuffer { +) : MutableBuffer, BufferView { init { require(offset >= 0) { "Offset must be non-negative" } require(size >= 0) { "Size must be non-negative" } - require(offset + size <= source.size) { "Maximum index must be inside source dimension" } + require(offset + size <= origin.size) { "Maximum index must be inside source dimension" } } override fun set(index: Int, value: Double) { require(index in 0 until size) { "Index must be in [0, size)" } - source[index + offset] = value + origin[index + offset] = value } - override fun get(index: Int): Double = source[index + offset] + override fun get(index: Int): Double = origin[index + offset] /** * Copy only a part of buffer that belongs to this [OffsetDoubleBuffer] */ - override fun copy(): DoubleBuffer = source.array.copyOfRange(offset, offset + size).asBuffer() + override fun copy(): DoubleBuffer = origin.array.copyOfRange(offset, offset + size).asBuffer() override fun iterator(): Iterator = iterator { for (i in indices) { @@ -41,7 +47,14 @@ public class OffsetDoubleBuffer( override fun toString(): String = Buffer.toString(this) public fun view(addOffset: Int, newSize: Int = size - addOffset): OffsetDoubleBuffer = - OffsetDoubleBuffer(source, offset + addOffset, newSize) + OffsetDoubleBuffer(origin, offset + addOffset, newSize) + + @UnstableKMathAPI + override fun originIndex(index: Int): Int = if (index in 0 until size) { + index + offset + } else { + -1 + } } public fun OffsetDoubleBuffer.slice(range: IntRange): OffsetDoubleBuffer = view(range.first, range.last - range.first) @@ -90,3 +103,59 @@ public class DoubleTensor( override fun toString(): String = toPrettyString() } + +@JvmInline +public value class DoubleTensor2D(public val tensor: DoubleTensor) : MutableStructureND by tensor, + MutableStructure2D { + + init { + require(tensor.shape.size == 2) { "Only 2D tensors could be cast to 2D" } + } + + override val rowNum: Int get() = shape[0] + override val colNum: Int get() = shape[1] + + override fun get(i: Int, j: Int): Double = tensor.source[i * colNum + j] + + override fun set(i: Int, j: Int, value: Double) { + tensor.source[i * colNum + j] = value + } + + @OptIn(PerformancePitfall::class) + override val rows: List + get() = List(rowNum) { i -> + tensor.source.view(i * colNum, colNum) + } + + +// @OptIn(PerformancePitfall::class) +// override val columns: List> get() = List(colNum) { j -> +// object : MutableBuffer{ +// +// override fun get(index: Int): Double { +// tensor.source.get() +// } +// +// override fun set(index: Int, value: Double) { +// TODO("Not yet implemented") +// } +// +// override fun copy(): MutableBuffer { +// TODO("Not yet implemented") +// } +// +// override val size: Int +// get() = TODO("Not yet implemented") +// +// override fun toString(): String { +// TODO("Not yet implemented") +// } +// +// } +// } + + @PerformancePitfall + override fun elements(): Sequence> = tensor.elements() + override fun get(index: IntArray): Double = tensor[index] + override val shape: Shape get() = tensor.shape +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 67248890c..4c65373d8 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -11,6 +11,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.* +import space.kscience.kmath.nd.Strides.Companion.linearSizeOf import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra @@ -121,7 +122,7 @@ public open class DoubleTensorAlgebra : */ override fun structureND(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): DoubleTensor = fromArray( shape, - TensorLinearStructure(shape).asSequence().map { DoubleField.initializer(it) }.toMutableList().toDoubleArray() + RowStrides(shape).asSequence().map { DoubleField.initializer(it) }.toMutableList().toDoubleArray() ) override fun Tensor.getTensor(i: Int): DoubleTensor { @@ -130,7 +131,7 @@ public open class DoubleTensorAlgebra : val newShape = if (lastShape.isNotEmpty()) lastShape else intArrayOf(1) return DoubleTensor( newShape, - dt.source.view(newShape.reduce(Int::times) * i, TensorLinearStructure.linearSizeOf(newShape)) + dt.source.view(newShape.reduce(Int::times) * i, linearSizeOf(newShape)) ) } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt index 124ccc668..3b00744a1 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt @@ -119,7 +119,7 @@ public open class IntTensorAlgebra : TensorAlgebra { */ override fun structureND(shape: IntArray, initializer: IntRing.(IntArray) -> Int): IntTensor = fromArray( shape, - TensorLinearStructure(shape).asSequence().map { IntRing.initializer(it) }.toMutableList().toIntArray() + RowStrides(shape).asSequence().map { IntRing.initializer(it) }.toMutableList().toIntArray() ) override fun Tensor.getTensor(i: Int): IntTensor { diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/TensorLinearStructure.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/TensorLinearStructure.kt deleted file mode 100644 index 729fc0d13..000000000 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/TensorLinearStructure.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2018-2022 KMath contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package space.kscience.kmath.tensors.core - -import space.kscience.kmath.nd.Strides -import kotlin.math.max - -/** - * This [Strides] implementation follows the last dimension first convention - * For more information: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.strides.html - * - * @param shape the shape of the tensor. - */ -public class TensorLinearStructure(override val shape: IntArray) : Strides() { - override val strides: IntArray get() = stridesFromShape(shape) - - override fun index(offset: Int): IntArray = - indexFromOffset(offset, strides, shape.size) - - override val linearSize: Int get() = linearSizeOf(shape) - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - - other as TensorLinearStructure - - if (!shape.contentEquals(other.shape)) return false - - return true - } - - override fun hashCode(): Int { - return shape.contentHashCode() - } - - public companion object { - - public fun linearSizeOf(shape: IntArray): Int = shape.reduce(Int::times) - - public fun stridesFromShape(shape: IntArray): IntArray { - val nDim = shape.size - val res = IntArray(nDim) - if (nDim == 0) - return res - - var current = nDim - 1 - res[current] = 1 - - while (current > 0) { - res[current - 1] = max(1, shape[current]) * res[current] - current-- - } - return res - } - - public fun indexFromOffset(offset: Int, strides: IntArray, nDim: Int): IntArray { - val res = IntArray(nDim) - var current = offset - var strideIndex = 0 - - while (strideIndex < nDim) { - res[strideIndex] = (current / strides[strideIndex]) - current %= strides[strideIndex] - strideIndex++ - } - return res - } - } - -} \ No newline at end of file diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt index c5910a436..daf6f5a07 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt @@ -5,17 +5,18 @@ package space.kscience.kmath.tensors.core.internal -import space.kscience.kmath.nd.MutableStructure2D -import space.kscience.kmath.nd.Structure2D -import space.kscience.kmath.nd.as2D -import space.kscience.kmath.nd.get +import space.kscience.kmath.nd.* +import space.kscience.kmath.nd.Strides.Companion.linearSizeOf import space.kscience.kmath.operations.asSequence import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.VirtualBuffer import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.indices -import space.kscience.kmath.tensors.core.* import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.eye +import space.kscience.kmath.tensors.core.BufferedTensor +import space.kscience.kmath.tensors.core.DoubleTensor +import space.kscience.kmath.tensors.core.OffsetDoubleBuffer +import space.kscience.kmath.tensors.core.copyToTensor import kotlin.math.abs import kotlin.math.max import kotlin.math.sqrt @@ -177,7 +178,7 @@ internal val DoubleTensor.matrices: VirtualBuffer val matrixOffset = shape[n - 1] * shape[n - 2] val matrixShape = intArrayOf(shape[n - 2], shape[n - 1]) - val size = TensorLinearStructure.linearSizeOf(matrixShape) + val size = linearSizeOf(matrixShape) return VirtualBuffer(linearSize / matrixOffset) { index -> val offset = index * matrixOffset diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/intTensorHelpers.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/intTensorHelpers.kt index db289a090..f938d1c61 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/intTensorHelpers.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/intTensorHelpers.kt @@ -32,20 +32,19 @@ internal fun List.concat(): IntBuffer { } -internal val IntTensor.vectors: VirtualBuffer - get() { - val n = shape.size - val vectorOffset = shape[n - 1] - val vectorShape = intArrayOf(shape.last()) +internal fun IntTensor.vectors(): VirtualBuffer { + val n = shape.size + val vectorOffset = shape[n - 1] + val vectorShape = intArrayOf(shape.last()) - return VirtualBuffer(linearSize / vectorOffset) { index -> - val offset = index * vectorOffset - IntTensor(vectorShape, source.view(offset, vectorShape.first())) - } + return VirtualBuffer(linearSize / vectorOffset) { index -> + val offset = index * vectorOffset + IntTensor(vectorShape, source.view(offset, vectorShape.first())) } +} -internal fun IntTensor.vectorSequence(): Sequence = vectors.asSequence() +internal fun IntTensor.vectorSequence(): Sequence = vectors().asSequence() internal val IntTensor.matrices: VirtualBuffer diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt index cfbd9bdc9..cba81f56b 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt @@ -5,9 +5,8 @@ package space.kscience.kmath.tensors.core.internal -import space.kscience.kmath.nd.as1D +import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.operations.DoubleBufferOps.Companion.map -import space.kscience.kmath.operations.toMutableList import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.samplers.GaussianSampler import space.kscience.kmath.structures.DoubleBuffer @@ -67,6 +66,7 @@ internal fun format(value: Double, digits: Int = 4): String = buildString { repeat(fLength - res.length) { append(' ') } } +@OptIn(PerformancePitfall::class) internal fun DoubleTensor.toPrettyString(): String = buildString { var offset = 0 val shape = this@toPrettyString.shape @@ -85,7 +85,7 @@ internal fun DoubleTensor.toPrettyString(): String = buildString { charOffset += 1 } - val values = vector.as1D().toMutableList().map(::format) + val values = vector.elements().map { format(it.second) } values.joinTo(this, separator = ", ") @@ -101,7 +101,7 @@ internal fun DoubleTensor.toPrettyString(): String = buildString { } offset += vectorSize - if (this@toPrettyString.linearSize == offset) { + if (this@toPrettyString.indices.linearSize == offset) { break } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt index 118624440..61bf2341e 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.nd.DoubleBufferND +import space.kscience.kmath.nd.RowStrides import space.kscience.kmath.nd.StructureND import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.asBuffer @@ -17,12 +18,12 @@ import space.kscience.kmath.tensors.api.Tensor */ public fun StructureND.copyToTensor(): DoubleTensor = if (this is DoubleTensor) { DoubleTensor(shape, source.copy()) -} else if (this is DoubleBufferND && indices is TensorLinearStructure) { +} else if (this is DoubleBufferND && indices is RowStrides) { DoubleTensor(shape, buffer.copy()) } else { DoubleTensor( shape, - TensorLinearStructure(this.shape).map(this::get).toDoubleArray().asBuffer(), + RowStrides(this.shape).map(this::get).toDoubleArray().asBuffer(), ) } @@ -46,7 +47,7 @@ public fun StructureND.toDoubleTensor(): DoubleTensor { */ public fun StructureND.asDoubleTensor(): DoubleTensor = if (this is DoubleTensor) { this -} else if (this is DoubleBufferND && indices is TensorLinearStructure) { +} else if (this is DoubleBufferND && indices is RowStrides) { DoubleTensor(shape, buffer) } else { copyToTensor() @@ -59,6 +60,6 @@ public fun StructureND.asIntTensor(): IntTensor = when (this) { is IntTensor -> this else -> IntTensor( this.shape, - TensorLinearStructure(this.shape).map(this::get).toIntArray().asBuffer() + RowStrides(this.shape).map(this::get).toIntArray().asBuffer() ) } \ No newline at end of file diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt index c87cf2a68..5261fab8c 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt @@ -66,7 +66,7 @@ internal class TestDoubleTensor { val doubleArray = DoubleBuffer(1.0, 2.0, 3.0) // create ND buffers, no data is copied - val ndArray: MutableBufferND = DoubleBufferND(DefaultStrides(intArrayOf(3)), doubleArray) + val ndArray: MutableBufferND = DoubleBufferND(ColumnStrides(intArrayOf(3)), doubleArray) // map to tensors val tensorArray = ndArray.asDoubleTensor() // Data is copied because of strides change. diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt index 493af54c9..1b7601a14 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt @@ -33,7 +33,7 @@ public open class ViktorFieldOpsND : override fun structureND(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND = F64Array(*shape).apply { - DefaultStrides(shape).asSequence().forEach { index -> + ColumnStrides(shape).asSequence().forEach { index -> set(value = DoubleField.initializer(index), indices = index) } }.asStructure() @@ -43,7 +43,7 @@ public open class ViktorFieldOpsND : @PerformancePitfall override fun StructureND.map(transform: DoubleField.(Double) -> Double): ViktorStructureND = F64Array(*shape).apply { - DefaultStrides(shape).asSequence().forEach { index -> + ColumnStrides(shape).asSequence().forEach { index -> set(value = DoubleField.transform(this@map[index]), indices = index) } }.asStructure() @@ -52,7 +52,7 @@ public open class ViktorFieldOpsND : override fun StructureND.mapIndexed( transform: DoubleField.(index: IntArray, Double) -> Double, ): ViktorStructureND = F64Array(*shape).apply { - DefaultStrides(shape).asSequence().forEach { index -> + ColumnStrides(shape).asSequence().forEach { index -> set(value = DoubleField.transform(index, this@mapIndexed[index]), indices = index) } }.asStructure() @@ -65,7 +65,7 @@ public open class ViktorFieldOpsND : ): ViktorStructureND { require(left.shape.contentEquals(right.shape)) return F64Array(*left.shape).apply { - DefaultStrides(left.shape).asSequence().forEach { index -> + ColumnStrides(left.shape).asSequence().forEach { index -> set(value = DoubleField.transform(left[index], right[index]), indices = index) } }.asStructure() diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt index c9749d41a..ea279a912 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.viktor import org.jetbrains.bio.viktor.F64Array import space.kscience.kmath.misc.PerformancePitfall -import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.MutableStructureND @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @@ -22,7 +22,7 @@ public class ViktorStructureND(public val f64Buffer: F64Array) : MutableStructur @PerformancePitfall override fun elements(): Sequence> = - DefaultStrides(shape).asSequence().map { it to get(it) } + ColumnStrides(shape).asSequence().map { it to get(it) } } public fun F64Array.asStructure(): ViktorStructureND = ViktorStructureND(this)