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