Merge branch 'feature/series' into dev

# Conflicts:
#	build.gradle.kts
#	kmath-commons/src/main/kotlin/space/kscience/kmath/commons/transform/Transformations.kt
#	kmath-core/build.gradle.kts
#	kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt
#	kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt
#	kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt
#	kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndices.kt
#	kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt
#	kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt
#	kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferExtensions.kt
#	kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt
#	kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/UniformDistribution.kt
#	kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt
#	kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt
#	kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt
#	kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt
#	kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt
#	kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt
#	kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt
#	kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt
#	settings.gradle.kts
This commit is contained in:
Alexander Nozik 2022-09-26 13:10:59 +03:00
commit 4d1137659b
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
65 changed files with 819 additions and 97 deletions

View File

@ -6,34 +6,75 @@
package space.kscience.kmath.benchmarks package space.kscience.kmath.benchmarks
import kotlinx.benchmark.Benchmark import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import space.kscience.kmath.complex.Complex import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.complex import space.kscience.kmath.complex.complex
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.getDouble
import space.kscience.kmath.structures.permute
@State(Scope.Benchmark) @State(Scope.Benchmark)
internal class BufferBenchmark { internal class BufferBenchmark {
@Benchmark
fun genericDoubleBufferReadWrite() {
val buffer = DoubleBuffer(size) { it.toDouble() }
@Benchmark
fun doubleArrayReadWrite(blackhole: Blackhole) {
val buffer = DoubleArray(size) { it.toDouble() }
var res = 0.0
(0 until size).forEach { (0 until size).forEach {
buffer[it] res += buffer[it]
} }
blackhole.consume(res)
} }
@Benchmark @Benchmark
fun complexBufferReadWrite() { fun doubleBufferReadWrite(blackhole: Blackhole) {
val buffer = MutableBuffer.complex(size / 2) { Complex(it.toDouble(), -it.toDouble()) } val buffer = DoubleBuffer(size) { it.toDouble() }
var res = 0.0
(0 until size / 2).forEach { (0 until size).forEach {
buffer[it] res += buffer[it]
} }
blackhole.consume(res)
}
@Benchmark
fun bufferViewReadWrite(blackhole: Blackhole) {
val buffer = DoubleBuffer(size) { it.toDouble() }.permute(reversedIndices)
var res = 0.0
(0 until size).forEach {
res += buffer[it]
}
blackhole.consume(res)
}
@Benchmark
fun bufferViewReadWriteSpecialized(blackhole: Blackhole) {
val buffer = DoubleBuffer(size) { it.toDouble() }.permute(reversedIndices)
var res = 0.0
(0 until size).forEach {
res += buffer.getDouble(it)
}
blackhole.consume(res)
}
@Benchmark
fun complexBufferReadWrite(blackhole: Blackhole) = ComplexField {
val buffer = Buffer.complex(size / 2) { Complex(it.toDouble(), -it.toDouble()) }
var res = zero
(0 until size / 2).forEach {
res += buffer[it]
}
blackhole.consume(res)
} }
private companion object { private companion object {
private const val size = 100 private const val size = 100
private val reversedIndices = IntArray(size){it}.apply { reverse() }
} }
} }

View File

@ -12,16 +12,16 @@ 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
import space.kscience.kmath.optimization.resultValue import space.kscience.kmath.optimization.resultValue
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.real.DoubleVector 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.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,15 +13,15 @@ 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
import space.kscience.kmath.optimization.resultPoint import space.kscience.kmath.optimization.resultPoint
import space.kscience.kmath.random.RandomGenerator
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.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

