Buffer views

This commit is contained in:
Alexander Nozik 2021-11-08 17:50:49 +03:00
parent bf504ae6c5
commit a1351aa942
55 changed files with 610 additions and 176 deletions

View File

@ -12,8 +12,6 @@ import space.kscience.kmath.commons.optimization.CMOptimizer
import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.expressions.chiSquaredExpression import space.kscience.kmath.expressions.chiSquaredExpression
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.operations.asIterable
import space.kscience.kmath.operations.toList
import space.kscience.kmath.optimization.FunctionOptimizationTarget import space.kscience.kmath.optimization.FunctionOptimizationTarget
import space.kscience.kmath.optimization.optimizeWith import space.kscience.kmath.optimization.optimizeWith
import space.kscience.kmath.optimization.resultPoint import space.kscience.kmath.optimization.resultPoint
@ -22,6 +20,8 @@ import space.kscience.kmath.real.DoubleVector
import space.kscience.kmath.real.map import space.kscience.kmath.real.map
import space.kscience.kmath.real.step import space.kscience.kmath.real.step
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.structures.asIterable
import space.kscience.kmath.structures.toList
import space.kscience.plotly.* import space.kscience.plotly.*
import space.kscience.plotly.models.ScatterMode import space.kscience.plotly.models.ScatterMode
import space.kscience.plotly.models.TraceValues import space.kscience.plotly.models.TraceValues

View File

@ -13,8 +13,6 @@ import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.expressions.binding import space.kscience.kmath.expressions.binding
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.operations.asIterable
import space.kscience.kmath.operations.toList
import space.kscience.kmath.optimization.QowOptimizer import space.kscience.kmath.optimization.QowOptimizer
import space.kscience.kmath.optimization.chiSquaredOrNull import space.kscience.kmath.optimization.chiSquaredOrNull
import space.kscience.kmath.optimization.fitWith import space.kscience.kmath.optimization.fitWith
@ -22,6 +20,8 @@ import space.kscience.kmath.optimization.resultPoint
import space.kscience.kmath.real.map import space.kscience.kmath.real.map
import space.kscience.kmath.real.step import space.kscience.kmath.real.step
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.structures.asIterable
import space.kscience.kmath.structures.toList
import space.kscience.plotly.* import space.kscience.plotly.*
import space.kscience.plotly.models.ScatterMode import space.kscience.plotly.models.ScatterMode
import kotlin.math.abs import kotlin.math.abs

View File

@ -1,37 +1,43 @@
package space.kscience.kmath.series package space.kscience.kmath.series
import net.jafama.StrictFastMath.abs import kotlinx.html.FlowContent
import kotlinx.html.h1
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.invoke
import space.kscience.kmath.operations.toList
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.plotly.Plotly import space.kscience.kmath.structures.toList
import space.kscience.plotly.makeFile import space.kscience.plotly.*
import space.kscience.plotly.scatter
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.max
fun main() = Double.algebra.bufferAlgebra.seriesAlgebra(0..100).invoke { fun main() = with(Double.algebra.bufferAlgebra.seriesAlgebra()) {
fun Buffer<Double>.plot() { fun FlowContent.plotSeries(buffer: Buffer<Double>) {
val ls = labels val ls = buffer.labels
Plotly.plot { plot {
scatter { scatter {
x.numbers = ls x.numbers = ls
y.numbers = toList() y.numbers = buffer.toList()
} }
layout {
xaxis {
range(0.0..100.0)
}
}
}
}
val s1 = series(100) { sin(2 * PI * it / 100) + 1.0 }
val s2 = s1.slice(20..50).moveTo(40)
val s3: Buffer<Double> = s1.zip(s2) { l, r -> l + r } //s1 + s2
val s4 = ln(s3)
Plotly.page {
h1 { +"This is my plot" }
plotSeries(s1)
plotSeries(s2)
plotSeries(s4)
}.makeFile() }.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))}
} }

View File

