Buffer views
This commit is contained in:
parent
bf504ae6c5
commit
a1351aa942
@ -12,8 +12,6 @@ import space.kscience.kmath.commons.optimization.CMOptimizer
|
||||
import space.kscience.kmath.distributions.NormalDistribution
|
||||
import space.kscience.kmath.expressions.chiSquaredExpression
|
||||
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.optimizeWith
|
||||
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.step
|
||||
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.models.ScatterMode
|
||||
import space.kscience.plotly.models.TraceValues
|
||||
|
@ -13,8 +13,6 @@ import space.kscience.kmath.distributions.NormalDistribution
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.expressions.binding
|
||||
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.chiSquaredOrNull
|
||||
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.step
|
||||
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.models.ScatterMode
|
||||
import kotlin.math.abs
|
||||
|
@ -1,37 +1,43 @@
|
||||
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.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 space.kscience.kmath.structures.toList
|
||||
import space.kscience.plotly.*
|
||||
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 {
|
||||
fun main() = with(Double.algebra.bufferAlgebra.seriesAlgebra()) {
|
||||
fun FlowContent.plotSeries(buffer: Buffer<Double>) {
|
||||
val ls = buffer.labels
|
||||
plot {
|
||||
scatter {
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
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))}
|
||||
|
||||
|
||||
}
|
@ -7,10 +7,11 @@ package space.kscience.kmath.commons.random
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import space.kscience.kmath.misc.PerformancePitfall
|
||||
import space.kscience.kmath.samplers.GaussianSampler
|
||||
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.next
|
||||
|
||||
|
||||
public class CMRandomGeneratorWrapper(
|
||||
public val factory: (IntArray) -> RandomGenerator,
|
||||
|
@ -10,13 +10,9 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.apache.commons.math3.transform.*
|
||||
import space.kscience.kmath.complex.Complex
|
||||
import space.kscience.kmath.operations.SuspendBufferTransform
|
||||
import space.kscience.kmath.streaming.chunked
|
||||
import space.kscience.kmath.streaming.spread
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import space.kscience.kmath.structures.VirtualBuffer
|
||||
import space.kscience.kmath.structures.asBuffer
|
||||
import space.kscience.kmath.structures.*
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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.chiSquaredExpression
|
||||
import space.kscience.kmath.expressions.symbol
|
||||
import space.kscience.kmath.operations.map
|
||||
import space.kscience.kmath.optimization.*
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import space.kscience.kmath.structures.asBuffer
|
||||
import space.kscience.kmath.structures.map
|
||||
import kotlin.math.pow
|
||||
import kotlin.test.Test
|
||||
|
||||
|
@ -7,6 +7,7 @@ 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
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -6,8 +6,9 @@
|
||||
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.asIterable
|
||||
import space.kscience.kmath.structures.indices
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
|
@ -13,6 +13,7 @@ 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>>(
|
||||
|
@ -6,10 +6,10 @@
|
||||
package space.kscience.kmath.nd
|
||||
|
||||
import space.kscience.kmath.misc.PerformancePitfall
|
||||
import space.kscience.kmath.operations.asSequence
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.MutableBuffer
|
||||
import space.kscience.kmath.structures.asMutableBuffer
|
||||
import space.kscience.kmath.structures.asSequence
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,6 @@ package space.kscience.kmath.operations
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.BufferFactory
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import space.kscience.kmath.structures.ShortBuffer
|
||||
|
||||
public interface WithSize {
|
||||
@ -183,7 +182,7 @@ public class BufferField<T, A : Field<T>>(
|
||||
/**
|
||||
* 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)
|
||||
|
||||
//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> =
|
||||
BufferFieldOps(this, bufferFactory)
|
||||
|
||||
public val DoubleField.bufferAlgebra: BufferFieldOps<Double, DoubleField>
|
||||
get() = BufferFieldOps(DoubleField, ::DoubleBuffer)
|
||||
|
||||
public val DoubleField.bufferAlgebra: DoubleBufferOps get() = DoubleBufferOps
|
||||
|
@ -7,10 +7,7 @@ package space.kscience.kmath.operations
|
||||
|
||||
import space.kscience.kmath.linear.Point
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.BufferFactory
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import space.kscience.kmath.structures.asBuffer
|
||||
import space.kscience.kmath.structures.*
|
||||
|
||||
import kotlin.math.*
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
package space.kscience.kmath.structures
|
||||
|
||||
import space.kscience.kmath.operations.asSequence
|
||||
import space.kscience.kmath.operations.WithSize
|
||||
import kotlin.jvm.JvmInline
|
||||
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.
|
||||
*/
|
||||
public interface Buffer<out T> {
|
||||
public interface Buffer<out T> : WithSize {
|
||||
/**
|
||||
* The size of this buffer.
|
||||
*/
|
||||
public val size: Int
|
||||
override val size: Int
|
||||
|
||||
/**
|
||||
* Gets element at given index.
|
||||
@ -44,15 +44,10 @@ public interface Buffer<out T> {
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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 =
|
||||
@ -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())
|
||||
|
||||
/**
|
||||
|
@ -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)
|
@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline
|
||||
* @property array the underlying array.
|
||||
*/
|
||||
@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 operator fun get(index: Int): Double = array[index]
|
||||
|
@ -14,7 +14,7 @@ import kotlin.jvm.JvmInline
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@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 operator fun get(index: Int): Float = array[index]
|
||||
|
@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline
|
||||
* @property array the underlying array.
|
||||
*/
|
||||
@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 operator fun get(index: Int): Int = array[index]
|
||||
|
@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline
|
||||
* @property array the underlying array.
|
||||
*/
|
||||
@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 operator fun get(index: Int): Long = array[index]
|
||||
|
@ -95,3 +95,6 @@ public interface MutableBuffer<T> : Buffer<T> {
|
||||
auto(T::class, size, initializer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public sealed interface PrimitiveBuffer<T>: MutableBuffer<T>
|
@ -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.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.operations
|
||||
package space.kscience.kmath.structures
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.structures.*
|
||||
|
||||
/**
|
||||
* 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}" }
|
||||
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()
|
||||
}
|
@ -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] }
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ package space.kscience.kmath.streaming
|
||||
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import space.kscience.kmath.operations.asSequence
|
||||
import space.kscience.kmath.structures.asSequence
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
@ -13,9 +13,9 @@ import space.kscience.kmath.misc.PerformancePitfall
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.algebra
|
||||
import space.kscience.kmath.operations.asIterable
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import space.kscience.kmath.structures.asIterable
|
||||
import kotlin.math.pow
|
||||
|
||||
/*
|
||||
|
@ -11,6 +11,7 @@ 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>
|
||||
|
@ -8,6 +8,7 @@ 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
|
||||
|
@ -5,10 +5,10 @@
|
||||
|
||||
package space.kscience.kmath.integration
|
||||
|
||||
import space.kscience.kmath.operations.map
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import space.kscience.kmath.structures.asBuffer
|
||||
import space.kscience.kmath.structures.map
|
||||
import kotlin.jvm.Synchronized
|
||||
import kotlin.math.ulp
|
||||
import kotlin.native.concurrent.ThreadLocal
|
||||
|
@ -12,10 +12,14 @@ import space.kscience.kmath.interpolation.SplineInterpolator
|
||||
import space.kscience.kmath.interpolation.interpolatePolynomials
|
||||
import space.kscience.kmath.misc.PerformancePitfall
|
||||
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.DoubleBuffer
|
||||
import space.kscience.kmath.structures.MutableBufferFactory
|
||||
import space.kscience.kmath.structures.map
|
||||
|
||||
/**
|
||||
* Compute analytical indefinite integral of this [PiecewisePolynomial], keeping all intervals intact
|
||||
|
@ -1,6 +1,7 @@
|
||||
package space.kscience.kmath.geometry
|
||||
|
||||
import space.kscience.kmath.operations.toList
|
||||
|
||||
import space.kscience.kmath.structures.toList
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package space.kscience.kmath.geometry
|
||||
|
||||
import space.kscience.kmath.operations.toList
|
||||
import space.kscience.kmath.structures.toList
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
@ -7,8 +7,8 @@ package space.kscience.kmath.histogram
|
||||
|
||||
import space.kscience.kmath.domains.UnivariateDomain
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.asSequence
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.asSequence
|
||||
|
||||
|
||||
@UnstableKMathAPI
|
||||
|
@ -21,9 +21,9 @@ import space.kscience.kmath.expressions.MST
|
||||
import space.kscience.kmath.expressions.MstRing
|
||||
import space.kscience.kmath.misc.PerformancePitfall
|
||||
import space.kscience.kmath.nd.Structure2D
|
||||
import space.kscience.kmath.operations.asSequence
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.asSequence
|
||||
|
||||
/**
|
||||
* A function for conversion of number to MST for pretty print
|
||||
|
@ -6,8 +6,8 @@
|
||||
package space.kscience.kmath.distributions
|
||||
|
||||
import space.kscience.kmath.chains.Chain
|
||||
import space.kscience.kmath.samplers.Sampler
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.stat.Sampler
|
||||
|
||||
/**
|
||||
* A distribution of typed objects.
|
||||
|
@ -6,8 +6,8 @@
|
||||
package space.kscience.kmath.distributions
|
||||
|
||||
import space.kscience.kmath.chains.Chain
|
||||
import space.kscience.kmath.internal.InternalErf
|
||||
import space.kscience.kmath.samplers.GaussianSampler
|
||||
import space.kscience.kmath.samplers.InternalErf
|
||||
import space.kscience.kmath.samplers.NormalizedGaussianSampler
|
||||
import space.kscience.kmath.samplers.ZigguratNormalizedGaussianSampler
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.stat
|
||||
package space.kscience.kmath.distributions
|
||||
|
||||
import space.kscience.kmath.chains.Chain
|
||||
import space.kscience.kmath.chains.SimpleChain
|
||||
import space.kscience.kmath.distributions.Distribution
|
||||
import space.kscience.kmath.distributions.UnivariateDistribution
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
|
||||
public class UniformDistribution(public val range: ClosedFloatingPointRange<Double>) : UnivariateDistribution<Double> {
|
||||
private val length: Double = range.endInclusive - range.start
|
@ -7,7 +7,6 @@ package space.kscience.kmath.samplers
|
||||
|
||||
import space.kscience.kmath.chains.BlockingDoubleChain
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.stat.Sampler
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import kotlin.math.ln
|
||||
import kotlin.math.pow
|
||||
@ -67,7 +66,7 @@ public class AhrensDieterExponentialSampler(public val mean: Double) : Sampler<D
|
||||
var qi = 0.0
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,7 @@ package space.kscience.kmath.samplers
|
||||
|
||||
import space.kscience.kmath.chains.Chain
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.stat.Sampler
|
||||
import space.kscience.kmath.stat.chain
|
||||
import space.kscience.kmath.stat.next
|
||||
import kotlin.math.*
|
||||
|
||||
/**
|
||||
|
@ -6,9 +6,7 @@
|
||||
package space.kscience.kmath.samplers
|
||||
|
||||
import space.kscience.kmath.chains.Chain
|
||||
import space.kscience.kmath.internal.InternalUtils
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.stat.Sampler
|
||||
import space.kscience.kmath.stat.chain
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.max
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.internal
|
||||
package space.kscience.kmath.samplers
|
||||
|
||||
import kotlin.math.abs
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.internal
|
||||
package space.kscience.kmath.samplers
|
||||
|
||||
import kotlin.math.*
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.internal
|
||||
package space.kscience.kmath.samplers
|
||||
|
||||
import kotlin.math.ln
|
||||
import kotlin.math.min
|
@ -7,7 +7,6 @@ package space.kscience.kmath.samplers
|
||||
|
||||
import space.kscience.kmath.chains.BlockingIntChain
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.stat.Sampler
|
||||
import space.kscience.kmath.structures.IntBuffer
|
||||
import kotlin.math.exp
|
||||
|
||||
|
@ -7,7 +7,6 @@ package space.kscience.kmath.samplers
|
||||
|
||||
import space.kscience.kmath.chains.BlockingDoubleChain
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.stat.Sampler
|
||||
|
||||
public interface BlockingDoubleSampler: Sampler<Double>{
|
||||
override fun sample(generator: RandomGenerator): BlockingDoubleChain
|
||||
|
@ -6,10 +6,8 @@
|
||||
package space.kscience.kmath.samplers
|
||||
|
||||
import space.kscience.kmath.chains.BlockingIntChain
|
||||
import space.kscience.kmath.internal.InternalUtils
|
||||
import space.kscience.kmath.misc.toIntExact
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.stat.Sampler
|
||||
import space.kscience.kmath.structures.IntBuffer
|
||||
import kotlin.math.*
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.stat
|
||||
package space.kscience.kmath.samplers
|
||||
|
||||
import kotlinx.coroutines.flow.first
|
||||
import space.kscience.kmath.chains.Chain
|
||||
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
|
||||
|
||||
/**
|
@ -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.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.stat
|
||||
package space.kscience.kmath.samplers
|
||||
|
||||
import space.kscience.kmath.chains.Chain
|
||||
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.ScaleOperations
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
|
||||
/**
|
||||
* Implements [Sampler] by sampling only certain [value].
|
@ -1,80 +1,125 @@
|
||||
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.BufferFactory
|
||||
import space.kscience.kmath.structures.getOrNull
|
||||
import space.kscience.kmath.structures.BufferView
|
||||
import kotlin.math.max
|
||||
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)
|
||||
|
||||
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> {
|
||||
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" }
|
||||
@PublishedApi
|
||||
internal operator fun IntRange.contains(other: IntRange): Boolean = (other.first in this) && (other.last in this)
|
||||
|
||||
//TODO add permutated buffer
|
||||
//TODO add rank function
|
||||
|
||||
|
||||
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
|
||||
*/
|
||||
public class SeriesAlgebra<T, A : Ring<T>, L>(
|
||||
public val bufferAlgebra: BufferRingOps<T, A>,
|
||||
public class SeriesAlgebra<T, out A : Ring<T>, out BA : BufferAlgebra<T, A>, L>(
|
||||
override val bufferAlgebra: BA,
|
||||
private val labelResolver: (Int) -> L,
|
||||
) : RingOps<Buffer<T>> {
|
||||
) : RingOps<Buffer<T>>, StatisticalAlgebra<T, A, BA> {
|
||||
|
||||
public val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
|
||||
public val bufferFactory: BufferFactory<T> get() = bufferAlgebra.bufferFactory
|
||||
public val Buffer<T>.indices: IntRange
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
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.
|
||||
*/
|
||||
@ -87,34 +132,15 @@ public class SeriesAlgebra<T, A : Ring<T>, L>(
|
||||
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]
|
||||
}
|
||||
return getAbsolute(index + offset.toInt())
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
elementAlgebra.transform(get(it))
|
||||
elementAlgebra.transform(getAbsolute(it))
|
||||
}
|
||||
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
|
||||
*/
|
||||
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 buf = bufferFactory(size) {
|
||||
elementAlgebra.transform(get(it), labels[it])
|
||||
elementAlgebra.transform(getAbsolute(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))
|
||||
for (index in this.indices) accumulator = elementAlgebra.operation(accumulator, getAbsolute(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])
|
||||
for (index in this.indices) accumulator =
|
||||
elementAlgebra.operation(accumulator, getAbsolute(index), labels[index])
|
||||
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(
|
||||
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) {
|
||||
crossinline operation: A.(left: T, right: T) -> T,
|
||||
): Series<T> {
|
||||
val newRange = indices.intersect(other.indices)
|
||||
return bufferFactory(newRange.size) {
|
||||
elementAlgebra.operation(
|
||||
getOrNull(it) ?: defaultValue,
|
||||
other.getOrNull(it) ?: defaultValue
|
||||
) ?: defaultValue
|
||||
}
|
||||
}
|
||||
getAbsolute(it),
|
||||
other.getAbsolute(it)
|
||||
)
|
||||
}.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()
|
||||
return SeriesAlgebra(this) {
|
||||
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 }
|
@ -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)
|
@ -7,6 +7,7 @@ package space.kscience.kmath.stat
|
||||
|
||||
import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.indices
|
||||
|
||||
/**
|
||||
* Arithmetic mean
|
||||
|
@ -5,8 +5,8 @@
|
||||
|
||||
package space.kscience.kmath.stat
|
||||
|
||||
import space.kscience.kmath.operations.asSequence
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.asSequence
|
||||
|
||||
/**
|
||||
* Non-composable median
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -6,6 +6,8 @@
|
||||
package space.kscience.kmath.stat
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import space.kscience.kmath.samplers.Sampler
|
||||
import space.kscience.kmath.samplers.sampleBuffer
|
||||
import kotlin.test.Test
|
||||
|
||||
class SamplerTest {
|
||||
|
@ -15,6 +15,7 @@ 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
|
||||
|
@ -12,9 +12,9 @@ import space.kscience.kmath.nd.MutableStructure1D
|
||||
import space.kscience.kmath.nd.MutableStructure2D
|
||||
import space.kscience.kmath.nd.as1D
|
||||
import space.kscience.kmath.nd.as2D
|
||||
import space.kscience.kmath.operations.asSequence
|
||||
import space.kscience.kmath.operations.invoke
|
||||
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.DoubleTensor
|
||||
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
||||
|
@ -6,7 +6,6 @@
|
||||
package space.kscience.kmath.tensors.core.internal
|
||||
|
||||
import space.kscience.kmath.nd.as1D
|
||||
import space.kscience.kmath.operations.toMutableList
|
||||
import space.kscience.kmath.samplers.GaussianSampler
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.structures.*
|
||||
|
Loading…
Reference in New Issue
Block a user