@ -0,0 +1,47 @@
package space.kscience.kmath.series
import kotlinx.html.FlowContent
import kotlinx.html.h1
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.operations.bufferAlgebra
import space.kscience.kmath.stat.ksComparisonStatistic
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.slice
import space.kscience.kmath.structures.toList
import space.kscience.plotly.*
import kotlin.math.PI
fun main() = with(Double.algebra.bufferAlgebra.seriesAlgebra()) {
fun FlowContent.plotSeries(buffer: Buffer<Double>) {
val ls = buffer.labels
plot {
scatter {
x.numbers = ls
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(20U..50U).moveTo(40)
val s3: Buffer<Double> = s1.zip(s2) { l, r -> l + r } //s1 + s2
val s4 = ln(s3)
val kmTest = ksComparisonStatistic(s1, s2)
Plotly.page {
h1 { +"This is my plot" }
plotSeries(s1)
plotSeries(s2)
plotSeries(s4)
}.makeFile()
}

View File

@ -10,6 +10,7 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.apache.commons.rng.sampling.distribution.BoxMullerNormalizedGaussianSampler import org.apache.commons.rng.sampling.distribution.BoxMullerNormalizedGaussianSampler
import org.apache.commons.rng.simple.RandomSource import org.apache.commons.rng.simple.RandomSource
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.samplers.GaussianSampler import space.kscience.kmath.samplers.GaussianSampler
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant

View File

@ -9,6 +9,7 @@ import kotlinx.coroutines.runBlocking
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.chains.combineWithState import space.kscience.kmath.chains.combineWithState
import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.random.RandomGenerator
private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0)

View File

@ -37,7 +37,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND<Double, Double
this@StreamDoubleFieldND.shape, this@StreamDoubleFieldND.shape,
shape shape
) )
this is BufferND && this.indices == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer this is BufferND && this.shapeIndices == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer
else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
} }

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.stat.RandomGenerator import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.stat.next import space.kscience.kmath.samplers.GaussianSampler
import space.kscience.kmath.samplers.next
public class CMRandomGeneratorWrapper( public class CMRandomGeneratorWrapper(
public val factory: (IntArray) -> RandomGenerator, public val factory: (IntArray) -> RandomGenerator,

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.random.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

@ -6,8 +6,8 @@
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 space.kscience.kmath.structures.indices
import kotlin.jvm.JvmName import kotlin.jvm.JvmName

View File

@ -15,7 +15,7 @@ import kotlin.math.pow
import kotlin.math.pow as kpow import kotlin.math.pow as kpow
public class DoubleBufferND( public class DoubleBufferND(
indexes: ShapeIndexer, indexes: ShapeIndices,
override val buffer: DoubleBuffer, override val buffer: DoubleBuffer,
) : MutableBufferND<Double>(indexes, buffer) ) : MutableBufferND<Double>(indexes, buffer)
@ -35,7 +35,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
arg: DoubleBufferND, arg: DoubleBufferND,
transform: (Double) -> Double, transform: (Double) -> Double,
): DoubleBufferND { ): DoubleBufferND {
val indexes = arg.indices val indexes = arg.shapeIndices
val array = arg.buffer.array val array = arg.buffer.array
return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { transform(array[it]) }) return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { transform(array[it]) })
} }
@ -45,8 +45,8 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
r: DoubleBufferND, r: DoubleBufferND,
block: (l: Double, r: Double) -> Double, block: (l: Double, r: Double) -> Double,
): DoubleBufferND { ): DoubleBufferND {
require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } require(l.shapeIndices == r.shapeIndices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" }
val indexes = l.indices val indexes = l.shapeIndices
val lArray = l.buffer.array val lArray = l.buffer.array
val rArray = r.buffer.array val rArray = r.buffer.array
return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { block(lArray[it], rArray[it]) }) return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { block(lArray[it], rArray[it]) })

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

