Basic series
This commit is contained in:
parent
0e1e97a3ff
commit
bf504ae6c5
@ -2,6 +2,7 @@ import java.net.URL
|
||||
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.project")
|
||||
id("org.jetbrains.kotlinx.kover") version "0.4.1"
|
||||
kotlin("jupyter.api") apply false
|
||||
}
|
||||
|
||||
@ -69,4 +70,4 @@ ksciencePublish {
|
||||
sonatype(publish = true)
|
||||
}
|
||||
|
||||
apiValidation.nonPublicMarkers.add("space.kscience.kmath.misc.UnstableKMathAPI")
|
||||
apiValidation.nonPublicMarkers.add("space.kscience.kmath.misc.UnstableKMathAPI")
|
@ -0,0 +1,37 @@
|
||||
package space.kscience.kmath.series
|
||||
|
||||
|
||||
import net.jafama.StrictFastMath.abs
|
||||
import space.kscience.kmath.operations.algebra
|
||||
import space.kscience.kmath.operations.bufferAlgebra
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.operations.toList
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.plotly.Plotly
|
||||
import space.kscience.plotly.makeFile
|
||||
import space.kscience.plotly.scatter
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.max
|
||||
|
||||
fun main() = Double.algebra.bufferAlgebra.seriesAlgebra(0..100).invoke {
|
||||
fun Buffer<Double>.plot() {
|
||||
val ls = labels
|
||||
Plotly.plot {
|
||||
scatter {
|
||||
x.numbers = ls
|
||||
y.numbers = toList()
|
||||
}
|
||||
}.makeFile()
|
||||
}
|
||||
|
||||
|
||||
val s1 = series(100) { sin(2 * PI * it / 100) }
|
||||
val s2 = series(100) { 1.0 }
|
||||
|
||||
(s1 - s2).plot()
|
||||
|
||||
// Kolmogorov-Smirnov test statistic
|
||||
val kst = (s1 - s2).fold(0.0) { sup, arg -> max(sup, abs(arg))}
|
||||
|
||||
|
||||
}
|
@ -36,7 +36,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND<Double, Double
|
||||
this@StreamDoubleFieldND.shape,
|
||||
shape
|
||||
)
|
||||
this is BufferND && this.indices == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer
|
||||
this is BufferND && this.shapeIndices == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer
|
||||
else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ plugins {
|
||||
kotlin("multiplatform")
|
||||
id("ru.mipt.npm.gradle.common")
|
||||
id("ru.mipt.npm.gradle.native")
|
||||
// id("com.xcporter.metaview") version "0.0.5"
|
||||
}
|
||||
|
||||
kotlin.sourceSets {
|
||||
@ -13,12 +12,6 @@ kotlin.sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
//generateUml {
|
||||
// classTree {
|
||||
//
|
||||
// }
|
||||
//}
|
||||
|
||||
readme {
|
||||
description = "Core classes, algebra definitions, basic linear algebra"
|
||||
maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT
|
||||
|
@ -7,7 +7,6 @@ package space.kscience.kmath.domains
|
||||
import space.kscience.kmath.linear.Point
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.indices
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -8,7 +8,6 @@ package space.kscience.kmath.expressions
|
||||
import space.kscience.kmath.operations.ExtendedField
|
||||
import space.kscience.kmath.operations.asIterable
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.indices
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
|
@ -13,11 +13,10 @@ import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.BufferFactory
|
||||
import space.kscience.kmath.structures.VirtualBuffer
|
||||
import space.kscience.kmath.structures.indices
|
||||
|
||||
|
||||
public class BufferedLinearSpace<T, out A : Ring<T>>(
|
||||
private val bufferAlgebra: BufferAlgebra<T, A>
|
||||
private val bufferAlgebra: BufferAlgebra<T, A>,
|
||||
) : LinearSpace<T, A> {
|
||||
override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
|
||||
|
||||
|
@ -26,7 +26,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 shapeIndices: ShapeIndices get() = DefaultStrides(shape)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,7 @@ import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.structures.BufferFactory
|
||||
|
||||
public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
|
||||
public val indexerBuilder: (IntArray) -> ShapeIndexer
|
||||
public val indexerBuilder: (IntArray) -> ShapeIndices
|
||||
public val bufferAlgebra: BufferAlgebra<T, A>
|
||||
override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
|
||||
|
||||
@ -47,7 +47,7 @@ public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
|
||||
zipInline(left.toBufferND(), right.toBufferND(), transform)
|
||||
|
||||
public companion object {
|
||||
public val defaultIndexerBuilder: (IntArray) -> ShapeIndexer = DefaultStrides.Companion::invoke
|
||||
public val defaultIndexerBuilder: (IntArray) -> ShapeIndices = DefaultStrides.Companion::invoke
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ public inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.mapInline(
|
||||
arg: BufferND<T>,
|
||||
crossinline transform: A.(T) -> T,
|
||||
): BufferND<T> {
|
||||
val indexes = arg.indices
|
||||
val indexes = arg.shapeIndices
|
||||
val buffer = arg.buffer
|
||||
return BufferND(
|
||||
indexes,
|
||||
@ -69,7 +69,7 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.mapIndexedInline(
|
||||
arg: BufferND<T>,
|
||||
crossinline transform: A.(index: IntArray, arg: T) -> T,
|
||||
): BufferND<T> {
|
||||
val indexes = arg.indices
|
||||
val indexes = arg.shapeIndices
|
||||
val buffer = arg.buffer
|
||||
return BufferND(
|
||||
indexes,
|
||||
@ -84,8 +84,8 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.zipInline(
|
||||
r: BufferND<T>,
|
||||
crossinline block: A.(l: T, r: T) -> T,
|
||||
): BufferND<T> {
|
||||
require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" }
|
||||
val indexes = l.indices
|
||||
require(l.shapeIndices == r.shapeIndices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" }
|
||||
val indexes = l.shapeIndices
|
||||
val lbuffer = l.buffer
|
||||
val rbuffer = r.buffer
|
||||
return BufferND(
|
||||
@ -99,25 +99,25 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.zipInline(
|
||||
@OptIn(PerformancePitfall::class)
|
||||
public open class BufferedGroupNDOps<T, out A : Group<T>>(
|
||||
override val bufferAlgebra: BufferAlgebra<T, A>,
|
||||
override val indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
||||
override val indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder,
|
||||
) : GroupOpsND<T, A>, BufferAlgebraND<T, A> {
|
||||
override fun StructureND<T>.unaryMinus(): StructureND<T> = map { -it }
|
||||
}
|
||||
|
||||
public open class BufferedRingOpsND<T, out A : Ring<T>>(
|
||||
bufferAlgebra: BufferAlgebra<T, A>,
|
||||
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
||||
indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder,
|
||||
) : BufferedGroupNDOps<T, A>(bufferAlgebra, indexerBuilder), RingOpsND<T, A>
|
||||
|
||||
public open class BufferedFieldOpsND<T, out A : Field<T>>(
|
||||
bufferAlgebra: BufferAlgebra<T, A>,
|
||||
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
||||
indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder,
|
||||
) : BufferedRingOpsND<T, A>(bufferAlgebra, indexerBuilder), FieldOpsND<T, A> {
|
||||
|
||||
public constructor(
|
||||
elementAlgebra: A,
|
||||
bufferFactory: BufferFactory<T>,
|
||||
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
||||
indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder,
|
||||
) : this(BufferFieldOps(elementAlgebra, bufferFactory), indexerBuilder)
|
||||
|
||||
@OptIn(PerformancePitfall::class)
|
||||
|
@ -14,17 +14,17 @@ import space.kscience.kmath.structures.MutableBufferFactory
|
||||
* Represents [StructureND] over [Buffer].
|
||||
*
|
||||
* @param T the type of items.
|
||||
* @param indices The strides to access elements of [Buffer] by linear indices.
|
||||
* @param shapeIndices The strides to access elements of [Buffer] by linear indices.
|
||||
* @param buffer The underlying buffer.
|
||||
*/
|
||||
public open class BufferND<out T>(
|
||||
override val indices: ShapeIndexer,
|
||||
override val shapeIndices: ShapeIndices,
|
||||
public open val buffer: Buffer<T>,
|
||||
) : StructureND<T> {
|
||||
|
||||
override operator fun get(index: IntArray): T = buffer[indices.offset(index)]
|
||||
override operator fun get(index: IntArray): T = buffer[shapeIndices.offset(index)]
|
||||
|
||||
override val shape: IntArray get() = indices.shape
|
||||
override val shape: IntArray get() = shapeIndices.shape
|
||||
|
||||
override fun toString(): String = StructureND.toString(this)
|
||||
}
|
||||
@ -37,7 +37,7 @@ public inline fun <T, reified R : Any> StructureND<T>.mapToBuffer(
|
||||
crossinline transform: (T) -> R,
|
||||
): BufferND<R> {
|
||||
return if (this is BufferND<T>)
|
||||
BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
||||
BufferND(this.shapeIndices, factory.invoke(shapeIndices.linearSize) { transform(buffer[it]) })
|
||||
else {
|
||||
val strides = DefaultStrides(shape)
|
||||
BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
|
||||
@ -52,11 +52,11 @@ public inline fun <T, reified R : Any> StructureND<T>.mapToBuffer(
|
||||
* @param buffer The underlying buffer.
|
||||
*/
|
||||
public class MutableBufferND<T>(
|
||||
strides: ShapeIndexer,
|
||||
strides: ShapeIndices,
|
||||
override val buffer: MutableBuffer<T>,
|
||||
) : MutableStructureND<T>, BufferND<T>(strides, buffer) {
|
||||
override fun set(index: IntArray, value: T) {
|
||||
buffer[indices.offset(index)] = value
|
||||
buffer[shapeIndices.offset(index)] = value
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ public inline fun <T, reified R : Any> MutableStructureND<T>.mapToMutableBuffer(
|
||||
crossinline transform: (T) -> R,
|
||||
): MutableBufferND<R> {
|
||||
return if (this is MutableBufferND<T>)
|
||||
MutableBufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
||||
MutableBufferND(this.shapeIndices, factory.invoke(shapeIndices.linearSize) { transform(buffer[it]) })
|
||||
else {
|
||||
val strides = DefaultStrides(shape)
|
||||
MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
|
||||
|
@ -14,7 +14,7 @@ import kotlin.contracts.contract
|
||||
import kotlin.math.pow as kpow
|
||||
|
||||
public class DoubleBufferND(
|
||||
indexes: ShapeIndexer,
|
||||
indexes: ShapeIndices,
|
||||
override val buffer: DoubleBuffer,
|
||||
) : BufferND<Double>(indexes, buffer)
|
||||
|
||||
@ -34,7 +34,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
|
||||
arg: DoubleBufferND,
|
||||
transform: (Double) -> Double,
|
||||
): DoubleBufferND {
|
||||
val indexes = arg.indices
|
||||
val indexes = arg.shapeIndices
|
||||
val array = arg.buffer.array
|
||||
return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { transform(array[it]) })
|
||||
}
|
||||
@ -44,8 +44,8 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
|
||||
r: DoubleBufferND,
|
||||
block: (l: Double, r: Double) -> Double,
|
||||
): DoubleBufferND {
|
||||
require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" }
|
||||
val indexes = l.indices
|
||||
require(l.shapeIndices == r.shapeIndices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" }
|
||||
val indexes = l.shapeIndices
|
||||
val lArray = l.buffer.array
|
||||
val rArray = r.buffer.array
|
||||
return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { block(lArray[it], rArray[it]) })
|
||||
|
@ -10,7 +10,7 @@ import kotlin.native.concurrent.ThreadLocal
|
||||
/**
|
||||
* A converter from linear index to multivariate index
|
||||
*/
|
||||
public interface ShapeIndexer: Iterable<IntArray>{
|
||||
public interface ShapeIndices: Iterable<IntArray>{
|
||||
public val shape: Shape
|
||||
|
||||
/**
|
||||
@ -44,7 +44,7 @@ public interface ShapeIndexer: Iterable<IntArray>{
|
||||
/**
|
||||
* Linear transformation of indexes
|
||||
*/
|
||||
public abstract class Strides: ShapeIndexer {
|
||||
public abstract class Strides: ShapeIndices {
|
||||
/**
|
||||
* Array strides
|
||||
*/
|
@ -54,7 +54,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
|
||||
* @return the lazy sequence of pairs of indices to values.
|
||||
*/
|
||||
@PerformancePitfall
|
||||
public fun elements(): Sequence<Pair<IntArray, T>> = indices.asSequence().map { it to get(it) }
|
||||
public fun elements(): Sequence<Pair<IntArray, T>> = shapeIndices.asSequence().map { it to get(it) }
|
||||
|
||||
/**
|
||||
* Feature is some additional structure information that allows to access it special properties or hints.
|
||||
@ -71,7 +71,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
|
||||
if (st1 === st2) return true
|
||||
|
||||
// fast comparison of buffers if possible
|
||||
if (st1 is BufferND && st2 is BufferND && st1.indices == st2.indices)
|
||||
if (st1 is BufferND && st2 is BufferND && st1.shapeIndices == st2.shapeIndices)
|
||||
return Buffer.contentEquals(st1.buffer, st2.buffer)
|
||||
|
||||
//element by element comparison if it could not be avoided
|
||||
@ -87,7 +87,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
|
||||
if (st1 === st2) return true
|
||||
|
||||
// fast comparison of buffers if possible
|
||||
if (st1 is BufferND && st2 is BufferND && st1.indices == st2.indices)
|
||||
if (st1 is BufferND && st2 is BufferND && st1.shapeIndices == st2.shapeIndices)
|
||||
return Buffer.contentEquals(st1.buffer, st2.buffer)
|
||||
|
||||
//element by element comparison if it could not be avoided
|
||||
|
@ -61,14 +61,14 @@ public inline fun <reified T> Buffer<T>.toTypedArray(): Array<T> = Array(size, :
|
||||
/**
|
||||
* Create a new buffer from this one with the given mapping function and using [Buffer.Companion.auto] buffer factory.
|
||||
*/
|
||||
public inline fun <T : Any, reified R : Any> Buffer<T>.map(block: (T) -> R): Buffer<R> =
|
||||
public inline fun <T, reified R : Any> Buffer<T>.map(block: (T) -> R): Buffer<R> =
|
||||
Buffer.auto(size) { block(get(it)) }
|
||||
|
||||
/**
|
||||
* Create a new buffer from this one with the given mapping function.
|
||||
* Provided [bufferFactory] is used to construct the new buffer.
|
||||
*/
|
||||
public inline fun <T : Any, R : Any> Buffer<T>.map(
|
||||
public inline fun <T, R> Buffer<T>.map(
|
||||
bufferFactory: BufferFactory<R>,
|
||||
crossinline block: (T) -> R,
|
||||
): Buffer<R> = bufferFactory(size) { block(get(it)) }
|
||||
@ -77,15 +77,16 @@ public inline fun <T : Any, R : Any> Buffer<T>.map(
|
||||
* Create a new buffer from this one with the given indexed mapping function.
|
||||
* Provided [BufferFactory] is used to construct the new buffer.
|
||||
*/
|
||||
public inline fun <T : Any, reified R : Any> Buffer<T>.mapIndexed(
|
||||
public inline fun <T, reified R : Any> Buffer<T>.mapIndexed(
|
||||
bufferFactory: BufferFactory<R> = Buffer.Companion::auto,
|
||||
crossinline block: (index: Int, value: T) -> R,
|
||||
): Buffer<R> = bufferFactory(size) { block(it, get(it)) }
|
||||
|
||||
/**
|
||||
* Fold given buffer according to [operation]
|
||||
* TODO add element algebra as fold receiver
|
||||
*/
|
||||
public inline fun <T : Any, R> Buffer<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
|
||||
public inline fun <T, R> Buffer<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
|
||||
var accumulator = initial
|
||||
for (index in this.indices) accumulator = operation(accumulator, get(index))
|
||||
return accumulator
|
||||
@ -95,7 +96,7 @@ public inline fun <T : Any, R> Buffer<T>.fold(initial: R, operation: (acc: R, T)
|
||||
* Zip two buffers using given [transform].
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public inline fun <T1 : Any, T2 : Any, reified R : Any> Buffer<T1>.zip(
|
||||
public inline fun <T1, T2, reified R : Any> Buffer<T1>.zip(
|
||||
other: Buffer<T2>,
|
||||
bufferFactory: BufferFactory<R> = Buffer.Companion::auto,
|
||||
crossinline transform: (T1, T2) -> R,
|
||||
|
@ -48,6 +48,11 @@ public interface Buffer<out T> {
|
||||
|
||||
override fun toString(): String
|
||||
|
||||
/**
|
||||
* Returns an [IntRange] of the valid indices for this [Buffer].
|
||||
*/
|
||||
public val indices: IntRange get() = 0 until size
|
||||
|
||||
public companion object {
|
||||
|
||||
public fun toString(buffer: Buffer<*>): String =
|
||||
@ -100,10 +105,12 @@ public interface Buffer<out T> {
|
||||
}
|
||||
}
|
||||
|
||||
public operator fun<T> Buffer<T>.get(index: UInt): T = get(index.toInt())
|
||||
|
||||
/**
|
||||
* Returns an [IntRange] of the valid indices for this [Buffer].
|
||||
* if index is in range of buffer, return the value. Otherwise, return null.
|
||||
*/
|
||||
public val Buffer<*>.indices: IntRange get() = 0 until size
|
||||
public fun <T> Buffer<T>.getOrNull(index: Int): T? = if (index in indices) get(index) else null
|
||||
|
||||
/**
|
||||
* Immutable wrapper for [MutableBuffer].
|
||||
|
@ -11,7 +11,6 @@ import space.kscience.kmath.operations.DoubleL2Norm
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.MutableBuffer.Companion.double
|
||||
import space.kscience.kmath.structures.asBuffer
|
||||
import space.kscience.kmath.structures.indices
|
||||
import kotlin.math.pow
|
||||
|
||||
public typealias DoubleVector = Point<Double>
|
||||
|
@ -13,8 +13,8 @@ import space.kscience.kmath.structures.DoubleBuffer
|
||||
* Map one [BufferND] using function without indices.
|
||||
*/
|
||||
public inline fun BufferND<Double>.mapInline(crossinline transform: DoubleField.(Double) -> Double): BufferND<Double> {
|
||||
val array = DoubleArray(indices.linearSize) { offset -> DoubleField.transform(buffer[offset]) }
|
||||
return BufferND(indices, DoubleBuffer(array))
|
||||
val array = DoubleArray(shapeIndices.linearSize) { offset -> DoubleField.transform(buffer[offset]) }
|
||||
return BufferND(shapeIndices, DoubleBuffer(array))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,6 @@ import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.Field
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.asBuffer
|
||||
import space.kscience.kmath.structures.indices
|
||||
|
||||
/**
|
||||
* A simple one-pass integrator based on Gauss rule
|
||||
|
@ -0,0 +1,170 @@
|
||||
package space.kscience.kmath.series
|
||||
|
||||
import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.BufferFactory
|
||||
import space.kscience.kmath.structures.getOrNull
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
private fun IntRange.intersect(other: IntRange): IntRange =
|
||||
max(first, other.first)..min(last, other.last)
|
||||
|
||||
private val IntRange.size get() = last - first + 1
|
||||
|
||||
private class BufferView<T>(val buffer: Buffer<T>, val offset: Int, override val size: Int) : Buffer<T> {
|
||||
init {
|
||||
require(offset >= 0) { " Range offset must be positive" }
|
||||
require(offset < buffer.size) { "Range offset is beyond the buffer size" }
|
||||
require(size > 0) { "Size must be positive" }
|
||||
require(size < buffer.size) { "Slice size is larger than the buffer" }
|
||||
}
|
||||
|
||||
override fun get(index: Int): T = buffer[index - offset]
|
||||
|
||||
override fun iterator(): Iterator<T> = buffer.asSequence().drop(offset).take(size).iterator()
|
||||
|
||||
override fun toString(): String = "$buffer[${offset}:${offset + size - 1}]"
|
||||
|
||||
override val indices: IntRange = offset until offset + size
|
||||
}
|
||||
|
||||
/**
|
||||
* A scope to operation on series
|
||||
*/
|
||||
public class SeriesAlgebra<T, A : Ring<T>, L>(
|
||||
public val bufferAlgebra: BufferRingOps<T, A>,
|
||||
private val labelResolver: (Int) -> L,
|
||||
) : RingOps<Buffer<T>> {
|
||||
|
||||
public val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
|
||||
public val bufferFactory: BufferFactory<T> get() = bufferAlgebra.bufferFactory
|
||||
|
||||
public val Buffer<T>.offset: UInt get() = indices.first.toUInt()
|
||||
|
||||
/**
|
||||
* Build a new series
|
||||
*/
|
||||
public fun series(size: Int, fromIndex: Int = 0, block: A.(label: L) -> T): Buffer<T> {
|
||||
return bufferFactory(size) {
|
||||
val index = it + fromIndex
|
||||
elementAlgebra.block(labelResolver(index))
|
||||
}.moveTo(fromIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a series starting to start at a given index
|
||||
*/
|
||||
public fun Buffer<T>.moveTo(index: Int): Buffer<T> = if (index == 0) {
|
||||
this
|
||||
} else if (this is BufferView) {
|
||||
BufferView(buffer, index.toInt(), size)
|
||||
} else {
|
||||
BufferView(this, index.toInt(), size)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a buffer view using given range
|
||||
*/
|
||||
public fun Buffer<T>.get(range: IntRange): Buffer<T> {
|
||||
val size = range.size
|
||||
return if (this is BufferView) {
|
||||
BufferView(this, indices.first + range.first, size)
|
||||
} else {
|
||||
BufferView(this, range.first, size)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a label buffer for given buffer.
|
||||
*/
|
||||
public val Buffer<T>.labels: List<L> get() = indices.map(labelResolver)
|
||||
|
||||
|
||||
/**
|
||||
* Try to resolve element by label and return null if element with a given label is not found
|
||||
*/
|
||||
public operator fun Buffer<T>.get(label: L): T? {
|
||||
val index = labels.indexOf(label)
|
||||
if (index == -1) return null
|
||||
return get(index + offset.toInt())
|
||||
}
|
||||
|
||||
override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = elementAlgebra.invoke {
|
||||
val newRange = left.indices.intersect(right.indices)
|
||||
//TODO optimize copy at BufferAlgebra level
|
||||
bufferFactory(newRange.size) {
|
||||
val offset = it + newRange.first
|
||||
left[offset] + right[offset]
|
||||
}.moveTo(newRange.first)
|
||||
}
|
||||
|
||||
override fun Buffer<T>.unaryMinus(): Buffer<T> = map { -it }
|
||||
|
||||
override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = elementAlgebra.invoke {
|
||||
val newRange = left.indices.intersect(right.indices)
|
||||
bufferFactory(newRange.size) {
|
||||
val offset = it + newRange.first
|
||||
left[offset] * right[offset]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a series to another series of the same size
|
||||
*/
|
||||
public inline fun Buffer<T>.map(crossinline transform: A.(T) -> T): Buffer<T> {
|
||||
val buf = bufferFactory(size) {
|
||||
elementAlgebra.transform(get(it))
|
||||
}
|
||||
return buf.moveTo(indices.first)
|
||||
}
|
||||
|
||||
/**
|
||||
* Map series to another series of the same size with label
|
||||
*/
|
||||
public inline fun Buffer<T>.mapWithLabel(crossinline transform: A.(arg: T, label: L) -> T): Buffer<T> {
|
||||
val labels = labels
|
||||
val buf = bufferFactory(size) {
|
||||
elementAlgebra.transform(get(it), labels[it])
|
||||
}
|
||||
return buf.moveTo(indices.first)
|
||||
}
|
||||
|
||||
public inline fun <R> Buffer<T>.fold(initial: R, operation: A.(acc: R, T) -> R): R {
|
||||
var accumulator = initial
|
||||
for (index in this.indices) accumulator = elementAlgebra.operation(accumulator, get(index))
|
||||
return accumulator
|
||||
}
|
||||
|
||||
public inline fun <R> Buffer<T>.foldWithLabel(initial: R, operation: A.(acc: R, arg: T, label: L) -> R): R {
|
||||
val labels = labels
|
||||
var accumulator = initial
|
||||
for (index in this.indices) accumulator = elementAlgebra.operation(accumulator, get(index), labels[index])
|
||||
return accumulator
|
||||
}
|
||||
|
||||
/**
|
||||
* Zip two buffers replacing missing values with [defaultValue]
|
||||
*/
|
||||
public inline fun Buffer<T>.zip(
|
||||
other: Buffer<T>,
|
||||
defaultValue: T,
|
||||
crossinline operation: A.(left: T?, right: T?) -> T?,
|
||||
): Buffer<T> {
|
||||
val start = min(indices.first, other.indices.first)
|
||||
val size = max(indices.last, other.indices.last) - start
|
||||
return bufferFactory(size) {
|
||||
elementAlgebra.operation(
|
||||
getOrNull(it) ?: defaultValue,
|
||||
other.getOrNull(it) ?: defaultValue
|
||||
) ?: defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun <T, A : Ring<T>, L> BufferRingOps<T, A>.seriesAlgebra(labels: Iterable<L>): SeriesAlgebra<T, A, L> {
|
||||
val l = labels.toList()
|
||||
return SeriesAlgebra(this) {
|
||||
if (it in l.indices) l[it] else error("Index $it is outside of labels range ${l.indices}")
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ package space.kscience.kmath.stat
|
||||
|
||||
import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.indices
|
||||
|
||||
/**
|
||||
* Arithmetic mean
|
||||
@ -45,8 +44,10 @@ public class Mean<T>(
|
||||
public companion object {
|
||||
@Deprecated("Use Double.mean instead")
|
||||
public val double: Mean<Double> = Mean(DoubleField) { sum, count -> sum / count }
|
||||
|
||||
@Deprecated("Use Int.mean instead")
|
||||
public val int: Mean<Int> = Mean(IntRing) { sum, count -> sum / count }
|
||||
|
||||
@Deprecated("Use Long.mean instead")
|
||||
public val long: Mean<Long> = Mean(LongRing) { sum, count -> sum / count }
|
||||
|
||||
@ -60,6 +61,6 @@ public class Mean<T>(
|
||||
//TODO replace with optimized version which respects overflow
|
||||
public val Double.Companion.mean: Mean<Double> get() = Mean(DoubleField) { sum, count -> sum / count }
|
||||
public val Int.Companion.mean: Mean<Int> get() = Mean(IntRing) { sum, count -> sum / count }
|
||||
public val Long.Companion.mean: Mean<Long> get() = Mean(LongRing) { sum, count -> sum / count }
|
||||
public val Long.Companion.mean: Mean<Long> get() = Mean(LongRing) { sum, count -> sum / count }
|
||||
|
||||
|
||||
|
@ -26,7 +26,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
||||
val broadcast = broadcastTensors(tensor, arg.tensor)
|
||||
val newThis = broadcast[0]
|
||||
val newOther = broadcast[1]
|
||||
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
||||
val resBuffer = DoubleArray(newThis.shapeIndices.linearSize) { i ->
|
||||
newThis.mutableBuffer.array()[i] + newOther.mutableBuffer.array()[i]
|
||||
}
|
||||
return DoubleTensor(newThis.shape, resBuffer)
|
||||
@ -34,7 +34,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
||||
|
||||
override fun Tensor<Double>.plusAssign(arg: StructureND<Double>) {
|
||||
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
||||
for (i in 0 until tensor.indices.linearSize) {
|
||||
for (i in 0 until tensor.shapeIndices.linearSize) {
|
||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] +=
|
||||
newOther.mutableBuffer.array()[tensor.bufferStart + i]
|
||||
}
|
||||
@ -44,7 +44,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
||||
val broadcast = broadcastTensors(tensor, arg.tensor)
|
||||
val newThis = broadcast[0]
|
||||
val newOther = broadcast[1]
|
||||
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
||||
val resBuffer = DoubleArray(newThis.shapeIndices.linearSize) { i ->
|
||||
newThis.mutableBuffer.array()[i] - newOther.mutableBuffer.array()[i]
|
||||
}
|
||||
return DoubleTensor(newThis.shape, resBuffer)
|
||||
@ -52,7 +52,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
||||
|
||||
override fun Tensor<Double>.minusAssign(arg: StructureND<Double>) {
|
||||
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
||||
for (i in 0 until tensor.indices.linearSize) {
|
||||
for (i in 0 until tensor.shapeIndices.linearSize) {
|
||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] -=
|
||||
newOther.mutableBuffer.array()[tensor.bufferStart + i]
|
||||
}
|
||||
@ -62,7 +62,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
||||
val broadcast = broadcastTensors(tensor, arg.tensor)
|
||||
val newThis = broadcast[0]
|
||||
val newOther = broadcast[1]
|
||||
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
||||
val resBuffer = DoubleArray(newThis.shapeIndices.linearSize) { i ->
|
||||
newThis.mutableBuffer.array()[newThis.bufferStart + i] *
|
||||
newOther.mutableBuffer.array()[newOther.bufferStart + i]
|
||||
}
|
||||
@ -71,7 +71,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
||||
|
||||
override fun Tensor<Double>.timesAssign(arg: StructureND<Double>) {
|
||||
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
||||
for (i in 0 until tensor.indices.linearSize) {
|
||||
for (i in 0 until tensor.shapeIndices.linearSize) {
|
||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] *=
|
||||
newOther.mutableBuffer.array()[tensor.bufferStart + i]
|
||||
}
|
||||
@ -81,7 +81,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
||||
val broadcast = broadcastTensors(tensor, arg.tensor)
|
||||
val newThis = broadcast[0]
|
||||
val newOther = broadcast[1]
|
||||
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
||||
val resBuffer = DoubleArray(newThis.shapeIndices.linearSize) { i ->
|
||||
newThis.mutableBuffer.array()[newOther.bufferStart + i] /
|
||||
newOther.mutableBuffer.array()[newOther.bufferStart + i]
|
||||
}
|
||||
@ -90,7 +90,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
||||
|
||||
override fun Tensor<Double>.divAssign(arg: StructureND<Double>) {
|
||||
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
||||
for (i in 0 until tensor.indices.linearSize) {
|
||||
for (i in 0 until tensor.shapeIndices.linearSize) {
|
||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] /=
|
||||
newOther.mutableBuffer.array()[tensor.bufferStart + i]
|
||||
}
|
||||
|
@ -22,22 +22,22 @@ public open class BufferedTensor<T> internal constructor(
|
||||
/**
|
||||
* Buffer strides based on [TensorLinearStructure] implementation
|
||||
*/
|
||||
override val indices: Strides get() = TensorLinearStructure(shape)
|
||||
override val shapeIndices: Strides get() = TensorLinearStructure(shape)
|
||||
|
||||
/**
|
||||
* Number of elements in tensor
|
||||
*/
|
||||
public val numElements: Int
|
||||
get() = indices.linearSize
|
||||
get() = shapeIndices.linearSize
|
||||
|
||||
override fun get(index: IntArray): T = mutableBuffer[bufferStart + indices.offset(index)]
|
||||
override fun get(index: IntArray): T = mutableBuffer[bufferStart + shapeIndices.offset(index)]
|
||||
|
||||
override fun set(index: IntArray, value: T) {
|
||||
mutableBuffer[bufferStart + indices.offset(index)] = value
|
||||
mutableBuffer[bufferStart + shapeIndices.offset(index)] = value
|
||||
}
|
||||
|
||||
@PerformancePitfall
|
||||
override fun elements(): Sequence<Pair<IntArray, T>> = indices.asSequence().map {
|
||||
override fun elements(): Sequence<Pair<IntArray, T>> = shapeIndices.asSequence().map {
|
||||
it to get(it)
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import space.kscience.kmath.nd.as1D
|
||||
import space.kscience.kmath.nd.as2D
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.structures.MutableBuffer
|
||||
import space.kscience.kmath.structures.indices
|
||||
import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra
|
||||
import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra
|
||||
import space.kscience.kmath.tensors.api.Tensor
|
||||
@ -63,7 +62,7 @@ public open class DoubleTensorAlgebra :
|
||||
val tensor = this.tensor
|
||||
//TODO remove additional copy
|
||||
val sourceArray = tensor.copyArray()
|
||||
val array = DoubleArray(tensor.numElements) { DoubleField.transform(tensor.indices.index(it), sourceArray[it]) }
|
||||
val array = DoubleArray(tensor.numElements) { DoubleField.transform(tensor.shapeIndices.index(it), sourceArray[it]) }
|
||||
return DoubleTensor(
|
||||
tensor.shape,
|
||||
array,
|
||||
@ -365,11 +364,11 @@ public open class DoubleTensorAlgebra :
|
||||
val resTensor = DoubleTensor(resShape, resBuffer)
|
||||
|
||||
for (offset in 0 until n) {
|
||||
val oldMultiIndex = tensor.indices.index(offset)
|
||||
val oldMultiIndex = tensor.shapeIndices.index(offset)
|
||||
val newMultiIndex = oldMultiIndex.copyOf()
|
||||
newMultiIndex[ii] = newMultiIndex[jj].also { newMultiIndex[jj] = newMultiIndex[ii] }
|
||||
|
||||
val linearIndex = resTensor.indices.offset(newMultiIndex)
|
||||
val linearIndex = resTensor.shapeIndices.offset(newMultiIndex)
|
||||
resTensor.mutableBuffer.array()[linearIndex] =
|
||||
tensor.mutableBuffer.array()[tensor.bufferStart + offset]
|
||||
}
|
||||
@ -467,7 +466,7 @@ public open class DoubleTensorAlgebra :
|
||||
val resTensor = zeros(resShape)
|
||||
|
||||
for (i in 0 until diagonalEntries.tensor.numElements) {
|
||||
val multiIndex = diagonalEntries.tensor.indices.index(i)
|
||||
val multiIndex = diagonalEntries.tensor.shapeIndices.index(i)
|
||||
|
||||
var offset1 = 0
|
||||
var offset2 = abs(realOffset)
|
||||
@ -592,7 +591,7 @@ public open class DoubleTensorAlgebra :
|
||||
val init = foldFunction(DoubleArray(1) { 0.0 })
|
||||
val resTensor = BufferedTensor(resShape,
|
||||
MutableBuffer.auto(resNumElements) { init }, 0)
|
||||
for (index in resTensor.indices) {
|
||||
for (index in resTensor.shapeIndices) {
|
||||
val prefix = index.take(dim).toIntArray()
|
||||
val suffix = index.takeLast(dimension - dim - 1).toIntArray()
|
||||
resTensor[index] = foldFunction(DoubleArray(shape[dim]) { i ->
|
||||
|
@ -10,7 +10,7 @@ import kotlin.math.max
|
||||
|
||||
internal fun multiIndexBroadCasting(tensor: DoubleTensor, resTensor: DoubleTensor, linearSize: Int) {
|
||||
for (linearIndex in 0 until linearSize) {
|
||||
val totalMultiIndex = resTensor.indices.index(linearIndex)
|
||||
val totalMultiIndex = resTensor.shapeIndices.index(linearIndex)
|
||||
val curMultiIndex = tensor.shape.copyOf()
|
||||
|
||||
val offset = totalMultiIndex.size - curMultiIndex.size
|
||||
@ -23,7 +23,7 @@ internal fun multiIndexBroadCasting(tensor: DoubleTensor, resTensor: DoubleTenso
|
||||
}
|
||||
}
|
||||
|
||||
val curLinearIndex = tensor.indices.offset(curMultiIndex)
|
||||
val curLinearIndex = tensor.shapeIndices.offset(curMultiIndex)
|
||||
resTensor.mutableBuffer.array()[linearIndex] =
|
||||
tensor.mutableBuffer.array()[tensor.bufferStart + curLinearIndex]
|
||||
}
|
||||
@ -112,7 +112,7 @@ internal fun broadcastOuterTensors(vararg tensors: DoubleTensor): List<DoubleTen
|
||||
val resTensor = DoubleTensor(totalShape + matrixShape, DoubleArray(n * matrixSize))
|
||||
|
||||
for (linearIndex in 0 until n) {
|
||||
val totalMultiIndex = outerTensor.indices.index(linearIndex)
|
||||
val totalMultiIndex = outerTensor.shapeIndices.index(linearIndex)
|
||||
var curMultiIndex = tensor.shape.sliceArray(0..tensor.shape.size - 3).copyOf()
|
||||
curMultiIndex = IntArray(totalMultiIndex.size - curMultiIndex.size) { 1 } + curMultiIndex
|
||||
|
||||
@ -127,13 +127,13 @@ internal fun broadcastOuterTensors(vararg tensors: DoubleTensor): List<DoubleTen
|
||||
}
|
||||
|
||||
for (i in 0 until matrixSize) {
|
||||
val curLinearIndex = newTensor.indices.offset(
|
||||
val curLinearIndex = newTensor.shapeIndices.offset(
|
||||
curMultiIndex +
|
||||
matrix.indices.index(i)
|
||||
matrix.shapeIndices.index(i)
|
||||
)
|
||||
val newLinearIndex = resTensor.indices.offset(
|
||||
val newLinearIndex = resTensor.shapeIndices.offset(
|
||||
totalMultiIndex +
|
||||
matrix.indices.index(i)
|
||||
matrix.shapeIndices.index(i)
|
||||
)
|
||||
|
||||
resTensor.mutableBuffer.array()[resTensor.bufferStart + newLinearIndex] =
|
||||
|
@ -3,8 +3,11 @@
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
@file:OptIn(PerformancePitfall::class)
|
||||
|
||||
package space.kscience.kmath.tensors.core.internal
|
||||
|
||||
import space.kscience.kmath.misc.PerformancePitfall
|
||||
import space.kscience.kmath.nd.MutableStructure1D
|
||||
import space.kscience.kmath.nd.MutableStructure2D
|
||||
import space.kscience.kmath.nd.as1D
|
||||
|
@ -28,7 +28,7 @@ internal fun <T> StructureND<T>.copyToBufferedTensor(): BufferedTensor<T> =
|
||||
|
||||
internal fun <T> StructureND<T>.toBufferedTensor(): BufferedTensor<T> = when (this) {
|
||||
is BufferedTensor<T> -> this
|
||||
is MutableBufferND<T> -> if (this.indices == TensorLinearStructure(this.shape)) {
|
||||
is MutableBufferND<T> -> if (this.shapeIndices == TensorLinearStructure(this.shape)) {
|
||||
BufferedTensor(this.shape, this.buffer, 0)
|
||||
} else {
|
||||
this.copyToBufferedTensor()
|
||||
|
@ -85,7 +85,7 @@ internal fun format(value: Double, digits: Int = 4): String = buildString {
|
||||
internal fun DoubleTensor.toPrettyString(): String = buildString {
|
||||
var offset = 0
|
||||
val shape = this@toPrettyString.shape
|
||||
val linearStructure = this@toPrettyString.indices
|
||||
val linearStructure = this@toPrettyString.shapeIndices
|
||||
val vectorSize = shape.last()
|
||||
append("DoubleTensor(\n")
|
||||
var charOffset = 3
|
||||
|
@ -5,8 +5,8 @@ pluginManagement {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
val kotlinVersion = "1.6.0-RC"
|
||||
val toolsVersion = "0.10.5"
|
||||
val kotlinVersion = "1.6.0-RC2"
|
||||
val toolsVersion = "0.10.6"
|
||||
|
||||
plugins {
|
||||
id("org.jetbrains.kotlinx.benchmark") version "0.3.1"
|
||||
|
Loading…
Reference in New Issue
Block a user