diff --git a/examples/src/main/kotlin/space/kscience/kmath/fit/chiSquared.kt b/examples/src/main/kotlin/space/kscience/kmath/fit/chiSquared.kt index dbe0b8454..45678c425 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/fit/chiSquared.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/fit/chiSquared.kt @@ -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 diff --git a/examples/src/main/kotlin/space/kscience/kmath/fit/qowFit.kt b/examples/src/main/kotlin/space/kscience/kmath/fit/qowFit.kt index d52976671..04764d763 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/fit/qowFit.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/fit/qowFit.kt @@ -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 diff --git a/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt b/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt index 466aa2039..d67d59bfc 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt @@ -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.plot() { - val ls = labels - Plotly.plot { +fun main() = with(Double.algebra.bufferAlgebra.seriesAlgebra()) { + fun FlowContent.plotSeries(buffer: Buffer) { + val ls = buffer.labels + plot { scatter { x.numbers = ls - y.numbers = toList() + y.numbers = buffer.toList() } - }.makeFile() + layout { + xaxis { + range(0.0..100.0) + } + } + } } - val s1 = series(100) { sin(2 * PI * it / 100) } - val s2 = series(100) { 1.0 } + val s1 = series(100) { sin(2 * PI * it / 100) + 1.0 } + val s2 = s1.slice(20..50).moveTo(40) - (s1 - s2).plot() - - // Kolmogorov-Smirnov test statistic - val kst = (s1 - s2).fold(0.0) { sup, arg -> max(sup, abs(arg))} + val s3: Buffer = 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() } \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/random/CMRandomGeneratorWrapper.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/random/CMRandomGeneratorWrapper.kt index 28294cf14..2183399ef 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/random/CMRandomGeneratorWrapper.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/random/CMRandomGeneratorWrapper.kt @@ -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, diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/transform/Transformations.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/transform/Transformations.kt index 73ab91542..3ac278ca0 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/transform/Transformations.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/transform/Transformations.kt @@ -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.* /** diff --git a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt index c670ceead..681ec9ebc 100644 --- a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -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 diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt index 2b288172a..7ea3e22c4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt @@ -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 /** * diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/specialExpressions.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/specialExpressions.kt index d68869491..6b17dfca5 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/specialExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/specialExpressions.kt @@ -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 /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt index 7674012e6..bc736fc50 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt @@ -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>( diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt index 3dcc77334..f2a4336eb 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt @@ -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 /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt index 634a115c7..17c26ad11 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt @@ -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>( /** * Generate full buffer field from given buffer operations */ -public fun > BufferFieldOps.withSize(size: Int): BufferField = +public fun > BufferAlgebra.withSize(size: Int): BufferField = BufferField(elementAlgebra, bufferFactory, size) //Double buffer specialization @@ -196,6 +195,4 @@ public fun BufferField.buffer(vararg elements: Number): Buffer> A.bufferAlgebra(bufferFactory: BufferFactory): BufferFieldOps = BufferFieldOps(this, bufferFactory) -public val DoubleField.bufferAlgebra: BufferFieldOps - get() = BufferFieldOps(DoubleField, ::DoubleBuffer) - +public val DoubleField.bufferAlgebra: DoubleBufferOps get() = DoubleBufferOps diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt index b0cce91d3..269823a10 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt @@ -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.* diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt index 2c34327b5..91b4ee71b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt @@ -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 = (Int, (Int) -> T) -> MutableBuffer * * @param T the type of elements contained in the buffer. */ -public interface Buffer { +public interface Buffer : 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 { /** * Iterates over all elements. */ - public operator fun iterator(): Iterator + public operator fun iterator(): Iterator = 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,7 +100,12 @@ public interface Buffer { } } -public operator fun Buffer.get(index: UInt): T = get(index.toInt()) +/** + * Returns an [IntRange] of the valid indices for this [Buffer]. + */ +public val Buffer.indices: IntRange get() = 0 until size + +public operator fun Buffer.get(index: UInt): T = get(index.toInt()) /** * if index is in range of buffer, return the value. Otherwise, return null. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt new file mode 100644 index 000000000..d4e6a4a58 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt @@ -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 : Buffer { + public val origin: Buffer + + /** + * 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( + override val origin: Buffer, + public val offset: UInt = 0U, + override val size: Int, +) : BufferView { + + 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 = + (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( + override val origin: Buffer, + public val defaultValue: T, + public val offset: Int = 0, + override val size: Int = origin.size, +) : BufferView { + + 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 Buffer.slice(range: UIntRange): BufferView = 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 Buffer.expand( + range: IntRange, + defaultValue: T, +): BufferView = 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( + override val origin: Buffer, + private val permutations: IntArray, +) : BufferView { + 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 = 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 Buffer.view(indices: IntArray): PermutatedBuffer = PermutatedBuffer(this, indices) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/DoubleBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/DoubleBuffer.kt index 3b554ab07..087f81934 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/DoubleBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/DoubleBuffer.kt @@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline * @property array the underlying array. */ @JvmInline -public value class DoubleBuffer(public val array: DoubleArray) : MutableBuffer { +public value class DoubleBuffer(public val array: DoubleArray) : PrimitiveBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Double = array[index] diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/FloatBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/FloatBuffer.kt index dc7903cbf..f99e73baa 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/FloatBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/FloatBuffer.kt @@ -14,7 +14,7 @@ import kotlin.jvm.JvmInline * @author Iaroslav Postovalov */ @JvmInline -public value class FloatBuffer(public val array: FloatArray) : MutableBuffer { +public value class FloatBuffer(public val array: FloatArray) : PrimitiveBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Float = array[index] diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/IntBuffer.kt index ca078746c..1de36453f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/IntBuffer.kt @@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline * @property array the underlying array. */ @JvmInline -public value class IntBuffer(public val array: IntArray) : MutableBuffer { +public value class IntBuffer(public val array: IntArray) : PrimitiveBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Int = array[index] diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/LongBuffer.kt index a0b5c78fa..0596729b4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/LongBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/LongBuffer.kt @@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline * @property array the underlying array. */ @JvmInline -public value class LongBuffer(public val array: LongArray) : MutableBuffer { +public value class LongBuffer(public val array: LongArray) : PrimitiveBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Long = array[index] diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt index 97185b918..396d51bc3 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt @@ -94,4 +94,7 @@ public interface MutableBuffer : Buffer { public inline fun auto(size: Int, initializer: (Int) -> T): MutableBuffer = auto(T::class, size, initializer) } -} \ No newline at end of file +} + + +public sealed interface PrimitiveBuffer: MutableBuffer \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/bufferOperation.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferExtensions.kt similarity index 73% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/bufferOperation.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferExtensions.kt index 115f8c23a..f8b785724 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/bufferOperation.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferExtensions.kt @@ -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. @@ -103,4 +102,40 @@ public inline fun Buffer.zip( ): Buffer { 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 > Buffer.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 > Buffer.sorted(): Buffer = when (this) { + is PrimitiveBuffer -> when (this) { + is LongBuffer -> toLongArray().apply { sort() } as Buffer + is FloatBuffer -> toFloatArray().apply { sort() } as Buffer + is IntBuffer -> toIntArray().apply { sort() } as Buffer + is DoubleBuffer -> toDoubleArray().apply { sort() } as Buffer + } + else -> asIterable().sorted().asBuffer() } \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/BufferExpandedTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/BufferExpandedTest.kt new file mode 100644 index 000000000..992080fb0 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/BufferExpandedTest.kt @@ -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 = buffer.expand(-20..113,0) + assertEquals(0,view[4]) + assertEquals(0,view[123]) + assertEquals(100, view[120]) + assertFails { view[-2] } + assertFails { view[134] } + } +} \ No newline at end of file diff --git a/kmath-coroutines/src/jvmTest/kotlin/space/kscience/kmath/streaming/RingBufferTest.kt b/kmath-coroutines/src/jvmTest/kotlin/space/kscience/kmath/streaming/RingBufferTest.kt index a3143a1ac..14081b0f5 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/space/kscience/kmath/streaming/RingBufferTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/space/kscience/kmath/streaming/RingBufferTest.kt @@ -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 diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt index c1ee8b48f..88932d59b 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt @@ -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 /* diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealVector.kt index b108f696e..cca1c3551 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealVector.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealVector.kt @@ -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 diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt index 9ee292998..2b426d204 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt @@ -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 diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt index 94c73832b..b09129626 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt @@ -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 diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt index 6abe89aad..662cdf3d7 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt @@ -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 diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt index 89ee23354..6f4a9e2ed 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt @@ -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 diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt index 70f8f4ebd..717e78871 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt @@ -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 diff --git a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt b/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt index d5b74fb9b..018f47a7f 100644 --- a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt +++ b/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt @@ -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 diff --git a/kmath-jupyter/src/main/kotlin/space/kscience/kmath/jupyter/KMathJupyter.kt b/kmath-jupyter/src/main/kotlin/space/kscience/kmath/jupyter/KMathJupyter.kt index 9731908b3..e646d2bd0 100644 --- a/kmath-jupyter/src/main/kotlin/space/kscience/kmath/jupyter/KMathJupyter.kt +++ b/kmath-jupyter/src/main/kotlin/space/kscience/kmath/jupyter/KMathJupyter.kt @@ -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 diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/Distribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/Distribution.kt index 298bbc858..1cff271a7 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/Distribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/Distribution.kt @@ -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. diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt index 24429cf32..8f9250ee5 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt @@ -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 diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/UniformDistribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/UniformDistribution.kt similarity index 86% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/UniformDistribution.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/UniformDistribution.kt index 4c0d08720..fd9fa0bdd 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/UniformDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/UniformDistribution.kt @@ -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) : UnivariateDistribution { private val length: Double = range.endInclusive - range.start diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt index 5f923fe5f..fd8437191 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt @@ -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 - 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 } } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt index 063e055ce..fdba8ec90 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt @@ -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.* /** diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AliasMethodDiscreteSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AliasMethodDiscreteSampler.kt index b00db5b30..03ba142b7 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AliasMethodDiscreteSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AliasMethodDiscreteSampler.kt @@ -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 diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/internal/InternalErf.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalErf.kt similarity index 93% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/internal/InternalErf.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalErf.kt index 5b3cb1859..b8f9fd25e 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/internal/InternalErf.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalErf.kt @@ -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 diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/internal/InternalGamma.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalGamma.kt similarity index 99% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/internal/InternalGamma.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalGamma.kt index 18abd669f..cdc40b44d 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/internal/InternalGamma.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalGamma.kt @@ -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.* diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/internal/InternalUtils.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalUtils.kt similarity index 98% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/internal/InternalUtils.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalUtils.kt index 77ba02a25..b0c63ff15 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/internal/InternalUtils.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalUtils.kt @@ -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 diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/KempSmallMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/KempSmallMeanPoissonSampler.kt index 16f91570f..5146169a3 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/KempSmallMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/KempSmallMeanPoissonSampler.kt @@ -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 diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/NormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/NormalizedGaussianSampler.kt index ceb324e8d..d427d8e7a 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/NormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/NormalizedGaussianSampler.kt @@ -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{ override fun sample(generator: RandomGenerator): BlockingDoubleChain diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/PoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/PoissonSampler.kt index d3ff05b06..3504a65bf 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/PoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/PoissonSampler.kt @@ -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.* diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Sampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt similarity index 86% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Sampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt index 0b3b52cab..2c842ab65 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Sampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt @@ -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 /** diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/SamplerAlgebra.kt similarity index 95% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/SamplerAlgebra.kt index e0be72d4b..c0b8e5b5c 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/SamplerAlgebra.kt @@ -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]. diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt index 3bf8c66b3..ae1bfe0e7 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt @@ -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 + +@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 : Buffer { + public val origin: Buffer + /** + * Absolute position of start of this [Series] in [SeriesAlgebra] + */ + public val position: Int +} + +public val Series.absoluteIndices: IntRange get() = position until position + size + +/** + * A [BufferView] with index offset (both positive and negative) and possible size change + */ +private class OffsetBufer( + override val origin: Buffer, + override val position: Int, + override val size: Int = origin.size, +) : Series, Buffer by origin { -private class BufferView(val buffer: Buffer, val offset: Int, override val size: Int) : Buffer { 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" } + require(size <= origin.size) { "Slice size is larger than the original buffer" } } - override fun get(index: Int): T = buffer[index - offset] - - override fun iterator(): Iterator = buffer.asSequence().drop(offset).take(size).iterator() - - override fun toString(): String = "$buffer[${offset}:${offset + size - 1}]" - - override val indices: IntRange = offset until offset + size + override fun toString(): String = "$origin-->${position}" } /** * A scope to operation on series */ -public class SeriesAlgebra, L>( - public val bufferAlgebra: BufferRingOps, +public class SeriesAlgebra, out BA : BufferAlgebra, L>( + override val bufferAlgebra: BA, private val labelResolver: (Int) -> L, -) : RingOps> { +) : RingOps>, StatisticalAlgebra { - public val elementAlgebra: A get() = bufferAlgebra.elementAlgebra - public val bufferFactory: BufferFactory get() = bufferAlgebra.bufferFactory + public val Buffer.indices: IntRange + get() = if (this is Series) { + absoluteIndices + } else { + 0 until size + } - public val Buffer.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.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.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.moveTo(index: Int): Series = if (this is Series) { + OffsetBufer(origin, index, size) + } else { + OffsetBufer(this, index, size) + } + + /** + * Create a buffer view using given absolute range + */ + public fun Buffer.slice(range: IntRange): Series { + 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.expand(range: IntRange, defaultValue: T): Series = if (range in indices) { + slice(range) + } else { + TODO() + } + + public val Buffer.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 { + public fun series(size: Int, fromIndex: Int = 0, block: A.(label: L) -> T): Series { 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.moveTo(index: Int): Buffer = 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.get(range: IntRange): Buffer { - 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, L>( public operator fun Buffer.get(label: L): T? { val index = labels.indexOf(label) if (index == -1) return null - return get(index + offset.toInt()) - } - - override fun add(left: Buffer, right: Buffer): Buffer = 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.unaryMinus(): Buffer = map { -it } - - override fun multiply(left: Buffer, right: Buffer): Buffer = 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.map(crossinline transform: A.(T) -> T): Buffer { + public inline fun Buffer.map(crossinline transform: A.(T) -> T): Series { val buf = bufferFactory(size) { - elementAlgebra.transform(get(it)) + elementAlgebra.transform(getAbsolute(it)) } return buf.moveTo(indices.first) } @@ -122,49 +148,63 @@ public class SeriesAlgebra, L>( /** * Map series to another series of the same size with label */ - public inline fun Buffer.mapWithLabel(crossinline transform: A.(arg: T, label: L) -> T): Buffer { + public inline fun Buffer.mapWithLabel(crossinline transform: A.(arg: T, label: L) -> T): Series { 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 Buffer.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 Buffer.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.zip( other: Buffer, - defaultValue: T, - crossinline operation: A.(left: T?, right: T?) -> T?, - ): Buffer { - 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 { + 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) } + + override fun Buffer.unaryMinus(): Buffer = map { -it } + + override fun add(left: Buffer, right: Buffer): Series = left.zip(right) { l, r -> l + r } + + override fun multiply(left: Buffer, right: Buffer): Buffer = left.zip(right) { l, r -> l * r } } -public fun , L> BufferRingOps.seriesAlgebra(labels: Iterable): SeriesAlgebra { +public fun , BA : BufferAlgebra, L> BA.seriesAlgebra(labels: Iterable): SeriesAlgebra { 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}") } -} \ No newline at end of file +} + +public fun , BA : BufferAlgebra, L> BA.seriesAlgebra(labelGenerator: (Int) -> L): SeriesAlgebra = + SeriesAlgebra(this, labelGenerator) + +/** + * Create a series algebra using offset as a label + */ +public fun , BA : BufferAlgebra> BA.seriesAlgebra(): SeriesAlgebra = + SeriesAlgebra(this) { it } \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/seriesExtensions.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/seriesExtensions.kt new file mode 100644 index 000000000..882fa2c46 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/seriesExtensions.kt @@ -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 SeriesAlgebra.sin( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = + bufferAlgebra.sin(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.cos( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = + bufferAlgebra.cos(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.tan( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = + bufferAlgebra.tan(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.asin( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = + bufferAlgebra.asin(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.acos( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = + bufferAlgebra.acos(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.atan( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = + bufferAlgebra.atan(arg).moveTo(arg.offset) + + +//exponential + +public fun SeriesAlgebra.exp( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : ExponentialOperations> = + bufferAlgebra.exp(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.ln( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : ExponentialOperations> = + bufferAlgebra.ln(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.sinh( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : ExponentialOperations> = + bufferAlgebra.sinh(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.cosh( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : ExponentialOperations> = + bufferAlgebra.cosh(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.tanh( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : ExponentialOperations> = + bufferAlgebra.tanh(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.asinh( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : ExponentialOperations> = + bufferAlgebra.asinh(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.acosh( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : ExponentialOperations> = + bufferAlgebra.acosh(arg).moveTo(arg.offset) + +public fun SeriesAlgebra.atanh( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : ExponentialOperations> = + bufferAlgebra.atanh(arg).moveTo(arg.offset) + + +//power + +public fun SeriesAlgebra.power( + arg: Buffer, + pow: Number, +): Series where BA : BufferAlgebra, BA : PowerOperations> = + bufferAlgebra.power(arg, pow).moveTo(arg.offset) + +public fun SeriesAlgebra.sqrt( + arg: Buffer, +): Series where BA : BufferAlgebra, BA : PowerOperations> = + bufferAlgebra.sqrt(arg).moveTo(arg.offset) \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt index 70da58171..f2658549a 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt @@ -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 diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt index 664e4e8e7..54b2e42b3 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt @@ -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 diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Rank.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Rank.kt new file mode 100644 index 000000000..92b7e1e7b --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Rank.kt @@ -0,0 +1,28 @@ +package space.kscience.kmath.stat + +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.asIterable + +public class Rank>: BlockingStatistic { + override fun evaluateBlocking(data: Buffer): IntArray { + // https://www.geeksforgeeks.org/rank-elements-array/ + val permutations = ArrayList>(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 + } +} \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/StatisticalAlgebra.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/StatisticalAlgebra.kt new file mode 100644 index 000000000..c8510a8eb --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/StatisticalAlgebra.kt @@ -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, out BA : BufferAlgebra> : Algebra> { + public val bufferAlgebra: BA + public val elementAlgebra: A get() = bufferAlgebra.elementAlgebra + public val bufferFactory: BufferFactory get() = bufferAlgebra.bufferFactory +} + +/** + * Compute [empirical CDF function](https://en.wikipedia.org/wiki/Empirical_distribution_function) + */ +public fun > StatisticalAlgebra.ecdf(buffer: Buffer): (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 , A, BA : BufferAlgebra> StatisticalAlgebra.kolmogorovSmirnovTest( + x: Buffer, + y: Buffer, +): T where A : Group, A : NumericAlgebra = 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; +} \ No newline at end of file diff --git a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/SamplerTest.kt b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/SamplerTest.kt index 4060c0505..8a5148f9e 100644 --- a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/SamplerTest.kt +++ b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/SamplerTest.kt @@ -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 { diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 6c6264989..6f10ad7ad 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -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 diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt index 5a2e69c87..dbfa09cdf 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt @@ -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 diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt index 5a1fe9d57..5ec2f240b 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt @@ -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.*