From 7e59ec5804370829d85b858362a5187b3cec1740 Mon Sep 17 00:00:00 2001 From: darksnake Date: Tue, 26 Oct 2021 09:16:24 +0300 Subject: [PATCH] Refactor TensorAlgebra to take StructureND and inherit AlgebraND --- .../kmath/structures/StreamDoubleFieldND.kt | 2 +- .../kscience/kmath/tensors/neuralNetwork.kt | 4 +- .../space/kscience/kmath/nd/AlgebraND.kt | 8 +- .../kscience/kmath/nd/BufferAlgebraND.kt | 8 +- .../space/kscience/kmath/nd/BufferND.kt | 16 +- .../space/kscience/kmath/nd/DoubleFieldND.kt | 6 +- .../space/kscience/kmath/nd/StructureND.kt | 4 +- .../space/kscience/kmath/real/realND.kt | 4 +- .../kmath/multik/MultikTensorAlgebra.kt | 53 +++--- .../tensors/api/AnalyticTensorAlgebra.kt | 4 +- .../tensors/api/LinearOpsTensorAlgebra.kt | 4 +- .../kmath/tensors/api/TensorAlgebra.kt | 58 +++--- .../api/TensorPartialDivisionAlgebra.kt | 26 ++- .../core/BroadcastDoubleTensorAlgebra.kt | 31 +-- .../kmath/tensors/core/BufferedTensor.kt | 10 +- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 180 +++++++++++------- .../tensors/core/internal/broadcastUtils.kt | 14 +- .../kmath/tensors/core/internal/checks.kt | 3 +- .../tensors/core/internal/tensorCastsUtils.kt | 9 +- .../kmath/tensors/core/internal/utils.kt | 2 +- .../kmath/tensors/core/tensorCasts.kt | 9 +- 21 files changed, 258 insertions(+), 197 deletions(-) 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 2b3e72136..05a13f5d2 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -36,7 +36,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND this.buffer as DoubleBuffer + this is BufferND && this.indices == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } } diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/neuralNetwork.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/neuralNetwork.kt index 3025ff8a3..cdfc06922 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/neuralNetwork.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/neuralNetwork.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.operations.invoke import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.DoubleTensor import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.toDoubleArray +import space.kscience.kmath.tensors.core.copyArray import kotlin.math.sqrt const val seed = 100500L @@ -111,7 +111,7 @@ class NeuralNetwork(private val layers: List) { private fun softMaxLoss(yPred: DoubleTensor, yTrue: DoubleTensor): DoubleTensor = BroadcastDoubleTensorAlgebra { val onesForAnswers = yPred.zeroesLike() - yTrue.toDoubleArray().forEachIndexed { index, labelDouble -> + yTrue.copyArray().forEachIndexed { index, labelDouble -> val label = labelDouble.toInt() onesForAnswers[intArrayOf(index, label)] = 1.0 } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt index 30cb01146..3d2d08fac 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt @@ -131,19 +131,19 @@ public interface GroupOpsND> : GroupOps>, * Adds an element to ND structure of it. * * @receiver the augend. - * @param arg the addend. + * @param other the addend. * @return the sum. */ - public operator fun T.plus(arg: StructureND): StructureND = arg + this + public operator fun T.plus(other: StructureND): StructureND = other.map { value -> add(this@plus, value) } /** * Subtracts an ND structure from an element of it. * * @receiver the dividend. - * @param arg the divisor. + * @param other the divisor. * @return the quotient. */ - public operator fun T.minus(arg: StructureND): StructureND = arg.map { value -> add(-this@minus, value) } + public operator fun T.minus(other: StructureND): StructureND = other.map { value -> add(-this@minus, value) } public companion object } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt index 859edefb8..cf007e7c9 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt @@ -51,7 +51,7 @@ public inline fun > BufferAlgebraND.mapInline( arg: BufferND, crossinline transform: A.(T) -> T ): BufferND { - val indexes = arg.indexes + val indexes = arg.indices return BufferND(indexes, bufferAlgebra.mapInline(arg.buffer, transform)) } @@ -59,7 +59,7 @@ internal inline fun > BufferAlgebraND.mapIndexedInline( arg: BufferND, crossinline transform: A.(index: IntArray, arg: T) -> T ): BufferND { - val indexes = arg.indexes + val indexes = arg.indices return BufferND( indexes, bufferAlgebra.mapIndexedInline(arg.buffer) { offset, value -> @@ -73,8 +73,8 @@ internal inline fun > BufferAlgebraND.zipInline( r: BufferND, crossinline block: A.(l: T, r: T) -> T ): BufferND { - require(l.indexes == r.indexes) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } - val indexes = l.indexes + require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } + val indexes = l.indices return BufferND(indexes, bufferAlgebra.zipInline(l.buffer, r.buffer, block)) } 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 afa8f8250..2b6fd3693 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt @@ -15,20 +15,20 @@ import space.kscience.kmath.structures.MutableBufferFactory * Represents [StructureND] over [Buffer]. * * @param T the type of items. - * @param indexes The strides to access elements of [Buffer] by linear indices. + * @param indices The strides to access elements of [Buffer] by linear indices. * @param buffer The underlying buffer. */ public open class BufferND( - public val indexes: ShapeIndexer, + public val indices: ShapeIndexer, public open val buffer: Buffer, ) : StructureND { - override operator fun get(index: IntArray): T = buffer[indexes.offset(index)] + override operator fun get(index: IntArray): T = buffer[indices.offset(index)] - override val shape: IntArray get() = indexes.shape + override val shape: IntArray get() = indices.shape @PerformancePitfall - override fun elements(): Sequence> = indexes.indices().map { + override fun elements(): Sequence> = indices.indices().map { it to this[it] } @@ -43,7 +43,7 @@ public inline fun StructureND.mapToBuffer( crossinline transform: (T) -> R, ): BufferND { return if (this is BufferND) - BufferND(this.indexes, factory.invoke(indexes.linearSize) { transform(buffer[it]) }) + BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) }) else { val strides = DefaultStrides(shape) BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) @@ -62,7 +62,7 @@ public class MutableBufferND( override val buffer: MutableBuffer, ) : MutableStructureND, BufferND(strides, buffer) { override fun set(index: IntArray, value: T) { - buffer[indexes.offset(index)] = value + buffer[indices.offset(index)] = value } } @@ -74,7 +74,7 @@ public inline fun MutableStructureND.mapToMutableBuffer( crossinline transform: (T) -> R, ): MutableBufferND { return if (this is MutableBufferND) - MutableBufferND(this.indexes, factory.invoke(indexes.linearSize) { transform(buffer[it]) }) + MutableBufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) }) else { val strides = DefaultStrides(shape) MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt index 853ac43b0..abb8e46ea 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt @@ -33,7 +33,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND(D arg: DoubleBufferND, transform: (Double) -> Double ): DoubleBufferND { - val indexes = arg.indexes + val indexes = arg.indices val array = arg.buffer.array return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { transform(array[it]) }) } @@ -43,8 +43,8 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND(D r: DoubleBufferND, block: (l: Double, r: Double) -> Double ): DoubleBufferND { - require(l.indexes == r.indexes) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } - val indexes = l.indexes + require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } + val indexes = l.indices val lArray = l.buffer.array val rArray = r.buffer.array return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { block(lArray[it], rArray[it]) }) 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 b4e62366a..496abf60f 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 @@ -71,7 +71,7 @@ public interface StructureND : Featured { if (st1 === st2) return true // fast comparison of buffers if possible - if (st1 is BufferND && st2 is BufferND && st1.indexes == st2.indexes) + if (st1 is BufferND && st2 is BufferND && st1.indices == st2.indices) return Buffer.contentEquals(st1.buffer, st2.buffer) //element by element comparison if it could not be avoided @@ -87,7 +87,7 @@ public interface StructureND : Featured { if (st1 === st2) return true // fast comparison of buffers if possible - if (st1 is BufferND && st2 is BufferND && st1.indexes == st2.indexes) + if (st1 is BufferND && st2 is BufferND && st1.indices == st2.indices) return Buffer.contentEquals(st1.buffer, st2.buffer) //element by element comparison if it could not be avoided diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt index 0edd51be2..56f50acbc 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt @@ -13,8 +13,8 @@ import space.kscience.kmath.structures.DoubleBuffer * Map one [BufferND] using function without indices. */ public inline fun BufferND.mapInline(crossinline transform: DoubleField.(Double) -> Double): BufferND { - val array = DoubleArray(indexes.linearSize) { offset -> DoubleField.transform(buffer[offset]) } - return BufferND(indexes, DoubleBuffer(array)) + val array = DoubleArray(indices.linearSize) { offset -> DoubleField.transform(buffer[offset]) } + return BufferND(indices, DoubleBuffer(array)) } /** diff --git a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt index ed15199a6..3bbe8e672 100644 --- a/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt +++ b/kmath-multik/src/main/kotlin/space/kscience/kmath/multik/MultikTensorAlgebra.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlinx.multik.ndarray.data.* import org.jetbrains.kotlinx.multik.ndarray.operations.* import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.nd.Shape +import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.mapInPlace import space.kscience.kmath.operations.* import space.kscience.kmath.tensors.api.Tensor @@ -49,17 +50,17 @@ private fun MultiArray.asD2Array(): D2Array { else throw ClassCastException("Cannot cast MultiArray to NDArray.") } -public class MultikTensorAlgebra internal constructor( +public class MultikTensorAlgebra> internal constructor( public val type: DataType, - public val elementAlgebra: Ring, + override val elementAlgebra: A, public val comparator: Comparator -) : TensorAlgebra { +) : TensorAlgebra { /** * Convert a tensor to [MultikTensor] if necessary. If tensor is converted, changes on the resulting tensor * are not reflected back onto the source */ - public fun Tensor.asMultik(): MultikTensor { + public fun StructureND.asMultik(): MultikTensor { return if (this is MultikTensor) { this } else { @@ -73,17 +74,17 @@ public class MultikTensorAlgebra internal constructor( public fun MutableMultiArray.wrap(): MultikTensor = MultikTensor(this) - override fun Tensor.valueOrNull(): T? = if (shape contentEquals intArrayOf(1)) { + override fun StructureND.valueOrNull(): T? = if (shape contentEquals intArrayOf(1)) { get(intArrayOf(0)) } else null - override fun T.plus(other: Tensor): MultikTensor = + override fun T.plus(other: StructureND): MultikTensor = other.plus(this) - override fun Tensor.plus(value: T): MultikTensor = + override fun StructureND.plus(value: T): MultikTensor = asMultik().array.deepCopy().apply { plusAssign(value) }.wrap() - override fun Tensor.plus(other: Tensor): MultikTensor = + override fun StructureND.plus(other: StructureND): MultikTensor = asMultik().array.plus(other.asMultik().array).wrap() override fun Tensor.plusAssign(value: T) { @@ -94,7 +95,7 @@ public class MultikTensorAlgebra internal constructor( } } - override fun Tensor.plusAssign(other: Tensor) { + override fun Tensor.plusAssign(other: StructureND) { if (this is MultikTensor) { array.plusAssign(other.asMultik().array) } else { @@ -102,12 +103,12 @@ public class MultikTensorAlgebra internal constructor( } } - override fun T.minus(other: Tensor): MultikTensor = (-(other.asMultik().array - this)).wrap() + override fun T.minus(other: StructureND): MultikTensor = (-(other.asMultik().array - this)).wrap() - override fun Tensor.minus(value: T): MultikTensor = - asMultik().array.deepCopy().apply { minusAssign(value) }.wrap() + override fun StructureND.minus(arg: T): MultikTensor = + asMultik().array.deepCopy().apply { minusAssign(arg) }.wrap() - override fun Tensor.minus(other: Tensor): MultikTensor = + override fun StructureND.minus(other: StructureND): MultikTensor = asMultik().array.minus(other.asMultik().array).wrap() override fun Tensor.minusAssign(value: T) { @@ -118,7 +119,7 @@ public class MultikTensorAlgebra internal constructor( } } - override fun Tensor.minusAssign(other: Tensor) { + override fun Tensor.minusAssign(other: StructureND) { if (this is MultikTensor) { array.minusAssign(other.asMultik().array) } else { @@ -126,13 +127,13 @@ public class MultikTensorAlgebra internal constructor( } } - override fun T.times(other: Tensor): MultikTensor = - other.asMultik().array.deepCopy().apply { timesAssign(this@times) }.wrap() + override fun T.times(arg: StructureND): MultikTensor = + arg.asMultik().array.deepCopy().apply { timesAssign(this@times) }.wrap() - override fun Tensor.times(value: T): Tensor = - asMultik().array.deepCopy().apply { timesAssign(value) }.wrap() + override fun StructureND.times(arg: T): Tensor = + asMultik().array.deepCopy().apply { timesAssign(arg) }.wrap() - override fun Tensor.times(other: Tensor): MultikTensor = + override fun StructureND.times(other: StructureND): MultikTensor = asMultik().array.times(other.asMultik().array).wrap() override fun Tensor.timesAssign(value: T) { @@ -143,7 +144,7 @@ public class MultikTensorAlgebra internal constructor( } } - override fun Tensor.timesAssign(other: Tensor) { + override fun Tensor.timesAssign(other: StructureND) { if (this is MultikTensor) { array.timesAssign(other.asMultik().array) } else { @@ -151,7 +152,7 @@ public class MultikTensorAlgebra internal constructor( } } - override fun Tensor.unaryMinus(): MultikTensor = + override fun StructureND.unaryMinus(): MultikTensor = asMultik().array.unaryMinus().wrap() override fun Tensor.get(i: Int): MultikTensor = asMultik().array.mutableView(i).wrap() @@ -224,17 +225,17 @@ public class MultikTensorAlgebra internal constructor( } } -public val DoubleField.multikTensorAlgebra: MultikTensorAlgebra +public val DoubleField.multikTensorAlgebra: MultikTensorAlgebra get() = MultikTensorAlgebra(DataType.DoubleDataType, DoubleField) { o1, o2 -> o1.compareTo(o2) } -public val FloatField.multikTensorAlgebra: MultikTensorAlgebra +public val FloatField.multikTensorAlgebra: MultikTensorAlgebra get() = MultikTensorAlgebra(DataType.FloatDataType, FloatField) { o1, o2 -> o1.compareTo(o2) } -public val ShortRing.multikTensorAlgebra: MultikTensorAlgebra +public val ShortRing.multikTensorAlgebra: MultikTensorAlgebra get() = MultikTensorAlgebra(DataType.ShortDataType, ShortRing) { o1, o2 -> o1.compareTo(o2) } -public val IntRing.multikTensorAlgebra: MultikTensorAlgebra +public val IntRing.multikTensorAlgebra: MultikTensorAlgebra get() = MultikTensorAlgebra(DataType.IntDataType, IntRing) { o1, o2 -> o1.compareTo(o2) } -public val LongRing.multikTensorAlgebra: MultikTensorAlgebra +public val LongRing.multikTensorAlgebra: MultikTensorAlgebra get() = MultikTensorAlgebra(DataType.LongDataType, LongRing) { o1, o2 -> o1.compareTo(o2) } \ No newline at end of file diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/AnalyticTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/AnalyticTensorAlgebra.kt index 851810c8d..caafcc7c1 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/AnalyticTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/AnalyticTensorAlgebra.kt @@ -5,13 +5,15 @@ package space.kscience.kmath.tensors.api +import space.kscience.kmath.operations.Field + /** * Analytic operations on [Tensor]. * * @param T the type of items closed under analytic functions in the tensors. */ -public interface AnalyticTensorAlgebra : TensorPartialDivisionAlgebra { +public interface AnalyticTensorAlgebra> : TensorPartialDivisionAlgebra { /** * @return the mean of all elements in the input tensor. diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt index 78aad2189..3f32eb9ca 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt @@ -5,12 +5,14 @@ package space.kscience.kmath.tensors.api +import space.kscience.kmath.operations.Field + /** * Common linear algebra operations. Operates on [Tensor]. * * @param T the type of items closed under division in the tensors. */ -public interface LinearOpsTensorAlgebra : TensorPartialDivisionAlgebra { +public interface LinearOpsTensorAlgebra> : TensorPartialDivisionAlgebra { /** * Computes the determinant of a square matrix input, or of each square matrix in a batched input. diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt index 810ebe777..e910c5c31 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt @@ -5,7 +5,9 @@ package space.kscience.kmath.tensors.api -import space.kscience.kmath.operations.RingOps +import space.kscience.kmath.nd.RingOpsND +import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.operations.Ring /** * Algebra over a ring on [Tensor]. @@ -13,20 +15,20 @@ import space.kscience.kmath.operations.RingOps * * @param T the type of items in the tensors. */ -public interface TensorAlgebra : RingOps> { +public interface TensorAlgebra> : RingOpsND { /** * Returns a single tensor value of unit dimension if tensor shape equals to [1]. * * @return a nullable value of a potentially scalar tensor. */ - public fun Tensor.valueOrNull(): T? + public fun StructureND.valueOrNull(): T? /** * Returns a single tensor value of unit dimension. The tensor shape must be equal to [1]. * * @return the value of a scalar tensor. */ - public fun Tensor.value(): T = + public fun StructureND.value(): T = valueOrNull() ?: throw IllegalArgumentException("Inconsistent value for tensor of with $shape shape") /** @@ -36,7 +38,7 @@ public interface TensorAlgebra : RingOps> { * @param other tensor to be added. * @return the sum of this value and tensor [other]. */ - public operator fun T.plus(other: Tensor): Tensor + override operator fun T.plus(other: StructureND): Tensor /** * Adds the scalar [value] to each element of this tensor and returns a new resulting tensor. @@ -44,7 +46,7 @@ public interface TensorAlgebra : RingOps> { * @param value the number to be added to each element of this tensor. * @return the sum of this tensor and [value]. */ - public operator fun Tensor.plus(value: T): Tensor + override operator fun StructureND.plus(value: T): Tensor /** * Each element of the tensor [other] is added to each element of this tensor. @@ -53,7 +55,7 @@ public interface TensorAlgebra : RingOps> { * @param other tensor to be added. * @return the sum of this tensor and [other]. */ - override fun Tensor.plus(other: Tensor): Tensor + override operator fun StructureND.plus(other: StructureND): Tensor /** * Adds the scalar [value] to each element of this tensor. @@ -67,7 +69,7 @@ public interface TensorAlgebra : RingOps> { * * @param other tensor to be added. */ - public operator fun Tensor.plusAssign(other: Tensor) + public operator fun Tensor.plusAssign(other: StructureND) /** * Each element of the tensor [other] is subtracted from this value. @@ -76,15 +78,15 @@ public interface TensorAlgebra : RingOps> { * @param other tensor to be subtracted. * @return the difference between this value and tensor [other]. */ - public operator fun T.minus(other: Tensor): Tensor + override operator fun T.minus(other: StructureND): Tensor /** - * Subtracts the scalar [value] from each element of this tensor and returns a new resulting tensor. + * Subtracts the scalar [arg] from each element of this tensor and returns a new resulting tensor. * - * @param value the number to be subtracted from each element of this tensor. - * @return the difference between this tensor and [value]. + * @param arg the number to be subtracted from each element of this tensor. + * @return the difference between this tensor and [arg]. */ - public operator fun Tensor.minus(value: T): Tensor + override operator fun StructureND.minus(arg: T): Tensor /** * Each element of the tensor [other] is subtracted from each element of this tensor. @@ -93,7 +95,7 @@ public interface TensorAlgebra : RingOps> { * @param other tensor to be subtracted. * @return the difference between this tensor and [other]. */ - override fun Tensor.minus(other: Tensor): Tensor + override operator fun StructureND.minus(other: StructureND): Tensor /** * Subtracts the scalar [value] from each element of this tensor. @@ -107,25 +109,25 @@ public interface TensorAlgebra : RingOps> { * * @param other tensor to be subtracted. */ - public operator fun Tensor.minusAssign(other: Tensor) + public operator fun Tensor.minusAssign(other: StructureND) /** - * Each element of the tensor [other] is multiplied by this value. + * Each element of the tensor [arg] is multiplied by this value. * The resulting tensor is returned. * - * @param other tensor to be multiplied. - * @return the product of this value and tensor [other]. + * @param arg tensor to be multiplied. + * @return the product of this value and tensor [arg]. */ - public operator fun T.times(other: Tensor): Tensor + override operator fun T.times(arg: StructureND): Tensor /** - * Multiplies the scalar [value] by each element of this tensor and returns a new resulting tensor. + * Multiplies the scalar [arg] by each element of this tensor and returns a new resulting tensor. * - * @param value the number to be multiplied by each element of this tensor. - * @return the product of this tensor and [value]. + * @param arg the number to be multiplied by each element of this tensor. + * @return the product of this tensor and [arg]. */ - public operator fun Tensor.times(value: T): Tensor + override operator fun StructureND.times(arg: T): Tensor /** * Each element of the tensor [other] is multiplied by each element of this tensor. @@ -134,7 +136,7 @@ public interface TensorAlgebra : RingOps> { * @param other tensor to be multiplied. * @return the product of this tensor and [other]. */ - override fun Tensor.times(other: Tensor): Tensor + override operator fun StructureND.times(other: StructureND): Tensor /** * Multiplies the scalar [value] by each element of this tensor. @@ -148,14 +150,14 @@ public interface TensorAlgebra : RingOps> { * * @param other tensor to be multiplied. */ - public operator fun Tensor.timesAssign(other: Tensor) + public operator fun Tensor.timesAssign(other: StructureND) /** * Numerical negative, element-wise. * * @return tensor negation of the original tensor. */ - override fun Tensor.unaryMinus(): Tensor + override operator fun StructureND.unaryMinus(): Tensor /** * Returns the tensor at index i @@ -324,7 +326,7 @@ public interface TensorAlgebra : RingOps> { */ public fun Tensor.argMax(dim: Int, keepDim: Boolean): Tensor - override fun add(left: Tensor, right: Tensor): Tensor = left + right + override fun add(left: StructureND, right: StructureND): Tensor = left + right - override fun multiply(left: Tensor, right: Tensor): Tensor = left * right + override fun multiply(left: StructureND, right: StructureND): Tensor = left * right } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorPartialDivisionAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorPartialDivisionAlgebra.kt index ce519288b..43c901ed5 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorPartialDivisionAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorPartialDivisionAlgebra.kt @@ -5,30 +5,34 @@ package space.kscience.kmath.tensors.api +import space.kscience.kmath.nd.FieldOpsND +import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.operations.Field + /** * Algebra over a field with partial division on [Tensor]. * For more information: https://proofwiki.org/wiki/Definition:Division_Algebra * * @param T the type of items closed under division in the tensors. */ -public interface TensorPartialDivisionAlgebra : TensorAlgebra { +public interface TensorPartialDivisionAlgebra> : TensorAlgebra, FieldOpsND { /** - * Each element of the tensor [other] is divided by this value. + * Each element of the tensor [arg] is divided by this value. * The resulting tensor is returned. * - * @param other tensor to divide by. - * @return the division of this value by the tensor [other]. + * @param arg tensor to divide by. + * @return the division of this value by the tensor [arg]. */ - public operator fun T.div(other: Tensor): Tensor + override operator fun T.div(arg: StructureND): Tensor /** - * Divide by the scalar [value] each element of this tensor returns a new resulting tensor. + * Divide by the scalar [arg] each element of this tensor returns a new resulting tensor. * - * @param value the number to divide by each element of this tensor. - * @return the division of this tensor by the [value]. + * @param arg the number to divide by each element of this tensor. + * @return the division of this tensor by the [arg]. */ - public operator fun Tensor.div(value: T): Tensor + override operator fun StructureND.div(arg: T): Tensor /** * Each element of the tensor [other] is divided by each element of this tensor. @@ -37,7 +41,9 @@ public interface TensorPartialDivisionAlgebra : TensorAlgebra { * @param other tensor to be divided by. * @return the division of this tensor by [other]. */ - public operator fun Tensor.div(other: Tensor): Tensor + override operator fun StructureND.div(other: StructureND): Tensor + + override fun divide(left: StructureND, right: StructureND): StructureND = left.div(right) /** * Divides by the scalar [value] each element of this tensor. diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt index 3d73fd53b..f3c5d2540 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.nd.StructureND import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.internal.array import space.kscience.kmath.tensors.core.internal.broadcastTensors @@ -18,66 +19,66 @@ import space.kscience.kmath.tensors.core.internal.tensor */ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { - override fun Tensor.plus(other: Tensor): DoubleTensor { + override fun StructureND.plus(other: StructureND): DoubleTensor { val broadcast = broadcastTensors(tensor, other.tensor) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleArray(newThis.linearStructure.linearSize) { i -> + val resBuffer = DoubleArray(newThis.indices.linearSize) { i -> newThis.mutableBuffer.array()[i] + newOther.mutableBuffer.array()[i] } return DoubleTensor(newThis.shape, resBuffer) } - override fun Tensor.plusAssign(other: Tensor) { + override fun Tensor.plusAssign(other: StructureND) { val newOther = broadcastTo(other.tensor, tensor.shape) - for (i in 0 until tensor.linearStructure.linearSize) { + for (i in 0 until tensor.indices.linearSize) { tensor.mutableBuffer.array()[tensor.bufferStart + i] += newOther.mutableBuffer.array()[tensor.bufferStart + i] } } - override fun Tensor.minus(other: Tensor): DoubleTensor { + override fun StructureND.minus(other: StructureND): DoubleTensor { val broadcast = broadcastTensors(tensor, other.tensor) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleArray(newThis.linearStructure.linearSize) { i -> + val resBuffer = DoubleArray(newThis.indices.linearSize) { i -> newThis.mutableBuffer.array()[i] - newOther.mutableBuffer.array()[i] } return DoubleTensor(newThis.shape, resBuffer) } - override fun Tensor.minusAssign(other: Tensor) { + override fun Tensor.minusAssign(other: StructureND) { val newOther = broadcastTo(other.tensor, tensor.shape) - for (i in 0 until tensor.linearStructure.linearSize) { + for (i in 0 until tensor.indices.linearSize) { tensor.mutableBuffer.array()[tensor.bufferStart + i] -= newOther.mutableBuffer.array()[tensor.bufferStart + i] } } - override fun Tensor.times(other: Tensor): DoubleTensor { + override fun StructureND.times(other: StructureND): DoubleTensor { val broadcast = broadcastTensors(tensor, other.tensor) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleArray(newThis.linearStructure.linearSize) { i -> + val resBuffer = DoubleArray(newThis.indices.linearSize) { i -> newThis.mutableBuffer.array()[newThis.bufferStart + i] * newOther.mutableBuffer.array()[newOther.bufferStart + i] } return DoubleTensor(newThis.shape, resBuffer) } - override fun Tensor.timesAssign(other: Tensor) { + override fun Tensor.timesAssign(other: StructureND) { val newOther = broadcastTo(other.tensor, tensor.shape) - for (i in 0 until tensor.linearStructure.linearSize) { + for (i in 0 until tensor.indices.linearSize) { tensor.mutableBuffer.array()[tensor.bufferStart + i] *= newOther.mutableBuffer.array()[tensor.bufferStart + i] } } - override fun Tensor.div(other: Tensor): DoubleTensor { + override fun StructureND.div(other: StructureND): DoubleTensor { val broadcast = broadcastTensors(tensor, other.tensor) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleArray(newThis.linearStructure.linearSize) { i -> + val resBuffer = DoubleArray(newThis.indices.linearSize) { i -> newThis.mutableBuffer.array()[newOther.bufferStart + i] / newOther.mutableBuffer.array()[newOther.bufferStart + i] } @@ -86,7 +87,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { override fun Tensor.divAssign(other: Tensor) { val newOther = broadcastTo(other.tensor, tensor.shape) - for (i in 0 until tensor.linearStructure.linearSize) { + for (i in 0 until tensor.indices.linearSize) { tensor.mutableBuffer.array()[tensor.bufferStart + i] /= newOther.mutableBuffer.array()[tensor.bufferStart + i] } 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 bf9a9f7f7..ba3331067 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 @@ -23,23 +23,23 @@ public open class BufferedTensor internal constructor( /** * Buffer strides based on [TensorLinearStructure] implementation */ - public val linearStructure: Strides + public val indices: Strides get() = TensorLinearStructure(shape) /** * Number of elements in tensor */ public val numElements: Int - get() = linearStructure.linearSize + get() = indices.linearSize - override fun get(index: IntArray): T = mutableBuffer[bufferStart + linearStructure.offset(index)] + override fun get(index: IntArray): T = mutableBuffer[bufferStart + indices.offset(index)] override fun set(index: IntArray, value: T) { - mutableBuffer[bufferStart + linearStructure.offset(index)] = value + mutableBuffer[bufferStart + indices.offset(index)] = value } @PerformancePitfall - override fun elements(): Sequence> = linearStructure.indices().map { + override fun elements(): Sequence> = indices.indices().map { it to get(it) } } 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 16ed4b834..472db8f5f 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 @@ -6,8 +6,10 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.nd.MutableStructure2D +import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.as1D import space.kscience.kmath.nd.as2D +import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.structures.indices import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra @@ -20,16 +22,71 @@ import kotlin.math.* * Implementation of basic operations over double tensors and basic algebra operations on them. */ public open class DoubleTensorAlgebra : - TensorPartialDivisionAlgebra, - AnalyticTensorAlgebra, - LinearOpsTensorAlgebra{ + TensorPartialDivisionAlgebra, + AnalyticTensorAlgebra, + LinearOpsTensorAlgebra { public companion object : DoubleTensorAlgebra() - override fun Tensor.valueOrNull(): Double? = if (tensor.shape contentEquals intArrayOf(1)) + override val elementAlgebra: DoubleField + get() = DoubleField + + + /** + * Applies the [transform] function to each element of the tensor and returns the resulting modified tensor. + * + * @param transform the function to be applied to each element of the tensor. + * @return the resulting tensor after applying the function. + */ + @Suppress("OVERRIDE_BY_INLINE") + final override inline fun StructureND.map(transform: DoubleField.(Double) -> Double): DoubleTensor { + val tensor = this.tensor + //TODO remove additional copy + val sourceArray = tensor.copyArray() + val array = DoubleArray(tensor.numElements) { DoubleField.transform(sourceArray[it]) } + return DoubleTensor( + tensor.shape, + array, + tensor.bufferStart + ) + } + + @Suppress("OVERRIDE_BY_INLINE") + final override inline fun StructureND.mapIndexed(transform: DoubleField.(index: IntArray, Double) -> Double): DoubleTensor { + val tensor = this.tensor + //TODO remove additional copy + val sourceArray = tensor.copyArray() + val array = DoubleArray(tensor.numElements) { DoubleField.transform(tensor.indices.index(it), sourceArray[it]) } + return DoubleTensor( + tensor.shape, + array, + tensor.bufferStart + ) + } + + override fun zip( + left: StructureND, + right: StructureND, + transform: DoubleField.(Double, Double) -> Double + ): DoubleTensor { + require(left.shape.contentEquals(right.shape)){ + "The shapes in zip are not equal: left - ${left.shape}, right - ${right.shape}" + } + val leftTensor = left.tensor + val leftArray = leftTensor.copyArray() + val rightTensor = right.tensor + val rightArray = rightTensor.copyArray() + val array = DoubleArray(leftTensor.numElements) { DoubleField.transform(leftArray[it], rightArray[it]) } + return DoubleTensor( + leftTensor.shape, + array + ) + } + + override fun StructureND.valueOrNull(): Double? = if (tensor.shape contentEquals intArrayOf(1)) tensor.mutableBuffer.array()[tensor.bufferStart] else null - override fun Tensor.value(): Double = valueOrNull() + override fun StructureND.value(): Double = valueOrNull() ?: throw IllegalArgumentException("The tensor shape is $shape, but value method is allowed only for shape [1]") /** @@ -53,11 +110,10 @@ public open class DoubleTensorAlgebra : * @param initializer mapping tensor indices to values. * @return tensor with the [shape] shape and data generated by the [initializer]. */ - public fun produce(shape: IntArray, initializer: (IntArray) -> Double): DoubleTensor = - fromArray( - shape, - TensorLinearStructure(shape).indices().map(initializer).toMutableList().toDoubleArray() - ) + override fun structureND(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): DoubleTensor = fromArray( + shape, + TensorLinearStructure(shape).indices().map { DoubleField.initializer(it) }.toMutableList().toDoubleArray() + ) override operator fun Tensor.get(i: Int): DoubleTensor { val lastShape = tensor.shape.drop(1).toIntArray() @@ -146,16 +202,16 @@ public open class DoubleTensorAlgebra : return DoubleTensor(tensor.shape, tensor.mutableBuffer.array().copyOf(), tensor.bufferStart) } - override fun Double.plus(other: Tensor): DoubleTensor { + override fun Double.plus(other: StructureND): DoubleTensor { val resBuffer = DoubleArray(other.tensor.numElements) { i -> other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] + this } return DoubleTensor(other.shape, resBuffer) } - override fun Tensor.plus(value: Double): DoubleTensor = value + tensor + override fun StructureND.plus(value: Double): DoubleTensor = value + tensor - override fun Tensor.plus(other: Tensor): DoubleTensor { + override fun StructureND.plus(other: StructureND): DoubleTensor { checkShapesCompatible(tensor, other.tensor) val resBuffer = DoubleArray(tensor.numElements) { i -> tensor.mutableBuffer.array()[i] + other.tensor.mutableBuffer.array()[i] @@ -169,7 +225,7 @@ public open class DoubleTensorAlgebra : } } - override fun Tensor.plusAssign(other: Tensor) { + override fun Tensor.plusAssign(other: StructureND) { checkShapesCompatible(tensor, other.tensor) for (i in 0 until tensor.numElements) { tensor.mutableBuffer.array()[tensor.bufferStart + i] += @@ -177,21 +233,21 @@ public open class DoubleTensorAlgebra : } } - override fun Double.minus(other: Tensor): DoubleTensor { + override fun Double.minus(other: StructureND): DoubleTensor { val resBuffer = DoubleArray(other.tensor.numElements) { i -> this - other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] } return DoubleTensor(other.shape, resBuffer) } - override fun Tensor.minus(value: Double): DoubleTensor { + override fun StructureND.minus(arg: Double): DoubleTensor { val resBuffer = DoubleArray(tensor.numElements) { i -> - tensor.mutableBuffer.array()[tensor.bufferStart + i] - value + tensor.mutableBuffer.array()[tensor.bufferStart + i] - arg } return DoubleTensor(tensor.shape, resBuffer) } - override fun Tensor.minus(other: Tensor): DoubleTensor { + override fun StructureND.minus(other: StructureND): DoubleTensor { checkShapesCompatible(tensor, other) val resBuffer = DoubleArray(tensor.numElements) { i -> tensor.mutableBuffer.array()[i] - other.tensor.mutableBuffer.array()[i] @@ -205,7 +261,7 @@ public open class DoubleTensorAlgebra : } } - override fun Tensor.minusAssign(other: Tensor) { + override fun Tensor.minusAssign(other: StructureND) { checkShapesCompatible(tensor, other) for (i in 0 until tensor.numElements) { tensor.mutableBuffer.array()[tensor.bufferStart + i] -= @@ -213,16 +269,16 @@ public open class DoubleTensorAlgebra : } } - override fun Double.times(other: Tensor): DoubleTensor { - val resBuffer = DoubleArray(other.tensor.numElements) { i -> - other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] * this + override fun Double.times(arg: StructureND): DoubleTensor { + val resBuffer = DoubleArray(arg.tensor.numElements) { i -> + arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i] * this } - return DoubleTensor(other.shape, resBuffer) + return DoubleTensor(arg.shape, resBuffer) } - override fun Tensor.times(value: Double): DoubleTensor = value * tensor + override fun StructureND.times(arg: Double): DoubleTensor = arg * tensor - override fun Tensor.times(other: Tensor): DoubleTensor { + override fun StructureND.times(other: StructureND): DoubleTensor { checkShapesCompatible(tensor, other) val resBuffer = DoubleArray(tensor.numElements) { i -> tensor.mutableBuffer.array()[tensor.bufferStart + i] * @@ -237,7 +293,7 @@ public open class DoubleTensorAlgebra : } } - override fun Tensor.timesAssign(other: Tensor) { + override fun Tensor.timesAssign(other: StructureND) { checkShapesCompatible(tensor, other) for (i in 0 until tensor.numElements) { tensor.mutableBuffer.array()[tensor.bufferStart + i] *= @@ -245,21 +301,21 @@ public open class DoubleTensorAlgebra : } } - override fun Double.div(other: Tensor): DoubleTensor { - val resBuffer = DoubleArray(other.tensor.numElements) { i -> - this / other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] + override fun Double.div(arg: StructureND): DoubleTensor { + val resBuffer = DoubleArray(arg.tensor.numElements) { i -> + this / arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i] } - return DoubleTensor(other.shape, resBuffer) + return DoubleTensor(arg.shape, resBuffer) } - override fun Tensor.div(value: Double): DoubleTensor { + override fun StructureND.div(arg: Double): DoubleTensor { val resBuffer = DoubleArray(tensor.numElements) { i -> - tensor.mutableBuffer.array()[tensor.bufferStart + i] / value + tensor.mutableBuffer.array()[tensor.bufferStart + i] / arg } return DoubleTensor(shape, resBuffer) } - override fun Tensor.div(other: Tensor): DoubleTensor { + override fun StructureND.div(other: StructureND): DoubleTensor { checkShapesCompatible(tensor, other) val resBuffer = DoubleArray(tensor.numElements) { i -> tensor.mutableBuffer.array()[other.tensor.bufferStart + i] / @@ -282,7 +338,7 @@ public open class DoubleTensorAlgebra : } } - override fun Tensor.unaryMinus(): DoubleTensor { + override fun StructureND.unaryMinus(): DoubleTensor { val resBuffer = DoubleArray(tensor.numElements) { i -> tensor.mutableBuffer.array()[tensor.bufferStart + i].unaryMinus() } @@ -302,11 +358,11 @@ public open class DoubleTensorAlgebra : val resTensor = DoubleTensor(resShape, resBuffer) for (offset in 0 until n) { - val oldMultiIndex = tensor.linearStructure.index(offset) + val oldMultiIndex = tensor.indices.index(offset) val newMultiIndex = oldMultiIndex.copyOf() newMultiIndex[ii] = newMultiIndex[jj].also { newMultiIndex[jj] = newMultiIndex[ii] } - val linearIndex = resTensor.linearStructure.offset(newMultiIndex) + val linearIndex = resTensor.indices.offset(newMultiIndex) resTensor.mutableBuffer.array()[linearIndex] = tensor.mutableBuffer.array()[tensor.bufferStart + offset] } @@ -406,7 +462,7 @@ public open class DoubleTensorAlgebra : val resTensor = zeros(resShape) for (i in 0 until diagonalEntries.tensor.numElements) { - val multiIndex = diagonalEntries.tensor.linearStructure.index(i) + val multiIndex = diagonalEntries.tensor.indices.index(i) var offset1 = 0 var offset2 = abs(realOffset) @@ -425,18 +481,6 @@ public open class DoubleTensorAlgebra : return resTensor.tensor } - /** - * Applies the [transform] function to each element of the tensor and returns the resulting modified tensor. - * - * @param transform the function to be applied to each element of the tensor. - * @return the resulting tensor after applying the function. - */ - public inline fun Tensor.map(transform: (Double) -> Double): DoubleTensor = DoubleTensor( - tensor.shape, - tensor.mutableBuffer.array().map { transform(it) }.toDoubleArray(), - tensor.bufferStart - ) - /** * Compares element-wise two tensors with a specified precision. * @@ -526,7 +570,7 @@ public open class DoubleTensorAlgebra : public fun Tensor.rowsByIndices(indices: IntArray): DoubleTensor = stack(indices.map { this[it] }) internal inline fun Tensor.fold(foldFunction: (DoubleArray) -> Double): Double = - foldFunction(tensor.toDoubleArray()) + foldFunction(tensor.copyArray()) internal inline fun Tensor.foldDim( foldFunction: (DoubleArray) -> Double, @@ -541,7 +585,7 @@ public open class DoubleTensorAlgebra : } val resNumElements = resShape.reduce(Int::times) val resTensor = DoubleTensor(resShape, DoubleArray(resNumElements) { 0.0 }, 0) - for (index in resTensor.linearStructure.indices()) { + for (index in resTensor.indices.indices()) { val prefix = index.take(dim).toIntArray() val suffix = index.takeLast(dimension - dim - 1).toIntArray() resTensor[index] = foldFunction(DoubleArray(shape[dim]) { i -> @@ -645,39 +689,39 @@ public open class DoubleTensorAlgebra : return resTensor } - override fun Tensor.exp(): DoubleTensor = tensor.map(::exp) + override fun Tensor.exp(): DoubleTensor = tensor.map { exp(it) } - override fun Tensor.ln(): DoubleTensor = tensor.map(::ln) + override fun Tensor.ln(): DoubleTensor = tensor.map { ln(it) } - override fun Tensor.sqrt(): DoubleTensor = tensor.map(::sqrt) + override fun Tensor.sqrt(): DoubleTensor = tensor.map { sqrt(it) } - override fun Tensor.cos(): DoubleTensor = tensor.map(::cos) + override fun Tensor.cos(): DoubleTensor = tensor.map { cos(it) } - override fun Tensor.acos(): DoubleTensor = tensor.map(::acos) + override fun Tensor.acos(): DoubleTensor = tensor.map { acos(it) } - override fun Tensor.cosh(): DoubleTensor = tensor.map(::cosh) + override fun Tensor.cosh(): DoubleTensor = tensor.map { cosh(it) } - override fun Tensor.acosh(): DoubleTensor = tensor.map(::acosh) + override fun Tensor.acosh(): DoubleTensor = tensor.map { acosh(it) } - override fun Tensor.sin(): DoubleTensor = tensor.map(::sin) + override fun Tensor.sin(): DoubleTensor = tensor.map { sin(it) } - override fun Tensor.asin(): DoubleTensor = tensor.map(::asin) + override fun Tensor.asin(): DoubleTensor = tensor.map { asin(it) } - override fun Tensor.sinh(): DoubleTensor = tensor.map(::sinh) + override fun Tensor.sinh(): DoubleTensor = tensor.map { sinh(it) } - override fun Tensor.asinh(): DoubleTensor = tensor.map(::asinh) + override fun Tensor.asinh(): DoubleTensor = tensor.map { asinh(it) } - override fun Tensor.tan(): DoubleTensor = tensor.map(::tan) + override fun Tensor.tan(): DoubleTensor = tensor.map { tan(it) } - override fun Tensor.atan(): DoubleTensor = tensor.map(::atan) + override fun Tensor.atan(): DoubleTensor = tensor.map { atan(it) } - override fun Tensor.tanh(): DoubleTensor = tensor.map(::tanh) + override fun Tensor.tanh(): DoubleTensor = tensor.map { tanh(it) } - override fun Tensor.atanh(): DoubleTensor = tensor.map(::atanh) + override fun Tensor.atanh(): DoubleTensor = tensor.map { atanh(it) } - override fun Tensor.ceil(): DoubleTensor = tensor.map(::ceil) + override fun Tensor.ceil(): DoubleTensor = tensor.map { ceil(it) } - override fun Tensor.floor(): DoubleTensor = tensor.map(::floor) + override fun Tensor.floor(): DoubleTensor = tensor.map { floor(it) } override fun Tensor.inv(): DoubleTensor = invLU(1e-9) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt index 4b9c0c382..3787c0972 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt @@ -10,7 +10,7 @@ import kotlin.math.max internal fun multiIndexBroadCasting(tensor: DoubleTensor, resTensor: DoubleTensor, linearSize: Int) { for (linearIndex in 0 until linearSize) { - val totalMultiIndex = resTensor.linearStructure.index(linearIndex) + val totalMultiIndex = resTensor.indices.index(linearIndex) val curMultiIndex = tensor.shape.copyOf() val offset = totalMultiIndex.size - curMultiIndex.size @@ -23,7 +23,7 @@ internal fun multiIndexBroadCasting(tensor: DoubleTensor, resTensor: DoubleTenso } } - val curLinearIndex = tensor.linearStructure.offset(curMultiIndex) + val curLinearIndex = tensor.indices.offset(curMultiIndex) resTensor.mutableBuffer.array()[linearIndex] = tensor.mutableBuffer.array()[tensor.bufferStart + curLinearIndex] } @@ -112,7 +112,7 @@ internal fun broadcastOuterTensors(vararg tensors: DoubleTensor): List checkShapesCompatible(a: Tensor, b: Tensor) = +internal fun checkShapesCompatible(a: StructureND, b: StructureND) = check(a.shape contentEquals b.shape) { "Incompatible shapes ${a.shape.toList()} and ${b.shape.toList()} " } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt index 1f5778d5e..4123ff923 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.tensors.core.internal import space.kscience.kmath.nd.MutableBufferND +import space.kscience.kmath.nd.StructureND import space.kscience.kmath.structures.asMutableBuffer import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.BufferedTensor @@ -18,15 +19,15 @@ internal fun BufferedTensor.asTensor(): IntTensor = internal fun BufferedTensor.asTensor(): DoubleTensor = DoubleTensor(this.shape, this.mutableBuffer.array(), this.bufferStart) -internal fun Tensor.copyToBufferedTensor(): BufferedTensor = +internal fun StructureND.copyToBufferedTensor(): BufferedTensor = BufferedTensor( this.shape, TensorLinearStructure(this.shape).indices().map(this::get).toMutableList().asMutableBuffer(), 0 ) -internal fun Tensor.toBufferedTensor(): BufferedTensor = when (this) { +internal fun StructureND.toBufferedTensor(): BufferedTensor = when (this) { is BufferedTensor -> this - is MutableBufferND -> if (this.indexes == TensorLinearStructure(this.shape)) { + is MutableBufferND -> if (this.indices == TensorLinearStructure(this.shape)) { BufferedTensor(this.shape, this.buffer, 0) } else { this.copyToBufferedTensor() @@ -35,7 +36,7 @@ internal fun Tensor.toBufferedTensor(): BufferedTensor = when (this) { } @PublishedApi -internal val Tensor.tensor: DoubleTensor +internal val StructureND.tensor: DoubleTensor get() = when (this) { is DoubleTensor -> this else -> this.toBufferedTensor().asTensor() 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 8428dae5c..553ed6add 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 @@ -85,7 +85,7 @@ internal fun format(value: Double, digits: Int = 4): String = buildString { internal fun DoubleTensor.toPrettyString(): String = buildString { var offset = 0 val shape = this@toPrettyString.shape - val linearStructure = this@toPrettyString.linearStructure + val linearStructure = this@toPrettyString.indices val vectorSize = shape.last() append("DoubleTensor(\n") var charOffset = 3 diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorCasts.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorCasts.kt index 021ca539c..feade56de 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorCasts.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorCasts.kt @@ -19,18 +19,19 @@ public fun Tensor.toDoubleTensor(): DoubleTensor = this.tensor public fun Tensor.toIntTensor(): IntTensor = this.tensor /** - * Returns [DoubleArray] of tensor elements + * Returns a copy-protected [DoubleArray] of tensor elements */ -public fun DoubleTensor.toDoubleArray(): DoubleArray { +public fun DoubleTensor.copyArray(): DoubleArray { + //TODO use ArrayCopy return DoubleArray(numElements) { i -> mutableBuffer[bufferStart + i] } } /** - * Returns [IntArray] of tensor elements + * Returns a copy-protected [IntArray] of tensor elements */ -public fun IntTensor.toIntArray(): IntArray { +public fun IntTensor.copyArray(): IntArray { return IntArray(numElements) { i -> mutableBuffer[bufferStart + i] }