@ -7,10 +7,11 @@ package space.kscience.kmath.commons.random
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.samplers.GaussianSampler
import space.kscience.kmath.misc.toIntExact import space.kscience.kmath.misc.toIntExact
import space.kscience.kmath.samplers.GaussianSampler
import space.kscience.kmath.samplers.next
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.next
public class CMRandomGeneratorWrapper( public class CMRandomGeneratorWrapper(
public val factory: (IntArray) -> RandomGenerator, public val factory: (IntArray) -> RandomGenerator,

View File

@ -10,13 +10,9 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import org.apache.commons.math3.transform.* import org.apache.commons.math3.transform.*
import space.kscience.kmath.complex.Complex import space.kscience.kmath.complex.Complex
import space.kscience.kmath.operations.SuspendBufferTransform
import space.kscience.kmath.streaming.chunked import space.kscience.kmath.streaming.chunked
import space.kscience.kmath.streaming.spread import space.kscience.kmath.streaming.spread
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.*
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.VirtualBuffer
import space.kscience.kmath.structures.asBuffer
/** /**

View File

@ -13,11 +13,11 @@ import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.Symbol.Companion.y import space.kscience.kmath.expressions.Symbol.Companion.y
import space.kscience.kmath.expressions.chiSquaredExpression import space.kscience.kmath.expressions.chiSquaredExpression
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.operations.map
import space.kscience.kmath.optimization.* import space.kscience.kmath.optimization.*
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.structures.map
import kotlin.math.pow import kotlin.math.pow
import kotlin.test.Test import kotlin.test.Test

View File

@ -7,6 +7,7 @@ package space.kscience.kmath.domains
import space.kscience.kmath.linear.Point import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.indices
/** /**
* *

View File

@ -6,8 +6,9 @@
package space.kscience.kmath.expressions package space.kscience.kmath.expressions
import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.asIterable
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asIterable
import space.kscience.kmath.structures.indices
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
/** /**

View File

@ -13,6 +13,7 @@ import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.VirtualBuffer import space.kscience.kmath.structures.VirtualBuffer
import space.kscience.kmath.structures.indices
public class BufferedLinearSpace<T, out A : Ring<T>>( public class BufferedLinearSpace<T, out A : Ring<T>>(

View File

@ -6,10 +6,10 @@
package space.kscience.kmath.nd package space.kscience.kmath.nd
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.operations.asSequence
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.asMutableBuffer import space.kscience.kmath.structures.asMutableBuffer
import space.kscience.kmath.structures.asSequence
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
/** /**

View File

@ -8,7 +8,6 @@ package space.kscience.kmath.operations
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.ShortBuffer import space.kscience.kmath.structures.ShortBuffer
public interface WithSize { public interface WithSize {
@ -183,7 +182,7 @@ public class BufferField<T, A : Field<T>>(
/** /**
* Generate full buffer field from given buffer operations * Generate full buffer field from given buffer operations
*/ */
public fun <T, A : Field<T>> BufferFieldOps<T, A>.withSize(size: Int): BufferField<T, A> = public fun <T, A : Field<T>> BufferAlgebra<T, A>.withSize(size: Int): BufferField<T, A> =
BufferField(elementAlgebra, bufferFactory, size) BufferField(elementAlgebra, bufferFactory, size)
//Double buffer specialization //Double buffer specialization
@ -196,6 +195,4 @@ public fun BufferField<Double, *>.buffer(vararg elements: Number): Buffer<Double
public fun <T, A : Field<T>> A.bufferAlgebra(bufferFactory: BufferFactory<T>): BufferFieldOps<T, A> = public fun <T, A : Field<T>> A.bufferAlgebra(bufferFactory: BufferFactory<T>): BufferFieldOps<T, A> =
BufferFieldOps(this, bufferFactory) BufferFieldOps(this, bufferFactory)
public val DoubleField.bufferAlgebra: BufferFieldOps<Double, DoubleField> public val DoubleField.bufferAlgebra: DoubleBufferOps get() = DoubleBufferOps
get() = BufferFieldOps(DoubleField, ::DoubleBuffer)

View File

@ -7,10 +7,7 @@ package space.kscience.kmath.operations
import space.kscience.kmath.linear.Point import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.*
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.asBuffer
import kotlin.math.* import kotlin.math.*

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.kmath.operations.asSequence import space.kscience.kmath.operations.WithSize
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -30,11 +30,11 @@ public typealias MutableBufferFactory<T> = (Int, (Int) -> T) -> MutableBuffer<T>
* *
* @param T the type of elements contained in the buffer. * @param T the type of elements contained in the buffer.
*/ */
public interface Buffer<out T> { public interface Buffer<out T> : WithSize {
/** /**
* The size of this buffer. * The size of this buffer.
*/ */
public val size: Int override val size: Int
/** /**
* Gets element at given index. * Gets element at given index.
@ -44,15 +44,10 @@ public interface Buffer<out T> {
/** /**
* Iterates over all elements. * Iterates over all elements.
*/ */
public operator fun iterator(): Iterator<T> public operator fun iterator(): Iterator<T> = indices.asSequence().map(::get).iterator()
override fun toString(): String 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 companion object {
public fun toString(buffer: Buffer<*>): String = public fun toString(buffer: Buffer<*>): String =
@ -105,6 +100,11 @@ public interface Buffer<out T> {
} }
} }
/**
* Returns an [IntRange] of the valid indices for this [Buffer].
*/
public val <T> Buffer<T>.indices: IntRange get() = 0 until size
public operator fun <T> Buffer<T>.get(index: UInt): T = get(index.toInt()) public operator fun <T> Buffer<T>.get(index: UInt): T = get(index.toInt())
/** /**

View File

@ -0,0 +1,140 @@
package space.kscience.kmath.structures
import space.kscience.kmath.misc.UnstableKMathAPI
/**
* A buffer that wraps an original buffer
*/
public interface BufferView<T> : Buffer<T> {
public val origin: Buffer<T>
/**
* Get the index in [origin] buffer from index in this buffer.
* Return -1 if element not present in the original buffer
* This method should be used internally to optimize non-boxing access.
*/
@UnstableKMathAPI
public fun originIndex(index: Int): Int
}
/**
* A zero-copy buffer that "sees" only part of original buffer. Slice can't go beyond original buffer borders.
*/
public class BufferSlice<T>(
override val origin: Buffer<T>,
public val offset: UInt = 0U,
override val size: Int,
) : BufferView<T> {
init {
require(size > 0) { "Size must be positive" }
require(offset + size.toUInt() <= origin.size.toUInt()) {
"End of buffer ${offset + size.toUInt()} is beyond the end of origin buffer size ${origin.size}"
}
}
override fun get(index: Int): T = if (index >= size) {
throw IndexOutOfBoundsException("$index is out of ${0 until size} rage")
} else {
origin[index.toUInt() + offset]
}
override fun iterator(): Iterator<T> =
(offset until (offset + size.toUInt())).asSequence().map { origin[it] }.iterator()
@UnstableKMathAPI
override fun originIndex(index: Int): Int = if (index >= size) -1 else index - offset.toInt()
override fun toString(): String = "$origin[$offset..${offset + size.toUInt()}"
}
/**
* An expanded buffer that could include the whole initial buffer ot its part and fills all space beyond it borders with [defaultValue].
*
* The [offset] parameter shows the shift of expanded buffer start relative to origin start and could be both positive and negative.
*/
public class BufferExpanded<T>(
override val origin: Buffer<T>,
public val defaultValue: T,
public val offset: Int = 0,
override val size: Int = origin.size,
) : BufferView<T> {
init {
require(size > 0) { "Size must be positive" }
}
override fun get(index: Int): T = when (index) {
!in 0 until size -> throw IndexOutOfBoundsException("Index $index is not in $indices")
in -offset until origin.size - offset -> origin[index + offset]
else -> defaultValue
}
@UnstableKMathAPI
override fun originIndex(index: Int): Int = if (index in -offset until origin.size - offset) index + offset else -1
override fun toString(): String = "$origin[$offset..${offset + size}]"
}
/**
* Zero-copy select a slice inside the original buffer
*/
public fun <T> Buffer<T>.slice(range: UIntRange): BufferView<T> = BufferSlice(
this,
range.first,
(range.last - range.first).toInt() + 1
)
/**
* Resize original buffer to a given range using given [range], filling additional segments with [defaultValue].
* Range left border could be negative to designate adding new blank segment to the beginning of the buffer
*/
public fun <T> Buffer<T>.expand(
range: IntRange,
defaultValue: T,
): BufferView<T> = if (range.first >= 0 && range.last < size) {
BufferSlice(
this,
range.first.toUInt(),
(range.last - range.first) + 1
)
} else {
BufferExpanded(
this,
defaultValue,
range.first,
(range.last - range.first) + 1
)
}
/**
* A [BufferView] that overrides indexing of the original buffer
*/
public class PermutatedBuffer<T>(
override val origin: Buffer<T>,
private val permutations: IntArray,
) : BufferView<T> {
init {
permutations.forEach { index ->
if (index !in origin.indices) {
throw IndexOutOfBoundsException("Index $index is not in ${origin.indices}")
}
}
}
override val size: Int get() = permutations.size
override fun get(index: Int): T = origin[permutations[index]]
override fun iterator(): Iterator<T> = permutations.asSequence().map { origin[it] }.iterator()
@UnstableKMathAPI
override fun originIndex(index: Int): Int = if (index in permutations.indices) permutations[index] else -1
override fun toString(): String = Buffer.toString(this)
}
/**
* Created a permuted view of given buffer using provided [indices]
*/
public fun <T> Buffer<T>.view(indices: IntArray): PermutatedBuffer<T> = PermutatedBuffer(this, indices)

View File

@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline
* @property array the underlying array. * @property array the underlying array.
*/ */
@JvmInline @JvmInline
public value class DoubleBuffer(public val array: DoubleArray) : MutableBuffer<Double> { public value class DoubleBuffer(public val array: DoubleArray) : PrimitiveBuffer<Double> {
override val size: Int get() = array.size override val size: Int get() = array.size
override operator fun get(index: Int): Double = array[index] override operator fun get(index: Int): Double = array[index]

View File

@ -14,7 +14,7 @@ import kotlin.jvm.JvmInline
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@JvmInline @JvmInline
public value class FloatBuffer(public val array: FloatArray) : MutableBuffer<Float> { public value class FloatBuffer(public val array: FloatArray) : PrimitiveBuffer<Float> {
override val size: Int get() = array.size override val size: Int get() = array.size
override operator fun get(index: Int): Float = array[index] override operator fun get(index: Int): Float = array[index]

View File

@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline
* @property array the underlying array. * @property array the underlying array.
*/ */
@JvmInline @JvmInline
public value class IntBuffer(public val array: IntArray) : MutableBuffer<Int> { public value class IntBuffer(public val array: IntArray) : PrimitiveBuffer<Int> {
override val size: Int get() = array.size override val size: Int get() = array.size
override operator fun get(index: Int): Int = array[index] override operator fun get(index: Int): Int = array[index]

View File

@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline
* @property array the underlying array. * @property array the underlying array.
*/ */
@JvmInline @JvmInline
public value class LongBuffer(public val array: LongArray) : MutableBuffer<Long> { public value class LongBuffer(public val array: LongArray) : PrimitiveBuffer<Long> {
override val size: Int get() = array.size override val size: Int get() = array.size
override operator fun get(index: Int): Long = array[index] override operator fun get(index: Int): Long = array[index]

View File

@ -95,3 +95,6 @@ public interface MutableBuffer<T> : Buffer<T> {
auto(T::class, size, initializer) auto(T::class, size, initializer)
} }
} }
public sealed interface PrimitiveBuffer<T>: MutableBuffer<T>

View File

@ -3,10 +3,9 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/ */
package space.kscience.kmath.operations package space.kscience.kmath.structures
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.*
/** /**
* Typealias for buffer transformations. * Typealias for buffer transformations.
@ -104,3 +103,39 @@ public inline fun <T1, T2, reified R : Any> Buffer<T1>.zip(
require(size == other.size) { "Buffer size mismatch in zip: expected $size but found ${other.size}" } require(size == other.size) { "Buffer size mismatch in zip: expected $size but found ${other.size}" }
return bufferFactory(size) { transform(get(it), other[it]) } return bufferFactory(size) { transform(get(it), other[it]) }
} }
/**
* Simular to https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/binary-search.html.
* The implementation is copied from Kotlin stdlib
*/
public fun <T : Comparable<T>> Buffer<T>.binarySearch(element: T, fromIndex: Int = 0, toIndex: Int = size): Int {
var low = fromIndex
var high = toIndex - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)
val cmp = compareValues(midVal, element)
when {
cmp < 0 -> low = mid + 1
cmp > 0 -> high = mid - 1
else -> return mid // key found
}
}
return -(low + 1) // key not found
}
/**
* Create a buffer containing sorted elements of this buffer.
*/
@Suppress("CAST_NEVER_SUCCEEDS")
public fun <T : Comparable<T>> Buffer<T>.sorted(): Buffer<T> = when (this) {
is PrimitiveBuffer -> when (this) {
is LongBuffer -> toLongArray().apply { sort() } as Buffer<T>
is FloatBuffer -> toFloatArray().apply { sort() } as Buffer<T>
is IntBuffer -> toIntArray().apply { sort() } as Buffer<T>
is DoubleBuffer -> toDoubleArray().apply { sort() } as Buffer<T>
}
else -> asIterable().sorted().asBuffer()
}

View File

@ -0,0 +1,27 @@
package space.kscience.kmath.structures
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFails
internal class BufferExpandedTest {
private val buffer = (0..100).toList().asBuffer()
@Test
fun shrink(){
val view = buffer.slice(20U..30U)
assertEquals(20, view[0])
assertEquals(30, view[10])
assertFails { view[11] }
}
@Test
fun expandNegative(){
val view: BufferView<Int> = buffer.expand(-20..113,0)
assertEquals(0,view[4])
assertEquals(0,view[123])
assertEquals(100, view[120])
assertFails { view[-2] }
assertFails { view[134] }
}
}

View File

@ -7,7 +7,7 @@ package space.kscience.kmath.streaming
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import space.kscience.kmath.operations.asSequence import space.kscience.kmath.structures.asSequence
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -13,9 +13,9 @@ import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.algebra
import space.kscience.kmath.operations.asIterable
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.asIterable
import kotlin.math.pow import kotlin.math.pow
/* /*

View File

@ -11,6 +11,7 @@ import space.kscience.kmath.operations.DoubleL2Norm
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBuffer.Companion.double import space.kscience.kmath.structures.MutableBuffer.Companion.double
import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.structures.indices
import kotlin.math.pow import kotlin.math.pow
public typealias DoubleVector = Point<Double> public typealias DoubleVector = Point<Double>

View File

@ -8,6 +8,7 @@ import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Field
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.structures.indices
/** /**
* A simple one-pass integrator based on Gauss rule * A simple one-pass integrator based on Gauss rule

View File

@ -5,10 +5,10 @@
package space.kscience.kmath.integration package space.kscience.kmath.integration
import space.kscience.kmath.operations.map
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.structures.map
import kotlin.jvm.Synchronized import kotlin.jvm.Synchronized
import kotlin.math.ulp import kotlin.math.ulp
import kotlin.native.concurrent.ThreadLocal import kotlin.native.concurrent.ThreadLocal

View File

@ -12,10 +12,14 @@ import space.kscience.kmath.interpolation.SplineInterpolator
import space.kscience.kmath.interpolation.interpolatePolynomials import space.kscience.kmath.interpolation.interpolatePolynomials
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.operations.* import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Field
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.operations.sum
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.MutableBufferFactory import space.kscience.kmath.structures.MutableBufferFactory
import space.kscience.kmath.structures.map
/** /**
* Compute analytical indefinite integral of this [PiecewisePolynomial], keeping all intervals intact * Compute analytical indefinite integral of this [PiecewisePolynomial], keeping all intervals intact

View File

@ -1,6 +1,7 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.operations.toList
import space.kscience.kmath.structures.toList
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -1,6 +1,6 @@
package space.kscience.kmath.geometry package space.kscience.kmath.geometry
import space.kscience.kmath.operations.toList import space.kscience.kmath.structures.toList
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -7,8 +7,8 @@ package space.kscience.kmath.histogram
import space.kscience.kmath.domains.UnivariateDomain import space.kscience.kmath.domains.UnivariateDomain
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.asSequence
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asSequence
@UnstableKMathAPI @UnstableKMathAPI

View File

@ -21,9 +21,9 @@ import space.kscience.kmath.expressions.MST
import space.kscience.kmath.expressions.MstRing import space.kscience.kmath.expressions.MstRing
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.operations.asSequence
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asSequence
/** /**
* A function for conversion of number to MST for pretty print * A function for conversion of number to MST for pretty print

View File

@ -6,8 +6,8 @@
package space.kscience.kmath.distributions package space.kscience.kmath.distributions
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.samplers.Sampler
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.Sampler
/** /**
* A distribution of typed objects. * A distribution of typed objects.

View File

@ -6,8 +6,8 @@
package space.kscience.kmath.distributions package space.kscience.kmath.distributions
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.internal.InternalErf
import space.kscience.kmath.samplers.GaussianSampler import space.kscience.kmath.samplers.GaussianSampler
import space.kscience.kmath.samplers.InternalErf
import space.kscience.kmath.samplers.NormalizedGaussianSampler import space.kscience.kmath.samplers.NormalizedGaussianSampler
import space.kscience.kmath.samplers.ZigguratNormalizedGaussianSampler import space.kscience.kmath.samplers.ZigguratNormalizedGaussianSampler
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator

View File

@ -3,12 +3,11 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/ */
package space.kscience.kmath.stat package space.kscience.kmath.distributions
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.chains.SimpleChain import space.kscience.kmath.chains.SimpleChain
import space.kscience.kmath.distributions.Distribution import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.distributions.UnivariateDistribution
public class UniformDistribution(public val range: ClosedFloatingPointRange<Double>) : UnivariateDistribution<Double> { public class UniformDistribution(public val range: ClosedFloatingPointRange<Double>) : UnivariateDistribution<Double> {
private val length: Double = range.endInclusive - range.start private val length: Double = range.endInclusive - range.start

View File

@ -7,7 +7,6 @@ package space.kscience.kmath.samplers
import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.chains.BlockingDoubleChain
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.Sampler
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import kotlin.math.ln import kotlin.math.ln
import kotlin.math.pow import kotlin.math.pow
@ -67,7 +66,7 @@ public class AhrensDieterExponentialSampler(public val mean: Double) : Sampler<D
var qi = 0.0 var qi = 0.0
DoubleArray(16) { i -> DoubleArray(16) { i ->
qi += ln2.pow(i + 1.0) / space.kscience.kmath.internal.InternalUtils.factorial(i + 1) qi += ln2.pow(i + 1.0) / InternalUtils.factorial(i + 1)
qi qi
} }
} }

View File

@ -7,9 +7,7 @@ package space.kscience.kmath.samplers
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.Sampler
import space.kscience.kmath.stat.chain import space.kscience.kmath.stat.chain
import space.kscience.kmath.stat.next
import kotlin.math.* import kotlin.math.*
/** /**

View File

@ -6,9 +6,7 @@
package space.kscience.kmath.samplers package space.kscience.kmath.samplers
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.internal.InternalUtils
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.Sampler
import space.kscience.kmath.stat.chain import space.kscience.kmath.stat.chain
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.max import kotlin.math.max

View File

@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/ */
package space.kscience.kmath.internal package space.kscience.kmath.samplers
import kotlin.math.abs import kotlin.math.abs

View File

@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/ */
package space.kscience.kmath.internal package space.kscience.kmath.samplers
import kotlin.math.* import kotlin.math.*

View File

@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/ */
package space.kscience.kmath.internal package space.kscience.kmath.samplers
import kotlin.math.ln import kotlin.math.ln
import kotlin.math.min import kotlin.math.min

View File

@ -7,7 +7,6 @@ package space.kscience.kmath.samplers
import space.kscience.kmath.chains.BlockingIntChain import space.kscience.kmath.chains.BlockingIntChain
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.Sampler
import space.kscience.kmath.structures.IntBuffer import space.kscience.kmath.structures.IntBuffer
import kotlin.math.exp import kotlin.math.exp

View File

@ -7,7 +7,6 @@ package space.kscience.kmath.samplers
import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.chains.BlockingDoubleChain
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.Sampler
public interface BlockingDoubleSampler: Sampler<Double>{ public interface BlockingDoubleSampler: Sampler<Double>{
override fun sample(generator: RandomGenerator): BlockingDoubleChain override fun sample(generator: RandomGenerator): BlockingDoubleChain

View File

@ -6,10 +6,8 @@
package space.kscience.kmath.samplers package space.kscience.kmath.samplers
import space.kscience.kmath.chains.BlockingIntChain import space.kscience.kmath.chains.BlockingIntChain
import space.kscience.kmath.internal.InternalUtils
import space.kscience.kmath.misc.toIntExact import space.kscience.kmath.misc.toIntExact
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.Sampler
import space.kscience.kmath.structures.IntBuffer import space.kscience.kmath.structures.IntBuffer
import kotlin.math.* import kotlin.math.*

View File

@ -3,12 +3,16 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/ */
package space.kscience.kmath.stat package space.kscience.kmath.samplers
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.chains.collect import space.kscience.kmath.chains.collect
import space.kscience.kmath.structures.* import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.IntBuffer
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
/** /**

View File

@ -3,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/ */
package space.kscience.kmath.stat package space.kscience.kmath.samplers
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.chains.ConstantChain import space.kscience.kmath.chains.ConstantChain
@ -12,6 +12,7 @@ import space.kscience.kmath.chains.zip
import space.kscience.kmath.operations.Group import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.stat.RandomGenerator
/** /**
* Implements [Sampler] by sampling only certain [value]. * Implements [Sampler] by sampling only certain [value].

View File

@ -1,80 +1,125 @@
package space.kscience.kmath.series package space.kscience.kmath.series
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.BufferAlgebra
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.RingOps
import space.kscience.kmath.stat.StatisticalAlgebra
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferView
import space.kscience.kmath.structures.getOrNull
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
private fun IntRange.intersect(other: IntRange): IntRange = @PublishedApi
internal fun IntRange.intersect(other: IntRange): IntRange =
max(first, other.first)..min(last, other.last) max(first, other.first)..min(last, other.last)
private val IntRange.size get() = last - first + 1 @PublishedApi
internal val IntRange.size
get() = last - first + 1
private class BufferView<T>(val buffer: Buffer<T>, val offset: Int, override val size: Int) : Buffer<T> { @PublishedApi
init { internal operator fun IntRange.contains(other: IntRange): Boolean = (other.first in this) && (other.last in this)
require(offset >= 0) { " Range offset must be positive" }
require(offset < buffer.size) { "Range offset is beyond the buffer size" } //TODO add permutated buffer
require(size > 0) { "Size must be positive" } //TODO add rank function
require(size < buffer.size) { "Slice size is larger than the buffer" }
public interface Series<T> : Buffer<T> {
public val origin: Buffer<T>
/**
* Absolute position of start of this [Series] in [SeriesAlgebra]
*/
public val position: Int
} }
override fun get(index: Int): T = buffer[index - offset] public val <T> Series<T>.absoluteIndices: IntRange get() = position until position + size
override fun iterator(): Iterator<T> = buffer.asSequence().drop(offset).take(size).iterator() /**
* A [BufferView] with index offset (both positive and negative) and possible size change
*/
private class OffsetBufer<T>(
override val origin: Buffer<T>,
override val position: Int,
override val size: Int = origin.size,
) : Series<T>, Buffer<T> by origin {
override fun toString(): String = "$buffer[${offset}:${offset + size - 1}]" init {
require(size > 0) { "Size must be positive" }
require(size <= origin.size) { "Slice size is larger than the original buffer" }
}
override val indices: IntRange = offset until offset + size override fun toString(): String = "$origin-->${position}"
} }
/** /**
* A scope to operation on series * A scope to operation on series
*/ */
public class SeriesAlgebra<T, A : Ring<T>, L>( public class SeriesAlgebra<T, out A : Ring<T>, out BA : BufferAlgebra<T, A>, L>(
public val bufferAlgebra: BufferRingOps<T, A>, override val bufferAlgebra: BA,
private val labelResolver: (Int) -> L, private val labelResolver: (Int) -> L,
) : RingOps<Buffer<T>> { ) : RingOps<Buffer<T>>, StatisticalAlgebra<T, A, BA> {
public val elementAlgebra: A get() = bufferAlgebra.elementAlgebra public val Buffer<T>.indices: IntRange
public val bufferFactory: BufferFactory<T> get() = bufferAlgebra.bufferFactory get() = if (this is Series) {
absoluteIndices
} else {
0 until size
}
public val Buffer<T>.offset: UInt get() = indices.first.toUInt() /**
* Get the value by absolute index in the series algebra or return null if index is out of range
*/
public fun Buffer<T>.getAbsoluteOrNull(index: Int): T? = when {
index !in indices -> null
this is Series -> origin[index - position]
else -> get(index)
}
/**
* Get the value by absolute index in the series algebra or throw [IndexOutOfBoundsException] if index is out of range
*/
public fun Buffer<T>.getAbsolute(index: Int): T =
getAbsoluteOrNull(index) ?: throw IndexOutOfBoundsException("Index $index is not in $indices")
/**
* Create an offset series with index starting point at [index]
*/
public fun Buffer<T>.moveTo(index: Int): Series<T> = if (this is Series) {
OffsetBufer(origin, index, size)
} else {
OffsetBufer(this, index, size)
}
/**
* Create a buffer view using given absolute range
*/
public fun Buffer<T>.slice(range: IntRange): Series<T> {
val size = range.size
return if (this is Series) {
OffsetBufer(this, indices.first + range.first, size)
} else {
OffsetBufer(this, range.first, size)
}
}
public fun Buffer<T>.expand(range: IntRange, defaultValue: T): Series<T> = if (range in indices) {
slice(range)
} else {
TODO()
}
public val Buffer<T>.offset: Int get() = if (this is Series) position else 0
/** /**
* Build a new series * Build a new series
*/ */
public fun series(size: Int, fromIndex: Int = 0, block: A.(label: L) -> T): Buffer<T> { public fun series(size: Int, fromIndex: Int = 0, block: A.(label: L) -> T): Series<T> {
return bufferFactory(size) { return bufferFactory(size) {
val index = it + fromIndex val index = it + fromIndex
elementAlgebra.block(labelResolver(index)) elementAlgebra.block(labelResolver(index))
}.moveTo(fromIndex) }.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. * Get a label buffer for given buffer.
*/ */
@ -87,34 +132,15 @@ public class SeriesAlgebra<T, A : Ring<T>, L>(
public operator fun Buffer<T>.get(label: L): T? { public operator fun Buffer<T>.get(label: L): T? {
val index = labels.indexOf(label) val index = labels.indexOf(label)
if (index == -1) return null if (index == -1) return null
return get(index + offset.toInt()) return getAbsolute(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 * Map a series to another series of the same size
*/ */
public inline fun Buffer<T>.map(crossinline transform: A.(T) -> T): Buffer<T> { public inline fun Buffer<T>.map(crossinline transform: A.(T) -> T): Series<T> {
val buf = bufferFactory(size) { val buf = bufferFactory(size) {
elementAlgebra.transform(get(it)) elementAlgebra.transform(getAbsolute(it))
} }
return buf.moveTo(indices.first) return buf.moveTo(indices.first)
} }
@ -122,49 +148,63 @@ public class SeriesAlgebra<T, A : Ring<T>, L>(
/** /**
* Map series to another series of the same size with label * 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> { public inline fun Buffer<T>.mapWithLabel(crossinline transform: A.(arg: T, label: L) -> T): Series<T> {
val labels = labels val labels = labels
val buf = bufferFactory(size) { val buf = bufferFactory(size) {
elementAlgebra.transform(get(it), labels[it]) elementAlgebra.transform(getAbsolute(it), labels[it])
} }
return buf.moveTo(indices.first) return buf.moveTo(indices.first)
} }
public inline fun <R> Buffer<T>.fold(initial: R, operation: A.(acc: R, T) -> R): R { public inline fun <R> Buffer<T>.fold(initial: R, operation: A.(acc: R, T) -> R): R {
var accumulator = initial var accumulator = initial
for (index in this.indices) accumulator = elementAlgebra.operation(accumulator, get(index)) for (index in this.indices) accumulator = elementAlgebra.operation(accumulator, getAbsolute(index))
return accumulator return accumulator
} }
public inline fun <R> Buffer<T>.foldWithLabel(initial: R, operation: A.(acc: R, arg: T, label: L) -> R): R { public inline fun <R> Buffer<T>.foldWithLabel(initial: R, operation: A.(acc: R, arg: T, label: L) -> R): R {
val labels = labels val labels = labels
var accumulator = initial var accumulator = initial
for (index in this.indices) accumulator = elementAlgebra.operation(accumulator, get(index), labels[index]) for (index in this.indices) accumulator =
elementAlgebra.operation(accumulator, getAbsolute(index), labels[index])
return accumulator return accumulator
} }
/** /**
* Zip two buffers replacing missing values with [defaultValue] * Zip two buffers in the range whe they overlap
*/ */
public inline fun Buffer<T>.zip( public inline fun Buffer<T>.zip(
other: Buffer<T>, other: Buffer<T>,
defaultValue: T, crossinline operation: A.(left: T, right: T) -> T,
crossinline operation: A.(left: T?, right: T?) -> T?, ): Series<T> {
): Buffer<T> { val newRange = indices.intersect(other.indices)
val start = min(indices.first, other.indices.first) return bufferFactory(newRange.size) {
val size = max(indices.last, other.indices.last) - start
return bufferFactory(size) {
elementAlgebra.operation( elementAlgebra.operation(
getOrNull(it) ?: defaultValue, getAbsolute(it),
other.getOrNull(it) ?: defaultValue other.getAbsolute(it)
) ?: defaultValue )
} }.moveTo(newRange.first)
}
} }
public fun <T, A : Ring<T>, L> BufferRingOps<T, A>.seriesAlgebra(labels: Iterable<L>): SeriesAlgebra<T, A, L> { override fun Buffer<T>.unaryMinus(): Buffer<T> = map { -it }
override fun add(left: Buffer<T>, right: Buffer<T>): Series<T> = left.zip(right) { l, r -> l + r }
override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = left.zip(right) { l, r -> l * r }
}
public fun <T, A : Ring<T>, BA : BufferAlgebra<T, A>, L> BA.seriesAlgebra(labels: Iterable<L>): SeriesAlgebra<T, A, BA, L> {
val l = labels.toList() val l = labels.toList()
return SeriesAlgebra(this) { return SeriesAlgebra(this) {
if (it in l.indices) l[it] else error("Index $it is outside of labels range ${l.indices}") if (it in l.indices) l[it] else error("Index $it is outside of labels range ${l.indices}")
} }
} }
public fun <T, A : Ring<T>, BA : BufferAlgebra<T, A>, L> BA.seriesAlgebra(labelGenerator: (Int) -> L): SeriesAlgebra<T, A, BA, L> =
SeriesAlgebra(this, labelGenerator)
/**
* Create a series algebra using offset as a label
*/
public fun <T, A : Ring<T>, BA : BufferAlgebra<T, A>> BA.seriesAlgebra(): SeriesAlgebra<T, A, BA, Int> =
SeriesAlgebra(this) { it }

View File

@ -0,0 +1,97 @@
package space.kscience.kmath.series
import space.kscience.kmath.operations.BufferAlgebra
import space.kscience.kmath.operations.ExponentialOperations
import space.kscience.kmath.operations.PowerOperations
import space.kscience.kmath.operations.TrigonometricOperations
import space.kscience.kmath.structures.Buffer
//trigonometric
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.sin(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : TrigonometricOperations<Buffer<T>> =
bufferAlgebra.sin(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.cos(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : TrigonometricOperations<Buffer<T>> =
bufferAlgebra.cos(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.tan(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : TrigonometricOperations<Buffer<T>> =
bufferAlgebra.tan(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.asin(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : TrigonometricOperations<Buffer<T>> =
bufferAlgebra.asin(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.acos(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : TrigonometricOperations<Buffer<T>> =
bufferAlgebra.acos(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.atan(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : TrigonometricOperations<Buffer<T>> =
bufferAlgebra.atan(arg).moveTo(arg.offset)
//exponential
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.exp(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : ExponentialOperations<Buffer<T>> =
bufferAlgebra.exp(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.ln(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : ExponentialOperations<Buffer<T>> =
bufferAlgebra.ln(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.sinh(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : ExponentialOperations<Buffer<T>> =
bufferAlgebra.sinh(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.cosh(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : ExponentialOperations<Buffer<T>> =
bufferAlgebra.cosh(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.tanh(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : ExponentialOperations<Buffer<T>> =
bufferAlgebra.tanh(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.asinh(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : ExponentialOperations<Buffer<T>> =
bufferAlgebra.asinh(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.acosh(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : ExponentialOperations<Buffer<T>> =
bufferAlgebra.acosh(arg).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.atanh(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : ExponentialOperations<Buffer<T>> =
bufferAlgebra.atanh(arg).moveTo(arg.offset)
//power
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.power(
arg: Buffer<T>,
pow: Number,
): Series<T> where BA : BufferAlgebra<T, *>, BA : PowerOperations<Buffer<T>> =
bufferAlgebra.power(arg, pow).moveTo(arg.offset)
public fun <T, BA> SeriesAlgebra<T, *, BA, *>.sqrt(
arg: Buffer<T>,
): Series<T> where BA : BufferAlgebra<T, *>, BA : PowerOperations<Buffer<T>> =
bufferAlgebra.sqrt(arg).moveTo(arg.offset)

View File

@ -7,6 +7,7 @@ package space.kscience.kmath.stat
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.indices
/** /**
* Arithmetic mean * Arithmetic mean

View File

@ -5,8 +5,8 @@
package space.kscience.kmath.stat package space.kscience.kmath.stat
import space.kscience.kmath.operations.asSequence
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asSequence
/** /**
* Non-composable median * Non-composable median

View File

@ -0,0 +1,28 @@
package space.kscience.kmath.stat
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asIterable
public class Rank<T: Comparable<T>>: BlockingStatistic<T, IntArray> {
override fun evaluateBlocking(data: Buffer<T>): IntArray {
// https://www.geeksforgeeks.org/rank-elements-array/
val permutations = ArrayList<Pair<T, Int>>(data.size)
data.asIterable().mapIndexedTo(permutations) { i, v -> v to i }
permutations.sortBy { it.first }
var rank = 1
var i = 0
val r = IntArray(data.size) { 0 }
while (i < data.size) {
var j = i
while (j < data.size - 1 && permutations[j].first == permutations[j + 1]) ++j
val n = j - i + 1
(0 until n).map { k ->
val idx = permutations[i + k].second
r[idx] = rank + ((n - 1) * 0.5f).toInt()
}
rank += n
i += n
}
return r
}
}

View File

@ -0,0 +1,59 @@
package space.kscience.kmath.stat
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.asIterable
import space.kscience.kmath.structures.sorted
public interface StatisticalAlgebra<T, out A : Algebra<T>, out BA : BufferAlgebra<T, A>> : Algebra<Buffer<T>> {
public val bufferAlgebra: BA
public val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
public val bufferFactory: BufferFactory<T> get() = bufferAlgebra.bufferFactory
}
/**
* Compute [empirical CDF function](https://en.wikipedia.org/wiki/Empirical_distribution_function)
*/
public fun <T : Comparable<T>> StatisticalAlgebra<T, *, *>.ecdf(buffer: Buffer<T>): (T) -> Double = { arg ->
buffer.asIterable().count { it < arg }.toDouble() / buffer.size
}
/**
* Implementation copied from https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/index.html?org/apache/commons/math3/stat/inference/KolmogorovSmirnovTest.html
*/
public fun <T : Comparable<T>, A, BA : BufferAlgebra<T, A>> StatisticalAlgebra<T, A, BA>.kolmogorovSmirnovTest(
x: Buffer<T>,
y: Buffer<T>,
): T where A : Group<T>, A : NumericAlgebra<T> = elementAlgebra.invoke {
// Copy and sort the sample arrays
val sx = x.sorted()
val sy = y.sorted()
val n = sx.size
val m = sy.size
var rankX = 0
var rankY = 0
var curD: T = zero
// Find the max difference between cdf_x and cdf_y
var supD: T = zero
do {
val z = if (sx[rankX] <= sy[rankY]) sx[rankX] else sy[rankY]
while (rankX < n && sx[rankX].compareTo(z) == 0) {
rankX += 1;
curD += number(m);
}
while (rankY < m && sy[rankY].compareTo(z) == 0) {
rankY += 1;
curD -= number(n);
}
when {
curD > supD -> supD = curD
-curD > supD -> supD = -curD
}
} while (rankX < n && rankY < m);
return supD;
}

View File

@ -6,6 +6,8 @@
package space.kscience.kmath.stat package space.kscience.kmath.stat
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import space.kscience.kmath.samplers.Sampler
import space.kscience.kmath.samplers.sampleBuffer
import kotlin.test.Test import kotlin.test.Test
class SamplerTest { class SamplerTest {

View File

@ -15,6 +15,7 @@ import space.kscience.kmath.nd.as1D
import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.as2D
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.MutableBuffer 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.AnalyticTensorAlgebra
import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra
import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.api.Tensor

View File

@ -12,9 +12,9 @@ import space.kscience.kmath.nd.MutableStructure1D
import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.MutableStructure2D
import space.kscience.kmath.nd.as1D import space.kscience.kmath.nd.as1D
import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.as2D
import space.kscience.kmath.operations.asSequence
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.VirtualBuffer import space.kscience.kmath.structures.VirtualBuffer
import space.kscience.kmath.structures.asSequence
import space.kscience.kmath.tensors.core.BufferedTensor import space.kscience.kmath.tensors.core.BufferedTensor
import space.kscience.kmath.tensors.core.DoubleTensor import space.kscience.kmath.tensors.core.DoubleTensor
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra import space.kscience.kmath.tensors.core.DoubleTensorAlgebra

View File

@ -6,7 +6,6 @@
package space.kscience.kmath.tensors.core.internal package space.kscience.kmath.tensors.core.internal
import space.kscience.kmath.nd.as1D import space.kscience.kmath.nd.as1D
import space.kscience.kmath.operations.toMutableList
import space.kscience.kmath.samplers.GaussianSampler import space.kscience.kmath.samplers.GaussianSampler
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.structures.* import space.kscience.kmath.structures.*