From 274be613302d8cdef5f8a81fc8aeee5c88bf8ccd Mon Sep 17 00:00:00 2001 From: Roland Grinis Date: Fri, 19 Mar 2021 19:40:17 +0000 Subject: [PATCH] Explicit broadcasting enforced --- .../kmath/tensors/LinearOpsTensorAlgebra.kt | 2 +- .../BroadcastDoubleTensorAlgebra.kt} | 151 ++++++++++-------- .../tensors/{ => core}/BufferedTensor.kt | 12 +- .../{ => core}/DoubleAnalyticTensorAlgebra.kt | 4 +- .../DoubleLinearOpsTensorAlgebra.kt | 4 +- .../{ => core}/DoubleOrderedTensorAlgebra.kt | 4 +- .../DoubleReduceOpsTensorAlgebra.kt | 6 +- .../tensors/{ => core}/DoubleTensorAlgebra.kt | 107 +++++++------ .../kscience/kmath/tensors/core/checks.kt | 67 ++++++++ .../kscience/kmath/tensors/core/utils.kt | 36 +++++ .../kmath/tensors/TestDoubleTensorAlgebra.kt | 105 ------------ .../kmath/tensors/core/TestBroadcasting.kt | 82 ++++++++++ .../tensors/{ => core}/TestDoubleTensor.kt | 10 +- .../tensors/core/TestDoubleTensorAlgebra.kt | 50 ++++++ 14 files changed, 407 insertions(+), 233 deletions(-) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/{utils.kt => core/BroadcastDoubleTensorAlgebra.kt} (50%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/{ => core}/BufferedTensor.kt (88%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/{ => core}/DoubleAnalyticTensorAlgebra.kt (96%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/{ => core}/DoubleLinearOpsTensorAlgebra.kt (96%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/{ => core}/DoubleOrderedTensorAlgebra.kt (89%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/{ => core}/DoubleReduceOpsTensorAlgebra.kt (68%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/{ => core}/DoubleTensorAlgebra.kt (77%) create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/checks.kt create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/utils.kt delete mode 100644 kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/TestDoubleTensorAlgebra.kt create mode 100644 kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestBroadcasting.kt rename kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/{ => core}/TestDoubleTensor.kt (74%) create mode 100644 kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/LinearOpsTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/LinearOpsTensorAlgebra.kt index 2e1c4a92c..94176564c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/LinearOpsTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/LinearOpsTensorAlgebra.kt @@ -17,7 +17,7 @@ public interface LinearOpsTensorAlgebra, Inde public fun TensorType.lu(): Pair //https://pytorch.org/docs/stable/generated/torch.lu_unpack.html - public fun luPivot(lu: TensorType, pivots: IntTensor): Triple + public fun luPivot(lu: TensorType, pivots: IndexTensorType): Triple //https://pytorch.org/docs/stable/linalg.html#torch.linalg.svd public fun TensorType.svd(): Triple diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/utils.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt similarity index 50% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/utils.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt index e7e043463..a4767a612 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/utils.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt @@ -1,10 +1,91 @@ -package space.kscience.kmath.tensors +package space.kscience.kmath.tensors.core -import space.kscience.kmath.structures.* import kotlin.math.max +public class BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { + + override fun DoubleTensor.plus(other: DoubleTensor): DoubleTensor { + val broadcast = broadcastTensors(this, other) + val newThis = broadcast[0] + val newOther = broadcast[1] + val resBuffer = DoubleArray(newThis.strides.linearSize) { i -> + newThis.buffer.array()[i] + newOther.buffer.array()[i] + } + return DoubleTensor(newThis.shape, resBuffer) + } + + override fun DoubleTensor.plusAssign(other: DoubleTensor) { + val newOther = broadcastTo(other, this.shape) + for (i in 0 until this.strides.linearSize) { + this.buffer.array()[this.bufferStart + i] += + newOther.buffer.array()[this.bufferStart + i] + } + } + + override fun DoubleTensor.minus(other: DoubleTensor): DoubleTensor { + val broadcast = broadcastTensors(this, other) + val newThis = broadcast[0] + val newOther = broadcast[1] + val resBuffer = DoubleArray(newThis.strides.linearSize) { i -> + newThis.buffer.array()[i] - newOther.buffer.array()[i] + } + return DoubleTensor(newThis.shape, resBuffer) + } + + override fun DoubleTensor.minusAssign(other: DoubleTensor) { + val newOther = broadcastTo(other, this.shape) + for (i in 0 until this.strides.linearSize) { + this.buffer.array()[this.bufferStart + i] -= + newOther.buffer.array()[this.bufferStart + i] + } + } + + override fun DoubleTensor.times(other: DoubleTensor): DoubleTensor { + val broadcast = broadcastTensors(this, other) + val newThis = broadcast[0] + val newOther = broadcast[1] + val resBuffer = DoubleArray(newThis.strides.linearSize) { i -> + newThis.buffer.array()[newOther.bufferStart + i] * + newOther.buffer.array()[newOther.bufferStart + i] + } + return DoubleTensor(newThis.shape, resBuffer) + } + + override fun DoubleTensor.timesAssign(other: DoubleTensor) { + val newOther = broadcastTo(other, this.shape) + for (i in 0 until this.strides.linearSize) { + this.buffer.array()[this.bufferStart + i] *= + newOther.buffer.array()[this.bufferStart + i] + } + } + + override fun DoubleTensor.div(other: DoubleTensor): DoubleTensor { + val broadcast = broadcastTensors(this, other) + val newThis = broadcast[0] + val newOther = broadcast[1] + val resBuffer = DoubleArray(newThis.strides.linearSize) { i -> + newThis.buffer.array()[newOther.bufferStart + i] / + newOther.buffer.array()[newOther.bufferStart + i] + } + return DoubleTensor(newThis.shape, resBuffer) + } + + override fun DoubleTensor.divAssign(other: DoubleTensor) { + val newOther = broadcastTo(other, this.shape) + for (i in 0 until this.strides.linearSize) { + this.buffer.array()[this.bufferStart + i] /= + newOther.buffer.array()[this.bufferStart + i] + } + } + +} + +public inline fun broadcastDoubleTensorAlgebra(block: BroadcastDoubleTensorAlgebra.() -> R): R = + BroadcastDoubleTensorAlgebra().block() + internal inline fun broadcastShapes(vararg shapes: IntArray): IntArray { + println(shapes) var totalDim = 0 for (shape in shapes) { totalDim = max(totalDim, shape.size) @@ -99,68 +180,4 @@ internal inline fun broadcastTensors(vararg tensors: DoubleTensor): List, - TorchTensorAlgebraType : TensorAlgebra> - TorchTensorAlgebraType.checkDot(a: TensorType, b: TensorType): Unit { - val sa = a.shape - val sb = b.shape - val na = sa.size - val nb = sb.size - var status: Boolean - if (nb == 1) { - status = sa.last() == sb[0] - } else { - status = sa.last() == sb[nb - 2] - if ((na > 2) and (nb > 2)) { - status = status and - (sa.take(nb - 2).toIntArray() contentEquals sb.take(nb - 2).toIntArray()) - } - } - check(status) { "Incompatible shapes $sa and $sb for dot product" } -} - -internal inline fun , - TorchTensorAlgebraType : TensorAlgebra> - TorchTensorAlgebraType.checkTranspose(dim: Int, i: Int, j: Int): Unit = - check((i < dim) and (j < dim)) { - "Cannot transpose $i to $j for a tensor of dim $dim" - } - -internal inline fun , - TorchTensorAlgebraType : TensorAlgebra> - TorchTensorAlgebraType.checkView(a: TensorType, shape: IntArray): Unit = - check(a.shape.reduce(Int::times) == shape.reduce(Int::times)) - -/** - * Returns a reference to [IntArray] containing all of the elements of this [Buffer]. - */ -internal fun Buffer.array(): IntArray = when(this) { - is IntBuffer -> array - else -> throw RuntimeException("Failed to cast Buffer to IntArray") -} - -/** - * Returns a reference to [LongArray] containing all of the elements of this [Buffer]. - */ -internal fun Buffer.array(): LongArray = when(this) { - is LongBuffer -> array - else -> throw RuntimeException("Failed to cast Buffer to LongArray") -} - -/** - * Returns a reference to [FloatArray] containing all of the elements of this [Buffer]. - */ -internal fun Buffer.array(): FloatArray = when(this) { - is FloatBuffer -> array - else -> throw RuntimeException("Failed to cast Buffer to FloatArray") -} - -/** - * Returns a reference to [DoubleArray] containing all of the elements of this [Buffer]. - */ -internal fun Buffer.array(): DoubleArray = when(this) { - is RealBuffer -> array - else -> throw RuntimeException("Failed to cast Buffer to DoubleArray") -} +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/BufferedTensor.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt similarity index 88% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/BufferedTensor.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt index c48e47f4c..cbfb15be0 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/BufferedTensor.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt @@ -1,8 +1,10 @@ -package space.kscience.kmath.tensors +package space.kscience.kmath.tensors.core import space.kscience.kmath.linear.Matrix import space.kscience.kmath.nd.* import space.kscience.kmath.structures.* +import space.kscience.kmath.tensors.TensorStrides +import space.kscience.kmath.tensors.TensorStructure public open class BufferedTensor( @@ -76,25 +78,25 @@ public open class BufferedTensor( } -public class IntTensor( +public class IntTensor internal constructor( shape: IntArray, buffer: IntArray, offset: Int = 0 ) : BufferedTensor(shape, IntBuffer(buffer), offset) -public class LongTensor( +public class LongTensor internal constructor( shape: IntArray, buffer: LongArray, offset: Int = 0 ) : BufferedTensor(shape, LongBuffer(buffer), offset) -public class FloatTensor( +public class FloatTensor internal constructor( shape: IntArray, buffer: FloatArray, offset: Int = 0 ) : BufferedTensor(shape, FloatBuffer(buffer), offset) -public class DoubleTensor( +public class DoubleTensor internal constructor( shape: IntArray, buffer: DoubleArray, offset: Int = 0 diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleAnalyticTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleAnalyticTensorAlgebra.kt similarity index 96% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleAnalyticTensorAlgebra.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleAnalyticTensorAlgebra.kt index 5349a9923..a34fe4b6c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleAnalyticTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleAnalyticTensorAlgebra.kt @@ -1,4 +1,6 @@ -package space.kscience.kmath.tensors +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.tensors.AnalyticTensorAlgebra public class DoubleAnalyticTensorAlgebra: AnalyticTensorAlgebra, diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleLinearOpsTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt similarity index 96% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleLinearOpsTensorAlgebra.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt index 3f44305b1..8a16dc3ed 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleLinearOpsTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt @@ -1,4 +1,6 @@ -package space.kscience.kmath.tensors +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.tensors.LinearOpsTensorAlgebra public class DoubleLinearOpsTensorAlgebra : LinearOpsTensorAlgebra, diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleOrderedTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleOrderedTensorAlgebra.kt similarity index 89% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleOrderedTensorAlgebra.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleOrderedTensorAlgebra.kt index bd6bcfe8f..a6bea59f4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleOrderedTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleOrderedTensorAlgebra.kt @@ -1,4 +1,6 @@ -package space.kscience.kmath.tensors +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.tensors.OrderedTensorAlgebra public open class DoubleOrderedTensorAlgebra: OrderedTensorAlgebra, diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleReduceOpsTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleReduceOpsTensorAlgebra.kt similarity index 68% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleReduceOpsTensorAlgebra.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleReduceOpsTensorAlgebra.kt index 00d9b3ff8..9a8aa9ebf 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleReduceOpsTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleReduceOpsTensorAlgebra.kt @@ -1,8 +1,10 @@ -package space.kscience.kmath.tensors +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.tensors.ReduceOpsTensorAlgebra public class DoubleReduceOpsTensorAlgebra: DoubleTensorAlgebra(), - ReduceOpsTensorAlgebra { + ReduceOpsTensorAlgebra { override fun DoubleTensor.value(): Double { check(this.shape contentEquals intArrayOf(1)) { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt similarity index 77% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleTensorAlgebra.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 675be2f33..9decc0e6a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/DoubleTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -1,8 +1,18 @@ -package space.kscience.kmath.tensors +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.tensors.TensorPartialDivisionAlgebra public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra { + public fun fromArray(shape: IntArray, buffer: DoubleArray): DoubleTensor { + checkEmptyShape(shape) + checkEmptyDoubleBuffer(buffer) + checkBufferShapeConsistency(shape, buffer) + return DoubleTensor(shape, buffer, 0) + } + + override operator fun DoubleTensor.get(i: Int): DoubleTensor { val lastShape = this.shape.drop(1).toIntArray() val newShape = if (lastShape.isNotEmpty()) lastShape else intArrayOf(1) @@ -53,13 +63,11 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra - newThis.buffer.array()[i] + newOther.buffer.array()[i] + checkShapesCompatible(this, other) + val resBuffer = DoubleArray(this.strides.linearSize) { i -> + this.buffer.array()[i] + other.buffer.array()[i] } - return DoubleTensor(newThis.shape, resBuffer) + return DoubleTensor(this.shape, resBuffer) } override fun DoubleTensor.plusAssign(value: Double) { @@ -69,10 +77,10 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra - newThis.buffer.array()[i] - newOther.buffer.array()[i] + checkShapesCompatible(this, other) + val resBuffer = DoubleArray(this.strides.linearSize) { i -> + this.buffer.array()[i] - other.buffer.array()[i] } - return DoubleTensor(newThis.shape, resBuffer) + return DoubleTensor(this.shape, resBuffer) } override fun DoubleTensor.minusAssign(value: Double) { @@ -107,10 +113,10 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra - newThis.buffer.array()[newOther.bufferStart + i] * - newOther.buffer.array()[newOther.bufferStart + i] + checkShapesCompatible(this, other) + val resBuffer = DoubleArray(this.strides.linearSize) { i -> + this.buffer.array()[other.bufferStart + i] * + other.buffer.array()[other.bufferStart + i] } - return DoubleTensor(newThis.shape, resBuffer) + return DoubleTensor(this.shape, resBuffer) } override fun DoubleTensor.timesAssign(value: Double) { @@ -142,10 +145,40 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra + this.buffer.array()[this.bufferStart + i] / value + } + return DoubleTensor(this.shape, resBuffer) + } + + override fun DoubleTensor.div(other: DoubleTensor): DoubleTensor { + checkShapesCompatible(this, other) + val resBuffer = DoubleArray(this.strides.linearSize) { i -> + this.buffer.array()[other.bufferStart + i] / + other.buffer.array()[other.bufferStart + i] + } + return DoubleTensor(this.shape, resBuffer) + } + + override fun DoubleTensor.divAssign(value: Double) { + for (i in 0 until this.strides.linearSize) { + this.buffer.array()[this.bufferStart + i] /= value + } + } + + override fun DoubleTensor.divAssign(other: DoubleTensor) { + checkShapesCompatible(this, other) + for (i in 0 until this.strides.linearSize) { + this.buffer.array()[this.bufferStart + i] /= + other.buffer.array()[this.bufferStart + i] } } @@ -229,27 +262,10 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra DoubleTensorAlgebra(block: DoubleTensorAlgebra.() -> R): R = - DoubleTensorAlgebra().block() \ No newline at end of file + DoubleTensorAlgebra().block() diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/checks.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/checks.kt new file mode 100644 index 000000000..f1ae89490 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/checks.kt @@ -0,0 +1,67 @@ +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.tensors.TensorAlgebra +import space.kscience.kmath.tensors.TensorStructure + + +internal inline fun , + TorchTensorAlgebraType : TensorAlgebra> + TorchTensorAlgebraType.checkEmptyShape(shape: IntArray): Unit = + check(shape.isNotEmpty()) { + "Illegal empty shape provided" + } + +internal inline fun < TensorType : TensorStructure, + TorchTensorAlgebraType : TensorAlgebra> + TorchTensorAlgebraType.checkEmptyDoubleBuffer(buffer: DoubleArray): Unit = + check(buffer.isNotEmpty()) { + "Illegal empty buffer provided" + } + +internal inline fun < TensorType : TensorStructure, + TorchTensorAlgebraType : TensorAlgebra> + TorchTensorAlgebraType.checkBufferShapeConsistency(shape: IntArray, buffer: DoubleArray): Unit = + check(buffer.size == shape.reduce(Int::times)) { + "Inconsistent shape ${shape.toList()} for buffer of size ${buffer.size} provided" + } + + +internal inline fun , + TorchTensorAlgebraType : TensorAlgebra> + TorchTensorAlgebraType.checkShapesCompatible(a: TensorType, b: TensorType): Unit = + check(a.shape contentEquals b.shape) { + "Incompatible shapes ${a.shape.toList()} and ${b.shape.toList()} " + } + + +internal inline fun , + TorchTensorAlgebraType : TensorAlgebra> + TorchTensorAlgebraType.checkDot(a: TensorType, b: TensorType): Unit { + val sa = a.shape + val sb = b.shape + val na = sa.size + val nb = sb.size + var status: Boolean + if (nb == 1) { + status = sa.last() == sb[0] + } else { + status = sa.last() == sb[nb - 2] + if ((na > 2) and (nb > 2)) { + status = status and + (sa.take(nb - 2).toIntArray() contentEquals sb.take(nb - 2).toIntArray()) + } + } + check(status) { "Incompatible shapes ${sa.toList()} and ${sb.toList()} provided for dot product" } +} + +internal inline fun , + TorchTensorAlgebraType : TensorAlgebra> + TorchTensorAlgebraType.checkTranspose(dim: Int, i: Int, j: Int): Unit = + check((i < dim) and (j < dim)) { + "Cannot transpose $i to $j for a tensor of dim $dim" + } + +internal inline fun , + TorchTensorAlgebraType : TensorAlgebra> + TorchTensorAlgebraType.checkView(a: TensorType, shape: IntArray): Unit = + check(a.shape.reduce(Int::times) == shape.reduce(Int::times)) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/utils.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/utils.kt new file mode 100644 index 000000000..69c8afde5 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/utils.kt @@ -0,0 +1,36 @@ +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.structures.* + + +/** + * Returns a reference to [IntArray] containing all of the elements of this [Buffer]. + */ +internal fun Buffer.array(): IntArray = when (this) { + is IntBuffer -> array + else -> throw RuntimeException("Failed to cast Buffer to IntArray") +} + +/** + * Returns a reference to [LongArray] containing all of the elements of this [Buffer]. + */ +internal fun Buffer.array(): LongArray = when (this) { + is LongBuffer -> array + else -> throw RuntimeException("Failed to cast Buffer to LongArray") +} + +/** + * Returns a reference to [FloatArray] containing all of the elements of this [Buffer]. + */ +internal fun Buffer.array(): FloatArray = when (this) { + is FloatBuffer -> array + else -> throw RuntimeException("Failed to cast Buffer to FloatArray") +} + +/** + * Returns a reference to [DoubleArray] containing all of the elements of this [Buffer]. + */ +internal fun Buffer.array(): DoubleArray = when (this) { + is RealBuffer -> array + else -> throw RuntimeException("Failed to cast Buffer to DoubleArray") +} diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/TestDoubleTensorAlgebra.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/TestDoubleTensorAlgebra.kt deleted file mode 100644 index a060a970f..000000000 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/TestDoubleTensorAlgebra.kt +++ /dev/null @@ -1,105 +0,0 @@ -package space.kscience.kmath.tensors - - -import kotlin.test.Test -import kotlin.test.assertTrue - -class TestDoubleTensorAlgebra { - - @Test - fun doublePlus() = DoubleTensorAlgebra { - val tensor = DoubleTensor(intArrayOf(2), doubleArrayOf(1.0, 2.0)) - val res = 10.0 + tensor - assertTrue(res.buffer.array() contentEquals doubleArrayOf(11.0,12.0)) - } - - @Test - fun transpose1x1() = DoubleTensorAlgebra { - val tensor = DoubleTensor(intArrayOf(1), doubleArrayOf(0.0)) - val res = tensor.transpose(0, 0) - - assertTrue(res.buffer.array() contentEquals doubleArrayOf(0.0)) - assertTrue(res.shape contentEquals intArrayOf(1)) - } - - @Test - fun transpose3x2() = DoubleTensorAlgebra { - val tensor = DoubleTensor(intArrayOf(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) - val res = tensor.transpose(1, 0) - - assertTrue(res.buffer.array() contentEquals doubleArrayOf(1.0, 3.0, 5.0, 2.0, 4.0, 6.0)) - assertTrue(res.shape contentEquals intArrayOf(2, 3)) - } - - @Test - fun transpose1x2x3() = DoubleTensorAlgebra { - val tensor = DoubleTensor(intArrayOf(1, 2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) - val res01 = tensor.transpose(0, 1) - val res02 = tensor.transpose(0, 2) - val res12 = tensor.transpose(1, 2) - - assertTrue(res01.shape contentEquals intArrayOf(2, 1, 3)) - assertTrue(res02.shape contentEquals intArrayOf(3, 2, 1)) - assertTrue(res12.shape contentEquals intArrayOf(1, 3, 2)) - - assertTrue(res01.buffer.array() contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) - assertTrue(res02.buffer.array() contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0)) - assertTrue(res12.buffer.array() contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0)) - } - - @Test - fun broadcastShapes() = DoubleTensorAlgebra { - assertTrue(broadcastShapes( - intArrayOf(2, 3), intArrayOf(1, 3), intArrayOf(1, 1, 1) - ) contentEquals intArrayOf(1, 2, 3)) - - assertTrue(broadcastShapes( - intArrayOf(6, 7), intArrayOf(5, 6, 1), intArrayOf(7,), intArrayOf(5, 1, 7) - ) contentEquals intArrayOf(5, 6, 7)) - } - - @Test - fun broadcastTo() = DoubleTensorAlgebra { - val tensor1 = DoubleTensor(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) - val tensor2 = DoubleTensor(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) - - val res = broadcastTo(tensor2, tensor1.shape) - assertTrue(res.shape contentEquals intArrayOf(2, 3)) - assertTrue(res.buffer.array() contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0)) - } - - @Test - fun broadcastTensors() = DoubleTensorAlgebra { - val tensor1 = DoubleTensor(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) - val tensor2 = DoubleTensor(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) - val tensor3 = DoubleTensor(intArrayOf(1, 1, 1), doubleArrayOf(500.0)) - - val res = broadcastTensors(tensor1, tensor2, tensor3) - - assertTrue(res[0].shape contentEquals intArrayOf(1, 2, 3)) - assertTrue(res[1].shape contentEquals intArrayOf(1, 2, 3)) - assertTrue(res[2].shape contentEquals intArrayOf(1, 2, 3)) - - assertTrue(res[0].buffer.array() contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) - assertTrue(res[1].buffer.array() contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0)) - assertTrue(res[2].buffer.array() contentEquals doubleArrayOf(500.0, 500.0, 500.0, 500.0, 500.0, 500.0)) - } - - @Test - fun minusTensor() = DoubleTensorAlgebra { - val tensor1 = DoubleTensor(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) - val tensor2 = DoubleTensor(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) - val tensor3 = DoubleTensor(intArrayOf(1, 1, 1), doubleArrayOf(500.0)) - - assertTrue((tensor2 - tensor1).shape contentEquals intArrayOf(2, 3)) - assertTrue((tensor2 - tensor1).buffer.array() contentEquals doubleArrayOf(9.0, 18.0, 27.0, 6.0, 15.0, 24.0)) - - assertTrue((tensor3 - tensor1).shape contentEquals intArrayOf(1, 2, 3)) - assertTrue((tensor3 - tensor1).buffer.array() - contentEquals doubleArrayOf(499.0, 498.0, 497.0, 496.0, 495.0, 494.0)) - - assertTrue((tensor3 - tensor2).shape contentEquals intArrayOf(1, 1, 3)) - assertTrue((tensor3 - tensor2).buffer.array() contentEquals doubleArrayOf(490.0, 480.0, 470.0)) - } - -} \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestBroadcasting.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestBroadcasting.kt new file mode 100644 index 000000000..2633229ea --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestBroadcasting.kt @@ -0,0 +1,82 @@ +package space.kscience.kmath.tensors.core + +import kotlin.test.Test +import kotlin.test.assertTrue + +class TestBroadcasting { + + @Test + fun broadcastShapes() = DoubleTensorAlgebra { + assertTrue( + broadcastShapes( + intArrayOf(2, 3), intArrayOf(1, 3), intArrayOf(1, 1, 1) + ) contentEquals intArrayOf(1, 2, 3) + ) + + assertTrue( + broadcastShapes( + intArrayOf(6, 7), intArrayOf(5, 6, 1), intArrayOf(7), intArrayOf(5, 1, 7) + ) contentEquals intArrayOf(5, 6, 7) + ) + } + + @Test + fun broadcastTo() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor2 = fromArray(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) + + val res = broadcastTo(tensor2, tensor1.shape) + assertTrue(res.shape contentEquals intArrayOf(2, 3)) + assertTrue(res.buffer.array() contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0)) + } + + @Test + fun broadcastTensors() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor2 = fromArray(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) + val tensor3 = fromArray(intArrayOf(1, 1, 1), doubleArrayOf(500.0)) + + val res = broadcastTensors(tensor1, tensor2, tensor3) + + assertTrue(res[0].shape contentEquals intArrayOf(1, 2, 3)) + assertTrue(res[1].shape contentEquals intArrayOf(1, 2, 3)) + assertTrue(res[2].shape contentEquals intArrayOf(1, 2, 3)) + + assertTrue(res[0].buffer.array() contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + assertTrue(res[1].buffer.array() contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0)) + assertTrue(res[2].buffer.array() contentEquals doubleArrayOf(500.0, 500.0, 500.0, 500.0, 500.0, 500.0)) + } + + @Test + fun minusTensor() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor2 = fromArray(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) + val tensor3 = fromArray(intArrayOf(1, 1, 1), doubleArrayOf(500.0)) + + val tensor21 = broadcastDoubleTensorAlgebra { + tensor2 - tensor1 + } + + val tensor31 = broadcastDoubleTensorAlgebra { + tensor3 - tensor1 + } + + val tensor32 = broadcastDoubleTensorAlgebra { + tensor3 - tensor2 + } + + + assertTrue(tensor21.shape contentEquals intArrayOf(2, 3)) + assertTrue(tensor21.buffer.array() contentEquals doubleArrayOf(9.0, 18.0, 27.0, 6.0, 15.0, 24.0)) + + assertTrue(tensor31.shape contentEquals intArrayOf(1, 2, 3)) + assertTrue( + tensor31.buffer.array() + contentEquals doubleArrayOf(499.0, 498.0, 497.0, 496.0, 495.0, 494.0) + ) + + assertTrue(tensor32.shape contentEquals intArrayOf(1, 1, 3)) + assertTrue(tensor32.buffer.array() contentEquals doubleArrayOf(490.0, 480.0, 470.0)) + } + +} \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/TestDoubleTensor.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt similarity index 74% rename from kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/TestDoubleTensor.kt rename to kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt index 6b20027c7..b12b08b52 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/TestDoubleTensor.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt @@ -1,4 +1,4 @@ -package space.kscience.kmath.tensors +package space.kscience.kmath.tensors.core import space.kscience.kmath.nd.as1D @@ -13,20 +13,20 @@ class TestDoubleTensor { @Test fun valueTest() = DoubleReduceOpsTensorAlgebra { val value = 12.5 - val tensor = DoubleTensor(intArrayOf(1), doubleArrayOf(value)) + val tensor = fromArray(intArrayOf(1), doubleArrayOf(value)) assertEquals(tensor.value(), value) } @Test - fun stridesTest(){ - val tensor = DoubleTensor(intArrayOf(2,2), doubleArrayOf(3.5,5.8,58.4,2.4)) + fun stridesTest() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(2,2), doubleArrayOf(3.5,5.8,58.4,2.4)) assertEquals(tensor[intArrayOf(0,1)], 5.8) assertTrue(tensor.elements().map{ it.second }.toList().toDoubleArray() contentEquals tensor.buffer.toDoubleArray()) } @Test fun getTest() = DoubleTensorAlgebra { - val tensor = DoubleTensor(intArrayOf(1,2,2), doubleArrayOf(3.5,5.8,58.4,2.4)) + val tensor = fromArray(intArrayOf(1,2,2), doubleArrayOf(3.5,5.8,58.4,2.4)) val matrix = tensor[0].as2D() assertEquals(matrix[0,1], 5.8) diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt new file mode 100644 index 000000000..8b4d5ca16 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt @@ -0,0 +1,50 @@ +package space.kscience.kmath.tensors.core + + +import kotlin.test.Test +import kotlin.test.assertTrue + +class TestDoubleTensorAlgebra { + + @Test + fun doublePlus() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(2), doubleArrayOf(1.0, 2.0)) + val res = 10.0 + tensor + assertTrue(res.buffer.array() contentEquals doubleArrayOf(11.0, 12.0)) + } + + @Test + fun transpose1x1() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(1), doubleArrayOf(0.0)) + val res = tensor.transpose(0, 0) + + assertTrue(res.buffer.array() contentEquals doubleArrayOf(0.0)) + assertTrue(res.shape contentEquals intArrayOf(1)) + } + + @Test + fun transpose3x2() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val res = tensor.transpose(1, 0) + + assertTrue(res.buffer.array() contentEquals doubleArrayOf(1.0, 3.0, 5.0, 2.0, 4.0, 6.0)) + assertTrue(res.shape contentEquals intArrayOf(2, 3)) + } + + @Test + fun transpose1x2x3() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(1, 2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val res01 = tensor.transpose(0, 1) + val res02 = tensor.transpose(0, 2) + val res12 = tensor.transpose(1, 2) + + assertTrue(res01.shape contentEquals intArrayOf(2, 1, 3)) + assertTrue(res02.shape contentEquals intArrayOf(3, 2, 1)) + assertTrue(res12.shape contentEquals intArrayOf(1, 3, 2)) + + assertTrue(res01.buffer.array() contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + assertTrue(res02.buffer.array() contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0)) + assertTrue(res12.buffer.array() contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0)) + } + +} \ No newline at end of file