@ -54,7 +54,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
* @return the lazy sequence of pairs of indices to values. * @return the lazy sequence of pairs of indices to values.
*/ */
@PerformancePitfall @PerformancePitfall
public fun elements(): Sequence<Pair<IntArray, T>> = indices.asSequence().map { it to get(it) } public fun elements(): Sequence<Pair<IntArray, T>> = shapeIndices.asSequence().map { it to get(it) }
/** /**
* Feature is some additional structure information that allows to access it special properties or hints. * Feature is some additional structure information that allows to access it special properties or hints.
@ -71,7 +71,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
if (st1 === st2) return true if (st1 === st2) return true
// fast comparison of buffers if possible // fast comparison of buffers if possible
if (st1 is BufferND && st2 is BufferND && st1.indices == st2.indices) if (st1 is BufferND && st2 is BufferND && st1.shapeIndices == st2.shapeIndices)
return Buffer.contentEquals(st1.buffer, st2.buffer) return Buffer.contentEquals(st1.buffer, st2.buffer)
//element by element comparison if it could not be avoided //element by element comparison if it could not be avoided
@ -87,7 +87,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
if (st1 === st2) return true if (st1 === st2) return true
// fast comparison of buffers if possible // fast comparison of buffers if possible
if (st1 is BufferND && st2 is BufferND && st1.indices == st2.indices) if (st1 is BufferND && st2 is BufferND && st1.shapeIndices == st2.shapeIndices)
return Buffer.contentEquals(st1.buffer, st2.buffer) return Buffer.contentEquals(st1.buffer, st2.buffer)
//element by element comparison if it could not be avoided //element by element comparison if it could not be avoided

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
@ -50,11 +50,11 @@ public fun interface MutableBufferFactory<T> : BufferFactory<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.
@ -64,7 +64,7 @@ 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
@ -122,7 +122,14 @@ public interface Buffer<out T> {
/** /**
* Returns an [IntRange] of the valid indices for this [Buffer]. * Returns an [IntRange] of the valid indices for this [Buffer].
*/ */
public val Buffer<*>.indices: IntRange get() = 0 until size public val <T> Buffer<T>.indices: IntRange get() = 0 until size
public operator fun <T> Buffer<T>.get(index: UInt): T = get(index.toInt())
/**
* if index is in range of buffer, return the value. Otherwise, return null.
*/
public fun <T> Buffer<T>.getOrNull(index: Int): T? = if (index in indices) get(index) else null
public fun <T> Buffer<T>.first(): T { public fun <T> Buffer<T>.first(): T {
require(size > 0) { "Can't get the first element of empty buffer" } require(size > 0) { "Can't get the first element of empty buffer" }

View File

@ -0,0 +1,148 @@
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> = if (this is BufferSlice) {
BufferSlice(
origin,
this.offset + range.first,
(range.last - range.first).toInt() + 1
)
} else {
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>.permute(indices: IntArray): PermutatedBuffer<T> = PermutatedBuffer(this, indices)

View File

@ -14,7 +14,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

@ -94,4 +94,7 @@ public interface MutableBuffer<T> : Buffer<T> {
public inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> = public inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> =
auto(T::class, size, initializer) auto(T::class, size, initializer)
} }
} }
public sealed interface PrimitiveBuffer<T>: MutableBuffer<T>

View File

@ -0,0 +1,38 @@
package space.kscience.kmath.structures
import space.kscience.kmath.misc.UnstableKMathAPI
/**
* Non-boxing access to primitive [Double]
*/
@UnstableKMathAPI
public fun Buffer<Double>.getDouble(index: Int): Double = if (this is BufferView) {
val originIndex = originIndex(index)
if( originIndex>=0) {
origin.getDouble(originIndex)
} else {
get(index)
}
} else if (this is DoubleBuffer) {
array[index]
} else {
get(index)
}
/**
* Non-boxing access to primitive [Int]
*/
@UnstableKMathAPI
public fun Buffer<Int>.getInt(index: Int): Int = if (this is BufferView) {
val originIndex = originIndex(index)
if( originIndex>=0) {
origin.getInt(originIndex)
} else {
get(index)
}
} else if (this is IntBuffer) {
array[index]
} else {
get(index)
}

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

@ -13,8 +13,8 @@ import space.kscience.kmath.structures.DoubleBuffer
* Map one [BufferND] using function without indices. * Map one [BufferND] using function without indices.
*/ */
public inline fun BufferND<Double>.mapInline(crossinline transform: DoubleField.(Double) -> Double): BufferND<Double> { public inline fun BufferND<Double>.mapInline(crossinline transform: DoubleField.(Double) -> Double): BufferND<Double> {
val array = DoubleArray(indices.linearSize) { offset -> DoubleField.transform(buffer[offset]) } val array = DoubleArray(shapeIndices.linearSize) { offset -> DoubleField.transform(buffer[offset]) }
return BufferND(indices, DoubleBuffer(array)) return BufferND(shapeIndices, DoubleBuffer(array))
} }
/** /**

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

@ -5,7 +5,8 @@
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

@ -5,7 +5,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

@ -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.stat.RandomGenerator import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.stat.Sampler import space.kscience.kmath.samplers.Sampler
/** /**
* A distribution of typed objects. * A distribution of typed objects.

View File

@ -7,7 +7,7 @@ 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.stat.RandomGenerator import space.kscience.kmath.random.RandomGenerator
/** /**
* A multivariate distribution that takes a map of parameters. * A multivariate distribution that takes a map of parameters.

View File

@ -6,11 +6,11 @@
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.random.RandomGenerator
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 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/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.stat package space.kscience.kmath.random
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
@ -22,6 +22,10 @@ public class MCScope(
public val random: RandomGenerator, public val random: RandomGenerator,
) )
public fun MCScope.asCoroutineScope(): CoroutineScope = object : CoroutineScope {
override val coroutineContext: CoroutineContext get() = this@asCoroutineScope.coroutineContext
}
/** /**
* Launches a supervised Monte-Carlo scope * Launches a supervised Monte-Carlo scope
*/ */

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/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.stat package space.kscience.kmath.random
import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.chains.BlockingDoubleChain
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain

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/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.stat package space.kscience.kmath.random
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import kotlin.random.Random import kotlin.random.Random

