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.algebra
|
||||||
import space.kscience.kmath.operations.bufferAlgebra
|
import space.kscience.kmath.operations.bufferAlgebra
|
||||||
import space.kscience.kmath.operations.toList
|
import space.kscience.kmath.operations.toList
|
||||||
|
import space.kscience.kmath.stat.KMComparisonResult
|
||||||
import space.kscience.kmath.stat.ksComparisonStatistic
|
import space.kscience.kmath.stat.ksComparisonStatistic
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.slice
|
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 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 s3: Buffer<Double> = s1.zip(s2) { l, r -> l + r } //s1 + s2
|
||||||
val s4 = DoubleBufferOps.ln(s3)
|
val s4 = DoubleBufferOps.ln(s3)
|
||||||
|
|
||||||
val kmTest = ksComparisonStatistic(s1, s2)
|
val kmTest: KMComparisonResult<Double> = ksComparisonStatistic(s1, s2)
|
||||||
|
|
||||||
Plotly.page {
|
Plotly.page {
|
||||||
h1 { +"This is my plot" }
|
h1 { +"This is my plot" }
|
||||||
|
@ -21,7 +21,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND<Double, Double
|
|||||||
NumbersAddOps<StructureND<Double>>,
|
NumbersAddOps<StructureND<Double>>,
|
||||||
ExtendedField<StructureND<Double>> {
|
ExtendedField<StructureND<Double>> {
|
||||||
|
|
||||||
private val strides = DefaultStrides(shape)
|
private val strides = ColumnStrides(shape)
|
||||||
override val elementAlgebra: DoubleField get() = DoubleField
|
override val elementAlgebra: DoubleField get() = DoubleField
|
||||||
override val zero: BufferND<Double> by lazy { structureND(shape) { zero } }
|
override val zero: BufferND<Double> by lazy { structureND(shape) { zero } }
|
||||||
override val one: BufferND<Double> by lazy { structureND(shape) { one } }
|
override val one: BufferND<Double> by lazy { structureND(shape) { one } }
|
||||||
|
@ -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.DefaultStrides
|
import space.kscience.kmath.nd.ColumnStrides
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||||
@ -14,7 +14,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 = DefaultStrides(intArrayOf(n, n))
|
val strides = ColumnStrides(intArrayOf(n, n))
|
||||||
val structure = BufferND(strides, buffer)
|
val structure = BufferND(strides, buffer)
|
||||||
|
|
||||||
measureTimeMillis {
|
measureTimeMillis {
|
||||||
|
@ -38,7 +38,7 @@ public inline fun <T, R : Any> StructureND<T>.mapToBuffer(
|
|||||||
): BufferND<R> = if (this is BufferND<T>)
|
): BufferND<R> = if (this is BufferND<T>)
|
||||||
BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
||||||
else {
|
else {
|
||||||
val strides = DefaultStrides(shape)
|
val strides = ColumnStrides(shape)
|
||||||
BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
|
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>)
|
return if (this is MutableBufferND<T>)
|
||||||
MutableBufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
MutableBufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
||||||
else {
|
else {
|
||||||
val strides = DefaultStrides(shape)
|
val strides = ColumnStrides(shape)
|
||||||
MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
|
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 interface WithShape {
|
||||||
public val shape: Shape
|
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) {
|
internal fun requireIndexInShape(index: IntArray, shape: Shape) {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.nd
|
package space.kscience.kmath.nd
|
||||||
|
|
||||||
|
import kotlin.math.max
|
||||||
import kotlin.native.concurrent.ThreadLocal
|
import kotlin.native.concurrent.ThreadLocal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,12 +62,16 @@ public abstract class Strides : ShapeIndexer {
|
|||||||
* Iterate over ND indices in a natural order
|
* Iterate over ND indices in a natural order
|
||||||
*/
|
*/
|
||||||
public override fun asSequence(): Sequence<IntArray> = (0 until linearSize).asSequence().map(::index)
|
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]
|
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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other !is DefaultStrides) return false
|
if (other !is ColumnStrides) return false
|
||||||
if (!shape.contentEquals(other.shape)) return false
|
return shape.contentEquals(other.shape)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int = shape.contentHashCode()
|
override fun hashCode(): Int = shape.contentHashCode()
|
||||||
|
|
||||||
|
|
||||||
public companion object {
|
public companion object
|
||||||
/**
|
}
|
||||||
* Cached builder for default strides
|
|
||||||
*/
|
/**
|
||||||
@Deprecated("Replace by Strides(shape)")
|
* This [Strides] implementation follows the last dimension first convention
|
||||||
public operator fun invoke(shape: IntArray): Strides =
|
* For more information: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.strides.html
|
||||||
defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) }
|
*
|
||||||
|
* @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
|
@ThreadLocal
|
||||||
@ -124,4 +171,4 @@ private val defaultStridesCache = HashMap<IntArray, Strides>()
|
|||||||
/**
|
/**
|
||||||
* Cached builder for default 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.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
|
import space.kscience.kmath.structures.MutableBuffer
|
||||||
import space.kscience.kmath.structures.MutableListBuffer
|
import space.kscience.kmath.structures.MutableListBuffer
|
||||||
import space.kscience.kmath.structures.VirtualBuffer
|
import space.kscience.kmath.structures.VirtualBuffer
|
||||||
import kotlin.jvm.JvmInline
|
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.
|
* The buffer of rows of this structure. It gets elements from the structure dynamically.
|
||||||
*/
|
*/
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
override val rows: List<MutableStructure1D<T>>
|
override val rows: List<MutableBuffer<T>>
|
||||||
get() = List(rowNum) { i -> MutableBuffer1DWrapper(MutableListBuffer(colNum) { j -> get(i, j) }) }
|
get() = List(rowNum) { i -> MutableListBuffer(colNum) { j -> get(i, j) } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The buffer of columns of this structure. It gets elements from the structure dynamically.
|
* The buffer of columns of this structure. It gets elements from the structure dynamically.
|
||||||
*/
|
*/
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
override val columns: List<MutableStructure1D<T>>
|
override val columns: List<MutableBuffer<T>>
|
||||||
get() = List(colNum) { j -> MutableBuffer1DWrapper(MutableListBuffer(rowNum) { i -> get(i, j) }) }
|
get() = List(colNum) { j -> MutableListBuffer(rowNum) { i -> get(i, j) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,25 +148,25 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
|
|||||||
shape: IntArray,
|
shape: IntArray,
|
||||||
bufferFactory: BufferFactory<T> = BufferFactory.boxing(),
|
bufferFactory: BufferFactory<T> = BufferFactory.boxing(),
|
||||||
initializer: (IntArray) -> T,
|
initializer: (IntArray) -> T,
|
||||||
): BufferND<T> = buffered(DefaultStrides(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: IntArray,
|
shape: IntArray,
|
||||||
crossinline initializer: (IntArray) -> T,
|
crossinline initializer: (IntArray) -> T,
|
||||||
): BufferND<T> = auto(DefaultStrides(shape), initializer)
|
): BufferND<T> = auto(ColumnStrides(shape), initializer)
|
||||||
|
|
||||||
@JvmName("autoVarArg")
|
@JvmName("autoVarArg")
|
||||||
public inline fun <reified T : Any> auto(
|
public inline fun <reified T : Any> auto(
|
||||||
vararg shape: Int,
|
vararg shape: Int,
|
||||||
crossinline initializer: (IntArray) -> T,
|
crossinline initializer: (IntArray) -> T,
|
||||||
): BufferND<T> =
|
): BufferND<T> =
|
||||||
auto(DefaultStrides(shape), initializer)
|
auto(ColumnStrides(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, DefaultStrides(shape), initializer)
|
): BufferND<T> = auto(type, ColumnStrides(shape), initializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.structures
|
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.Structure2D
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.nd.as2D
|
import space.kscience.kmath.nd.as2D
|
||||||
@ -31,7 +31,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(
|
||||||
DefaultStrides(intArrayOf(rowNum, colNum)),
|
ColumnStrides(intArrayOf(rowNum, colNum)),
|
||||||
factory
|
factory
|
||||||
) { (i, j) ->
|
) { (i, j) ->
|
||||||
get(i, j)
|
get(i, j)
|
||||||
|
@ -15,6 +15,9 @@ public interface BufferView<T> : Buffer<T> {
|
|||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public fun originIndex(index: Int): Int
|
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>(
|
public class BufferSlice<T>(
|
||||||
override val origin: Buffer<T>,
|
override val origin: Buffer<T>,
|
||||||
public val offset: UInt = 0U,
|
public val offset: Int = 0,
|
||||||
override val size: Int,
|
override val size: Int,
|
||||||
) : BufferView<T> {
|
) : BufferView<T> {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(size > 0) { "Size must be positive" }
|
require(size > 0) { "Size must be positive" }
|
||||||
require(offset + size.toUInt() <= origin.size.toUInt()) {
|
require(offset + size <= origin.size) {
|
||||||
"End of buffer ${offset + size.toUInt()} is beyond the end of origin buffer 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) {
|
override fun get(index: Int): T = if (index >= size) {
|
||||||
throw IndexOutOfBoundsException("$index is out of ${0 until size} rage")
|
throw IndexOutOfBoundsException("$index is out of ${0 until size} rage")
|
||||||
} else {
|
} else {
|
||||||
origin[index.toUInt() + offset]
|
origin[index + offset]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun iterator(): Iterator<T> =
|
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
|
@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.
|
* 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>(
|
public class BufferExpanded<T>(
|
||||||
override val origin: Buffer<T>,
|
override val origin: Buffer<T>,
|
||||||
public val defaultValue: T,
|
private val defaultValue: T,
|
||||||
public val offset: Int = 0,
|
public val offset: Int = 0,
|
||||||
override val size: Int = origin.size,
|
override val size: Int = origin.size,
|
||||||
) : BufferView<T> {
|
) : BufferView<T> {
|
||||||
@ -79,17 +82,17 @@ public class BufferExpanded<T>(
|
|||||||
/**
|
/**
|
||||||
* Zero-copy select a slice inside the original buffer
|
* 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(
|
BufferSlice(
|
||||||
origin,
|
origin,
|
||||||
this.offset + range.first,
|
this.offset + range.first,
|
||||||
(range.last - range.first).toInt() + 1
|
(range.last - range.first) + 1
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
BufferSlice(
|
BufferSlice(
|
||||||
this,
|
this,
|
||||||
range.first,
|
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) {
|
): BufferView<T> = if (range.first >= 0 && range.last < size) {
|
||||||
BufferSlice(
|
BufferSlice(
|
||||||
this,
|
this,
|
||||||
range.first.toUInt(),
|
range.first,
|
||||||
(range.last - range.first) + 1
|
(range.last - range.first) + 1
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -118,7 +121,7 @@ public fun <T> Buffer<T>.expand(
|
|||||||
/**
|
/**
|
||||||
* A [BufferView] that overrides indexing of the original buffer
|
* A [BufferView] that overrides indexing of the original buffer
|
||||||
*/
|
*/
|
||||||
public class PermutatedBuffer<T>(
|
public class PermutedBuffer<T>(
|
||||||
override val origin: Buffer<T>,
|
override val origin: Buffer<T>,
|
||||||
private val permutations: IntArray,
|
private val permutations: IntArray,
|
||||||
) : BufferView<T> {
|
) : BufferView<T> {
|
||||||
@ -145,4 +148,4 @@ public class PermutatedBuffer<T>(
|
|||||||
/**
|
/**
|
||||||
* Created a permuted view of given buffer using provided [indices]
|
* 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
|
@UnstableKMathAPI
|
||||||
public fun Buffer<Double>.getDouble(index: Int): Double = if (this is BufferView) {
|
public fun Buffer<Double>.getDouble(index: Int): Double = if (this is BufferView) {
|
||||||
val originIndex = originIndex(index)
|
val originIndex = originIndex(index)
|
||||||
if( originIndex>=0) {
|
if (originIndex >= 0) {
|
||||||
origin.getDouble(originIndex)
|
origin.getDouble(originIndex)
|
||||||
} else {
|
} else {
|
||||||
get(index)
|
get(index)
|
||||||
@ -26,7 +26,7 @@ public fun Buffer<Double>.getDouble(index: Int): Double = if (this is BufferView
|
|||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public fun Buffer<Int>.getInt(index: Int): Int = if (this is BufferView) {
|
public fun Buffer<Int>.getInt(index: Int): Int = if (this is BufferView) {
|
||||||
val originIndex = originIndex(index)
|
val originIndex = originIndex(index)
|
||||||
if( originIndex>=0) {
|
if (originIndex >= 0) {
|
||||||
origin.getInt(originIndex)
|
origin.getInt(originIndex)
|
||||||
} else {
|
} else {
|
||||||
get(index)
|
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
|
@Test
|
||||||
fun shrink(){
|
fun shrink(){
|
||||||
val view = buffer.slice(20U..30U)
|
val view = buffer.slice(20..30)
|
||||||
assertEquals(20, view[0])
|
assertEquals(20, view[0])
|
||||||
assertEquals(30, view[10])
|
assertEquals(30, view[10])
|
||||||
assertFails { view[11] }
|
assertFails { view[11] }
|
||||||
|
@ -8,7 +8,7 @@ package space.kscience.kmath.structures
|
|||||||
import kotlinx.coroutines.*
|
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.DefaultStrides
|
import space.kscience.kmath.nd.ColumnStrides
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
|
|
||||||
public class LazyStructureND<out T>(
|
public class LazyStructureND<out T>(
|
||||||
@ -27,7 +27,7 @@ public class LazyStructureND<out T>(
|
|||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun elements(): Sequence<Pair<IntArray, T>> {
|
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) } }
|
val res = runBlocking { strides.asSequence().toList().map { index -> index to await(index) } }
|
||||||
return res.asSequence()
|
return res.asSequence()
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ package space.kscience.kmath.histogram
|
|||||||
|
|
||||||
import space.kscience.kmath.domains.Domain
|
import space.kscience.kmath.domains.Domain
|
||||||
import space.kscience.kmath.linear.Point
|
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.FieldOpsND
|
||||||
import space.kscience.kmath.nd.Shape
|
import space.kscience.kmath.nd.Shape
|
||||||
import space.kscience.kmath.nd.StructureND
|
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 dimension: Int get() = group.shape.size
|
||||||
|
|
||||||
override val bins: Iterable<DomainBin<T, D, V>>
|
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])
|
group.produceBin(it, values[it])
|
||||||
}.asIterable()
|
}.asIterable()
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
package space.kscience.kmath.histogram
|
package space.kscience.kmath.histogram
|
||||||
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
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.operations.invoke
|
||||||
import space.kscience.kmath.real.DoubleVector
|
import space.kscience.kmath.real.DoubleVector
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
@ -73,7 +73,7 @@ internal class MultivariateHistogramTest {
|
|||||||
}
|
}
|
||||||
val res = histogram1 - histogram2
|
val res = histogram1 - histogram2
|
||||||
assertTrue {
|
assertTrue {
|
||||||
DefaultStrides(shape).asSequence().all { index ->
|
ColumnStrides(shape).asSequence().all { index ->
|
||||||
res.values[index] <= histogram1.values[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()
|
protected val multikStat: Statistics = multikEngine.getStatistics()
|
||||||
|
|
||||||
override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): MultikTensor<T> {
|
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)
|
val memoryView = initMemoryView<T>(strides.linearSize, type)
|
||||||
strides.asSequence().forEachIndexed { linearIndex, tensorIndex ->
|
strides.asSequence().forEachIndexed { linearIndex, tensorIndex ->
|
||||||
memoryView[linearIndex] = elementAlgebra.initializer(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.factory.ops.NDBase
|
||||||
import org.nd4j.linalg.ops.transforms.Transforms
|
import org.nd4j.linalg.ops.transforms.Transforms
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
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.Shape
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.operations.DoubleField
|
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> {
|
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): Nd4jArrayStructure<Double> {
|
||||||
val array: INDArray = Nd4j.zeros(*shape)
|
val array: INDArray = Nd4j.zeros(*shape)
|
||||||
val indices = DefaultStrides(shape)
|
val indices = ColumnStrides(shape)
|
||||||
indices.asSequence().forEach { index ->
|
indices.asSequence().forEach { index ->
|
||||||
array.putScalar(index, elementAlgebra.initializer(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
|
* 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 origin: Buffer<T>,
|
||||||
override val position: Int,
|
override val position: Int,
|
||||||
override val size: Int = origin.size,
|
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]
|
* Create an offset series with index starting point at [index]
|
||||||
*/
|
*/
|
||||||
public fun Buffer<T>.moveTo(index: Int): Series<T> = if (this is Series) {
|
public fun Buffer<T>.moveTo(index: Int): Series<T> = if (this is Series) {
|
||||||
OffsetBufer(origin, index, size)
|
SeriesImpl(origin, index, size)
|
||||||
} else {
|
} else {
|
||||||
OffsetBufer(this, index, size)
|
SeriesImpl(this, index, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
public val Buffer<T>.offset: Int get() = if (this is Series) position else 0
|
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.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.DefaultStrides
|
import space.kscience.kmath.nd.ColumnStrides
|
||||||
import space.kscience.kmath.nd.Shape
|
import space.kscience.kmath.nd.Shape
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
@ -39,7 +39,7 @@ public class DoubleTensorFlowAlgebra internal constructor(
|
|||||||
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 ->
|
||||||
DefaultStrides(shape).forEach { index ->
|
ColumnStrides(shape).forEach { index ->
|
||||||
array.setDouble(elementAlgebra.initializer(index), *index.toLongArray())
|
array.setDouble(elementAlgebra.initializer(index), *index.toLongArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +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.RowStrides
|
||||||
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
|
||||||
@ -20,9 +21,9 @@ public abstract class BufferedTensor<T>(
|
|||||||
public abstract val source: MutableBuffer<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
|
* Number of elements in tensor
|
||||||
|
@ -5,32 +5,38 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.tensors.core
|
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.structures.*
|
||||||
import space.kscience.kmath.tensors.core.internal.toPrettyString
|
import space.kscience.kmath.tensors.core.internal.toPrettyString
|
||||||
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
public class OffsetDoubleBuffer(
|
public class OffsetDoubleBuffer(
|
||||||
private val source: DoubleBuffer,
|
override val origin: DoubleBuffer,
|
||||||
private val offset: Int,
|
private val offset: Int,
|
||||||
override val size: Int,
|
override val size: Int,
|
||||||
) : MutableBuffer<Double> {
|
) : MutableBuffer<Double>, BufferView<Double> {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(offset >= 0) { "Offset must be non-negative" }
|
require(offset >= 0) { "Offset must be non-negative" }
|
||||||
require(size >= 0) { "Size 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) {
|
override fun set(index: Int, value: Double) {
|
||||||
require(index in 0 until size) { "Index must be in [0, size)" }
|
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]
|
* 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 {
|
override fun iterator(): Iterator<Double> = iterator {
|
||||||
for (i in indices) {
|
for (i in indices) {
|
||||||
@ -41,7 +47,14 @@ public class OffsetDoubleBuffer(
|
|||||||
override fun toString(): String = Buffer.toString(this)
|
override fun toString(): String = Buffer.toString(this)
|
||||||
|
|
||||||
public fun view(addOffset: Int, newSize: Int = size - addOffset): OffsetDoubleBuffer =
|
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)
|
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()
|
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.PerformancePitfall
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.nd.*
|
import space.kscience.kmath.nd.*
|
||||||
|
import space.kscience.kmath.nd.Strides.Companion.linearSizeOf
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
import space.kscience.kmath.structures.*
|
import space.kscience.kmath.structures.*
|
||||||
import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra
|
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(
|
override fun structureND(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): DoubleTensor = fromArray(
|
||||||
shape,
|
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 {
|
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)
|
val newShape = if (lastShape.isNotEmpty()) lastShape else intArrayOf(1)
|
||||||
return DoubleTensor(
|
return DoubleTensor(
|
||||||
newShape,
|
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(
|
override fun structureND(shape: IntArray, initializer: IntRing.(IntArray) -> Int): IntTensor = fromArray(
|
||||||
shape,
|
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 {
|
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
|
package space.kscience.kmath.tensors.core.internal
|
||||||
|
|
||||||
import space.kscience.kmath.nd.MutableStructure2D
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.nd.Structure2D
|
import space.kscience.kmath.nd.Strides.Companion.linearSizeOf
|
||||||
import space.kscience.kmath.nd.as2D
|
|
||||||
import space.kscience.kmath.nd.get
|
|
||||||
import space.kscience.kmath.operations.asSequence
|
import space.kscience.kmath.operations.asSequence
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
import space.kscience.kmath.structures.DoubleBuffer
|
||||||
import space.kscience.kmath.structures.VirtualBuffer
|
import space.kscience.kmath.structures.VirtualBuffer
|
||||||
import space.kscience.kmath.structures.asBuffer
|
import space.kscience.kmath.structures.asBuffer
|
||||||
import space.kscience.kmath.structures.indices
|
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.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.abs
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
@ -177,7 +178,7 @@ internal val DoubleTensor.matrices: VirtualBuffer<DoubleTensor>
|
|||||||
val matrixOffset = shape[n - 1] * shape[n - 2]
|
val matrixOffset = shape[n - 1] * shape[n - 2]
|
||||||
val matrixShape = intArrayOf(shape[n - 2], shape[n - 1])
|
val matrixShape = intArrayOf(shape[n - 2], shape[n - 1])
|
||||||
|
|
||||||
val size = TensorLinearStructure.linearSizeOf(matrixShape)
|
val size = linearSizeOf(matrixShape)
|
||||||
|
|
||||||
return VirtualBuffer(linearSize / matrixOffset) { index ->
|
return VirtualBuffer(linearSize / matrixOffset) { index ->
|
||||||
val offset = index * matrixOffset
|
val offset = index * matrixOffset
|
||||||
|
@ -32,20 +32,19 @@ internal fun List<OffsetIntBuffer>.concat(): IntBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal val IntTensor.vectors: VirtualBuffer<IntTensor>
|
internal fun IntTensor.vectors(): VirtualBuffer<IntTensor> {
|
||||||
get() {
|
val n = shape.size
|
||||||
val n = shape.size
|
val vectorOffset = shape[n - 1]
|
||||||
val vectorOffset = shape[n - 1]
|
val vectorShape = intArrayOf(shape.last())
|
||||||
val vectorShape = intArrayOf(shape.last())
|
|
||||||
|
|
||||||
return VirtualBuffer(linearSize / vectorOffset) { index ->
|
return VirtualBuffer(linearSize / vectorOffset) { index ->
|
||||||
val offset = index * vectorOffset
|
val offset = index * vectorOffset
|
||||||
IntTensor(vectorShape, source.view(offset, vectorShape.first()))
|
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>
|
internal val IntTensor.matrices: VirtualBuffer<IntTensor>
|
||||||
|
@ -5,9 +5,8 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.tensors.core.internal
|
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.DoubleBufferOps.Companion.map
|
||||||
import space.kscience.kmath.operations.toMutableList
|
|
||||||
import space.kscience.kmath.random.RandomGenerator
|
import space.kscience.kmath.random.RandomGenerator
|
||||||
import space.kscience.kmath.samplers.GaussianSampler
|
import space.kscience.kmath.samplers.GaussianSampler
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
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(' ') }
|
repeat(fLength - res.length) { append(' ') }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
internal fun DoubleTensor.toPrettyString(): String = buildString {
|
internal fun DoubleTensor.toPrettyString(): String = buildString {
|
||||||
var offset = 0
|
var offset = 0
|
||||||
val shape = this@toPrettyString.shape
|
val shape = this@toPrettyString.shape
|
||||||
@ -85,7 +85,7 @@ internal fun DoubleTensor.toPrettyString(): String = buildString {
|
|||||||
charOffset += 1
|
charOffset += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
val values = vector.as1D().toMutableList().map(::format)
|
val values = vector.elements().map { format(it.second) }
|
||||||
|
|
||||||
values.joinTo(this, separator = ", ")
|
values.joinTo(this, separator = ", ")
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ internal fun DoubleTensor.toPrettyString(): String = buildString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
offset += vectorSize
|
offset += vectorSize
|
||||||
if (this@toPrettyString.linearSize == offset) {
|
if (this@toPrettyString.indices.linearSize == offset) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package space.kscience.kmath.tensors.core
|
package space.kscience.kmath.tensors.core
|
||||||
|
|
||||||
import space.kscience.kmath.nd.DoubleBufferND
|
import space.kscience.kmath.nd.DoubleBufferND
|
||||||
|
import space.kscience.kmath.nd.RowStrides
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
import space.kscience.kmath.structures.DoubleBuffer
|
||||||
import space.kscience.kmath.structures.asBuffer
|
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) {
|
public fun StructureND<Double>.copyToTensor(): DoubleTensor = if (this is DoubleTensor) {
|
||||||
DoubleTensor(shape, source.copy())
|
DoubleTensor(shape, source.copy())
|
||||||
} else if (this is DoubleBufferND && indices is TensorLinearStructure) {
|
} else if (this is DoubleBufferND && indices is RowStrides) {
|
||||||
DoubleTensor(shape, buffer.copy())
|
DoubleTensor(shape, buffer.copy())
|
||||||
} else {
|
} else {
|
||||||
DoubleTensor(
|
DoubleTensor(
|
||||||
shape,
|
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) {
|
public fun StructureND<Double>.asDoubleTensor(): DoubleTensor = if (this is DoubleTensor) {
|
||||||
this
|
this
|
||||||
} else if (this is DoubleBufferND && indices is TensorLinearStructure) {
|
} else if (this is DoubleBufferND && indices is RowStrides) {
|
||||||
DoubleTensor(shape, buffer)
|
DoubleTensor(shape, buffer)
|
||||||
} else {
|
} else {
|
||||||
copyToTensor()
|
copyToTensor()
|
||||||
@ -59,6 +60,6 @@ public fun StructureND<Int>.asIntTensor(): IntTensor = when (this) {
|
|||||||
is IntTensor -> this
|
is IntTensor -> this
|
||||||
else -> IntTensor(
|
else -> IntTensor(
|
||||||
this.shape,
|
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)
|
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(DefaultStrides(intArrayOf(3)), doubleArray)
|
val ndArray: MutableBufferND<Double> = DoubleBufferND(ColumnStrides(intArrayOf(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.
|
||||||
|
@ -33,7 +33,7 @@ public open class ViktorFieldOpsND :
|
|||||||
|
|
||||||
override fun structureND(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND =
|
override fun structureND(shape: IntArray, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND =
|
||||||
F64Array(*shape).apply {
|
F64Array(*shape).apply {
|
||||||
DefaultStrides(shape).asSequence().forEach { index ->
|
ColumnStrides(shape).asSequence().forEach { index ->
|
||||||
set(value = DoubleField.initializer(index), indices = index)
|
set(value = DoubleField.initializer(index), indices = index)
|
||||||
}
|
}
|
||||||
}.asStructure()
|
}.asStructure()
|
||||||
@ -43,7 +43,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).apply {
|
F64Array(*shape).apply {
|
||||||
DefaultStrides(shape).asSequence().forEach { index ->
|
ColumnStrides(shape).asSequence().forEach { index ->
|
||||||
set(value = DoubleField.transform(this@map[index]), indices = index)
|
set(value = DoubleField.transform(this@map[index]), indices = index)
|
||||||
}
|
}
|
||||||
}.asStructure()
|
}.asStructure()
|
||||||
@ -52,7 +52,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).apply {
|
): ViktorStructureND = F64Array(*shape).apply {
|
||||||
DefaultStrides(shape).asSequence().forEach { index ->
|
ColumnStrides(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()
|
||||||
@ -65,7 +65,7 @@ public open class ViktorFieldOpsND :
|
|||||||
): ViktorStructureND {
|
): ViktorStructureND {
|
||||||
require(left.shape.contentEquals(right.shape))
|
require(left.shape.contentEquals(right.shape))
|
||||||
return F64Array(*left.shape).apply {
|
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)
|
set(value = DoubleField.transform(left[index], right[index]), indices = index)
|
||||||
}
|
}
|
||||||
}.asStructure()
|
}.asStructure()
|
||||||
|
@ -7,7 +7,7 @@ package space.kscience.kmath.viktor
|
|||||||
|
|
||||||
import org.jetbrains.bio.viktor.F64Array
|
import org.jetbrains.bio.viktor.F64Array
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.nd.DefaultStrides
|
import space.kscience.kmath.nd.ColumnStrides
|
||||||
import space.kscience.kmath.nd.MutableStructureND
|
import space.kscience.kmath.nd.MutableStructureND
|
||||||
|
|
||||||
@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
|
@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
|
||||||
@ -22,7 +22,7 @@ public class ViktorStructureND(public val f64Buffer: F64Array) : MutableStructur
|
|||||||
|
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
override fun elements(): Sequence<Pair<IntArray, Double>> =
|
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)
|
public fun F64Array.asStructure(): ViktorStructureND = ViktorStructureND(this)
|
||||||
|
Loading…
Reference in New Issue
Block a user