Safe shapes

This commit is contained in:
Alexander Nozik 2022-10-14 13:05:39 +03:00
parent b0abcf2d0c
commit ee569b85f8
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
66 changed files with 416 additions and 408 deletions

View File

@ -85,7 +85,7 @@ internal class NDFieldBenchmark {
private companion object { private companion object {
private const val dim = 1000 private const val dim = 1000
private const val n = 100 private const val n = 100
private val shape = Shape(dim, dim) private val shape = ShapeND(dim, dim)
private val specializedField = DoubleField.ndAlgebra private val specializedField = DoubleField.ndAlgebra
private val genericField = BufferedFieldOpsND(DoubleField) private val genericField = BufferedFieldOpsND(DoubleField)
private val nd4jField = DoubleField.nd4j private val nd4jField = DoubleField.nd4j

View File

@ -10,7 +10,7 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.nd.one import space.kscience.kmath.nd.one
@ -49,7 +49,7 @@ internal class ViktorBenchmark {
private companion object { private companion object {
private const val dim = 1000 private const val dim = 1000
private const val n = 100 private const val n = 100
private val shape = Shape(dim, dim) private val shape = ShapeND(dim, dim)
// automatically build context most suited for given type. // automatically build context most suited for given type.
private val doubleField = DoubleField.ndAlgebra private val doubleField = DoubleField.ndAlgebra

View File

@ -10,7 +10,7 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.nd.one import space.kscience.kmath.nd.one
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
@ -49,7 +49,7 @@ internal class ViktorLogBenchmark {
private companion object { private companion object {
private const val dim = 1000 private const val dim = 1000
private const val n = 100 private const val n = 100
private val shape = Shape(dim, dim) private val shape = ShapeND(dim, dim)
// automatically build context most suited for given type. // automatically build context most suited for given type.
private val doubleField = DoubleField.ndAlgebra private val doubleField = DoubleField.ndAlgebra

View File

@ -8,14 +8,14 @@ package space.kscience.kmath.operations
import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.linear.matrix import space.kscience.kmath.linear.matrix
import space.kscience.kmath.nd.DoubleBufferND import space.kscience.kmath.nd.DoubleBufferND
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.viktor.ViktorStructureND import space.kscience.kmath.viktor.ViktorStructureND
import space.kscience.kmath.viktor.viktorAlgebra import space.kscience.kmath.viktor.viktorAlgebra
fun main() { fun main() {
val viktorStructure: ViktorStructureND = DoubleField.viktorAlgebra.structureND(Shape(2, 2)) { (i, j) -> val viktorStructure: ViktorStructureND = DoubleField.viktorAlgebra.structureND(ShapeND(2, 2)) { (i, j) ->
if (i == j) 2.0 else 0.0 if (i == j) 2.0 else 0.0
} }

View File

@ -29,7 +29,7 @@ fun main() {
Nd4j.zeros(0) Nd4j.zeros(0)
val dim = 1000 val dim = 1000
val n = 1000 val n = 1000
val shape = Shape(dim, dim) val shape = ShapeND(dim, dim)
// specialized nd-field for Double. It works as generic Double field as well. // specialized nd-field for Double. It works as generic Double field as well.

View File

@ -17,7 +17,7 @@ import java.util.stream.IntStream
* A demonstration implementation of NDField over Real using Java [java.util.stream.DoubleStream] for parallel * A demonstration implementation of NDField over Real using Java [java.util.stream.DoubleStream] for parallel
* execution. * execution.
*/ */
class StreamDoubleFieldND(override val shape: Shape) : FieldND<Double, DoubleField>, class StreamDoubleFieldND(override val shape: ShapeND) : FieldND<Double, DoubleField>,
NumbersAddOps<StructureND<Double>>, NumbersAddOps<StructureND<Double>>,
ExtendedField<StructureND<Double>> { ExtendedField<StructureND<Double>> {
@ -43,7 +43,7 @@ class StreamDoubleFieldND(override val shape: Shape) : FieldND<Double, DoubleFie
else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
} }
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): BufferND<Double> { override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): BufferND<Double> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
val index = strides.index(offset) val index = strides.index(offset)
DoubleField.initializer(index) DoubleField.initializer(index)
@ -111,4 +111,4 @@ class StreamDoubleFieldND(override val shape: Shape) : FieldND<Double, DoubleFie
override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) } override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
} }
fun DoubleField.ndStreaming(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(Shape(shape)) fun DoubleField.ndStreaming(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(ShapeND(shape))

View File

@ -8,7 +8,7 @@ package space.kscience.kmath.structures
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.BufferND import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.ColumnStrides
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
@ -17,7 +17,7 @@ fun main() {
val n = 6000 val n = 6000
val array = DoubleArray(n * n) { 1.0 } val array = DoubleArray(n * n) { 1.0 }
val buffer = DoubleBuffer(array) val buffer = DoubleBuffer(array)
val strides = ColumnStrides(Shape(n, n)) val strides = ColumnStrides(ShapeND(n, n))
val structure = BufferND(strides, buffer) val structure = BufferND(strides, buffer)
measureTimeMillis { measureTimeMillis {

View File

@ -6,7 +6,7 @@
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.kmath.nd.BufferND import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.map import space.kscience.kmath.operations.map
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -16,7 +16,7 @@ private inline fun <T, reified R: Any> BufferND<T>.map(block: (T) -> R): BufferN
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
fun main() { fun main() {
val n = 6000 val n = 6000
val structure = StructureND.buffered(Shape(n, n), Buffer.Companion::auto) { 1.0 } val structure = StructureND.buffered(ShapeND(n, n), Buffer.Companion::auto) { 1.0 }
structure.map { it + 1 } // warm-up structure.map { it + 1 } // warm-up
val time1 = measureTimeMillis { val res = structure.map { it + 1 } } val time1 = measureTimeMillis { val res = structure.map { it + 1 } }
println("Structure mapping finished in $time1 millis") println("Structure mapping finished in $time1 millis")

View File

@ -6,7 +6,7 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.contentEquals import space.kscience.kmath.nd.contentEquals
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.tensors.core.DoubleTensor import space.kscience.kmath.tensors.core.DoubleTensor
@ -25,10 +25,10 @@ fun main() {
DoubleTensorAlgebra { DoubleTensorAlgebra {
// take coefficient vector from normal distribution // take coefficient vector from normal distribution
val alpha = randomNormal( val alpha = randomNormal(
Shape(5), ShapeND(5),
randSeed randSeed
) + fromArray( ) + fromArray(
Shape(5), ShapeND(5),
doubleArrayOf(1.0, 2.5, 3.4, 5.0, 10.1) doubleArrayOf(1.0, 2.5, 3.4, 5.0, 10.1)
) )
@ -36,7 +36,7 @@ fun main() {
// also take sample of size 20 from normal distribution for x // also take sample of size 20 from normal distribution for x
val x = randomNormal( val x = randomNormal(
Shape(20, 5), ShapeND(20, 5),
randSeed randSeed
) )

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.tensors.core.tensorAlgebra import space.kscience.kmath.tensors.core.tensorAlgebra
import space.kscience.kmath.tensors.core.withBroadcast import space.kscience.kmath.tensors.core.withBroadcast
@ -17,7 +17,7 @@ fun main(): Unit = Double.tensorAlgebra.withBroadcast { // work in context with
// assume x is range from 0 until 10 // assume x is range from 0 until 10
val x = fromArray( val x = fromArray(
Shape(10), ShapeND(10),
DoubleArray(10) { it.toDouble() } DoubleArray(10) { it.toDouble() }
) )
@ -42,13 +42,13 @@ fun main(): Unit = Double.tensorAlgebra.withBroadcast { // work in context with
// save means ans standard deviations for further recovery // save means ans standard deviations for further recovery
val mean = fromArray( val mean = fromArray(
Shape(2), ShapeND(2),
doubleArrayOf(xMean, yMean) doubleArrayOf(xMean, yMean)
) )
println("Means:\n$mean") println("Means:\n$mean")
val std = fromArray( val std = fromArray(
Shape(2), ShapeND(2),
doubleArrayOf(xStd, yStd) doubleArrayOf(xStd, yStd)
) )
println("Standard deviations:\n$std") println("Standard deviations:\n$std")
@ -69,7 +69,7 @@ fun main(): Unit = Double.tensorAlgebra.withBroadcast { // work in context with
// we can restore original data from reduced data; // we can restore original data from reduced data;
// for example, find 7th element of dataset. // for example, find 7th element of dataset.
val n = 7 val n = 7
val restored = (datasetReduced.getTensor(n) dot v.view(Shape(1, 2))) * std + mean val restored = (datasetReduced.getTensor(n) dot v.view(ShapeND(1, 2))) * std + mean
println("Original value:\n${dataset.getTensor(n)}") println("Original value:\n${dataset.getTensor(n)}")
println("Restored value:\n$restored") println("Restored value:\n$restored")
} }

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.tensors.core.tensorAlgebra import space.kscience.kmath.tensors.core.tensorAlgebra
import space.kscience.kmath.tensors.core.withBroadcast import space.kscience.kmath.tensors.core.withBroadcast
@ -14,10 +14,10 @@ import space.kscience.kmath.tensors.core.withBroadcast
fun main() = Double.tensorAlgebra.withBroadcast { // work in context with broadcast methods fun main() = Double.tensorAlgebra.withBroadcast { // work in context with broadcast methods
// take dataset of 5-element vectors from normal distribution // take dataset of 5-element vectors from normal distribution
val dataset = randomNormal(Shape(100, 5)) * 1.5 // all elements from N(0, 1.5) val dataset = randomNormal(ShapeND(100, 5)) * 1.5 // all elements from N(0, 1.5)
dataset += fromArray( dataset += fromArray(
Shape(5), ShapeND(5),
doubleArrayOf(0.0, 1.0, 1.5, 3.0, 5.0) // row means doubleArrayOf(0.0, 1.0, 1.5, 3.0, 5.0) // row means
) )

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.tensors.core.DoubleTensor import space.kscience.kmath.tensors.core.DoubleTensor
import space.kscience.kmath.tensors.core.tensorAlgebra import space.kscience.kmath.tensors.core.tensorAlgebra
import space.kscience.kmath.tensors.core.withBroadcast import space.kscience.kmath.tensors.core.withBroadcast
@ -16,13 +16,13 @@ fun main() = Double.tensorAlgebra.withBroadcast {// work in context with linear
// set true value of x // set true value of x
val trueX = fromArray( val trueX = fromArray(
Shape(4), ShapeND(4),
doubleArrayOf(-2.0, 1.5, 6.8, -2.4) doubleArrayOf(-2.0, 1.5, 6.8, -2.4)
) )
// and A matrix // and A matrix
val a = fromArray( val a = fromArray(
Shape(4, 4), ShapeND(4, 4),
doubleArrayOf( doubleArrayOf(
0.5, 10.5, 4.5, 1.0, 0.5, 10.5, 4.5, 1.0,
8.5, 0.9, 12.8, 0.1, 8.5, 0.9, 12.8, 0.1,
@ -65,7 +65,7 @@ fun main() = Double.tensorAlgebra.withBroadcast {// work in context with linear
// this function returns solution x of a system lx = b, l should be lower triangular // this function returns solution x of a system lx = b, l should be lower triangular
fun solveLT(l: DoubleTensor, b: DoubleTensor): DoubleTensor { fun solveLT(l: DoubleTensor, b: DoubleTensor): DoubleTensor {
val n = l.shape[0] val n = l.shape[0]
val x = zeros(Shape(n)) val x = zeros(ShapeND(n))
for (i in 0 until n) { for (i in 0 until n) {
x[intArrayOf(i)] = (b[intArrayOf(i)] - l.getTensor(i).dot(x).value()) / l[intArrayOf(i, i)] x[intArrayOf(i)] = (b[intArrayOf(i)] - l.getTensor(i).dot(x).value()) / l[intArrayOf(i, i)]
} }

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.contentEquals import space.kscience.kmath.nd.contentEquals
import space.kscience.kmath.operations.asIterable import space.kscience.kmath.operations.asIterable
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
@ -70,12 +70,12 @@ class Dense(
private val weights: DoubleTensor = DoubleTensorAlgebra { private val weights: DoubleTensor = DoubleTensorAlgebra {
randomNormal( randomNormal(
Shape(inputUnits, outputUnits), ShapeND(inputUnits, outputUnits),
seed seed
) * sqrt(2.0 / (inputUnits + outputUnits)) ) * sqrt(2.0 / (inputUnits + outputUnits))
} }
private val bias: DoubleTensor = DoubleTensorAlgebra { zeros(Shape(outputUnits)) } private val bias: DoubleTensor = DoubleTensorAlgebra { zeros(ShapeND(outputUnits)) }
override fun forward(input: DoubleTensor): DoubleTensor = BroadcastDoubleTensorAlgebra { override fun forward(input: DoubleTensor): DoubleTensor = BroadcastDoubleTensorAlgebra {
(input dot weights) + bias (input dot weights) + bias
@ -184,17 +184,17 @@ fun main() = BroadcastDoubleTensorAlgebra {
//val testSize = sampleSize - trainSize //val testSize = sampleSize - trainSize
// take sample of features from normal distribution // take sample of features from normal distribution
val x = randomNormal(Shape(sampleSize, features), seed) * 2.5 val x = randomNormal(ShapeND(sampleSize, features), seed) * 2.5
x += fromArray( x += fromArray(
Shape(5), ShapeND(5),
doubleArrayOf(0.0, -1.0, -2.5, -3.0, 5.5) // row means doubleArrayOf(0.0, -1.0, -2.5, -3.0, 5.5) // row means
) )
// define class like '1' if the sum of features > 0 and '0' otherwise // define class like '1' if the sum of features > 0 and '0' otherwise
val y = fromArray( val y = fromArray(
Shape(sampleSize, 1), ShapeND(sampleSize, 1),
DoubleArray(sampleSize) { i -> DoubleArray(sampleSize) { i ->
if (x.getTensor(i).sum() > 0.0) { if (x.getTensor(i).sum() > 0.0) {
1.0 1.0

View File

@ -59,7 +59,7 @@ public sealed class ComplexFieldOpsND : BufferedFieldOpsND<Complex, ComplexField
} }
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public class ComplexFieldND(override val shape: Shape) : public class ComplexFieldND(override val shape: ShapeND) :
ComplexFieldOpsND(), FieldND<Complex, ComplexField>, ComplexFieldOpsND(), FieldND<Complex, ComplexField>,
NumbersAddOps<StructureND<Complex>> { NumbersAddOps<StructureND<Complex>> {
@ -71,12 +71,12 @@ public class ComplexFieldND(override val shape: Shape) :
public val ComplexField.ndAlgebra: ComplexFieldOpsND get() = ComplexFieldOpsND public val ComplexField.ndAlgebra: ComplexFieldOpsND get() = ComplexFieldOpsND
public fun ComplexField.ndAlgebra(vararg shape: Int): ComplexFieldND = ComplexFieldND(Shape(shape)) public fun ComplexField.ndAlgebra(vararg shape: Int): ComplexFieldND = ComplexFieldND(ShapeND(shape))
/** /**
* Produce a context for n-dimensional operations inside this real field * Produce a context for n-dimensional operations inside this real field
*/ */
public inline fun <R> ComplexField.withNdAlgebra(vararg shape: Int, action: ComplexFieldND.() -> R): R { public inline fun <R> ComplexField.withNdAlgebra(vararg shape: Int, action: ComplexFieldND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ComplexFieldND(Shape(shape)).action() return ComplexFieldND(ShapeND(shape)).action()
} }

View File

@ -21,7 +21,7 @@ public class BufferedLinearSpace<T, out A : Ring<T>>(
private val ndAlgebra = BufferedRingOpsND(bufferAlgebra) private val ndAlgebra = BufferedRingOpsND(bufferAlgebra)
override fun buildMatrix(rows: Int, columns: Int, initializer: A.(i: Int, j: Int) -> T): Matrix<T> = override fun buildMatrix(rows: Int, columns: Int, initializer: A.(i: Int, j: Int) -> T): Matrix<T> =
ndAlgebra.structureND(Shape(rows, columns)) { (i, j) -> elementAlgebra.initializer(i, j) }.as2D() ndAlgebra.structureND(ShapeND(rows, columns)) { (i, j) -> elementAlgebra.initializer(i, j) }.as2D()
override fun buildVector(size: Int, initializer: A.(Int) -> T): Point<T> = override fun buildVector(size: Int, initializer: A.(Int) -> T): Point<T> =
bufferAlgebra.buffer(size) { elementAlgebra.initializer(it) } bufferAlgebra.buffer(size) { elementAlgebra.initializer(it) }

View File

@ -21,7 +21,7 @@ public object DoubleLinearSpace : LinearSpace<Double, DoubleField> {
rows: Int, rows: Int,
columns: Int, columns: Int,
initializer: DoubleField.(i: Int, j: Int) -> Double initializer: DoubleField.(i: Int, j: Int) -> Double
): Matrix<Double> = DoubleFieldOpsND.structureND(Shape(rows, columns)) { (i, j) -> ): Matrix<Double> = DoubleFieldOpsND.structureND(ShapeND(rows, columns)) { (i, j) ->
DoubleField.initializer(i, j) DoubleField.initializer(i, j)
}.as2D() }.as2D()

View File

@ -5,7 +5,8 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
/** /**
* The matrix where each element is evaluated each time when is being accessed. * The matrix where each element is evaluated each time when is being accessed.
@ -18,7 +19,7 @@ public class VirtualMatrix<out T : Any>(
public val generator: (i: Int, j: Int) -> T, public val generator: (i: Int, j: Int) -> T,
) : Matrix<T> { ) : Matrix<T> {
override val shape: Shape get() = Shape(rowNum, colNum) override val shape: ShapeND get() = ShapeND(rowNum, colNum)
override operator fun get(i: Int, j: Int): T = generator(i, j) override operator fun get(i: Int, j: Int): T = generator(i, j)
} }

View File

@ -25,7 +25,7 @@ public interface AlgebraND<T, out C : Algebra<T>>: Algebra<StructureND<T>> {
/** /**
* Produces a new [StructureND] using given initializer function. * Produces a new [StructureND] using given initializer function.
*/ */
public fun structureND(shape: Shape, initializer: C.(IntArray) -> T): StructureND<T> public fun structureND(shape: ShapeND, initializer: C.(IntArray) -> T): StructureND<T>
/** /**
* Maps elements from one structure to another one by applying [transform] to them. * Maps elements from one structure to another one by applying [transform] to them.

View File

@ -12,11 +12,11 @@ import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> { public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
public val indexerBuilder: (Shape) -> ShapeIndexer public val indexerBuilder: (ShapeND) -> ShapeIndexer
public val bufferAlgebra: BufferAlgebra<T, A> public val bufferAlgebra: BufferAlgebra<T, A>
override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): BufferND<T> { override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): BufferND<T> {
val indexer = indexerBuilder(shape) val indexer = indexerBuilder(shape)
return BufferND( return BufferND(
indexer, indexer,
@ -47,7 +47,7 @@ public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
zipInline(left.toBufferND(), right.toBufferND(), transform) zipInline(left.toBufferND(), right.toBufferND(), transform)
public companion object { public companion object {
public val defaultIndexerBuilder: (Shape) -> ShapeIndexer = ::Strides public val defaultIndexerBuilder: (ShapeND) -> ShapeIndexer = ::Strides
} }
} }
@ -99,24 +99,24 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.zipInline(
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
public open class BufferedGroupNDOps<T, out A : Group<T>>( public open class BufferedGroupNDOps<T, out A : Group<T>>(
override val bufferAlgebra: BufferAlgebra<T, A>, override val bufferAlgebra: BufferAlgebra<T, A>,
override val indexerBuilder: (Shape) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder, override val indexerBuilder: (ShapeND) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
) : GroupOpsND<T, A>, BufferAlgebraND<T, A> { ) : GroupOpsND<T, A>, BufferAlgebraND<T, A> {
override fun StructureND<T>.unaryMinus(): StructureND<T> = map { -it } override fun StructureND<T>.unaryMinus(): StructureND<T> = map { -it }
} }
public open class BufferedRingOpsND<T, out A : Ring<T>>( public open class BufferedRingOpsND<T, out A : Ring<T>>(
bufferAlgebra: BufferAlgebra<T, A>, bufferAlgebra: BufferAlgebra<T, A>,
indexerBuilder: (Shape) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder, indexerBuilder: (ShapeND) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
) : BufferedGroupNDOps<T, A>(bufferAlgebra, indexerBuilder), RingOpsND<T, A> ) : BufferedGroupNDOps<T, A>(bufferAlgebra, indexerBuilder), RingOpsND<T, A>
public open class BufferedFieldOpsND<T, out A : Field<T>>( public open class BufferedFieldOpsND<T, out A : Field<T>>(
bufferAlgebra: BufferAlgebra<T, A>, bufferAlgebra: BufferAlgebra<T, A>,
indexerBuilder: (Shape) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder, indexerBuilder: (ShapeND) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
) : BufferedRingOpsND<T, A>(bufferAlgebra, indexerBuilder), FieldOpsND<T, A> { ) : BufferedRingOpsND<T, A>(bufferAlgebra, indexerBuilder), FieldOpsND<T, A> {
public constructor( public constructor(
elementAlgebra: A, elementAlgebra: A,
indexerBuilder: (Shape) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder, indexerBuilder: (ShapeND) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
) : this(BufferFieldOps(elementAlgebra), indexerBuilder) ) : this(BufferFieldOps(elementAlgebra), indexerBuilder)
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
@ -131,7 +131,7 @@ public val <T, A : Field<T>> BufferAlgebra<T, A>.nd: BufferedFieldOpsND<T, A> ge
public fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.structureND( public fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.structureND(
vararg shape: Int, vararg shape: Int,
initializer: A.(IntArray) -> T, initializer: A.(IntArray) -> T,
): BufferND<T> = structureND(Shape(shape), initializer) ): BufferND<T> = structureND(ShapeND(shape), initializer)
public fun <T, EA : Algebra<T>, A> A.structureND( public fun <T, EA : Algebra<T>, A> A.structureND(
initializer: EA.(IntArray) -> T, initializer: EA.(IntArray) -> T,

View File

@ -24,7 +24,7 @@ public open class BufferND<out T>(
@PerformancePitfall @PerformancePitfall
override operator fun get(index: IntArray): T = buffer[indices.offset(index)] override operator fun get(index: IntArray): T = buffer[indices.offset(index)]
override val shape: Shape get() = indices.shape override val shape: ShapeND get() = indices.shape
override fun toString(): String = StructureND.toString(this) override fun toString(): String = StructureND.toString(this)
} }

View File

@ -74,7 +74,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
transform: DoubleField.(Double, Double) -> Double, transform: DoubleField.(Double, Double) -> Double,
): BufferND<Double> = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> DoubleField.transform(l, r) } ): BufferND<Double> = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> DoubleField.transform(l, r) }
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND { override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND {
val indexer = indexerBuilder(shape) val indexer = indexerBuilder(shape)
return DoubleBufferND( return DoubleBufferND(
indexer, indexer,
@ -189,7 +189,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
} }
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public class DoubleFieldND(override val shape: Shape) : public class DoubleFieldND(override val shape: ShapeND) :
DoubleFieldOpsND(), FieldND<Double, DoubleField>, NumbersAddOps<StructureND<Double>>, DoubleFieldOpsND(), FieldND<Double, DoubleField>, NumbersAddOps<StructureND<Double>>,
ExtendedField<StructureND<Double>> { ExtendedField<StructureND<Double>> {
@ -231,8 +231,8 @@ public class DoubleFieldND(override val shape: Shape) :
public val DoubleField.ndAlgebra: DoubleFieldOpsND get() = DoubleFieldOpsND public val DoubleField.ndAlgebra: DoubleFieldOpsND get() = DoubleFieldOpsND
public fun DoubleField.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleFieldND(Shape(shape)) public fun DoubleField.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleFieldND(ShapeND(shape))
public fun DoubleField.ndAlgebra(shape: Shape): DoubleFieldND = DoubleFieldND(shape) public fun DoubleField.ndAlgebra(shape: ShapeND): DoubleFieldND = DoubleFieldND(shape)
/** /**
* Produce a context for n-dimensional operations inside this real field * Produce a context for n-dimensional operations inside this real field
@ -240,5 +240,5 @@ public fun DoubleField.ndAlgebra(shape: Shape): DoubleFieldND = DoubleFieldND(sh
@UnstableKMathAPI @UnstableKMathAPI
public inline fun <R> DoubleField.withNdAlgebra(vararg shape: Int, action: DoubleFieldND.() -> R): R { public inline fun <R> DoubleField.withNdAlgebra(vararg shape: Int, action: DoubleFieldND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return DoubleFieldND(Shape(shape)).run(action) return DoubleFieldND(ShapeND(shape)).run(action)
} }

View File

@ -20,7 +20,7 @@ public class IntBufferND(
public sealed class IntRingOpsND : BufferedRingOpsND<Int, IntRing>(IntRing.bufferAlgebra) { public sealed class IntRingOpsND : BufferedRingOpsND<Int, IntRing>(IntRing.bufferAlgebra) {
override fun structureND(shape: Shape, initializer: IntRing.(IntArray) -> Int): IntBufferND { override fun structureND(shape: ShapeND, initializer: IntRing.(IntArray) -> Int): IntBufferND {
val indexer = indexerBuilder(shape) val indexer = indexerBuilder(shape)
return IntBufferND( return IntBufferND(
indexer, indexer,
@ -35,7 +35,7 @@ public sealed class IntRingOpsND : BufferedRingOpsND<Int, IntRing>(IntRing.buffe
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public class IntRingND( public class IntRingND(
override val shape: Shape override val shape: ShapeND
) : IntRingOpsND(), RingND<Int, IntRing>, NumbersAddOps<StructureND<Int>> { ) : IntRingOpsND(), RingND<Int, IntRing>, NumbersAddOps<StructureND<Int>> {
override fun number(value: Number): BufferND<Int> { override fun number(value: Number): BufferND<Int> {
@ -46,5 +46,5 @@ public class IntRingND(
public inline fun <R> IntRing.withNdAlgebra(vararg shape: Int, action: IntRingND.() -> R): R { public inline fun <R> IntRing.withNdAlgebra(vararg shape: Int, action: IntRingND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return IntRingND(Shape(shape)).run(action) return IntRingND(ShapeND(shape)).run(action)
} }

View File

@ -13,7 +13,7 @@ public class PermutedStructureND<T>(
public val permutation: (IntArray) -> IntArray, public val permutation: (IntArray) -> IntArray,
) : StructureND<T> { ) : StructureND<T> {
override val shape: Shape override val shape: ShapeND
get() = origin.shape get() = origin.shape
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
@ -28,7 +28,7 @@ public fun <T> StructureND<T>.permute(
public class PermutedMutableStructureND<T>( public class PermutedMutableStructureND<T>(
public val origin: MutableStructureND<T>, public val origin: MutableStructureND<T>,
override val shape: Shape = origin.shape, override val shape: ShapeND = origin.shape,
public val permutation: (IntArray) -> IntArray, public val permutation: (IntArray) -> IntArray,
) : MutableStructureND<T> { ) : MutableStructureND<T> {
@ -45,6 +45,6 @@ public class PermutedMutableStructureND<T>(
} }
public fun <T> MutableStructureND<T>.permute( public fun <T> MutableStructureND<T>.permute(
newShape: Shape = shape, newShape: ShapeND = shape,
permutation: (IntArray) -> IntArray, permutation: (IntArray) -> IntArray,
): PermutedMutableStructureND<T> = PermutedMutableStructureND(this, newShape, permutation) ): PermutedMutableStructureND<T> = PermutedMutableStructureND(this, newShape, permutation)

View File

@ -1,102 +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.nd
import space.kscience.kmath.misc.UnsafeKMathAPI
import kotlin.jvm.JvmInline
/**
* A read-only ND shape
*/
@JvmInline
public value class Shape(@PublishedApi internal val array: IntArray) {
public val size: Int get() = array.size
public operator fun get(index: Int): Int = array[index]
override fun toString(): String = array.contentToString()
}
public inline fun Shape.forEach(block: (value: Int) -> Unit): Unit = array.forEach(block)
public inline fun Shape.forEachIndexed(block: (index: Int, value: Int) -> Unit): Unit = array.forEachIndexed(block)
public infix fun Shape.contentEquals(other: Shape): Boolean = array.contentEquals(other.array)
public fun Shape.contentHashCode(): Int = array.contentHashCode()
public val Shape.indices: IntRange get() = array.indices
public val Shape.linearSize: Int get() = array.reduce(Int::times)
public fun Shape.slice(range: IntRange): Shape = Shape(array.sliceArray(range))
public fun Shape.last(): Int = array.last()
/**
* A shape including last [n] dimensions of this shape
*/
public fun Shape.last(n: Int): Shape = Shape(array.copyOfRange(size - n, size))
public fun Shape.first(): Int = array.first()
/**
* A shape including first [n] dimensions of this shape
*/
public fun Shape.first(n: Int): Shape = Shape(array.copyOfRange(0, n))
public operator fun Shape.plus(add: IntArray): Shape = Shape(array + add)
public operator fun Shape.plus(add: Shape): Shape = Shape(array + add.array)
public fun Shape.isEmpty(): Boolean = size == 0
public fun Shape.isNotEmpty(): Boolean = size > 0
public fun Shape.transposed(i: Int, j: Int): Shape = Shape(array.copyOf().apply {
val ith = get(i)
val jth = get(j)
set(i, jth)
set(j, ith)
})
public operator fun Shape.component1(): Int = get(0)
public operator fun Shape.component2(): Int = get(1)
public operator fun Shape.component3(): Int = get(2)
/**
* Convert to array with protective copy
*/
public fun Shape.toArray(): IntArray = array.copyOf()
@UnsafeKMathAPI
public fun Shape.asArray(): IntArray = array
public fun Shape.asList(): List<Int> = array.asList()
/**
* An exception is thrown when the expected and actual shape of NDArray differ.
*
* @property expected the expected shape.
* @property actual the actual shape.
*/
public class ShapeMismatchException(public val expected: Shape, public val actual: Shape) :
RuntimeException("Shape $actual doesn't fit in expected shape ${expected}.")
public class IndexOutOfShapeException(public val shape: Shape, public val index: IntArray) :
RuntimeException("Index ${index.contentToString()} is out of shape ${shape}")
public fun Shape(shapeFirst: Int, vararg shapeRest: Int): Shape = Shape(intArrayOf(shapeFirst, *shapeRest))
public interface WithShape {
public val shape: Shape
public val indices: ShapeIndexer get() = ColumnStrides(shape)
}
internal fun requireIndexInShape(index: IntArray, shape: Shape) {
if (index.size != shape.size) throw IndexOutOfShapeException(shape, index)
shape.forEachIndexed { axis, axisShape ->
if (index[axis] !in 0 until axisShape) throw IndexOutOfShapeException(shape, index)
}
}

View File

@ -12,7 +12,7 @@ import kotlin.native.concurrent.ThreadLocal
* A converter from linear index to multivariate index * A converter from linear index to multivariate index
*/ */
public interface ShapeIndexer : Iterable<IntArray> { public interface ShapeIndexer : Iterable<IntArray> {
public val shape: Shape public val shape: ShapeND
/** /**
* Get linear index from multidimensional index * Get linear index from multidimensional index
@ -68,7 +68,7 @@ public abstract class Strides : ShapeIndexer {
/** /**
* Column-first [Strides]. Columns are represented as continuous arrays * Column-first [Strides]. Columns are represented as continuous arrays
*/ */
public class ColumnStrides(override val shape: Shape) : Strides() { public class ColumnStrides(override val shape: ShapeND) : Strides() {
override val linearSize: Int get() = strides[shape.size] override val linearSize: Int get() = strides[shape.size]
/** /**
@ -118,7 +118,7 @@ public class ColumnStrides(override val shape: Shape) : Strides() {
* *
* @param shape the shape of the tensor. * @param shape the shape of the tensor.
*/ */
public class RowStrides(override val shape: Shape) : Strides() { public class RowStrides(override val shape: ShapeND) : Strides() {
override val strides: IntArray by lazy { override val strides: IntArray by lazy {
val nDim = shape.size val nDim = shape.size
@ -163,9 +163,9 @@ public class RowStrides(override val shape: Shape) : Strides() {
} }
@ThreadLocal @ThreadLocal
private val defaultStridesCache = HashMap<Shape, Strides>() private val defaultStridesCache = HashMap<ShapeND, Strides>()
/** /**
* Cached builder for default strides * Cached builder for default strides
*/ */
public fun Strides(shape: Shape): Strides = defaultStridesCache.getOrPut(shape) { RowStrides(shape) } public fun Strides(shape: ShapeND): Strides = defaultStridesCache.getOrPut(shape) { RowStrides(shape) }

View File

@ -0,0 +1,102 @@
/*
* 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 space.kscience.kmath.misc.UnsafeKMathAPI
import kotlin.jvm.JvmInline
/**
* A read-only ND shape
*/
@JvmInline
public value class ShapeND(@PublishedApi internal val array: IntArray) {
public val size: Int get() = array.size
public operator fun get(index: Int): Int = array[index]
override fun toString(): String = array.contentToString()
}
public inline fun ShapeND.forEach(block: (value: Int) -> Unit): Unit = array.forEach(block)
public inline fun ShapeND.forEachIndexed(block: (index: Int, value: Int) -> Unit): Unit = array.forEachIndexed(block)
public infix fun ShapeND.contentEquals(other: ShapeND): Boolean = array.contentEquals(other.array)
public fun ShapeND.contentHashCode(): Int = array.contentHashCode()
public val ShapeND.indices: IntRange get() = array.indices
public val ShapeND.linearSize: Int get() = array.reduce(Int::times)
public fun ShapeND.slice(range: IntRange): ShapeND = ShapeND(array.sliceArray(range))
public fun ShapeND.last(): Int = array.last()
/**
* A shape including last [n] dimensions of this shape
*/
public fun ShapeND.last(n: Int): ShapeND = ShapeND(array.copyOfRange(size - n, size))
public fun ShapeND.first(): Int = array.first()
/**
* A shape including first [n] dimensions of this shape
*/
public fun ShapeND.first(n: Int): ShapeND = ShapeND(array.copyOfRange(0, n))
public operator fun ShapeND.plus(add: IntArray): ShapeND = ShapeND(array + add)
public operator fun ShapeND.plus(add: ShapeND): ShapeND = ShapeND(array + add.array)
public fun ShapeND.isEmpty(): Boolean = size == 0
public fun ShapeND.isNotEmpty(): Boolean = size > 0
public fun ShapeND.transposed(i: Int, j: Int): ShapeND = ShapeND(array.copyOf().apply {
val ith = get(i)
val jth = get(j)
set(i, jth)
set(j, ith)
})
public operator fun ShapeND.component1(): Int = get(0)
public operator fun ShapeND.component2(): Int = get(1)
public operator fun ShapeND.component3(): Int = get(2)
/**
* Convert to array with protective copy
*/
public fun ShapeND.toArray(): IntArray = array.copyOf()
@UnsafeKMathAPI
public fun ShapeND.asArray(): IntArray = array
public fun ShapeND.asList(): List<Int> = array.asList()
/**
* An exception is thrown when the expected and actual shape of NDArray differ.
*
* @property expected the expected shape.
* @property actual the actual shape.
*/
public class ShapeMismatchException(public val expected: ShapeND, public val actual: ShapeND) :
RuntimeException("Shape $actual doesn't fit in expected shape ${expected}.")
public class IndexOutOfShapeException(public val shape: ShapeND, public val index: IntArray) :
RuntimeException("Index ${index.contentToString()} is out of shape ${shape}")
public fun ShapeND(shapeFirst: Int, vararg shapeRest: Int): ShapeND = ShapeND(intArrayOf(shapeFirst, *shapeRest))
public interface WithShape {
public val shape: ShapeND
public val indices: ShapeIndexer get() = ColumnStrides(shape)
}
internal fun requireIndexInShape(index: IntArray, shape: ShapeND) {
if (index.size != shape.size) throw IndexOutOfShapeException(shape, index)
shape.forEachIndexed { axis, axisShape ->
if (index[axis] !in 0 until axisShape) throw IndexOutOfShapeException(shape, index)
}
}

View File

@ -18,7 +18,7 @@ public sealed class ShortRingOpsND : BufferedRingOpsND<Short, ShortRing>(ShortRi
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public class ShortRingND( public class ShortRingND(
override val shape: Shape override val shape: ShapeND
) : ShortRingOpsND(), RingND<Short, ShortRing>, NumbersAddOps<StructureND<Short>> { ) : ShortRingOpsND(), RingND<Short, ShortRing>, NumbersAddOps<StructureND<Short>> {
override fun number(value: Number): BufferND<Short> { override fun number(value: Number): BufferND<Short> {
@ -30,5 +30,5 @@ public class ShortRingND(
public inline fun <R> ShortRing.withNdAlgebra(vararg shape: Int, action: ShortRingND.() -> R): R { public inline fun <R> ShortRing.withNdAlgebra(vararg shape: Int, action: ShortRingND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ShortRingND(Shape(shape)).run(action) return ShortRingND(ShapeND(shape)).run(action)
} }

View File

@ -46,7 +46,7 @@ public interface MutableStructure1D<T> : Structure1D<T>, MutableStructureND<T>,
*/ */
@JvmInline @JvmInline
private value class Structure1DWrapper<out T>(val structure: StructureND<T>) : Structure1D<T> { private value class Structure1DWrapper<out T>(val structure: StructureND<T>) : Structure1D<T> {
override val shape: Shape get() = structure.shape override val shape: ShapeND get() = structure.shape
override val size: Int get() = structure.shape[0] override val size: Int get() = structure.shape[0]
@PerformancePitfall @PerformancePitfall
@ -60,7 +60,7 @@ private value class Structure1DWrapper<out T>(val structure: StructureND<T>) : S
* A 1D wrapper for a mutable nd-structure * A 1D wrapper for a mutable nd-structure
*/ */
private class MutableStructure1DWrapper<T>(val structure: MutableStructureND<T>) : MutableStructure1D<T> { private class MutableStructure1DWrapper<T>(val structure: MutableStructureND<T>) : MutableStructure1D<T> {
override val shape: Shape get() = structure.shape override val shape: ShapeND get() = structure.shape
override val size: Int get() = structure.shape[0] override val size: Int get() = structure.shape[0]
@PerformancePitfall @PerformancePitfall
@ -90,7 +90,7 @@ private class MutableStructure1DWrapper<T>(val structure: MutableStructureND<T>)
*/ */
@JvmInline @JvmInline
private value class Buffer1DWrapper<out T>(val buffer: Buffer<T>) : Structure1D<T> { private value class Buffer1DWrapper<out T>(val buffer: Buffer<T>) : Structure1D<T> {
override val shape: Shape get() = Shape(buffer.size) override val shape: ShapeND get() = ShapeND(buffer.size)
override val size: Int get() = buffer.size override val size: Int get() = buffer.size
@PerformancePitfall @PerformancePitfall
@ -102,7 +102,7 @@ private value class Buffer1DWrapper<out T>(val buffer: Buffer<T>) : Structure1D<
} }
internal class MutableBuffer1DWrapper<T>(val buffer: MutableBuffer<T>) : MutableStructure1D<T> { internal class MutableBuffer1DWrapper<T>(val buffer: MutableBuffer<T>) : MutableStructure1D<T> {
override val shape: Shape get() = Shape(buffer.size) override val shape: ShapeND get() = ShapeND(buffer.size)
override val size: Int get() = buffer.size override val size: Int get() = buffer.size
@PerformancePitfall @PerformancePitfall

View File

@ -29,7 +29,7 @@ public interface Structure2D<out T> : StructureND<T> {
*/ */
public val colNum: Int public val colNum: Int
override val shape: Shape get() = Shape(rowNum, colNum) override val shape: ShapeND get() = ShapeND(rowNum, colNum)
/** /**
* The buffer of rows of this structure. It gets elements from the structure dynamically. * The buffer of rows of this structure. It gets elements from the structure dynamically.
@ -102,7 +102,7 @@ public interface MutableStructure2D<T> : Structure2D<T>, MutableStructureND<T> {
*/ */
@JvmInline @JvmInline
private value class Structure2DWrapper<out T>(val structure: StructureND<T>) : Structure2D<T> { private value class Structure2DWrapper<out T>(val structure: StructureND<T>) : Structure2D<T> {
override val shape: Shape get() = structure.shape override val shape: ShapeND get() = structure.shape
override val rowNum: Int get() = shape[0] override val rowNum: Int get() = shape[0]
override val colNum: Int get() = shape[1] override val colNum: Int get() = shape[1]
@ -120,7 +120,7 @@ private value class Structure2DWrapper<out T>(val structure: StructureND<T>) : S
* A 2D wrapper for a mutable nd-structure * A 2D wrapper for a mutable nd-structure
*/ */
private class MutableStructure2DWrapper<T>(val structure: MutableStructureND<T>) : MutableStructure2D<T> { private class MutableStructure2DWrapper<T>(val structure: MutableStructureND<T>) : MutableStructure2D<T> {
override val shape: Shape get() = structure.shape override val shape: ShapeND get() = structure.shape
override val rowNum: Int get() = shape[0] override val rowNum: Int get() = shape[0]
override val colNum: Int get() = shape[1] override val colNum: Int get() = shape[1]

View File

@ -33,7 +33,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
* The shape of structure i.e., non-empty sequence of non-negative integers that specify sizes of dimensions of * The shape of structure i.e., non-empty sequence of non-negative integers that specify sizes of dimensions of
* this structure. * this structure.
*/ */
override val shape: Shape override val shape: ShapeND
/** /**
* The count of dimensions in this structure. It should be equal to size of [shape]. * The count of dimensions in this structure. It should be equal to size of [shape].
@ -147,13 +147,13 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
): BufferND<T> = BufferND(strides, Buffer.auto(type, strides.linearSize) { i -> initializer(strides.index(i)) }) ): BufferND<T> = BufferND(strides, Buffer.auto(type, strides.linearSize) { i -> initializer(strides.index(i)) })
public fun <T> buffered( public fun <T> buffered(
shape: Shape, shape: ShapeND,
bufferFactory: BufferFactory<T> = BufferFactory.boxing(), bufferFactory: BufferFactory<T> = BufferFactory.boxing(),
initializer: (IntArray) -> T, initializer: (IntArray) -> T,
): BufferND<T> = buffered(ColumnStrides(shape), bufferFactory, initializer) ): BufferND<T> = buffered(ColumnStrides(shape), bufferFactory, initializer)
public inline fun <reified T : Any> auto( public inline fun <reified T : Any> auto(
shape: Shape, shape: ShapeND,
crossinline initializer: (IntArray) -> T, crossinline initializer: (IntArray) -> T,
): BufferND<T> = auto(ColumnStrides(shape), initializer) ): BufferND<T> = auto(ColumnStrides(shape), initializer)
@ -162,13 +162,13 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
vararg shape: Int, vararg shape: Int,
crossinline initializer: (IntArray) -> T, crossinline initializer: (IntArray) -> T,
): BufferND<T> = ): BufferND<T> =
auto(ColumnStrides(Shape(shape)), initializer) auto(ColumnStrides(ShapeND(shape)), initializer)
public inline fun <T : Any> auto( public inline fun <T : Any> auto(
type: KClass<T>, type: KClass<T>,
vararg shape: Int, vararg shape: Int,
crossinline initializer: (IntArray) -> T, crossinline initializer: (IntArray) -> T,
): BufferND<T> = auto(type, ColumnStrides(Shape(shape)), initializer) ): BufferND<T> = auto(type, ColumnStrides(ShapeND(shape)), initializer)
} }
} }

View File

@ -9,7 +9,7 @@ import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
public open class VirtualStructureND<T>( public open class VirtualStructureND<T>(
override val shape: Shape, override val shape: ShapeND,
public val producer: (IntArray) -> T, public val producer: (IntArray) -> T,
) : StructureND<T> { ) : StructureND<T> {
@ -22,12 +22,12 @@ public open class VirtualStructureND<T>(
@UnstableKMathAPI @UnstableKMathAPI
public class VirtualDoubleStructureND( public class VirtualDoubleStructureND(
shape: Shape, shape: ShapeND,
producer: (IntArray) -> Double, producer: (IntArray) -> Double,
) : VirtualStructureND<Double>(shape, producer) ) : VirtualStructureND<Double>(shape, producer)
@UnstableKMathAPI @UnstableKMathAPI
public class VirtualIntStructureND( public class VirtualIntStructureND(
shape: Shape, shape: ShapeND,
producer: (IntArray) -> Int, producer: (IntArray) -> Int,
) : VirtualStructureND<Int>(shape, producer) ) : VirtualStructureND<Int>(shape, producer)

View File

@ -15,9 +15,9 @@ public fun <T, A : Algebra<T>> AlgebraND<T, A>.structureND(
shapeFirst: Int, shapeFirst: Int,
vararg shapeRest: Int, vararg shapeRest: Int,
initializer: A.(IntArray) -> T initializer: A.(IntArray) -> T
): StructureND<T> = structureND(Shape(shapeFirst, *shapeRest), initializer) ): StructureND<T> = structureND(ShapeND(shapeFirst, *shapeRest), initializer)
public fun <T, A : Group<T>> AlgebraND<T, A>.zero(shape: Shape): StructureND<T> = structureND(shape) { zero } public fun <T, A : Group<T>> AlgebraND<T, A>.zero(shape: ShapeND): StructureND<T> = structureND(shape) { zero }
@JvmName("zeroVarArg") @JvmName("zeroVarArg")
public fun <T, A : Group<T>> AlgebraND<T, A>.zero( public fun <T, A : Group<T>> AlgebraND<T, A>.zero(
@ -25,7 +25,7 @@ public fun <T, A : Group<T>> AlgebraND<T, A>.zero(
vararg shapeRest: Int, vararg shapeRest: Int,
): StructureND<T> = structureND(shapeFirst, *shapeRest) { zero } ): StructureND<T> = structureND(shapeFirst, *shapeRest) { zero }
public fun <T, A : Ring<T>> AlgebraND<T, A>.one(shape: Shape): StructureND<T> = structureND(shape) { one } public fun <T, A : Ring<T>> AlgebraND<T, A>.one(shape: ShapeND): StructureND<T> = structureND(shape) { one }
@JvmName("oneVarArg") @JvmName("oneVarArg")
public fun <T, A : Ring<T>> AlgebraND<T, A>.one( public fun <T, A : Ring<T>> AlgebraND<T, A>.one(

View File

@ -28,7 +28,7 @@ internal class BufferAccessor2D<T>(
//TODO optimize wrapper //TODO optimize wrapper
fun MutableBuffer<T>.collect(): Structure2D<T> = StructureND.buffered( fun MutableBuffer<T>.collect(): Structure2D<T> = StructureND.buffered(
ColumnStrides(Shape(rowNum, colNum)), ColumnStrides(ShapeND(rowNum, colNum)),
factory factory
) { (i, j) -> ) { (i, j) ->
get(i, j) get(i, j)

View File

@ -10,7 +10,7 @@ import kotlin.test.Test
class StridesTest { class StridesTest {
@Test @Test
fun checkRowBasedStrides() { fun checkRowBasedStrides() {
val strides = RowStrides(Shape(3, 3)) val strides = RowStrides(ShapeND(3, 3))
var counter = 0 var counter = 0
for(i in 0..2){ for(i in 0..2){
for(j in 0..2){ for(j in 0..2){
@ -24,7 +24,7 @@ class StridesTest {
@Test @Test
fun checkColumnBasedStrides() { fun checkColumnBasedStrides() {
val strides = ColumnStrides(Shape(3, 3)) val strides = ColumnStrides(ShapeND(3, 3))
var counter = 0 var counter = 0
for(i in 0..2){ for(i in 0..2){
for(j in 0..2){ for(j in 0..2){

View File

@ -9,12 +9,12 @@ import kotlinx.coroutines.*
import space.kscience.kmath.coroutines.Math import space.kscience.kmath.coroutines.Math
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.ColumnStrides
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
public class LazyStructureND<out T>( public class LazyStructureND<out T>(
public val scope: CoroutineScope, public val scope: CoroutineScope,
override val shape: Shape, override val shape: ShapeND,
public val function: suspend (IntArray) -> T, public val function: suspend (IntArray) -> T,
) : StructureND<T> { ) : StructureND<T> {
private val cache: MutableMap<IntArray, Deferred<T>> = HashMap() private val cache: MutableMap<IntArray, Deferred<T>> = HashMap()
@ -35,20 +35,24 @@ public class LazyStructureND<out T>(
} }
} }
@OptIn(PerformancePitfall::class)
public fun <T> StructureND<T>.async(index: IntArray): Deferred<T> = public fun <T> StructureND<T>.async(index: IntArray): Deferred<T> =
if (this is LazyStructureND<T>) this@async.async(index) else CompletableDeferred(get(index)) if (this is LazyStructureND<T>) this@async.async(index) else CompletableDeferred(get(index))
@OptIn(PerformancePitfall::class)
public suspend fun <T> StructureND<T>.await(index: IntArray): T = public suspend fun <T> StructureND<T>.await(index: IntArray): T =
if (this is LazyStructureND<T>) await(index) else get(index) if (this is LazyStructureND<T>) await(index) else get(index)
/** /**
* PENDING would benefit from KEEP-176 * PENDING would benefit from KEEP-176
*/ */
@OptIn(PerformancePitfall::class)
public inline fun <T, R> StructureND<T>.mapAsyncIndexed( public inline fun <T, R> StructureND<T>.mapAsyncIndexed(
scope: CoroutineScope, scope: CoroutineScope,
crossinline function: suspend (T, index: IntArray) -> R, crossinline function: suspend (T, index: IntArray) -> R,
): LazyStructureND<R> = LazyStructureND(scope, shape) { index -> function(get(index), index) } ): LazyStructureND<R> = LazyStructureND(scope, shape) { index -> function(get(index), index) }
@OptIn(PerformancePitfall::class)
public inline fun <T, R> StructureND<T>.mapAsync( public inline fun <T, R> StructureND<T>.mapAsync(
scope: CoroutineScope, scope: CoroutineScope,
crossinline function: suspend (T) -> R, crossinline function: suspend (T) -> R,

View File

@ -6,7 +6,7 @@
package space.kscience.kmath.dimensions package space.kscience.kmath.dimensions
import space.kscience.kmath.linear.* import space.kscience.kmath.linear.*
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
@ -48,7 +48,7 @@ public interface DMatrix<out T, R : Dimension, C : Dimension> : Structure2D<T> {
public value class DMatrixWrapper<out T, R : Dimension, C : Dimension>( public value class DMatrixWrapper<out T, R : Dimension, C : Dimension>(
private val structure: Structure2D<T>, private val structure: Structure2D<T>,
) : DMatrix<T, R, C> { ) : DMatrix<T, R, C> {
override val shape: Shape get() = structure.shape override val shape: ShapeND get() = structure.shape
override val rowNum: Int get() = shape[0] override val rowNum: Int get() = shape[0]
override val colNum: Int get() = shape[1] override val colNum: Int get() = shape[1]
override operator fun get(i: Int, j: Int): T = structure[i, j] override operator fun get(i: Int, j: Int): T = structure[i, j]

View File

@ -10,7 +10,7 @@ import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.ColumnStrides
import space.kscience.kmath.nd.FieldOpsND import space.kscience.kmath.nd.FieldOpsND
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.Group import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.ScaleOperations
@ -45,7 +45,7 @@ public class HistogramND<T : Comparable<T>, D : Domain<T>, V : Any>(
*/ */
public interface HistogramGroupND<T : Comparable<T>, D : Domain<T>, V : Any> : public interface HistogramGroupND<T : Comparable<T>, D : Domain<T>, V : Any> :
Group<HistogramND<T, D, V>>, ScaleOperations<HistogramND<T, D, V>> { Group<HistogramND<T, D, V>>, ScaleOperations<HistogramND<T, D, V>> {
public val shape: Shape public val shape: ShapeND
public val valueAlgebraND: FieldOpsND<V, *> //= NDAlgebra.space(valueSpace, Buffer.Companion::boxing, *shape), public val valueAlgebraND: FieldOpsND<V, *> //= NDAlgebra.space(valueSpace, Buffer.Companion::boxing, *shape),
/** /**

View File

@ -39,7 +39,7 @@ public class UniformHistogramGroupND<V : Any, A : Field<V>>(
public val dimension: Int get() = lower.size public val dimension: Int get() = lower.size
override val shape: Shape = Shape(IntArray(binNums.size) { binNums[it] + 2 }) override val shape: ShapeND = ShapeND(IntArray(binNums.size) { binNums[it] + 2 })
private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] } private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] }

View File

@ -7,13 +7,13 @@ package space.kscience.kmath.multik
import org.jetbrains.kotlinx.multik.ndarray.data.* import org.jetbrains.kotlinx.multik.ndarray.data.*
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.api.Tensor
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@JvmInline @JvmInline
public value class MultikTensor<T>(public val array: MutableMultiArray<T, DN>) : Tensor<T> { public value class MultikTensor<T>(public val array: MutableMultiArray<T, DN>) : Tensor<T> {
override val shape: Shape get() = Shape(array.shape) override val shape: ShapeND get() = ShapeND(array.shape)
@PerformancePitfall @PerformancePitfall
override fun get(index: IntArray): T = array[index] override fun get(index: IntArray): T = array[index]

View File

@ -32,7 +32,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
protected val multikStat: Statistics = multikEngine.getStatistics() protected val multikStat: Statistics = multikEngine.getStatistics()
@OptIn(UnsafeKMathAPI::class) @OptIn(UnsafeKMathAPI::class)
override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): MultikTensor<T> { override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): MultikTensor<T> {
val strides = ColumnStrides(shape) val strides = ColumnStrides(shape)
val memoryView = initMemoryView<T>(strides.linearSize, type) val memoryView = initMemoryView<T>(strides.linearSize, type)
strides.asSequence().forEachIndexed { linearIndex, tensorIndex -> strides.asSequence().forEachIndexed { linearIndex, tensorIndex ->
@ -123,7 +123,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
public fun MutableMultiArray<T, *>.wrap(): MultikTensor<T> = MultikTensor(this.asDNArray()) public fun MutableMultiArray<T, *>.wrap(): MultikTensor<T> = MultikTensor(this.asDNArray())
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
override fun StructureND<T>.valueOrNull(): T? = if (shape contentEquals Shape(1)) { override fun StructureND<T>.valueOrNull(): T? = if (shape contentEquals ShapeND(1)) {
get(intArrayOf(0)) get(intArrayOf(0))
} else null } else null
@ -211,7 +211,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
override fun StructureND<T>.transposed(i: Int, j: Int): MultikTensor<T> = asMultik().array.transpose(i, j).wrap() override fun StructureND<T>.transposed(i: Int, j: Int): MultikTensor<T> = asMultik().array.transpose(i, j).wrap()
override fun Tensor<T>.view(shape: Shape): MultikTensor<T> { override fun Tensor<T>.view(shape: ShapeND): MultikTensor<T> {
require(shape.asList().all { it > 0 }) require(shape.asList().all { it > 0 })
require(shape.linearSize == this.shape.size) { require(shape.linearSize == this.shape.size) {
"Cannot reshape array of size ${this.shape.size} into a new shape ${ "Cannot reshape array of size ${this.shape.size} into a new shape ${
@ -223,7 +223,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
} }
val mt = asMultik().array val mt = asMultik().array
return if (Shape(mt.shape).contentEquals(shape)) { return if (ShapeND(mt.shape).contentEquals(shape)) {
mt mt
} else { } else {
@OptIn(UnsafeKMathAPI::class) @OptIn(UnsafeKMathAPI::class)

View File

@ -7,7 +7,7 @@ package space.kscience.kmath.multik
import org.jetbrains.kotlinx.multik.default.DefaultEngine import org.jetbrains.kotlinx.multik.default.DefaultEngine
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.one import space.kscience.kmath.nd.one
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
@ -29,8 +29,8 @@ internal class MultikNDTest {
fun dotResult() { fun dotResult() {
val dim = 100 val dim = 100
val tensor1 = DoubleTensorAlgebra.randomNormal(shape = Shape(dim, dim), 12224) val tensor1 = DoubleTensorAlgebra.randomNormal(shape = ShapeND(dim, dim), 12224)
val tensor2 = DoubleTensorAlgebra.randomNormal(shape = Shape(dim, dim), 12225) val tensor2 = DoubleTensorAlgebra.randomNormal(shape = ShapeND(dim, dim), 12225)
val multikResult = with(multikAlgebra) { val multikResult = with(multikAlgebra) {
tensor1 dot tensor2 tensor1 dot tensor2

View File

@ -33,7 +33,8 @@ public sealed interface Nd4jArrayAlgebra<T, out C : Algebra<T>> : AlgebraND<T, C
*/ */
public val StructureND<T>.ndArray: INDArray public val StructureND<T>.ndArray: INDArray
override fun structureND(shape: Shape, initializer: C.(IntArray) -> T): Nd4jArrayStructure<T> { @OptIn(PerformancePitfall::class)
override fun structureND(shape: ShapeND, initializer: C.(IntArray) -> T): Nd4jArrayStructure<T> {
@OptIn(UnsafeKMathAPI::class) @OptIn(UnsafeKMathAPI::class)
val struct: Nd4jArrayStructure<T> = Nd4j.create(*shape.asArray())!!.wrap() val struct: Nd4jArrayStructure<T> = Nd4j.create(*shape.asArray())!!.wrap()
struct.indicesIterator().forEach { struct[it] = elementAlgebra.initializer(it) } struct.indicesIterator().forEach { struct[it] = elementAlgebra.initializer(it) }
@ -224,10 +225,10 @@ public open class DoubleNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps<Double, Do
public val DoubleField.nd4j: DoubleNd4jArrayFieldOps get() = DoubleNd4jArrayFieldOps public val DoubleField.nd4j: DoubleNd4jArrayFieldOps get() = DoubleNd4jArrayFieldOps
public class DoubleNd4jArrayField(override val shape: Shape) : DoubleNd4jArrayFieldOps(), FieldND<Double, DoubleField> public class DoubleNd4jArrayField(override val shape: ShapeND) : DoubleNd4jArrayFieldOps(), FieldND<Double, DoubleField>
public fun DoubleField.nd4j(shapeFirst: Int, vararg shapeRest: Int): DoubleNd4jArrayField = public fun DoubleField.nd4j(shapeFirst: Int, vararg shapeRest: Int): DoubleNd4jArrayField =
DoubleNd4jArrayField(Shape(shapeFirst, * shapeRest)) DoubleNd4jArrayField(ShapeND(shapeFirst, * shapeRest))
/** /**
@ -271,12 +272,12 @@ public open class FloatNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps<Float, Floa
public companion object : FloatNd4jArrayFieldOps() public companion object : FloatNd4jArrayFieldOps()
} }
public class FloatNd4jArrayField(override val shape: Shape) : FloatNd4jArrayFieldOps(), RingND<Float, FloatField> public class FloatNd4jArrayField(override val shape: ShapeND) : FloatNd4jArrayFieldOps(), RingND<Float, FloatField>
public val FloatField.nd4j: FloatNd4jArrayFieldOps get() = FloatNd4jArrayFieldOps public val FloatField.nd4j: FloatNd4jArrayFieldOps get() = FloatNd4jArrayFieldOps
public fun FloatField.nd4j(shapeFirst: Int, vararg shapeRest: Int): FloatNd4jArrayField = public fun FloatField.nd4j(shapeFirst: Int, vararg shapeRest: Int): FloatNd4jArrayField =
FloatNd4jArrayField(Shape(shapeFirst, * shapeRest)) FloatNd4jArrayField(ShapeND(shapeFirst, * shapeRest))
/** /**
* Represents [RingND] over [Nd4jArrayIntStructure]. * Represents [RingND] over [Nd4jArrayIntStructure].
@ -312,7 +313,7 @@ public open class IntNd4jArrayRingOps : Nd4jArrayRingOps<Int, IntRing> {
public val IntRing.nd4j: IntNd4jArrayRingOps get() = IntNd4jArrayRingOps public val IntRing.nd4j: IntNd4jArrayRingOps get() = IntNd4jArrayRingOps
public class IntNd4jArrayRing(override val shape: Shape) : IntNd4jArrayRingOps(), RingND<Int, IntRing> public class IntNd4jArrayRing(override val shape: ShapeND) : IntNd4jArrayRingOps(), RingND<Int, IntRing>
public fun IntRing.nd4j(shapeFirst: Int, vararg shapeRest: Int): IntNd4jArrayRing = public fun IntRing.nd4j(shapeFirst: Int, vararg shapeRest: Int): IntNd4jArrayRing =
IntNd4jArrayRing(Shape(shapeFirst, * shapeRest)) IntNd4jArrayRing(ShapeND(shapeFirst, * shapeRest))

View File

@ -21,7 +21,7 @@ public sealed class Nd4jArrayStructure<T> : MutableStructureND<T> {
*/ */
public abstract val ndArray: INDArray public abstract val ndArray: INDArray
override val shape: Shape get() = Shape(ndArray.shape().toIntArray()) override val shape: ShapeND get() = ShapeND(ndArray.shape().toIntArray())
internal abstract fun elementsIterator(): Iterator<Pair<IntArray, T>> internal abstract fun elementsIterator(): Iterator<Pair<IntArray, T>>
internal fun indicesIterator(): Iterator<IntArray> = ndArray.indicesIterator() internal fun indicesIterator(): Iterator<IntArray> = ndArray.indicesIterator()

View File

@ -37,7 +37,7 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
*/ */
public val StructureND<T>.ndArray: INDArray public val StructureND<T>.ndArray: INDArray
override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): Nd4jArrayStructure<T> override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): Nd4jArrayStructure<T>
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
override fun StructureND<T>.map(transform: A.(T) -> T): Nd4jArrayStructure<T> = override fun StructureND<T>.map(transform: A.(T) -> T): Nd4jArrayStructure<T> =
@ -108,7 +108,7 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
ndArray.max(keepDim, dim).wrap() ndArray.max(keepDim, dim).wrap()
@OptIn(UnsafeKMathAPI::class) @OptIn(UnsafeKMathAPI::class)
override fun Tensor<T>.view(shape: Shape): Nd4jArrayStructure<T> = ndArray.reshape(shape.asArray()).wrap() override fun Tensor<T>.view(shape: ShapeND): Nd4jArrayStructure<T> = ndArray.reshape(shape.asArray()).wrap()
override fun Tensor<T>.viewAs(other: StructureND<T>): Nd4jArrayStructure<T> = view(other.shape) override fun Tensor<T>.viewAs(other: StructureND<T>): Nd4jArrayStructure<T> = view(other.shape)
@ -178,7 +178,7 @@ public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra<Double, DoubleField> {
override fun INDArray.wrap(): Nd4jArrayStructure<Double> = asDoubleStructure() override fun INDArray.wrap(): Nd4jArrayStructure<Double> = asDoubleStructure()
@OptIn(UnsafeKMathAPI::class) @OptIn(UnsafeKMathAPI::class)
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): Nd4jArrayStructure<Double> { override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): Nd4jArrayStructure<Double> {
val array: INDArray = Nd4j.zeros(*shape.asArray()) val array: INDArray = Nd4j.zeros(*shape.asArray())
val indices = ColumnStrides(shape) val indices = ColumnStrides(shape)
indices.asSequence().forEach { index -> indices.asSequence().forEach { index ->
@ -198,7 +198,7 @@ public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra<Double, DoubleField> {
} }
override fun StructureND<Double>.valueOrNull(): Double? = override fun StructureND<Double>.valueOrNull(): Double? =
if (shape contentEquals Shape(1)) ndArray.getDouble(0) else null if (shape contentEquals ShapeND(1)) ndArray.getDouble(0) else null
// TODO rewrite // TODO rewrite
override fun diagonalEmbedding( override fun diagonalEmbedding(

View File

@ -14,7 +14,7 @@ import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.ColumnStrides
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.PowerOperations import space.kscience.kmath.operations.PowerOperations
@ -28,7 +28,7 @@ public class DoubleTensorFlowOutput(
} }
internal fun Shape.toLongArray(): LongArray = LongArray(size) { get(it).toLong() } internal fun ShapeND.toLongArray(): LongArray = LongArray(size) { get(it).toLong() }
public class DoubleTensorFlowAlgebra internal constructor( public class DoubleTensorFlowAlgebra internal constructor(
graph: Graph, graph: Graph,
@ -37,7 +37,7 @@ public class DoubleTensorFlowAlgebra internal constructor(
override val elementAlgebra: DoubleField get() = DoubleField override val elementAlgebra: DoubleField get() = DoubleField
override fun structureND( override fun structureND(
shape: Shape, shape: ShapeND,
initializer: DoubleField.(IntArray) -> Double, initializer: DoubleField.(IntArray) -> Double,
): StructureND<Double> { ): StructureND<Double> {
val res = TFloat64.tensorOf(org.tensorflow.ndarray.Shape.of(*shape.toLongArray())) { array -> val res = TFloat64.tensorOf(org.tensorflow.ndarray.Shape.of(*shape.toLongArray())) { array ->

View File

@ -20,7 +20,7 @@ import org.tensorflow.types.family.TType
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnsafeKMathAPI import space.kscience.kmath.misc.UnsafeKMathAPI
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.asArray import space.kscience.kmath.nd.asArray
import space.kscience.kmath.nd.contentEquals import space.kscience.kmath.nd.contentEquals
@ -41,12 +41,14 @@ public sealed interface TensorFlowTensor<T> : Tensor<T>
*/ */
@JvmInline @JvmInline
public value class TensorFlowArray<T>(public val tensor: NdArray<T>) : Tensor<T> { public value class TensorFlowArray<T>(public val tensor: NdArray<T>) : Tensor<T> {
override val shape: Shape get() = Shape(tensor.shape().asArray().toIntArray()) override val shape: ShapeND get() = ShapeND(tensor.shape().asArray().toIntArray())
@PerformancePitfall
override fun get(index: IntArray): T = tensor.getObject(*index.toLongArray()) override fun get(index: IntArray): T = tensor.getObject(*index.toLongArray())
//TODO implement native element sequence //TODO implement native element sequence
@PerformancePitfall
override fun set(index: IntArray, value: T) { override fun set(index: IntArray, value: T) {
tensor.setObject(value, *index.toLongArray()) tensor.setObject(value, *index.toLongArray())
} }
@ -65,7 +67,7 @@ public abstract class TensorFlowOutput<T, TT : TType>(
public var output: Output<TT> = output public var output: Output<TT> = output
internal set internal set
override val shape: Shape get() = Shape(output.shape().asArray().toIntArray()) override val shape: ShapeND get() = ShapeND(output.shape().asArray().toIntArray())
protected abstract fun org.tensorflow.Tensor.actualizeTensor(): NdArray<T> protected abstract fun org.tensorflow.Tensor.actualizeTensor(): NdArray<T>
@ -99,7 +101,7 @@ public abstract class TensorFlowAlgebra<T, TT : TNumber, A : Ring<T>> internal c
protected abstract fun const(value: T): Constant<TT> protected abstract fun const(value: T): Constant<TT>
override fun StructureND<T>.valueOrNull(): T? = if (shape contentEquals Shape(1)) override fun StructureND<T>.valueOrNull(): T? = if (shape contentEquals ShapeND(1))
get(intArrayOf(0)) else null get(intArrayOf(0)) else null
/** /**
@ -195,7 +197,7 @@ public abstract class TensorFlowAlgebra<T, TT : TNumber, A : Ring<T>> internal c
ops.linalg.transpose(it, ops.constant(intArrayOf(i, j))) ops.linalg.transpose(it, ops.constant(intArrayOf(i, j)))
} }
override fun Tensor<T>.view(shape: Shape): Tensor<T> = operate { override fun Tensor<T>.view(shape: ShapeND): Tensor<T> = operate {
@OptIn(UnsafeKMathAPI::class) @OptIn(UnsafeKMathAPI::class)
ops.reshape(it, ops.constant(shape.asArray())) ops.reshape(it, ops.constant(shape.asArray()))
} }

View File

@ -7,7 +7,7 @@ package space.kscience.kmath.tensorflow
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.get import space.kscience.kmath.nd.get
import space.kscience.kmath.nd.structureND import space.kscience.kmath.nd.structureND
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
@ -32,8 +32,8 @@ class DoubleTensorFlowOps {
fun dot(){ fun dot(){
val dim = 1000 val dim = 1000
val tensor1 = DoubleTensorAlgebra.randomNormal(shape = Shape(dim, dim), 12224) val tensor1 = DoubleTensorAlgebra.randomNormal(shape = ShapeND(dim, dim), 12224)
val tensor2 = DoubleTensorAlgebra.randomNormal(shape = Shape(dim, dim), 12225) val tensor2 = DoubleTensorAlgebra.randomNormal(shape = ShapeND(dim, dim), 12225)
DoubleField.produceWithTF { DoubleField.produceWithTF {
tensor1 dot tensor2 tensor1 dot tensor2

View File

@ -6,7 +6,7 @@
package space.kscience.kmath.tensors.api package space.kscience.kmath.tensors.api
import space.kscience.kmath.nd.RingOpsND import space.kscience.kmath.nd.RingOpsND
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
@ -193,7 +193,7 @@ public interface TensorAlgebra<T, A : Ring<T>> : RingOpsND<T, A> {
* @param shape the desired size * @param shape the desired size
* @return tensor with new shape * @return tensor with new shape
*/ */
public fun Tensor<T>.view(shape: Shape): Tensor<T> public fun Tensor<T>.view(shape: ShapeND): Tensor<T>
/** /**
* View this tensor as the same size as [other]. * View this tensor as the same size as [other].

View File

@ -7,7 +7,7 @@ package space.kscience.kmath.tensors.core
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.RowStrides import space.kscience.kmath.nd.RowStrides
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.Strides import space.kscience.kmath.nd.Strides
import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.api.Tensor
@ -16,7 +16,7 @@ import space.kscience.kmath.tensors.api.Tensor
* Represents [Tensor] over a [MutableBuffer] intended to be used through [DoubleTensor] and [IntTensor] * Represents [Tensor] over a [MutableBuffer] intended to be used through [DoubleTensor] and [IntTensor]
*/ */
public abstract class BufferedTensor<T>( public abstract class BufferedTensor<T>(
override val shape: Shape, override val shape: ShapeND,
) : Tensor<T> { ) : Tensor<T> {
public abstract val source: MutableBuffer<T> public abstract val source: MutableBuffer<T>

View File

@ -84,7 +84,7 @@ public inline fun OffsetDoubleBuffer.mapInPlace(operation: (Double) -> Double) {
* [DoubleTensor] always uses row-based strides * [DoubleTensor] always uses row-based strides
*/ */
public class DoubleTensor( public class DoubleTensor(
shape: Shape, shape: ShapeND,
override val source: OffsetDoubleBuffer, override val source: OffsetDoubleBuffer,
) : BufferedTensor<Double>(shape), MutableStructureNDOfDouble { ) : BufferedTensor<Double>(shape), MutableStructureNDOfDouble {
@ -92,7 +92,7 @@ public class DoubleTensor(
require(linearSize == source.size) { "Source buffer size must be equal tensor size" } require(linearSize == source.size) { "Source buffer size must be equal tensor size" }
} }
public constructor(shape: Shape, buffer: DoubleBuffer) : this(shape, OffsetDoubleBuffer(buffer, 0, buffer.size)) public constructor(shape: ShapeND, buffer: DoubleBuffer) : this(shape, OffsetDoubleBuffer(buffer, 0, buffer.size))
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
@ -147,7 +147,7 @@ public value class DoubleTensor2D(public val tensor: DoubleTensor) : MutableStru
override fun elements(): Sequence<Pair<IntArray, Double>> = tensor.elements() override fun elements(): Sequence<Pair<IntArray, Double>> = tensor.elements()
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
override fun get(index: IntArray): Double = tensor[index] override fun get(index: IntArray): Double = tensor[index]
override val shape: Shape get() = tensor.shape override val shape: ShapeND get() = tensor.shape
} }
public fun DoubleTensor.asDoubleTensor2D(): DoubleTensor2D = DoubleTensor2D(this) public fun DoubleTensor.asDoubleTensor2D(): DoubleTensor2D = DoubleTensor2D(this)
@ -162,7 +162,7 @@ public inline fun DoubleTensor.forEachMatrix(block: (index: IntArray, matrix: Do
val n = shape.size val n = shape.size
check(n >= 2) { "Expected tensor with 2 or more dimensions, got size $n" } check(n >= 2) { "Expected tensor with 2 or more dimensions, got size $n" }
val matrixOffset = shape[n - 1] * shape[n - 2] val matrixOffset = shape[n - 1] * shape[n - 2]
val matrixShape = Shape(shape[n - 2], shape[n - 1]) val matrixShape = ShapeND(shape[n - 2], shape[n - 1])
val size = matrixShape.linearSize val size = matrixShape.linearSize
for (i in 0 until linearSize / matrixOffset) { for (i in 0 until linearSize / matrixOffset) {

View File

@ -92,7 +92,7 @@ public open class DoubleTensorAlgebra :
override fun StructureND<Double>.valueOrNull(): Double? { override fun StructureND<Double>.valueOrNull(): Double? {
val dt = asDoubleTensor() val dt = asDoubleTensor()
return if (dt.shape contentEquals Shape(1)) dt.source[0] else null return if (dt.shape contentEquals ShapeND(1)) dt.source[0] else null
} }
override fun StructureND<Double>.value(): Double = valueOrNull() override fun StructureND<Double>.value(): Double = valueOrNull()
@ -105,7 +105,7 @@ public open class DoubleTensorAlgebra :
* @param array one-dimensional data array. * @param array one-dimensional data array.
* @return tensor with the [shape] shape and [array] data. * @return tensor with the [shape] shape and [array] data.
*/ */
public fun fromArray(shape: Shape, array: DoubleArray): DoubleTensor { public fun fromArray(shape: ShapeND, array: DoubleArray): DoubleTensor {
checkNotEmptyShape(shape) checkNotEmptyShape(shape)
checkEmptyDoubleBuffer(array) checkEmptyDoubleBuffer(array)
checkBufferShapeConsistency(shape, array) checkBufferShapeConsistency(shape, array)
@ -119,7 +119,7 @@ public open class DoubleTensorAlgebra :
* @param initializer mapping tensor indices to values. * @param initializer mapping tensor indices to values.
* @return tensor with the [shape] shape and data generated by the [initializer]. * @return tensor with the [shape] shape and data generated by the [initializer].
*/ */
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): DoubleTensor = fromArray( override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): DoubleTensor = fromArray(
shape, shape,
RowStrides(shape).asSequence().map { DoubleField.initializer(it) }.toMutableList().toDoubleArray() RowStrides(shape).asSequence().map { DoubleField.initializer(it) }.toMutableList().toDoubleArray()
) )
@ -127,7 +127,7 @@ public open class DoubleTensorAlgebra :
override fun Tensor<Double>.getTensor(i: Int): DoubleTensor { override fun Tensor<Double>.getTensor(i: Int): DoubleTensor {
val dt = asDoubleTensor() val dt = asDoubleTensor()
val lastShape = shape.last(shape.size - 1) val lastShape = shape.last(shape.size - 1)
val newShape: Shape = if (lastShape.isNotEmpty()) lastShape else Shape(1) val newShape: ShapeND = if (lastShape.isNotEmpty()) lastShape else ShapeND(1)
return DoubleTensor( return DoubleTensor(
newShape, newShape,
dt.source.view(newShape.linearSize * i, newShape.linearSize) dt.source.view(newShape.linearSize * i, newShape.linearSize)
@ -141,7 +141,7 @@ public open class DoubleTensorAlgebra :
* @param shape array of integers defining the shape of the output tensor. * @param shape array of integers defining the shape of the output tensor.
* @return tensor with the [shape] shape and filled with [value]. * @return tensor with the [shape] shape and filled with [value].
*/ */
public fun full(value: Double, shape: Shape): DoubleTensor { public fun full(value: Double, shape: ShapeND): DoubleTensor {
checkNotEmptyShape(shape) checkNotEmptyShape(shape)
val buffer = DoubleBuffer(shape.linearSize) { value } val buffer = DoubleBuffer(shape.linearSize) { value }
return DoubleTensor(shape, buffer) return DoubleTensor(shape, buffer)
@ -165,7 +165,7 @@ public open class DoubleTensorAlgebra :
* @param shape array of integers defining the shape of the output tensor. * @param shape array of integers defining the shape of the output tensor.
* @return tensor filled with the scalar value `0.0`, with the [shape] shape. * @return tensor filled with the scalar value `0.0`, with the [shape] shape.
*/ */
public fun zeros(shape: Shape): DoubleTensor = full(0.0, shape) public fun zeros(shape: ShapeND): DoubleTensor = full(0.0, shape)
/** /**
* Returns a tensor filled with the scalar value `0.0`, with the same shape as a given array. * Returns a tensor filled with the scalar value `0.0`, with the same shape as a given array.
@ -180,7 +180,7 @@ public open class DoubleTensorAlgebra :
* @param shape array of integers defining the shape of the output tensor. * @param shape array of integers defining the shape of the output tensor.
* @return tensor filled with the scalar value `1.0`, with the [shape] shape. * @return tensor filled with the scalar value `1.0`, with the [shape] shape.
*/ */
public fun ones(shape: Shape): DoubleTensor = full(1.0, shape) public fun ones(shape: ShapeND): DoubleTensor = full(1.0, shape)
/** /**
* Returns a tensor filled with the scalar value `1.0`, with the same shape as a given array. * Returns a tensor filled with the scalar value `1.0`, with the same shape as a given array.
@ -196,7 +196,7 @@ public open class DoubleTensorAlgebra :
* @return a 2-D tensor with ones on the diagonal and zeros elsewhere. * @return a 2-D tensor with ones on the diagonal and zeros elsewhere.
*/ */
public fun eye(n: Int): DoubleTensor { public fun eye(n: Int): DoubleTensor {
val shape = Shape(n, n) val shape = ShapeND(n, n)
val buffer = DoubleBuffer(n * n) { 0.0 } val buffer = DoubleBuffer(n * n) { 0.0 }
val res = DoubleTensor(shape, buffer) val res = DoubleTensor(shape, buffer)
for (i in 0 until n) { for (i in 0 until n) {
@ -306,7 +306,7 @@ public open class DoubleTensorAlgebra :
// return resTensor // return resTensor
} }
override fun Tensor<Double>.view(shape: Shape): DoubleTensor { override fun Tensor<Double>.view(shape: ShapeND): DoubleTensor {
checkView(asDoubleTensor(), shape) checkView(asDoubleTensor(), shape)
return DoubleTensor(shape, asDoubleTensor().source) return DoubleTensor(shape, asDoubleTensor().source)
} }
@ -346,7 +346,7 @@ public open class DoubleTensorAlgebra :
@UnstableKMathAPI @UnstableKMathAPI
public infix fun StructureND<Double>.matmul(other: StructureND<Double>): DoubleTensor { public infix fun StructureND<Double>.matmul(other: StructureND<Double>): DoubleTensor {
if (shape.size == 1 && other.shape.size == 1) { if (shape.size == 1 && other.shape.size == 1) {
return DoubleTensor(Shape(1), DoubleBuffer(times(other).sum())) return DoubleTensor(ShapeND(1), DoubleBuffer(times(other).sum()))
} }
var penultimateDim = false var penultimateDim = false
@ -358,7 +358,7 @@ public open class DoubleTensorAlgebra :
if (shape.size == 1) { if (shape.size == 1) {
penultimateDim = true penultimateDim = true
newThis = newThis.view(Shape(1) + shape) newThis = newThis.view(ShapeND(1) + shape)
} }
if (other.shape.size == 1) { if (other.shape.size == 1) {
@ -396,7 +396,7 @@ public open class DoubleTensorAlgebra :
// } // }
return if (penultimateDim) { return if (penultimateDim) {
resTensor.view(resTensor.shape.first(resTensor.shape.size - 2) + Shape(resTensor.shape.last())) resTensor.view(resTensor.shape.first(resTensor.shape.size - 2) + ShapeND(resTensor.shape.last()))
} else if (lastDim) { } else if (lastDim) {
resTensor.view(resTensor.shape.first(resTensor.shape.size - 1)) resTensor.view(resTensor.shape.first(resTensor.shape.size - 1))
} else { } else {
@ -506,7 +506,7 @@ public open class DoubleTensorAlgebra :
* @return tensor of a given shape filled with numbers from the normal distribution * @return tensor of a given shape filled with numbers from the normal distribution
* with `0.0` mean and `1.0` standard deviation. * with `0.0` mean and `1.0` standard deviation.
*/ */
public fun randomNormal(shape: Shape, seed: Long = 0): DoubleTensor = public fun randomNormal(shape: ShapeND, seed: Long = 0): DoubleTensor =
DoubleTensor(shape, DoubleBuffer.randomNormals(shape.linearSize, seed)) DoubleTensor(shape, DoubleBuffer.randomNormals(shape.linearSize, seed))
/** /**
@ -531,7 +531,7 @@ public open class DoubleTensorAlgebra :
check(tensors.isNotEmpty()) { "List must have at least 1 element" } check(tensors.isNotEmpty()) { "List must have at least 1 element" }
val shape = tensors[0].shape val shape = tensors[0].shape
check(tensors.all { it.shape contentEquals shape }) { "Tensors must have same shapes" } check(tensors.all { it.shape contentEquals shape }) { "Tensors must have same shapes" }
val resShape = Shape(tensors.size) + shape val resShape = ShapeND(tensors.size) + shape
// val resBuffer: List<Double> = tensors.flatMap { // val resBuffer: List<Double> = tensors.flatMap {
// it.asDoubleTensor().source.array.drop(it.asDoubleTensor().bufferStart) // it.asDoubleTensor().source.array.drop(it.asDoubleTensor().bufferStart)
// .take(it.asDoubleTensor().linearSize) // .take(it.asDoubleTensor().linearSize)
@ -685,9 +685,9 @@ public open class DoubleTensorAlgebra :
check(tensors.isNotEmpty()) { "List must have at least 1 element" } check(tensors.isNotEmpty()) { "List must have at least 1 element" }
val n = tensors.size val n = tensors.size
val m = tensors[0].shape[0] val m = tensors[0].shape[0]
check(tensors.all { it.shape contentEquals Shape(m) }) { "Tensors must have same shapes" } check(tensors.all { it.shape contentEquals ShapeND(m) }) { "Tensors must have same shapes" }
val resTensor = DoubleTensor( val resTensor = DoubleTensor(
Shape(n, n), ShapeND(n, n),
DoubleBuffer(n * n) { 0.0 } DoubleBuffer(n * n) { 0.0 }
) )
for (i in 0 until n) { for (i in 0 until n) {
@ -875,9 +875,9 @@ public open class DoubleTensorAlgebra :
val size = dimension val size = dimension
val commonShape = shape.slice(0 until size - 2) val commonShape = shape.slice(0 until size - 2)
val (n, m) = shape.slice(size - 2 until size) val (n, m) = shape.slice(size - 2 until size)
val uTensor = zeros(commonShape + Shape(min(n, m), n)) val uTensor = zeros(commonShape + ShapeND(min(n, m), n))
val sTensor = zeros(commonShape + Shape(min(n, m))) val sTensor = zeros(commonShape + ShapeND(min(n, m)))
val vTensor = zeros(commonShape + Shape(min(n, m), m)) val vTensor = zeros(commonShape + ShapeND(min(n, m), m))
val matrices = asDoubleTensor().matrices val matrices = asDoubleTensor().matrices
val uTensors = uTensor.matrices val uTensors = uTensor.matrices
@ -988,7 +988,7 @@ public open class DoubleTensorAlgebra :
val n = shape.size val n = shape.size
val detTensorShape = Shape(IntArray(n - 1) { i -> shape[i] }.apply { val detTensorShape = ShapeND(IntArray(n - 1) { i -> shape[i] }.apply {
set(n - 2, 1) set(n - 2, 1)
}) })

View File

@ -6,7 +6,7 @@
package space.kscience.kmath.tensors.core package space.kscience.kmath.tensors.core
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.structures.* import space.kscience.kmath.structures.*
/** /**
@ -75,7 +75,7 @@ public inline fun OffsetIntBuffer.mapInPlace(operation: (Int) -> Int) {
* Default [BufferedTensor] implementation for [Int] values * Default [BufferedTensor] implementation for [Int] values
*/ */
public class IntTensor( public class IntTensor(
shape: Shape, shape: ShapeND,
override val source: OffsetIntBuffer, override val source: OffsetIntBuffer,
) : BufferedTensor<Int>(shape) { ) : BufferedTensor<Int>(shape) {
@ -83,7 +83,7 @@ public class IntTensor(
require(linearSize == source.size) { "Source buffer size must be equal tensor size" } require(linearSize == source.size) { "Source buffer size must be equal tensor size" }
} }
public constructor(shape: Shape, buffer: IntBuffer) : this(shape, OffsetIntBuffer(buffer, 0, buffer.size)) public constructor(shape: ShapeND, buffer: IntBuffer) : this(shape, OffsetIntBuffer(buffer, 0, buffer.size))
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
override fun get(index: IntArray): Int = this.source[indices.offset(index)] override fun get(index: IntArray): Int = this.source[indices.offset(index)]

View File

@ -89,7 +89,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
override fun StructureND<Int>.valueOrNull(): Int? { override fun StructureND<Int>.valueOrNull(): Int? {
val dt = asIntTensor() val dt = asIntTensor()
return if (dt.shape contentEquals Shape(1)) dt.source[0] else null return if (dt.shape contentEquals ShapeND(1)) dt.source[0] else null
} }
override fun StructureND<Int>.value(): Int = valueOrNull() override fun StructureND<Int>.value(): Int = valueOrNull()
@ -102,7 +102,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
* @param array one-dimensional data array. * @param array one-dimensional data array.
* @return tensor with the [shape] shape and [array] data. * @return tensor with the [shape] shape and [array] data.
*/ */
public fun fromArray(shape: Shape, array: IntArray): IntTensor { public fun fromArray(shape: ShapeND, array: IntArray): IntTensor {
checkNotEmptyShape(shape) checkNotEmptyShape(shape)
check(array.isNotEmpty()) { "Illegal empty buffer provided" } check(array.isNotEmpty()) { "Illegal empty buffer provided" }
check(array.size == shape.linearSize) { check(array.size == shape.linearSize) {
@ -118,7 +118,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
* @param initializer mapping tensor indices to values. * @param initializer mapping tensor indices to values.
* @return tensor with the [shape] shape and data generated by the [initializer]. * @return tensor with the [shape] shape and data generated by the [initializer].
*/ */
override fun structureND(shape: Shape, initializer: IntRing.(IntArray) -> Int): IntTensor = fromArray( override fun structureND(shape: ShapeND, initializer: IntRing.(IntArray) -> Int): IntTensor = fromArray(
shape, shape,
RowStrides(shape).asSequence().map { IntRing.initializer(it) }.toMutableList().toIntArray() RowStrides(shape).asSequence().map { IntRing.initializer(it) }.toMutableList().toIntArray()
) )
@ -126,7 +126,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
override fun Tensor<Int>.getTensor(i: Int): IntTensor { override fun Tensor<Int>.getTensor(i: Int): IntTensor {
val dt = asIntTensor() val dt = asIntTensor()
val lastShape = shape.last(shape.size - 1) val lastShape = shape.last(shape.size - 1)
val newShape = if (lastShape.isNotEmpty()) lastShape else Shape(1) val newShape = if (lastShape.isNotEmpty()) lastShape else ShapeND(1)
return IntTensor(newShape, dt.source.view(newShape.linearSize * i)) return IntTensor(newShape, dt.source.view(newShape.linearSize * i))
} }
@ -137,7 +137,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
* @param shape array of integers defining the shape of the output tensor. * @param shape array of integers defining the shape of the output tensor.
* @return tensor with the [shape] shape and filled with [value]. * @return tensor with the [shape] shape and filled with [value].
*/ */
public fun full(value: Int, shape: Shape): IntTensor { public fun full(value: Int, shape: ShapeND): IntTensor {
checkNotEmptyShape(shape) checkNotEmptyShape(shape)
val buffer = IntBuffer(shape.linearSize) { value } val buffer = IntBuffer(shape.linearSize) { value }
return IntTensor(shape, buffer) return IntTensor(shape, buffer)
@ -161,7 +161,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
* @param shape array of integers defining the shape of the output tensor. * @param shape array of integers defining the shape of the output tensor.
* @return tensor filled with the scalar value `0`, with the [shape] shape. * @return tensor filled with the scalar value `0`, with the [shape] shape.
*/ */
public fun zeros(shape: Shape): IntTensor = full(0, shape) public fun zeros(shape: ShapeND): IntTensor = full(0, shape)
/** /**
* Returns a tensor filled with the scalar value `0`, with the same shape as a given array. * Returns a tensor filled with the scalar value `0`, with the same shape as a given array.
@ -176,7 +176,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
* @param shape array of integers defining the shape of the output tensor. * @param shape array of integers defining the shape of the output tensor.
* @return tensor filled with the scalar value `1`, with the [shape] shape. * @return tensor filled with the scalar value `1`, with the [shape] shape.
*/ */
public fun ones(shape: Shape): IntTensor = full(1, shape) public fun ones(shape: ShapeND): IntTensor = full(1, shape)
/** /**
* Returns a tensor filled with the scalar value `1`, with the same shape as a given array. * Returns a tensor filled with the scalar value `1`, with the same shape as a given array.
@ -192,7 +192,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
* @return a 2-D tensor with ones on the diagonal and zeros elsewhere. * @return a 2-D tensor with ones on the diagonal and zeros elsewhere.
*/ */
public fun eye(n: Int): IntTensor { public fun eye(n: Int): IntTensor {
val shape = Shape(n, n) val shape = ShapeND(n, n)
val buffer = IntBuffer(n * n) { 0 } val buffer = IntBuffer(n * n) { 0 }
val res = IntTensor(shape, buffer) val res = IntTensor(shape, buffer)
for (i in 0 until n) { for (i in 0 until n) {
@ -287,7 +287,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
// return resTensor // return resTensor
} }
override fun Tensor<Int>.view(shape: Shape): IntTensor { override fun Tensor<Int>.view(shape: ShapeND): IntTensor {
checkView(asIntTensor(), shape) checkView(asIntTensor(), shape)
return IntTensor(shape, asIntTensor().source) return IntTensor(shape, asIntTensor().source)
} }
@ -388,7 +388,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
check(tensors.isNotEmpty()) { "List must have at least 1 element" } check(tensors.isNotEmpty()) { "List must have at least 1 element" }
val shape = tensors[0].shape val shape = tensors[0].shape
check(tensors.all { it.shape contentEquals shape }) { "Tensors must have same shapes" } check(tensors.all { it.shape contentEquals shape }) { "Tensors must have same shapes" }
val resShape = Shape(tensors.size) + shape val resShape = ShapeND(tensors.size) + shape
// val resBuffer: List<Int> = tensors.flatMap { // val resBuffer: List<Int> = tensors.flatMap {
// it.asIntTensor().source.array.drop(it.asIntTensor().bufferStart) // it.asIntTensor().source.array.drop(it.asIntTensor().bufferStart)
// .take(it.asIntTensor().linearSize) // .take(it.asIntTensor().linearSize)

View File

@ -32,7 +32,7 @@ internal fun multiIndexBroadCasting(tensor: DoubleTensor, resTensor: DoubleTenso
} }
} }
internal fun broadcastShapes(shapes: List<Shape>): Shape { internal fun broadcastShapes(shapes: List<ShapeND>): ShapeND {
var totalDim = 0 var totalDim = 0
for (shape in shapes) { for (shape in shapes) {
totalDim = max(totalDim, shape.size) totalDim = max(totalDim, shape.size)
@ -57,10 +57,10 @@ internal fun broadcastShapes(shapes: List<Shape>): Shape {
} }
} }
return Shape(totalShape) return ShapeND(totalShape)
} }
internal fun broadcastTo(tensor: DoubleTensor, newShape: Shape): DoubleTensor { internal fun broadcastTo(tensor: DoubleTensor, newShape: ShapeND): DoubleTensor {
require(tensor.shape.size <= newShape.size) { require(tensor.shape.size <= newShape.size) {
"Tensor is not compatible with the new shape" "Tensor is not compatible with the new shape"
} }
@ -120,7 +120,7 @@ internal fun broadcastOuterTensors(vararg tensors: DoubleTensor): List<DoubleTen
var curMultiIndex = tensor.shape.slice(0..tensor.shape.size - 3).asArray() var curMultiIndex = tensor.shape.slice(0..tensor.shape.size - 3).asArray()
curMultiIndex = IntArray(totalMultiIndex.size - curMultiIndex.size) { 1 } + curMultiIndex curMultiIndex = IntArray(totalMultiIndex.size - curMultiIndex.size) { 1 } + curMultiIndex
val newTensor = DoubleTensor(Shape(curMultiIndex) + matrixShape, tensor.source) val newTensor = DoubleTensor(ShapeND(curMultiIndex) + matrixShape, tensor.source)
for (i in curMultiIndex.indices) { for (i in curMultiIndex.indices) {
if (curMultiIndex[i] != 1) { if (curMultiIndex[i] != 1) {

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.tensors.core.internal package space.kscience.kmath.tensors.core.internal
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.contentEquals import space.kscience.kmath.nd.contentEquals
import space.kscience.kmath.nd.linearSize import space.kscience.kmath.nd.linearSize
@ -15,7 +15,7 @@ import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.asDoubleTensor import space.kscience.kmath.tensors.core.asDoubleTensor
internal fun checkNotEmptyShape(shape: Shape) = internal fun checkNotEmptyShape(shape: ShapeND) =
check(shape.size > 0) { check(shape.size > 0) {
"Illegal empty shape provided" "Illegal empty shape provided"
} }
@ -24,7 +24,7 @@ internal fun checkEmptyDoubleBuffer(buffer: DoubleArray) = check(buffer.isNotEmp
"Illegal empty buffer provided" "Illegal empty buffer provided"
} }
internal fun checkBufferShapeConsistency(shape: Shape, buffer: DoubleArray) = internal fun checkBufferShapeConsistency(shape: ShapeND, buffer: DoubleArray) =
check(buffer.size == shape.linearSize) { check(buffer.size == shape.linearSize) {
"Inconsistent shape ${shape} for buffer of size ${buffer.size} provided" "Inconsistent shape ${shape} for buffer of size ${buffer.size} provided"
} }
@ -40,10 +40,10 @@ internal fun checkTranspose(dim: Int, i: Int, j: Int) =
"Cannot transpose $i to $j for a tensor of dim $dim" "Cannot transpose $i to $j for a tensor of dim $dim"
} }
internal fun <T> checkView(a: Tensor<T>, shape: Shape) = internal fun <T> checkView(a: Tensor<T>, shape: ShapeND) =
check(a.shape.linearSize == shape.linearSize) check(a.shape.linearSize == shape.linearSize)
internal fun checkSquareMatrix(shape: Shape) { internal fun checkSquareMatrix(shape: ShapeND) {
val n = shape.size val n = shape.size
check(n >= 2) { check(n >= 2) {
"Expected tensor with 2 or more dimensions, got size $n instead" "Expected tensor with 2 or more dimensions, got size $n instead"

View File

@ -156,7 +156,7 @@ internal val DoubleTensor.vectors: List<DoubleTensor>
get() { get() {
val n = shape.size val n = shape.size
val vectorOffset = shape[n - 1] val vectorOffset = shape[n - 1]
val vectorShape = Shape(shape.last()) val vectorShape = ShapeND(shape.last())
return List(linearSize / vectorOffset) { index -> return List(linearSize / vectorOffset) { index ->
val offset = index * vectorOffset val offset = index * vectorOffset
@ -173,7 +173,7 @@ internal val DoubleTensor.matrices: List<DoubleTensor>
val n = shape.size val n = shape.size
check(n >= 2) { "Expected tensor with 2 or more dimensions, got size $n" } check(n >= 2) { "Expected tensor with 2 or more dimensions, got size $n" }
val matrixOffset = shape[n - 1] * shape[n - 2] val matrixOffset = shape[n - 1] * shape[n - 2]
val matrixShape = Shape(shape[n - 2], shape[n - 1]) val matrixShape = ShapeND(shape[n - 2], shape[n - 1])
val size = matrixShape.linearSize val size = matrixShape.linearSize

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.tensors.core.internal package space.kscience.kmath.tensors.core.internal
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.first import space.kscience.kmath.nd.first
import space.kscience.kmath.nd.last import space.kscience.kmath.nd.last
import space.kscience.kmath.operations.asSequence import space.kscience.kmath.operations.asSequence
@ -55,7 +55,7 @@ internal val IntTensor.matrices: VirtualBuffer<IntTensor>
val n = shape.size val n = shape.size
check(n >= 2) { "Expected tensor with 2 or more dimensions, got size $n" } check(n >= 2) { "Expected tensor with 2 or more dimensions, got size $n" }
val matrixOffset = shape[n - 1] * shape[n - 2] val matrixOffset = shape[n - 1] * shape[n - 2]
val matrixShape = Shape(shape[n - 2], shape[n - 1]) val matrixShape = ShapeND(shape[n - 2], shape[n - 1])
return VirtualBuffer(linearSize / matrixOffset) { index -> return VirtualBuffer(linearSize / matrixOffset) { index ->
val offset = index * matrixOffset val offset = index * matrixOffset

View File

@ -95,7 +95,7 @@ internal fun <T> StructureND<T>.setUpPivots(): IntTensor {
pivotsShape[n - 2] = m + 1 pivotsShape[n - 2] = m + 1
return IntTensor( return IntTensor(
Shape(pivotsShape), ShapeND(pivotsShape),
IntBuffer(pivotsShape.reduce(Int::times)) { 0 } IntBuffer(pivotsShape.reduce(Int::times)) { 0 }
) )
} }
@ -240,10 +240,10 @@ internal fun DoubleTensorAlgebra.svd1d(a: DoubleTensor, epsilon: Double = 1e-10)
val b: DoubleTensor val b: DoubleTensor
if (n > m) { if (n > m) {
b = a.transposed(0, 1).dot(a) b = a.transposed(0, 1).dot(a)
v = DoubleTensor(Shape(m), DoubleBuffer.randomUnitVector(m, 0)) v = DoubleTensor(ShapeND(m), DoubleBuffer.randomUnitVector(m, 0))
} else { } else {
b = a.dot(a.transposed(0, 1)) b = a.dot(a.transposed(0, 1))
v = DoubleTensor(Shape(n), DoubleBuffer.randomUnitVector(n, 0)) v = DoubleTensor(ShapeND(n), DoubleBuffer.randomUnitVector(n, 0))
} }
var lastV: DoubleTensor var lastV: DoubleTensor
@ -275,7 +275,7 @@ internal fun DoubleTensorAlgebra.svdHelper(
outerProduct[i * v.shape[0] + j] = u.getTensor(i).value() * v.getTensor(j).value() outerProduct[i * v.shape[0] + j] = u.getTensor(i).value() * v.getTensor(j).value()
} }
} }
a = a - singularValue.times(DoubleTensor(Shape(u.shape[0], v.shape[0]), outerProduct.asBuffer())) a = a - singularValue.times(DoubleTensor(ShapeND(u.shape[0], v.shape[0]), outerProduct.asBuffer()))
} }
var v: DoubleTensor var v: DoubleTensor
var u: DoubleTensor var u: DoubleTensor

View File

@ -8,15 +8,15 @@
package space.kscience.kmath.tensors.core package space.kscience.kmath.tensors.core
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
@JvmName("varArgOne") @JvmName("varArgOne")
public fun DoubleTensorAlgebra.one(vararg shape: Int): DoubleTensor = ones(Shape(shape)) public fun DoubleTensorAlgebra.one(vararg shape: Int): DoubleTensor = ones(ShapeND(shape))
public fun DoubleTensorAlgebra.one(shape: Shape): DoubleTensor = ones(shape) public fun DoubleTensorAlgebra.one(shape: ShapeND): DoubleTensor = ones(shape)
@JvmName("varArgZero") @JvmName("varArgZero")
public fun DoubleTensorAlgebra.zero(vararg shape: Int): DoubleTensor = zeros(Shape(shape)) public fun DoubleTensorAlgebra.zero(vararg shape: Int): DoubleTensor = zeros(ShapeND(shape))
public fun DoubleTensorAlgebra.zero(shape: Shape): DoubleTensor = zeros(shape) public fun DoubleTensorAlgebra.zero(shape: ShapeND): DoubleTensor = zeros(shape)

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.tensors.core package space.kscience.kmath.tensors.core
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.contentEquals import space.kscience.kmath.nd.contentEquals
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.tensors.core.internal.broadcastOuterTensors import space.kscience.kmath.tensors.core.internal.broadcastOuterTensors
@ -21,38 +21,38 @@ internal class TestBroadcasting {
fun testBroadcastShapes() = DoubleTensorAlgebra { fun testBroadcastShapes() = DoubleTensorAlgebra {
assertTrue( assertTrue(
broadcastShapes( broadcastShapes(
listOf(Shape(2, 3), Shape(1, 3), Shape(1, 1, 1)) listOf(ShapeND(2, 3), ShapeND(1, 3), ShapeND(1, 1, 1))
) contentEquals Shape(1, 2, 3) ) contentEquals ShapeND(1, 2, 3)
) )
assertTrue( assertTrue(
broadcastShapes( broadcastShapes(
listOf(Shape(6, 7), Shape(5, 6, 1), Shape(7), Shape(5, 1, 7)) listOf(ShapeND(6, 7), ShapeND(5, 6, 1), ShapeND(7), ShapeND(5, 1, 7))
) contentEquals Shape(5, 6, 7) ) contentEquals ShapeND(5, 6, 7)
) )
} }
@Test @Test
fun testBroadcastTo() = DoubleTensorAlgebra { fun testBroadcastTo() = DoubleTensorAlgebra {
val tensor1 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor1 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val tensor2 = fromArray(Shape(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) val tensor2 = fromArray(ShapeND(1, 3), doubleArrayOf(10.0, 20.0, 30.0))
val res = broadcastTo(tensor2, tensor1.shape) val res = broadcastTo(tensor2, tensor1.shape)
assertTrue(res.shape contentEquals Shape(2, 3)) assertTrue(res.shape contentEquals ShapeND(2, 3))
assertTrue(res.source contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0)) assertTrue(res.source contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0))
} }
@Test @Test
fun testBroadcastTensors() = DoubleTensorAlgebra { fun testBroadcastTensors() = DoubleTensorAlgebra {
val tensor1 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor1 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val tensor2 = fromArray(Shape(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) val tensor2 = fromArray(ShapeND(1, 3), doubleArrayOf(10.0, 20.0, 30.0))
val tensor3 = fromArray(Shape(1, 1, 1), doubleArrayOf(500.0)) val tensor3 = fromArray(ShapeND(1, 1, 1), doubleArrayOf(500.0))
val res = broadcastTensors(tensor1, tensor2, tensor3) val res = broadcastTensors(tensor1, tensor2, tensor3)
assertTrue(res[0].shape contentEquals Shape(1, 2, 3)) assertTrue(res[0].shape contentEquals ShapeND(1, 2, 3))
assertTrue(res[1].shape contentEquals Shape(1, 2, 3)) assertTrue(res[1].shape contentEquals ShapeND(1, 2, 3))
assertTrue(res[2].shape contentEquals Shape(1, 2, 3)) assertTrue(res[2].shape contentEquals ShapeND(1, 2, 3))
assertTrue(res[0].source contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) assertTrue(res[0].source contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
assertTrue(res[1].source contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0)) assertTrue(res[1].source contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0))
@ -61,15 +61,15 @@ internal class TestBroadcasting {
@Test @Test
fun testBroadcastOuterTensors() = DoubleTensorAlgebra { fun testBroadcastOuterTensors() = DoubleTensorAlgebra {
val tensor1 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor1 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val tensor2 = fromArray(Shape(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) val tensor2 = fromArray(ShapeND(1, 3), doubleArrayOf(10.0, 20.0, 30.0))
val tensor3 = fromArray(Shape(1, 1, 1), doubleArrayOf(500.0)) val tensor3 = fromArray(ShapeND(1, 1, 1), doubleArrayOf(500.0))
val res = broadcastOuterTensors(tensor1, tensor2, tensor3) val res = broadcastOuterTensors(tensor1, tensor2, tensor3)
assertTrue(res[0].shape contentEquals Shape(1, 2, 3)) assertTrue(res[0].shape contentEquals ShapeND(1, 2, 3))
assertTrue(res[1].shape contentEquals Shape(1, 1, 3)) assertTrue(res[1].shape contentEquals ShapeND(1, 1, 3))
assertTrue(res[2].shape contentEquals Shape(1, 1, 1)) assertTrue(res[2].shape contentEquals ShapeND(1, 1, 1))
assertTrue(res[0].source contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) assertTrue(res[0].source contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
assertTrue(res[1].source contentEquals doubleArrayOf(10.0, 20.0, 30.0)) assertTrue(res[1].source contentEquals doubleArrayOf(10.0, 20.0, 30.0))
@ -78,37 +78,37 @@ internal class TestBroadcasting {
@Test @Test
fun testBroadcastOuterTensorsShapes() = DoubleTensorAlgebra { fun testBroadcastOuterTensorsShapes() = DoubleTensorAlgebra {
val tensor1 = fromArray(Shape(2, 1, 3, 2, 3), DoubleArray(2 * 1 * 3 * 2 * 3) { 0.0 }) val tensor1 = fromArray(ShapeND(2, 1, 3, 2, 3), DoubleArray(2 * 1 * 3 * 2 * 3) { 0.0 })
val tensor2 = fromArray(Shape(4, 2, 5, 1, 3, 3), DoubleArray(4 * 2 * 5 * 1 * 3 * 3) { 0.0 }) val tensor2 = fromArray(ShapeND(4, 2, 5, 1, 3, 3), DoubleArray(4 * 2 * 5 * 1 * 3 * 3) { 0.0 })
val tensor3 = fromArray(Shape(1, 1), doubleArrayOf(500.0)) val tensor3 = fromArray(ShapeND(1, 1), doubleArrayOf(500.0))
val res = broadcastOuterTensors(tensor1, tensor2, tensor3) val res = broadcastOuterTensors(tensor1, tensor2, tensor3)
assertTrue(res[0].shape contentEquals Shape(4, 2, 5, 3, 2, 3)) assertTrue(res[0].shape contentEquals ShapeND(4, 2, 5, 3, 2, 3))
assertTrue(res[1].shape contentEquals Shape(4, 2, 5, 3, 3, 3)) assertTrue(res[1].shape contentEquals ShapeND(4, 2, 5, 3, 3, 3))
assertTrue(res[2].shape contentEquals Shape(4, 2, 5, 3, 1, 1)) assertTrue(res[2].shape contentEquals ShapeND(4, 2, 5, 3, 1, 1))
} }
@Test @Test
fun testMinusTensor() = BroadcastDoubleTensorAlgebra.invoke { fun testMinusTensor() = BroadcastDoubleTensorAlgebra.invoke {
val tensor1 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor1 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val tensor2 = fromArray(Shape(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) val tensor2 = fromArray(ShapeND(1, 3), doubleArrayOf(10.0, 20.0, 30.0))
val tensor3 = fromArray(Shape(1, 1, 1), doubleArrayOf(500.0)) val tensor3 = fromArray(ShapeND(1, 1, 1), doubleArrayOf(500.0))
val tensor21 = tensor2 - tensor1 val tensor21 = tensor2 - tensor1
val tensor31 = tensor3 - tensor1 val tensor31 = tensor3 - tensor1
val tensor32 = tensor3 - tensor2 val tensor32 = tensor3 - tensor2
assertTrue(tensor21.shape contentEquals Shape(2, 3)) assertTrue(tensor21.shape contentEquals ShapeND(2, 3))
assertTrue(tensor21.source contentEquals doubleArrayOf(9.0, 18.0, 27.0, 6.0, 15.0, 24.0)) assertTrue(tensor21.source contentEquals doubleArrayOf(9.0, 18.0, 27.0, 6.0, 15.0, 24.0))
assertTrue(tensor31.shape contentEquals Shape(1, 2, 3)) assertTrue(tensor31.shape contentEquals ShapeND(1, 2, 3))
assertTrue( assertTrue(
tensor31.source tensor31.source
contentEquals doubleArrayOf(499.0, 498.0, 497.0, 496.0, 495.0, 494.0) contentEquals doubleArrayOf(499.0, 498.0, 497.0, 496.0, 495.0, 494.0)
) )
assertTrue(tensor32.shape contentEquals Shape(1, 1, 3)) assertTrue(tensor32.shape contentEquals ShapeND(1, 1, 3))
assertTrue(tensor32.source contentEquals doubleArrayOf(490.0, 480.0, 470.0)) assertTrue(tensor32.source contentEquals doubleArrayOf(490.0, 480.0, 470.0))
} }

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.tensors.core package space.kscience.kmath.tensors.core
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.asBuffer
import kotlin.math.* import kotlin.math.*
@ -14,7 +14,7 @@ import kotlin.test.assertTrue
internal class TestDoubleAnalyticTensorAlgebra { internal class TestDoubleAnalyticTensorAlgebra {
val shape = Shape(2, 1, 3, 2) val shape = ShapeND(2, 1, 3, 2)
val buffer = doubleArrayOf( val buffer = doubleArrayOf(
27.1, 20.0, 19.84, 27.1, 20.0, 19.84,
23.123, 3.0, 2.0, 23.123, 3.0, 2.0,
@ -103,7 +103,7 @@ internal class TestDoubleAnalyticTensorAlgebra {
assertTrue { tensor.floor() eq expectedTensor(::floor) } assertTrue { tensor.floor() eq expectedTensor(::floor) }
} }
val shape2 = Shape(2, 2) val shape2 = ShapeND(2, 2)
val buffer2 = doubleArrayOf( val buffer2 = doubleArrayOf(
1.0, 2.0, 1.0, 2.0,
-3.0, 4.0 -3.0, 4.0
@ -115,13 +115,13 @@ internal class TestDoubleAnalyticTensorAlgebra {
assertTrue { tensor2.min() == -3.0 } assertTrue { tensor2.min() == -3.0 }
assertTrue { assertTrue {
tensor2.min(0, true) eq fromArray( tensor2.min(0, true) eq fromArray(
Shape(1, 2), ShapeND(1, 2),
doubleArrayOf(-3.0, 2.0) doubleArrayOf(-3.0, 2.0)
) )
} }
assertTrue { assertTrue {
tensor2.min(1, false) eq fromArray( tensor2.min(1, false) eq fromArray(
Shape(2), ShapeND(2),
doubleArrayOf(1.0, -3.0) doubleArrayOf(1.0, -3.0)
) )
} }
@ -132,13 +132,13 @@ internal class TestDoubleAnalyticTensorAlgebra {
assertTrue { tensor2.max() == 4.0 } assertTrue { tensor2.max() == 4.0 }
assertTrue { assertTrue {
tensor2.max(0, true) eq fromArray( tensor2.max(0, true) eq fromArray(
Shape(1, 2), ShapeND(1, 2),
doubleArrayOf(1.0, 4.0) doubleArrayOf(1.0, 4.0)
) )
} }
assertTrue { assertTrue {
tensor2.max(1, false) eq fromArray( tensor2.max(1, false) eq fromArray(
Shape(2), ShapeND(2),
doubleArrayOf(2.0, 4.0) doubleArrayOf(2.0, 4.0)
) )
} }
@ -149,13 +149,13 @@ internal class TestDoubleAnalyticTensorAlgebra {
assertTrue { tensor2.sum() == 4.0 } assertTrue { tensor2.sum() == 4.0 }
assertTrue { assertTrue {
tensor2.sum(0, true) eq fromArray( tensor2.sum(0, true) eq fromArray(
Shape(1, 2), ShapeND(1, 2),
doubleArrayOf(-2.0, 6.0) doubleArrayOf(-2.0, 6.0)
) )
} }
assertTrue { assertTrue {
tensor2.sum(1, false) eq fromArray( tensor2.sum(1, false) eq fromArray(
Shape(2), ShapeND(2),
doubleArrayOf(3.0, 1.0) doubleArrayOf(3.0, 1.0)
) )
} }
@ -166,13 +166,13 @@ internal class TestDoubleAnalyticTensorAlgebra {
assertTrue { tensor2.mean() == 1.0 } assertTrue { tensor2.mean() == 1.0 }
assertTrue { assertTrue {
tensor2.mean(0, true) eq fromArray( tensor2.mean(0, true) eq fromArray(
Shape(1, 2), ShapeND(1, 2),
doubleArrayOf(-1.0, 3.0) doubleArrayOf(-1.0, 3.0)
) )
} }
assertTrue { assertTrue {
tensor2.mean(1, false) eq fromArray( tensor2.mean(1, false) eq fromArray(
Shape(2), ShapeND(2),
doubleArrayOf(1.5, 0.5) doubleArrayOf(1.5, 0.5)
) )
} }

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.tensors.core package space.kscience.kmath.tensors.core
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.contentEquals import space.kscience.kmath.nd.contentEquals
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.tensors.core.internal.svd1d import space.kscience.kmath.tensors.core.internal.svd1d
@ -19,7 +19,7 @@ internal class TestDoubleLinearOpsTensorAlgebra {
@Test @Test
fun testDetLU() = DoubleTensorAlgebra { fun testDetLU() = DoubleTensorAlgebra {
val tensor = fromArray( val tensor = fromArray(
Shape(2, 2, 2), ShapeND(2, 2, 2),
doubleArrayOf( doubleArrayOf(
1.0, 3.0, 1.0, 3.0,
1.0, 2.0, 1.0, 2.0,
@ -29,7 +29,7 @@ internal class TestDoubleLinearOpsTensorAlgebra {
) )
val expectedTensor = fromArray( val expectedTensor = fromArray(
Shape(2, 1), ShapeND(2, 1),
doubleArrayOf( doubleArrayOf(
-1.0, -1.0,
-7.0 -7.0
@ -45,7 +45,7 @@ internal class TestDoubleLinearOpsTensorAlgebra {
fun testDet() = DoubleTensorAlgebra { fun testDet() = DoubleTensorAlgebra {
val expectedValue = 0.019827417 val expectedValue = 0.019827417
val m = fromArray( val m = fromArray(
Shape(3, 3), doubleArrayOf( ShapeND(3, 3), doubleArrayOf(
2.1843, 1.4391, -0.4845, 2.1843, 1.4391, -0.4845,
1.4391, 1.7772, 0.4055, 1.4391, 1.7772, 0.4055,
-0.4845, 0.4055, 0.7519 -0.4845, 0.4055, 0.7519
@ -59,7 +59,7 @@ internal class TestDoubleLinearOpsTensorAlgebra {
fun testDetSingle() = DoubleTensorAlgebra { fun testDetSingle() = DoubleTensorAlgebra {
val expectedValue = 48.151623 val expectedValue = 48.151623
val m = fromArray( val m = fromArray(
Shape(1, 1), doubleArrayOf( ShapeND(1, 1), doubleArrayOf(
expectedValue expectedValue
) )
) )
@ -70,7 +70,7 @@ internal class TestDoubleLinearOpsTensorAlgebra {
@Test @Test
fun testInvLU() = DoubleTensorAlgebra { fun testInvLU() = DoubleTensorAlgebra {
val tensor = fromArray( val tensor = fromArray(
Shape(2, 2, 2), ShapeND(2, 2, 2),
doubleArrayOf( doubleArrayOf(
1.0, 0.0, 1.0, 0.0,
0.0, 2.0, 0.0, 2.0,
@ -80,7 +80,7 @@ internal class TestDoubleLinearOpsTensorAlgebra {
) )
val expectedTensor = fromArray( val expectedTensor = fromArray(
Shape(2, 2, 2), doubleArrayOf( ShapeND(2, 2, 2), doubleArrayOf(
1.0, 0.0, 1.0, 0.0,
0.0, 0.5, 0.0, 0.5,
0.0, 1.0, 0.0, 1.0,
@ -94,14 +94,14 @@ internal class TestDoubleLinearOpsTensorAlgebra {
@Test @Test
fun testScalarProduct() = DoubleTensorAlgebra { fun testScalarProduct() = DoubleTensorAlgebra {
val a = fromArray(Shape(3), doubleArrayOf(1.8, 2.5, 6.8)) val a = fromArray(ShapeND(3), doubleArrayOf(1.8, 2.5, 6.8))
val b = fromArray(Shape(3), doubleArrayOf(5.5, 2.6, 6.4)) val b = fromArray(ShapeND(3), doubleArrayOf(5.5, 2.6, 6.4))
assertEquals(a.dot(b).value(), 59.92) assertEquals(a.dot(b).value(), 59.92)
} }
@Test @Test
fun testQR() = DoubleTensorAlgebra { fun testQR() = DoubleTensorAlgebra {
val shape = Shape(2, 2, 2) val shape = ShapeND(2, 2, 2)
val buffer = doubleArrayOf( val buffer = doubleArrayOf(
1.0, 3.0, 1.0, 3.0,
1.0, 2.0, 1.0, 2.0,
@ -122,7 +122,7 @@ internal class TestDoubleLinearOpsTensorAlgebra {
@Test @Test
fun testLU() = DoubleTensorAlgebra { fun testLU() = DoubleTensorAlgebra {
val shape = Shape(2, 2, 2) val shape = ShapeND(2, 2, 2)
val buffer = doubleArrayOf( val buffer = doubleArrayOf(
1.0, 3.0, 1.0, 3.0,
1.0, 2.0, 1.0, 2.0,
@ -142,9 +142,9 @@ internal class TestDoubleLinearOpsTensorAlgebra {
@Test @Test
fun testCholesky() = DoubleTensorAlgebra { fun testCholesky() = DoubleTensorAlgebra {
val tensor = randomNormal(Shape(2, 5, 5), 0) val tensor = randomNormal(ShapeND(2, 5, 5), 0)
val sigma = (tensor matmul tensor.transposed()) + diagonalEmbedding( val sigma = (tensor matmul tensor.transposed()) + diagonalEmbedding(
fromArray(Shape(2, 5), DoubleArray(10) { 0.1 }) fromArray(ShapeND(2, 5), DoubleArray(10) { 0.1 })
) )
val low = sigma.cholesky() val low = sigma.cholesky()
val sigmChol = low matmul low.transposed() val sigmChol = low matmul low.transposed()
@ -153,24 +153,24 @@ internal class TestDoubleLinearOpsTensorAlgebra {
@Test @Test
fun testSVD1D() = DoubleTensorAlgebra { fun testSVD1D() = DoubleTensorAlgebra {
val tensor2 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor2 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val res = svd1d(tensor2) val res = svd1d(tensor2)
assertTrue(res.shape contentEquals Shape(2)) assertTrue(res.shape contentEquals ShapeND(2))
assertTrue { abs(abs(res.source[0]) - 0.386) < 0.01 } assertTrue { abs(abs(res.source[0]) - 0.386) < 0.01 }
assertTrue { abs(abs(res.source[1]) - 0.922) < 0.01 } assertTrue { abs(abs(res.source[1]) - 0.922) < 0.01 }
} }
@Test @Test
fun testSVD() = DoubleTensorAlgebra { fun testSVD() = DoubleTensorAlgebra {
testSVDFor(fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))) testSVDFor(fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))
testSVDFor(fromArray(Shape(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0))) testSVDFor(fromArray(ShapeND(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0)))
} }
@Test @Test
fun testBatchedSVD() = DoubleTensorAlgebra { fun testBatchedSVD() = DoubleTensorAlgebra {
val tensor = randomNormal(Shape(2, 5, 3), 0) val tensor = randomNormal(ShapeND(2, 5, 3), 0)
val (tensorU, tensorS, tensorV) = tensor.svd() val (tensorU, tensorS, tensorV) = tensor.svd()
val tensorSVD = tensorU matmul (diagonalEmbedding(tensorS) matmul tensorV.transposed()) val tensorSVD = tensorU matmul (diagonalEmbedding(tensorS) matmul tensorV.transposed())
assertTrue(tensor.eq(tensorSVD)) assertTrue(tensor.eq(tensorSVD))
@ -178,7 +178,7 @@ internal class TestDoubleLinearOpsTensorAlgebra {
@Test @Test
fun testBatchedSymEig() = DoubleTensorAlgebra { fun testBatchedSymEig() = DoubleTensorAlgebra {
val tensor = randomNormal(shape = Shape(2, 3, 3), 0) val tensor = randomNormal(shape = ShapeND(2, 3, 3), 0)
val tensorSigma = tensor + tensor.transposed() val tensorSigma = tensor + tensor.transposed()
val (tensorS, tensorV) = tensorSigma.symEig() val (tensorS, tensorV) = tensorSigma.symEig()
val tensorSigmaCalc = tensorV matmul (diagonalEmbedding(tensorS) matmul tensorV.transposed()) val tensorSigmaCalc = tensorV matmul (diagonalEmbedding(tensorS) matmul tensorV.transposed())

View File

@ -21,14 +21,14 @@ internal class TestDoubleTensor {
@Test @Test
fun testValue() = DoubleTensorAlgebra { fun testValue() = DoubleTensorAlgebra {
val value = 12.5 val value = 12.5
val tensor = fromArray(Shape(1), doubleArrayOf(value)) val tensor = fromArray(ShapeND(1), doubleArrayOf(value))
assertEquals(tensor.value(), value) assertEquals(tensor.value(), value)
} }
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
@Test @Test
fun testStrides() = DoubleTensorAlgebra { fun testStrides() = DoubleTensorAlgebra {
val tensor = fromArray(Shape(2, 2), doubleArrayOf(3.5, 5.8, 58.4, 2.4)) val tensor = fromArray(ShapeND(2, 2), doubleArrayOf(3.5, 5.8, 58.4, 2.4))
assertEquals(tensor[intArrayOf(0, 1)], 5.8) assertEquals(tensor[intArrayOf(0, 1)], 5.8)
assertTrue( assertTrue(
tensor.elements().map { it.second }.toList() tensor.elements().map { it.second }.toList()
@ -38,7 +38,7 @@ internal class TestDoubleTensor {
@Test @Test
fun testGet() = DoubleTensorAlgebra { fun testGet() = DoubleTensorAlgebra {
val tensor = fromArray(Shape(1, 2, 2), doubleArrayOf(3.5, 5.8, 58.4, 2.4)) val tensor = fromArray(ShapeND(1, 2, 2), doubleArrayOf(3.5, 5.8, 58.4, 2.4))
val matrix = tensor.getTensor(0).asDoubleTensor2D() val matrix = tensor.getTensor(0).asDoubleTensor2D()
assertEquals(matrix[0, 1], 5.8) assertEquals(matrix[0, 1], 5.8)
@ -67,7 +67,7 @@ internal class TestDoubleTensor {
val doubleArray = DoubleBuffer(1.0, 2.0, 3.0) val doubleArray = DoubleBuffer(1.0, 2.0, 3.0)
// create ND buffers, no data is copied // create ND buffers, no data is copied
val ndArray: MutableBufferND<Double> = DoubleBufferND(ColumnStrides(Shape(3)), doubleArray) val ndArray: MutableBufferND<Double> = DoubleBufferND(ColumnStrides(ShapeND(3)), doubleArray)
// map to tensors // map to tensors
val tensorArray = ndArray.asDoubleTensor() // Data is copied because of strides change. val tensorArray = ndArray.asDoubleTensor() // Data is copied because of strides change.
@ -91,7 +91,7 @@ internal class TestDoubleTensor {
@Test @Test
fun test2D() = with(DoubleTensorAlgebra) { fun test2D() = with(DoubleTensorAlgebra) {
val tensor: DoubleTensor = structureND(Shape(3, 3)) { (i, j) -> (i - j).toDouble() } val tensor: DoubleTensor = structureND(ShapeND(3, 3)) { (i, j) -> (i - j).toDouble() }
//println(tensor.toPrettyString()) //println(tensor.toPrettyString())
val tensor2d = tensor.asDoubleTensor2D() val tensor2d = tensor.asDoubleTensor2D()
assertBufferEquals(DoubleBuffer(1.0, 0.0, -1.0), tensor2d.rows[1]) assertBufferEquals(DoubleBuffer(1.0, 0.0, -1.0), tensor2d.rows[1])
@ -100,7 +100,7 @@ internal class TestDoubleTensor {
@Test @Test
fun testMatrixIteration() = with(DoubleTensorAlgebra) { fun testMatrixIteration() = with(DoubleTensorAlgebra) {
val tensor = structureND(Shape(3, 3, 3, 3)) { index -> index.sum().toDouble() } val tensor = structureND(ShapeND(3, 3, 3, 3)) { index -> index.sum().toDouble() }
tensor.forEachMatrix { index, matrix -> tensor.forEachMatrix { index, matrix ->
println(index.joinToString { it.toString() }) println(index.joinToString { it.toString() })
println(matrix) println(matrix)

View File

@ -6,7 +6,7 @@
package space.kscience.kmath.tensors.core package space.kscience.kmath.tensors.core
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.contentEquals import space.kscience.kmath.nd.contentEquals
import space.kscience.kmath.nd.get import space.kscience.kmath.nd.get
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
@ -20,53 +20,53 @@ internal class TestDoubleTensorAlgebra {
@Test @Test
fun testDoublePlus() = DoubleTensorAlgebra { fun testDoublePlus() = DoubleTensorAlgebra {
val tensor = fromArray(Shape(2), doubleArrayOf(1.0, 2.0)) val tensor = fromArray(ShapeND(2), doubleArrayOf(1.0, 2.0))
val res = 10.0 + tensor val res = 10.0 + tensor
assertTrue(res.source contentEquals doubleArrayOf(11.0, 12.0)) assertTrue(res.source contentEquals doubleArrayOf(11.0, 12.0))
} }
@Test @Test
fun testDoubleDiv() = DoubleTensorAlgebra { fun testDoubleDiv() = DoubleTensorAlgebra {
val tensor = fromArray(Shape(2), doubleArrayOf(2.0, 4.0)) val tensor = fromArray(ShapeND(2), doubleArrayOf(2.0, 4.0))
val res = 2.0 / tensor val res = 2.0 / tensor
assertTrue(res.source contentEquals doubleArrayOf(1.0, 0.5)) assertTrue(res.source contentEquals doubleArrayOf(1.0, 0.5))
} }
@Test @Test
fun testDivDouble() = DoubleTensorAlgebra { fun testDivDouble() = DoubleTensorAlgebra {
val tensor = fromArray(Shape(2), doubleArrayOf(10.0, 5.0)) val tensor = fromArray(ShapeND(2), doubleArrayOf(10.0, 5.0))
val res = tensor / 2.5 val res = tensor / 2.5
assertTrue(res.source contentEquals doubleArrayOf(4.0, 2.0)) assertTrue(res.source contentEquals doubleArrayOf(4.0, 2.0))
} }
@Test @Test
fun testTranspose1x1() = DoubleTensorAlgebra { fun testTranspose1x1() = DoubleTensorAlgebra {
val tensor = fromArray(Shape(1), doubleArrayOf(0.0)) val tensor = fromArray(ShapeND(1), doubleArrayOf(0.0))
val res = tensor.transposed(0, 0) val res = tensor.transposed(0, 0)
assertTrue(res.asDoubleTensor().source contentEquals doubleArrayOf(0.0)) assertTrue(res.asDoubleTensor().source contentEquals doubleArrayOf(0.0))
assertTrue(res.shape contentEquals Shape(1)) assertTrue(res.shape contentEquals ShapeND(1))
} }
@Test @Test
fun testTranspose3x2() = DoubleTensorAlgebra { fun testTranspose3x2() = DoubleTensorAlgebra {
val tensor = fromArray(Shape(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor = fromArray(ShapeND(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val res = tensor.transposed(1, 0) val res = tensor.transposed(1, 0)
assertTrue(res.asDoubleTensor().source contentEquals doubleArrayOf(1.0, 3.0, 5.0, 2.0, 4.0, 6.0)) assertTrue(res.asDoubleTensor().source contentEquals doubleArrayOf(1.0, 3.0, 5.0, 2.0, 4.0, 6.0))
assertTrue(res.shape contentEquals Shape(2, 3)) assertTrue(res.shape contentEquals ShapeND(2, 3))
} }
@Test @Test
fun testTranspose1x2x3() = DoubleTensorAlgebra { fun testTranspose1x2x3() = DoubleTensorAlgebra {
val tensor = fromArray(Shape(1, 2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor = fromArray(ShapeND(1, 2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val res01 = tensor.transposed(0, 1) val res01 = tensor.transposed(0, 1)
val res02 = tensor.transposed(-3, 2) val res02 = tensor.transposed(-3, 2)
val res12 = tensor.transposed() val res12 = tensor.transposed()
assertTrue(res01.shape contentEquals Shape(2, 1, 3)) assertTrue(res01.shape contentEquals ShapeND(2, 1, 3))
assertTrue(res02.shape contentEquals Shape(3, 2, 1)) assertTrue(res02.shape contentEquals ShapeND(3, 2, 1))
assertTrue(res12.shape contentEquals Shape(1, 3, 2)) assertTrue(res12.shape contentEquals ShapeND(1, 3, 2))
assertTrue(res01.asDoubleTensor().source contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) assertTrue(res01.asDoubleTensor().source contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
assertTrue(res02.asDoubleTensor().source contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0)) assertTrue(res02.asDoubleTensor().source contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0))
@ -75,7 +75,7 @@ internal class TestDoubleTensorAlgebra {
@Test @Test
fun testLinearStructure() = DoubleTensorAlgebra { fun testLinearStructure() = DoubleTensorAlgebra {
val shape = Shape(3) val shape = ShapeND(3)
val tensorA = full(value = -4.5, shape = shape) val tensorA = full(value = -4.5, shape = shape)
val tensorB = full(value = 10.9, shape = shape) val tensorB = full(value = 10.9, shape = shape)
val tensorC = full(value = 789.3, shape = shape) val tensorC = full(value = 789.3, shape = shape)
@ -107,28 +107,28 @@ internal class TestDoubleTensorAlgebra {
@Test @Test
fun testDot() = DoubleTensorAlgebra { fun testDot() = DoubleTensorAlgebra {
val tensor1 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor1 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val tensor11 = fromArray(Shape(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor11 = fromArray(ShapeND(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val tensor2 = fromArray(Shape(3), doubleArrayOf(10.0, 20.0, 30.0)) val tensor2 = fromArray(ShapeND(3), doubleArrayOf(10.0, 20.0, 30.0))
val tensor3 = fromArray(Shape(1, 1, 3), doubleArrayOf(-1.0, -2.0, -3.0)) val tensor3 = fromArray(ShapeND(1, 1, 3), doubleArrayOf(-1.0, -2.0, -3.0))
val tensor4 = fromArray(Shape(2, 3, 3), (1..18).map { it.toDouble() }.toDoubleArray()) val tensor4 = fromArray(ShapeND(2, 3, 3), (1..18).map { it.toDouble() }.toDoubleArray())
val tensor5 = fromArray(Shape(2, 3, 3), (1..18).map { 1 + it.toDouble() }.toDoubleArray()) val tensor5 = fromArray(ShapeND(2, 3, 3), (1..18).map { 1 + it.toDouble() }.toDoubleArray())
val res12 = tensor1.dot(tensor2) val res12 = tensor1.dot(tensor2)
assertTrue(res12.source contentEquals doubleArrayOf(140.0, 320.0)) assertTrue(res12.source contentEquals doubleArrayOf(140.0, 320.0))
assertTrue(res12.shape contentEquals Shape(2)) assertTrue(res12.shape contentEquals ShapeND(2))
val res32 = tensor3.matmul(tensor2) val res32 = tensor3.matmul(tensor2)
assertTrue(res32.source contentEquals doubleArrayOf(-140.0)) assertTrue(res32.source contentEquals doubleArrayOf(-140.0))
assertTrue(res32.shape contentEquals Shape(1, 1)) assertTrue(res32.shape contentEquals ShapeND(1, 1))
val res22 = tensor2.dot(tensor2) val res22 = tensor2.dot(tensor2)
assertTrue(res22.source contentEquals doubleArrayOf(1400.0)) assertTrue(res22.source contentEquals doubleArrayOf(1400.0))
assertTrue(res22.shape contentEquals Shape(1)) assertTrue(res22.shape contentEquals ShapeND(1))
val res11 = tensor1.dot(tensor11) val res11 = tensor1.dot(tensor11)
assertTrue(res11.source contentEquals doubleArrayOf(22.0, 28.0, 49.0, 64.0)) assertTrue(res11.source contentEquals doubleArrayOf(22.0, 28.0, 49.0, 64.0))
assertTrue(res11.shape contentEquals Shape(2, 2)) assertTrue(res11.shape contentEquals ShapeND(2, 2))
val res45 = tensor4.matmul(tensor5) val res45 = tensor4.matmul(tensor5)
assertTrue( assertTrue(
@ -137,44 +137,44 @@ internal class TestDoubleTensorAlgebra {
468.0, 501.0, 534.0, 594.0, 636.0, 678.0, 720.0, 771.0, 822.0 468.0, 501.0, 534.0, 594.0, 636.0, 678.0, 720.0, 771.0, 822.0
) )
) )
assertTrue(res45.shape contentEquals Shape(2, 3, 3)) assertTrue(res45.shape contentEquals ShapeND(2, 3, 3))
} }
@Test @Test
fun testDiagonalEmbedding() = DoubleTensorAlgebra { fun testDiagonalEmbedding() = DoubleTensorAlgebra {
val tensor1 = fromArray(Shape(3), doubleArrayOf(10.0, 20.0, 30.0)) val tensor1 = fromArray(ShapeND(3), doubleArrayOf(10.0, 20.0, 30.0))
val tensor2 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor2 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val tensor3 = zeros(Shape(2, 3, 4, 5)) val tensor3 = zeros(ShapeND(2, 3, 4, 5))
assertTrue( assertTrue(
diagonalEmbedding(tensor3, 0, 3, 4).shape contentEquals diagonalEmbedding(tensor3, 0, 3, 4).shape contentEquals
Shape(2, 3, 4, 5, 5) ShapeND(2, 3, 4, 5, 5)
) )
assertTrue( assertTrue(
diagonalEmbedding(tensor3, 1, 3, 4).shape contentEquals diagonalEmbedding(tensor3, 1, 3, 4).shape contentEquals
Shape(2, 3, 4, 6, 6) ShapeND(2, 3, 4, 6, 6)
) )
assertTrue( assertTrue(
diagonalEmbedding(tensor3, 2, 0, 3).shape contentEquals diagonalEmbedding(tensor3, 2, 0, 3).shape contentEquals
Shape(7, 2, 3, 7, 4) ShapeND(7, 2, 3, 7, 4)
) )
val diagonal1 = diagonalEmbedding(tensor1, 0, 1, 0) val diagonal1 = diagonalEmbedding(tensor1, 0, 1, 0)
assertTrue(diagonal1.shape contentEquals Shape(3, 3)) assertTrue(diagonal1.shape contentEquals ShapeND(3, 3))
assertTrue( assertTrue(
diagonal1.source contentEquals diagonal1.source contentEquals
doubleArrayOf(10.0, 0.0, 0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 30.0) doubleArrayOf(10.0, 0.0, 0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 30.0)
) )
val diagonal1Offset = diagonalEmbedding(tensor1, 1, 1, 0) val diagonal1Offset = diagonalEmbedding(tensor1, 1, 1, 0)
assertTrue(diagonal1Offset.shape contentEquals Shape(4, 4)) assertTrue(diagonal1Offset.shape contentEquals ShapeND(4, 4))
assertTrue( assertTrue(
diagonal1Offset.source contentEquals diagonal1Offset.source contentEquals
doubleArrayOf(0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 30.0, 0.0) doubleArrayOf(0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 30.0, 0.0)
) )
val diagonal2 = diagonalEmbedding(tensor2, 1, 0, 2) val diagonal2 = diagonalEmbedding(tensor2, 1, 0, 2)
assertTrue(diagonal2.shape contentEquals Shape(4, 2, 4)) assertTrue(diagonal2.shape contentEquals ShapeND(4, 2, 4))
assertTrue( assertTrue(
diagonal2.source contentEquals diagonal2.source contentEquals
doubleArrayOf( doubleArrayOf(
@ -188,9 +188,9 @@ internal class TestDoubleTensorAlgebra {
@Test @Test
fun testEq() = DoubleTensorAlgebra { fun testEq() = DoubleTensorAlgebra {
val tensor1 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor1 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val tensor2 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor2 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val tensor3 = fromArray(Shape(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 5.0)) val tensor3 = fromArray(ShapeND(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 5.0))
assertTrue(tensor1 eq tensor1) assertTrue(tensor1 eq tensor1)
assertTrue(tensor1 eq tensor2) assertTrue(tensor1 eq tensor2)
@ -204,7 +204,7 @@ internal class TestDoubleTensorAlgebra {
val l = tensor.getTensor(0).map { it + 1.0 } val l = tensor.getTensor(0).map { it + 1.0 }
val r = tensor.getTensor(1).map { it - 1.0 } val r = tensor.getTensor(1).map { it - 1.0 }
val res = l + r val res = l + r
assertTrue { Shape(5, 5) contentEquals res.shape } assertTrue { ShapeND(5, 5) contentEquals res.shape }
assertEquals(2.0, res[4, 4]) assertEquals(2.0, res[4, 4])
} }
} }

View File

@ -33,7 +33,7 @@ public open class ViktorFieldOpsND :
override val elementAlgebra: DoubleField get() = DoubleField override val elementAlgebra: DoubleField get() = DoubleField
@OptIn(UnsafeKMathAPI::class) @OptIn(UnsafeKMathAPI::class)
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND = override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND =
F64Array(*shape.asArray()).apply { F64Array(*shape.asArray()).apply {
ColumnStrides(shape).asSequence().forEach { index -> ColumnStrides(shape).asSequence().forEach { index ->
set(value = DoubleField.initializer(index), indices = index) set(value = DoubleField.initializer(index), indices = index)
@ -46,7 +46,7 @@ public open class ViktorFieldOpsND :
@PerformancePitfall @PerformancePitfall
override fun StructureND<Double>.map(transform: DoubleField.(Double) -> Double): ViktorStructureND = override fun StructureND<Double>.map(transform: DoubleField.(Double) -> Double): ViktorStructureND =
F64Array(*shape.asArray()).apply { F64Array(*shape.asArray()).apply {
ColumnStrides(Shape(shape)).asSequence().forEach { index -> ColumnStrides(ShapeND(shape)).asSequence().forEach { index ->
set(value = DoubleField.transform(this@map[index]), indices = index) set(value = DoubleField.transform(this@map[index]), indices = index)
} }
}.asStructure() }.asStructure()
@ -56,7 +56,7 @@ public open class ViktorFieldOpsND :
override fun StructureND<Double>.mapIndexed( override fun StructureND<Double>.mapIndexed(
transform: DoubleField.(index: IntArray, Double) -> Double, transform: DoubleField.(index: IntArray, Double) -> Double,
): ViktorStructureND = F64Array(*shape.asArray()).apply { ): ViktorStructureND = F64Array(*shape.asArray()).apply {
ColumnStrides(Shape(shape)).asSequence().forEach { index -> ColumnStrides(ShapeND(shape)).asSequence().forEach { index ->
set(value = DoubleField.transform(index, this@mapIndexed[index]), indices = index) set(value = DoubleField.transform(index, this@mapIndexed[index]), indices = index)
} }
}.asStructure() }.asStructure()
@ -127,7 +127,7 @@ public open class ViktorFieldND(
private val shapeAsArray: IntArray, private val shapeAsArray: IntArray,
) : ViktorFieldOpsND(), FieldND<Double, DoubleField>, NumbersAddOps<StructureND<Double>> { ) : ViktorFieldOpsND(), FieldND<Double, DoubleField>, NumbersAddOps<StructureND<Double>> {
override val shape: Shape = Shape(shapeAsArray) override val shape: ShapeND = ShapeND(shapeAsArray)
override val zero: ViktorStructureND by lazy { F64Array.full(init = 0.0, shape = shapeAsArray).asStructure() } override val zero: ViktorStructureND by lazy { F64Array.full(init = 0.0, shape = shapeAsArray).asStructure() }

View File

@ -9,11 +9,11 @@ import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.ColumnStrides
import space.kscience.kmath.nd.MutableStructureND import space.kscience.kmath.nd.MutableStructureND
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.ShapeND
@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public class ViktorStructureND(public val f64Buffer: F64Array) : MutableStructureND<Double> { public class ViktorStructureND(public val f64Buffer: F64Array) : MutableStructureND<Double> {
override val shape: Shape get() = Shape(f64Buffer.shape) override val shape: ShapeND get() = ShapeND(f64Buffer.shape)
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
override inline fun get(index: IntArray): Double = f64Buffer.get(*index) override inline fun get(index: IntArray): Double = f64Buffer.get(*index)