View File

@ -6,8 +6,7 @@
package space.kscience.kmath.samplers 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.random.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

@ -6,10 +6,8 @@
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.stat.RandomGenerator import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.stat.Sampler import space.kscience.kmath.random.chain
import space.kscience.kmath.stat.chain
import space.kscience.kmath.stat.next
import kotlin.math.* import kotlin.math.*
/** /**
@ -25,14 +23,14 @@ import kotlin.math.*
*/ */
public class AhrensDieterMarsagliaTsangGammaSampler private constructor( public class AhrensDieterMarsagliaTsangGammaSampler private constructor(
alpha: Double, alpha: Double,
theta: Double theta: Double,
) : Sampler<Double> { ) : Sampler<Double> {
private val delegate: BaseGammaSampler = private val delegate: BaseGammaSampler =
if (alpha < 1) AhrensDieterGammaSampler(alpha, theta) else MarsagliaTsangGammaSampler(alpha, theta) if (alpha < 1) AhrensDieterGammaSampler(alpha, theta) else MarsagliaTsangGammaSampler(alpha, theta)
private abstract class BaseGammaSampler internal constructor( private abstract class BaseGammaSampler internal constructor(
protected val alpha: Double, protected val alpha: Double,
protected val theta: Double protected val theta: Double,
) : Sampler<Double> { ) : Sampler<Double> {
init { init {
require(alpha > 0) { "alpha is not strictly positive: $alpha" } require(alpha > 0) { "alpha is not strictly positive: $alpha" }
@ -119,7 +117,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler private constructor(
public companion object { public companion object {
public fun of( public fun of(
alpha: Double, alpha: Double,
theta: Double theta: Double,
): Sampler<Double> = AhrensDieterMarsagliaTsangGammaSampler(alpha, theta) ): Sampler<Double> = AhrensDieterMarsagliaTsangGammaSampler(alpha, theta)
} }
} }

View File

@ -6,10 +6,8 @@
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.random.RandomGenerator
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.random.chain
import space.kscience.kmath.stat.Sampler
import space.kscience.kmath.stat.chain
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min

View File

@ -6,7 +6,7 @@
package space.kscience.kmath.samplers 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.random.RandomGenerator
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import kotlin.math.* import kotlin.math.*

View File

@ -7,7 +7,7 @@ package space.kscience.kmath.samplers
import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.chains.BlockingDoubleChain
import space.kscience.kmath.chains.map import space.kscience.kmath.chains.map
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.random.RandomGenerator
/** /**
* Sampling from a Gaussian distribution with given mean and standard deviation. * Sampling from a Gaussian distribution with given mean and standard deviation.
@ -21,7 +21,7 @@ import space.kscience.kmath.stat.RandomGenerator
public class GaussianSampler( public class GaussianSampler(
public val mean: Double, public val mean: Double,
public val standardDeviation: Double, public val standardDeviation: Double,
private val normalized: NormalizedGaussianSampler = BoxMullerSampler private val normalized: NormalizedGaussianSampler = BoxMullerSampler,
) : BlockingDoubleSampler { ) : BlockingDoubleSampler {
init { init {

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/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.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/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.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/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.internal package space.kscience.kmath.samplers
import kotlin.math.ln import kotlin.math.ln
import kotlin.math.min import kotlin.math.min

View File

@ -6,8 +6,7 @@
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.stat.RandomGenerator import space.kscience.kmath.random.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

@ -6,7 +6,7 @@
package space.kscience.kmath.samplers 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.random.RandomGenerator
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import kotlin.math.ln import kotlin.math.ln
import kotlin.math.sqrt import kotlin.math.sqrt

View File

@ -6,10 +6,9 @@
package space.kscience.kmath.samplers 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.random.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
} }
@ -18,6 +17,6 @@ public interface BlockingDoubleSampler: Sampler<Double>{
* Marker interface for a sampler that generates values from an N(0,1) * Marker interface for a sampler that generates values from an N(0,1)
* [Gaussian distribution](https://en.wikipedia.org/wiki/Normal_distribution). * [Gaussian distribution](https://en.wikipedia.org/wiki/Normal_distribution).
*/ */
public fun interface NormalizedGaussianSampler : BlockingDoubleSampler{ public fun interface NormalizedGaussianSampler : BlockingDoubleSampler {
public companion object public companion object
} }

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.random.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,7 +3,7 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.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.random.RandomGenerator
/** /**
* Implements [Sampler] by sampling only certain [value]. * Implements [Sampler] by sampling only certain [value].

View File

@ -6,8 +6,7 @@
package space.kscience.kmath.samplers package space.kscience.kmath.samplers
import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.chains.BlockingDoubleChain
import space.kscience.kmath.misc.toIntExact import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import kotlin.math.* import kotlin.math.*

View File

@ -0,0 +1,193 @@
package space.kscience.kmath.series
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.BufferView
import kotlin.math.max
import kotlin.math.min
@PublishedApi
internal fun IntRange.intersect(other: IntRange): IntRange =
max(first, other.first)..min(last, other.last)
@PublishedApi
internal val IntRange.size: Int
get() = last - first + 1
@PublishedApi
internal operator fun IntRange.contains(other: IntRange): Boolean = (other.first in this) && (other.last in this)
//TODO add permutation sort
//TODO check rank statistics
public interface Series<T> : Buffer<T> {
public val origin: Buffer<T>
/**
* Absolute position of start of this [Series] in [SeriesAlgebra]
*/
public val position: Int
}
public val <T> Series<T>.absoluteIndices: IntRange get() = position until position + size
/**
* 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 {
init {
require(size > 0) { "Size must be positive" }
require(size <= origin.size) { "Slice size is larger than the original buffer" }
}
override fun toString(): String = "$origin-->${position}"
}
/**
* A scope to operation on series
*/
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>>, StatisticalAlgebra<T, A, BA> {
public val Buffer<T>.indices: IntRange
get() = if (this is Series) {
absoluteIndices
} else {
0 until size
}
/**
* 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)
}
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): Series<T> {
return bufferFactory(size) {
val index = it + fromIndex
elementAlgebra.block(labelResolver(index))
}.moveTo(fromIndex)
}
/**
* Get a label buffer for given buffer.
*/
public val Buffer<T>.labels: List<L> get() = indices.map(labelResolver)
/**
* Try to resolve element by label and return null if element with a given label is not found
*/
public operator fun Buffer<T>.get(label: L): T? {
val index = labels.indexOf(label)
if (index == -1) return null
return getAbsolute(index + offset)
}
/**
* Map a series to another series of the same size
*/
public inline fun Buffer<T>.map(crossinline transform: A.(T) -> T): Series<T> {
val buf = bufferFactory(size) {
elementAlgebra.transform(getAbsolute(it))
}
return buf.moveTo(indices.first)
}
/**
* Map series to another series of the same size with label
*/
public inline fun Buffer<T>.mapWithLabel(crossinline transform: A.(arg: T, label: L) -> T): Series<T> {
val labels = labels
val buf = bufferFactory(size) {
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, 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, getAbsolute(index), labels[index])
return accumulator
}
/**
* Zip two buffers in the range whe they overlap
*/
public inline fun Buffer<T>.zip(
other: Buffer<T>,
crossinline operation: A.(left: T, right: T) -> T,
): Series<T> {
val newRange = indices.intersect(other.indices)
return bufferFactory(newRange.size) {
elementAlgebra.operation(
getAbsolute(it),
other.getAbsolute(it)
)
}.moveTo(newRange.first)
}
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 }

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

@ -45,8 +45,10 @@ public class Mean<T>(
public companion object { public companion object {
@Deprecated("Use Double.mean instead") @Deprecated("Use Double.mean instead")
public val double: Mean<Double> = Mean(DoubleField) { sum, count -> sum / count } public val double: Mean<Double> = Mean(DoubleField) { sum, count -> sum / count }
@Deprecated("Use Int.mean instead") @Deprecated("Use Int.mean instead")
public val int: Mean<Int> = Mean(IntRing) { sum, count -> sum / count } public val int: Mean<Int> = Mean(IntRing) { sum, count -> sum / count }
@Deprecated("Use Long.mean instead") @Deprecated("Use Long.mean instead")
public val long: Mean<Long> = Mean(LongRing) { sum, count -> sum / count } public val long: Mean<Long> = Mean(LongRing) { sum, count -> sum / count }
@ -60,6 +62,6 @@ public class Mean<T>(
//TODO replace with optimized version which respects overflow //TODO replace with optimized version which respects overflow
public val Double.Companion.mean: Mean<Double> get() = Mean(DoubleField) { sum, count -> sum / count } public val Double.Companion.mean: Mean<Double> get() = Mean(DoubleField) { sum, count -> sum / count }
public val Int.Companion.mean: Mean<Int> get() = Mean(IntRing) { sum, count -> sum / count } public val Int.Companion.mean: Mean<Int> get() = Mean(IntRing) { sum, count -> sum / count }
public val Long.Companion.mean: Mean<Long> get() = Mean(LongRing) { sum, count -> sum / count } public val Long.Companion.mean: Mean<Long> get() = Mean(LongRing) { sum, count -> sum / count }

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,35 @@
package space.kscience.kmath.stat
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asIterable
/**
* Rank statistics
*/
public class Rank<T : Comparable<T>> : BlockingStatistic<T, IntArray> {
override fun evaluateBlocking(data: Buffer<T>): IntArray = Companion.evaluate(data)
public companion object {
public fun <T : Comparable<T>> evaluate(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)
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,68 @@
package space.kscience.kmath.stat
import space.kscience.kmath.misc.UnstableKMathAPI
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
}
/**
* Resulting value of kolmogorov-smirnov two-sample statistic
*/
@UnstableKMathAPI
public data class KMComparisonResult<T : Comparable<T>>(val n: Int, val m: Int, val value: T)
/**
* Kolmogorov-Smirnov sample comparison test
* 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
*/
@UnstableKMathAPI
public fun <T : Comparable<T>, A, BA : BufferAlgebra<T, A>> StatisticalAlgebra<T, A, BA>.ksComparisonStatistic(
x: Buffer<T>,
y: Buffer<T>,
): KMComparisonResult<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 KMComparisonResult(n, m, supD)
}

View File

@ -7,13 +7,17 @@ package space.kscience.kmath.stat
import org.apache.commons.rng.UniformRandomProvider import org.apache.commons.rng.UniformRandomProvider
import org.apache.commons.rng.simple.RandomSource import org.apache.commons.rng.simple.RandomSource
import space.kscience.kmath.random.RandomGenerator
/** /**
* Implements [RandomGenerator] by delegating all operations to [RandomSource]. * Implements [RandomGenerator] by delegating all operations to [RandomSource].
* *
* @property source the underlying [RandomSource] object. * @property source the underlying [RandomSource] object.
*/ */
public class RandomSourceGenerator internal constructor(public val source: RandomSource, seed: Long?) : RandomGenerator { public class RandomSourceGenerator internal constructor(
public val source: RandomSource,
seed: Long?,
) : RandomGenerator {
internal val random: UniformRandomProvider = seed?.let { RandomSource.create(source, seed) } internal val random: UniformRandomProvider = seed?.let { RandomSource.create(source, seed) }
?: RandomSource.create(source) ?: RandomSource.create(source)

View File

@ -8,6 +8,7 @@ package space.kscience.kmath.stat
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.samplers.GaussianSampler import space.kscience.kmath.samplers.GaussianSampler
internal class CommonsDistributionsTest { internal class CommonsDistributionsTest {

View File

@ -6,20 +6,22 @@
package space.kscience.kmath.stat package space.kscience.kmath.stat
import kotlinx.coroutines.* import kotlinx.coroutines.*
import space.kscience.kmath.random.launch
import space.kscience.kmath.random.mcScope
import java.util.* import java.util.*
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
data class RandomResult(val branch: String, val order: Int, val value: Int) data class RandomResult(val branch: String, val order: Int, val value: Int)
typealias ATest = suspend CoroutineScope.() -> Set<RandomResult> internal typealias ATest = suspend () -> Set<RandomResult>
class MCScopeTest { internal class MCScopeTest {
val simpleTest: ATest = { val simpleTest: ATest = {
mcScope(1111) { mcScope(1111) {
val res = Collections.synchronizedSet(HashSet<RandomResult>()) val res = Collections.synchronizedSet(HashSet<RandomResult>())
launch { launch{
//println(random) //println(random)
repeat(10) { repeat(10) {
delay(10) delay(10)

View File

@ -6,6 +6,10 @@
package space.kscience.kmath.stat package space.kscience.kmath.stat
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.random.chain
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

@ -9,6 +9,8 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.last import kotlinx.coroutines.flow.last
import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.take
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.random.chain
import space.kscience.kmath.streaming.chunked import space.kscience.kmath.streaming.chunked
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals