From bf504ae6c5b9bc25655ee543426325ba40529ed8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 5 Nov 2021 16:58:13 +0300 Subject: [PATCH] Basic series --- build.gradle.kts | 3 +- .../space/kscience/kmath/series/analyzeDif.kt | 37 ++++ .../kmath/structures/StreamDoubleFieldND.kt | 2 +- kmath-core/build.gradle.kts | 7 - .../kmath/domains/HyperSquareDomain.kt | 1 - .../kmath/expressions/specialExpressions.kt | 1 - .../kmath/linear/BufferedLinearSpace.kt | 3 +- .../space/kscience/kmath/nd/AlgebraND.kt | 2 +- .../kscience/kmath/nd/BufferAlgebraND.kt | 20 +-- .../space/kscience/kmath/nd/BufferND.kt | 16 +- .../space/kscience/kmath/nd/DoubleFieldND.kt | 8 +- .../nd/{ShapeIndexer.kt => ShapeIndices.kt} | 4 +- .../space/kscience/kmath/nd/StructureND.kt | 6 +- .../kmath/operations/bufferOperation.kt | 11 +- .../space/kscience/kmath/structures/Buffer.kt | 11 +- .../space/kscience/kmath/real/RealVector.kt | 1 - .../space/kscience/kmath/real/realND.kt | 4 +- .../kmath/integration/GaussIntegrator.kt | 1 - .../kscience/kmath/series/SeriesAlgebra.kt | 170 ++++++++++++++++++ .../kotlin/space/kscience/kmath/stat/Mean.kt | 5 +- .../core/BroadcastDoubleTensorAlgebra.kt | 16 +- .../kmath/tensors/core/BufferedTensor.kt | 10 +- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 11 +- .../tensors/core/internal/broadcastUtils.kt | 14 +- .../kmath/tensors/core/internal/linUtils.kt | 3 + .../tensors/core/internal/tensorCastsUtils.kt | 2 +- .../kmath/tensors/core/internal/utils.kt | 2 +- settings.gradle.kts | 4 +- 28 files changed, 291 insertions(+), 84 deletions(-) create mode 100644 examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/{ShapeIndexer.kt => ShapeIndices.kt} (95%) create mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt diff --git a/build.gradle.kts b/build.gradle.kts index c2347f7be..7fb995aa1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ import java.net.URL plugins { id("ru.mipt.npm.gradle.project") + id("org.jetbrains.kotlinx.kover") version "0.4.1" kotlin("jupyter.api") apply false } @@ -69,4 +70,4 @@ ksciencePublish { sonatype(publish = true) } -apiValidation.nonPublicMarkers.add("space.kscience.kmath.misc.UnstableKMathAPI") +apiValidation.nonPublicMarkers.add("space.kscience.kmath.misc.UnstableKMathAPI") \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt b/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt new file mode 100644 index 000000000..466aa2039 --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt @@ -0,0 +1,37 @@ +package space.kscience.kmath.series + + +import net.jafama.StrictFastMath.abs +import space.kscience.kmath.operations.algebra +import space.kscience.kmath.operations.bufferAlgebra +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.operations.toList +import space.kscience.kmath.structures.Buffer +import space.kscience.plotly.Plotly +import space.kscience.plotly.makeFile +import space.kscience.plotly.scatter +import kotlin.math.PI +import kotlin.math.max + +fun main() = Double.algebra.bufferAlgebra.seriesAlgebra(0..100).invoke { + fun Buffer.plot() { + val ls = labels + Plotly.plot { + scatter { + x.numbers = ls + y.numbers = toList() + } + }.makeFile() + } + + + val s1 = series(100) { sin(2 * PI * it / 100) } + val s2 = series(100) { 1.0 } + + (s1 - s2).plot() + + // Kolmogorov-Smirnov test statistic + val kst = (s1 - s2).fold(0.0) { sup, arg -> max(sup, abs(arg))} + + +} \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt index 05a13f5d2..134c91e4a 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -36,7 +36,7 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND 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)) } } diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index e4436c1df..564d06f49 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -2,7 +2,6 @@ plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") id("ru.mipt.npm.gradle.native") -// id("com.xcporter.metaview") version "0.0.5" } kotlin.sourceSets { @@ -13,12 +12,6 @@ kotlin.sourceSets { } } -//generateUml { -// classTree { -// -// } -//} - readme { description = "Core classes, algebra definitions, basic linear algebra" maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT 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 7ea3e22c4..2b288172a 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,7 +7,6 @@ package space.kscience.kmath.domains import space.kscience.kmath.linear.Point import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.indices /** * 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 907ce4004..d68869491 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 @@ -8,7 +8,6 @@ package space.kscience.kmath.expressions import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.asIterable import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.indices import kotlin.jvm.JvmName /** 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 410fb8505..7674012e6 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,11 +13,10 @@ import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.VirtualBuffer -import space.kscience.kmath.structures.indices public class BufferedLinearSpace>( - private val bufferAlgebra: BufferAlgebra + private val bufferAlgebra: BufferAlgebra, ) : LinearSpace { override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt index 113bd4c52..9022b886a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt @@ -26,7 +26,7 @@ public fun Shape(shapeFirst: Int, vararg shapeRest: Int): Shape = intArrayOf(sha public interface WithShape { public val shape: Shape - public val indices: ShapeIndexer get() = DefaultStrides(shape) + public val shapeIndices: ShapeIndices get() = DefaultStrides(shape) } /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt index d25b455f4..447062886 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt @@ -13,7 +13,7 @@ import space.kscience.kmath.operations.* import space.kscience.kmath.structures.BufferFactory public interface BufferAlgebraND> : AlgebraND { - public val indexerBuilder: (IntArray) -> ShapeIndexer + public val indexerBuilder: (IntArray) -> ShapeIndices public val bufferAlgebra: BufferAlgebra override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra @@ -47,7 +47,7 @@ public interface BufferAlgebraND> : AlgebraND { zipInline(left.toBufferND(), right.toBufferND(), transform) public companion object { - public val defaultIndexerBuilder: (IntArray) -> ShapeIndexer = DefaultStrides.Companion::invoke + public val defaultIndexerBuilder: (IntArray) -> ShapeIndices = DefaultStrides.Companion::invoke } } @@ -55,7 +55,7 @@ public inline fun > BufferAlgebraND.mapInline( arg: BufferND, crossinline transform: A.(T) -> T, ): BufferND { - val indexes = arg.indices + val indexes = arg.shapeIndices val buffer = arg.buffer return BufferND( indexes, @@ -69,7 +69,7 @@ internal inline fun > BufferAlgebraND.mapIndexedInline( arg: BufferND, crossinline transform: A.(index: IntArray, arg: T) -> T, ): BufferND { - val indexes = arg.indices + val indexes = arg.shapeIndices val buffer = arg.buffer return BufferND( indexes, @@ -84,8 +84,8 @@ internal inline fun > BufferAlgebraND.zipInline( r: BufferND, crossinline block: A.(l: T, r: T) -> T, ): BufferND { - require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } - val indexes = l.indices + require(l.shapeIndices == r.shapeIndices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } + val indexes = l.shapeIndices val lbuffer = l.buffer val rbuffer = r.buffer return BufferND( @@ -99,25 +99,25 @@ internal inline fun > BufferAlgebraND.zipInline( @OptIn(PerformancePitfall::class) public open class BufferedGroupNDOps>( override val bufferAlgebra: BufferAlgebra, - override val indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder, + override val indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder, ) : GroupOpsND, BufferAlgebraND { override fun StructureND.unaryMinus(): StructureND = map { -it } } public open class BufferedRingOpsND>( bufferAlgebra: BufferAlgebra, - indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder, + indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder, ) : BufferedGroupNDOps(bufferAlgebra, indexerBuilder), RingOpsND public open class BufferedFieldOpsND>( bufferAlgebra: BufferAlgebra, - indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder, + indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder, ) : BufferedRingOpsND(bufferAlgebra, indexerBuilder), FieldOpsND { public constructor( elementAlgebra: A, bufferFactory: BufferFactory, - indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder, + indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder, ) : this(BufferFieldOps(elementAlgebra, bufferFactory), indexerBuilder) @OptIn(PerformancePitfall::class) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt index 515988159..eea382492 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt @@ -14,17 +14,17 @@ import space.kscience.kmath.structures.MutableBufferFactory * Represents [StructureND] over [Buffer]. * * @param T the type of items. - * @param indices The strides to access elements of [Buffer] by linear indices. + * @param shapeIndices The strides to access elements of [Buffer] by linear indices. * @param buffer The underlying buffer. */ public open class BufferND( - override val indices: ShapeIndexer, + override val shapeIndices: ShapeIndices, public open val buffer: Buffer, ) : StructureND { - override operator fun get(index: IntArray): T = buffer[indices.offset(index)] + override operator fun get(index: IntArray): T = buffer[shapeIndices.offset(index)] - override val shape: IntArray get() = indices.shape + override val shape: IntArray get() = shapeIndices.shape override fun toString(): String = StructureND.toString(this) } @@ -37,7 +37,7 @@ public inline fun StructureND.mapToBuffer( crossinline transform: (T) -> R, ): BufferND { return if (this is BufferND) - BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) }) + BufferND(this.shapeIndices, factory.invoke(shapeIndices.linearSize) { transform(buffer[it]) }) else { val strides = DefaultStrides(shape) BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) @@ -52,11 +52,11 @@ public inline fun StructureND.mapToBuffer( * @param buffer The underlying buffer. */ public class MutableBufferND( - strides: ShapeIndexer, + strides: ShapeIndices, override val buffer: MutableBuffer, ) : MutableStructureND, BufferND(strides, buffer) { override fun set(index: IntArray, value: T) { - buffer[indices.offset(index)] = value + buffer[shapeIndices.offset(index)] = value } } @@ -68,7 +68,7 @@ public inline fun MutableStructureND.mapToMutableBuffer( crossinline transform: (T) -> R, ): MutableBufferND { return if (this is MutableBufferND) - MutableBufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) }) + MutableBufferND(this.shapeIndices, factory.invoke(shapeIndices.linearSize) { transform(buffer[it]) }) else { val strides = DefaultStrides(shape) MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt index 8baeac21f..c51f80346 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt @@ -14,7 +14,7 @@ import kotlin.contracts.contract import kotlin.math.pow as kpow public class DoubleBufferND( - indexes: ShapeIndexer, + indexes: ShapeIndices, override val buffer: DoubleBuffer, ) : BufferND(indexes, buffer) @@ -34,7 +34,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND(D arg: DoubleBufferND, transform: (Double) -> Double, ): DoubleBufferND { - val indexes = arg.indices + val indexes = arg.shapeIndices val array = arg.buffer.array return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { transform(array[it]) }) } @@ -44,8 +44,8 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND(D r: DoubleBufferND, block: (l: Double, r: Double) -> Double, ): DoubleBufferND { - require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } - val indexes = l.indices + require(l.shapeIndices == r.shapeIndices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } + val indexes = l.shapeIndices val lArray = l.buffer.array val rArray = r.buffer.array return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { block(lArray[it], rArray[it]) }) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndexer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndices.kt similarity index 95% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndexer.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndices.kt index 20e180dd1..151899999 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndexer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndices.kt @@ -10,7 +10,7 @@ import kotlin.native.concurrent.ThreadLocal /** * A converter from linear index to multivariate index */ -public interface ShapeIndexer: Iterable{ +public interface ShapeIndices: Iterable{ public val shape: Shape /** @@ -44,7 +44,7 @@ public interface ShapeIndexer: Iterable{ /** * Linear transformation of indexes */ -public abstract class Strides: ShapeIndexer { +public abstract class Strides: ShapeIndices { /** * Array strides */ diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt index 614d97950..9256f0ba2 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt @@ -54,7 +54,7 @@ public interface StructureND : Featured, WithShape { * @return the lazy sequence of pairs of indices to values. */ @PerformancePitfall - public fun elements(): Sequence> = indices.asSequence().map { it to get(it) } + public fun elements(): Sequence> = shapeIndices.asSequence().map { it to get(it) } /** * Feature is some additional structure information that allows to access it special properties or hints. @@ -71,7 +71,7 @@ public interface StructureND : Featured, WithShape { if (st1 === st2) return true // fast comparison of buffers if possible - if (st1 is BufferND && st2 is BufferND && st1.indices == st2.indices) + if (st1 is BufferND && st2 is BufferND && st1.shapeIndices == st2.shapeIndices) return Buffer.contentEquals(st1.buffer, st2.buffer) //element by element comparison if it could not be avoided @@ -87,7 +87,7 @@ public interface StructureND : Featured, WithShape { if (st1 === st2) return true // fast comparison of buffers if possible - if (st1 is BufferND && st2 is BufferND && st1.indices == st2.indices) + if (st1 is BufferND && st2 is BufferND && st1.shapeIndices == st2.shapeIndices) return Buffer.contentEquals(st1.buffer, st2.buffer) //element by element comparison if it could not be avoided diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/bufferOperation.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/bufferOperation.kt index 6bf3266e3..115f8c23a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/bufferOperation.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/bufferOperation.kt @@ -61,14 +61,14 @@ public inline fun Buffer.toTypedArray(): Array = Array(size, : /** * Create a new buffer from this one with the given mapping function and using [Buffer.Companion.auto] buffer factory. */ -public inline fun Buffer.map(block: (T) -> R): Buffer = +public inline fun Buffer.map(block: (T) -> R): Buffer = Buffer.auto(size) { block(get(it)) } /** * Create a new buffer from this one with the given mapping function. * Provided [bufferFactory] is used to construct the new buffer. */ -public inline fun Buffer.map( +public inline fun Buffer.map( bufferFactory: BufferFactory, crossinline block: (T) -> R, ): Buffer = bufferFactory(size) { block(get(it)) } @@ -77,15 +77,16 @@ public inline fun Buffer.map( * Create a new buffer from this one with the given indexed mapping function. * Provided [BufferFactory] is used to construct the new buffer. */ -public inline fun Buffer.mapIndexed( +public inline fun Buffer.mapIndexed( bufferFactory: BufferFactory = Buffer.Companion::auto, crossinline block: (index: Int, value: T) -> R, ): Buffer = bufferFactory(size) { block(it, get(it)) } /** * Fold given buffer according to [operation] + * TODO add element algebra as fold receiver */ -public inline fun Buffer.fold(initial: R, operation: (acc: R, T) -> R): R { +public inline fun Buffer.fold(initial: R, operation: (acc: R, T) -> R): R { var accumulator = initial for (index in this.indices) accumulator = operation(accumulator, get(index)) return accumulator @@ -95,7 +96,7 @@ public inline fun Buffer.fold(initial: R, operation: (acc: R, T) * Zip two buffers using given [transform]. */ @UnstableKMathAPI -public inline fun Buffer.zip( +public inline fun Buffer.zip( other: Buffer, bufferFactory: BufferFactory = Buffer.Companion::auto, crossinline transform: (T1, T2) -> R, 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 c68bca2d9..2c34327b5 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 @@ -48,6 +48,11 @@ public interface Buffer { override fun toString(): String + /** + * Returns an [IntRange] of the valid indices for this [Buffer]. + */ + public val indices: IntRange get() = 0 until size + public companion object { public fun toString(buffer: Buffer<*>): String = @@ -100,10 +105,12 @@ public interface Buffer { } } +public operator fun Buffer.get(index: UInt): T = get(index.toInt()) + /** - * Returns an [IntRange] of the valid indices for this [Buffer]. + * if index is in range of buffer, return the value. Otherwise, return null. */ -public val Buffer<*>.indices: IntRange get() = 0 until size +public fun Buffer.getOrNull(index: Int): T? = if (index in indices) get(index) else null /** * Immutable wrapper for [MutableBuffer]. 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 cca1c3551..b108f696e 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,7 +11,6 @@ import space.kscience.kmath.operations.DoubleL2Norm import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.MutableBuffer.Companion.double import space.kscience.kmath.structures.asBuffer -import space.kscience.kmath.structures.indices import kotlin.math.pow public typealias DoubleVector = Point diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt index 56f50acbc..dc8f259bc 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt @@ -13,8 +13,8 @@ import space.kscience.kmath.structures.DoubleBuffer * Map one [BufferND] using function without indices. */ public inline fun BufferND.mapInline(crossinline transform: DoubleField.(Double) -> Double): BufferND { - val array = DoubleArray(indices.linearSize) { offset -> DoubleField.transform(buffer[offset]) } - return BufferND(indices, DoubleBuffer(array)) + val array = DoubleArray(shapeIndices.linearSize) { offset -> DoubleField.transform(buffer[offset]) } + return BufferND(shapeIndices, DoubleBuffer(array)) } /** 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 2b426d204..9ee292998 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,7 +8,6 @@ import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.Field import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.asBuffer -import space.kscience.kmath.structures.indices /** * A simple one-pass integrator based on Gauss rule 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 new file mode 100644 index 000000000..3bf8c66b3 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt @@ -0,0 +1,170 @@ +package space.kscience.kmath.series + +import space.kscience.kmath.operations.* +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.BufferFactory +import space.kscience.kmath.structures.getOrNull +import kotlin.math.max +import kotlin.math.min + +private fun IntRange.intersect(other: IntRange): IntRange = + max(first, other.first)..min(last, other.last) + +private val IntRange.size get() = last - first + 1 + +private class BufferView(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" } + } + + 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 +} + +/** + * A scope to operation on series + */ +public class SeriesAlgebra, L>( + public val bufferAlgebra: BufferRingOps, + private val labelResolver: (Int) -> L, +) : RingOps> { + + public val elementAlgebra: A get() = bufferAlgebra.elementAlgebra + public val bufferFactory: BufferFactory get() = bufferAlgebra.bufferFactory + + public val Buffer.offset: UInt get() = indices.first.toUInt() + + /** + * Build a new series + */ + public fun series(size: Int, fromIndex: Int = 0, block: A.(label: L) -> T): Buffer { + 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. + */ + public val Buffer.labels: List 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.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] + } + } + + /** + * Map a series to another series of the same size + */ + public inline fun Buffer.map(crossinline transform: A.(T) -> T): Buffer { + val buf = bufferFactory(size) { + elementAlgebra.transform(get(it)) + } + return buf.moveTo(indices.first) + } + + /** + * Map series to another series of the same size with label + */ + public inline fun Buffer.mapWithLabel(crossinline transform: A.(arg: T, label: L) -> T): Buffer { + val labels = labels + val buf = bufferFactory(size) { + elementAlgebra.transform(get(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)) + 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]) + return accumulator + } + + /** + * Zip two buffers replacing missing values with [defaultValue] + */ + 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) { + elementAlgebra.operation( + getOrNull(it) ?: defaultValue, + other.getOrNull(it) ?: defaultValue + ) ?: defaultValue + } + } +} + +public fun , L> BufferRingOps.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 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 2a9bd3cd4..70da58171 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,7 +7,6 @@ package space.kscience.kmath.stat import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.indices /** * Arithmetic mean @@ -45,8 +44,10 @@ public class Mean( public companion object { @Deprecated("Use Double.mean instead") public val double: Mean = Mean(DoubleField) { sum, count -> sum / count } + @Deprecated("Use Int.mean instead") public val int: Mean = Mean(IntRing) { sum, count -> sum / count } + @Deprecated("Use Long.mean instead") public val long: Mean = Mean(LongRing) { sum, count -> sum / count } @@ -60,6 +61,6 @@ public class Mean( //TODO replace with optimized version which respects overflow public val Double.Companion.mean: Mean get() = Mean(DoubleField) { sum, count -> sum / count } public val Int.Companion.mean: Mean get() = Mean(IntRing) { sum, count -> sum / count } -public val Long.Companion.mean: Mean get() = Mean(LongRing) { sum, count -> sum / count } +public val Long.Companion.mean: Mean get() = Mean(LongRing) { sum, count -> sum / count } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt index 10c747777..37c25e99f 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt @@ -26,7 +26,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { val broadcast = broadcastTensors(tensor, arg.tensor) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleArray(newThis.indices.linearSize) { i -> + val resBuffer = DoubleArray(newThis.shapeIndices.linearSize) { i -> newThis.mutableBuffer.array()[i] + newOther.mutableBuffer.array()[i] } return DoubleTensor(newThis.shape, resBuffer) @@ -34,7 +34,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { override fun Tensor.plusAssign(arg: StructureND) { val newOther = broadcastTo(arg.tensor, tensor.shape) - for (i in 0 until tensor.indices.linearSize) { + for (i in 0 until tensor.shapeIndices.linearSize) { tensor.mutableBuffer.array()[tensor.bufferStart + i] += newOther.mutableBuffer.array()[tensor.bufferStart + i] } @@ -44,7 +44,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { val broadcast = broadcastTensors(tensor, arg.tensor) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleArray(newThis.indices.linearSize) { i -> + val resBuffer = DoubleArray(newThis.shapeIndices.linearSize) { i -> newThis.mutableBuffer.array()[i] - newOther.mutableBuffer.array()[i] } return DoubleTensor(newThis.shape, resBuffer) @@ -52,7 +52,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { override fun Tensor.minusAssign(arg: StructureND) { val newOther = broadcastTo(arg.tensor, tensor.shape) - for (i in 0 until tensor.indices.linearSize) { + for (i in 0 until tensor.shapeIndices.linearSize) { tensor.mutableBuffer.array()[tensor.bufferStart + i] -= newOther.mutableBuffer.array()[tensor.bufferStart + i] } @@ -62,7 +62,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { val broadcast = broadcastTensors(tensor, arg.tensor) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleArray(newThis.indices.linearSize) { i -> + val resBuffer = DoubleArray(newThis.shapeIndices.linearSize) { i -> newThis.mutableBuffer.array()[newThis.bufferStart + i] * newOther.mutableBuffer.array()[newOther.bufferStart + i] } @@ -71,7 +71,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { override fun Tensor.timesAssign(arg: StructureND) { val newOther = broadcastTo(arg.tensor, tensor.shape) - for (i in 0 until tensor.indices.linearSize) { + for (i in 0 until tensor.shapeIndices.linearSize) { tensor.mutableBuffer.array()[tensor.bufferStart + i] *= newOther.mutableBuffer.array()[tensor.bufferStart + i] } @@ -81,7 +81,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { val broadcast = broadcastTensors(tensor, arg.tensor) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleArray(newThis.indices.linearSize) { i -> + val resBuffer = DoubleArray(newThis.shapeIndices.linearSize) { i -> newThis.mutableBuffer.array()[newOther.bufferStart + i] / newOther.mutableBuffer.array()[newOther.bufferStart + i] } @@ -90,7 +90,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { override fun Tensor.divAssign(arg: StructureND) { val newOther = broadcastTo(arg.tensor, tensor.shape) - for (i in 0 until tensor.indices.linearSize) { + for (i in 0 until tensor.shapeIndices.linearSize) { tensor.mutableBuffer.array()[tensor.bufferStart + i] /= newOther.mutableBuffer.array()[tensor.bufferStart + i] } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt index 54d8f54dc..0b1006b6c 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt @@ -22,22 +22,22 @@ public open class BufferedTensor internal constructor( /** * Buffer strides based on [TensorLinearStructure] implementation */ - override val indices: Strides get() = TensorLinearStructure(shape) + override val shapeIndices: Strides get() = TensorLinearStructure(shape) /** * Number of elements in tensor */ public val numElements: Int - get() = indices.linearSize + get() = shapeIndices.linearSize - override fun get(index: IntArray): T = mutableBuffer[bufferStart + indices.offset(index)] + override fun get(index: IntArray): T = mutableBuffer[bufferStart + shapeIndices.offset(index)] override fun set(index: IntArray, value: T) { - mutableBuffer[bufferStart + indices.offset(index)] = value + mutableBuffer[bufferStart + shapeIndices.offset(index)] = value } @PerformancePitfall - override fun elements(): Sequence> = indices.asSequence().map { + override fun elements(): Sequence> = shapeIndices.asSequence().map { it to get(it) } } 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 864900adb..6c6264989 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,7 +15,6 @@ import space.kscience.kmath.nd.as1D import space.kscience.kmath.nd.as2D import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.structures.MutableBuffer -import space.kscience.kmath.structures.indices import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra import space.kscience.kmath.tensors.api.Tensor @@ -63,7 +62,7 @@ public open class DoubleTensorAlgebra : val tensor = this.tensor //TODO remove additional copy val sourceArray = tensor.copyArray() - val array = DoubleArray(tensor.numElements) { DoubleField.transform(tensor.indices.index(it), sourceArray[it]) } + val array = DoubleArray(tensor.numElements) { DoubleField.transform(tensor.shapeIndices.index(it), sourceArray[it]) } return DoubleTensor( tensor.shape, array, @@ -365,11 +364,11 @@ public open class DoubleTensorAlgebra : val resTensor = DoubleTensor(resShape, resBuffer) for (offset in 0 until n) { - val oldMultiIndex = tensor.indices.index(offset) + val oldMultiIndex = tensor.shapeIndices.index(offset) val newMultiIndex = oldMultiIndex.copyOf() newMultiIndex[ii] = newMultiIndex[jj].also { newMultiIndex[jj] = newMultiIndex[ii] } - val linearIndex = resTensor.indices.offset(newMultiIndex) + val linearIndex = resTensor.shapeIndices.offset(newMultiIndex) resTensor.mutableBuffer.array()[linearIndex] = tensor.mutableBuffer.array()[tensor.bufferStart + offset] } @@ -467,7 +466,7 @@ public open class DoubleTensorAlgebra : val resTensor = zeros(resShape) for (i in 0 until diagonalEntries.tensor.numElements) { - val multiIndex = diagonalEntries.tensor.indices.index(i) + val multiIndex = diagonalEntries.tensor.shapeIndices.index(i) var offset1 = 0 var offset2 = abs(realOffset) @@ -592,7 +591,7 @@ public open class DoubleTensorAlgebra : val init = foldFunction(DoubleArray(1) { 0.0 }) val resTensor = BufferedTensor(resShape, MutableBuffer.auto(resNumElements) { init }, 0) - for (index in resTensor.indices) { + for (index in resTensor.shapeIndices) { val prefix = index.take(dim).toIntArray() val suffix = index.takeLast(dimension - dim - 1).toIntArray() resTensor[index] = foldFunction(DoubleArray(shape[dim]) { i -> diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt index 3787c0972..e0ea40b32 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt @@ -10,7 +10,7 @@ import kotlin.math.max internal fun multiIndexBroadCasting(tensor: DoubleTensor, resTensor: DoubleTensor, linearSize: Int) { for (linearIndex in 0 until linearSize) { - val totalMultiIndex = resTensor.indices.index(linearIndex) + val totalMultiIndex = resTensor.shapeIndices.index(linearIndex) val curMultiIndex = tensor.shape.copyOf() val offset = totalMultiIndex.size - curMultiIndex.size @@ -23,7 +23,7 @@ internal fun multiIndexBroadCasting(tensor: DoubleTensor, resTensor: DoubleTenso } } - val curLinearIndex = tensor.indices.offset(curMultiIndex) + val curLinearIndex = tensor.shapeIndices.offset(curMultiIndex) resTensor.mutableBuffer.array()[linearIndex] = tensor.mutableBuffer.array()[tensor.bufferStart + curLinearIndex] } @@ -112,7 +112,7 @@ internal fun broadcastOuterTensors(vararg tensors: DoubleTensor): List StructureND.copyToBufferedTensor(): BufferedTensor = internal fun StructureND.toBufferedTensor(): BufferedTensor = when (this) { is BufferedTensor -> this - is MutableBufferND -> if (this.indices == TensorLinearStructure(this.shape)) { + is MutableBufferND -> if (this.shapeIndices == TensorLinearStructure(this.shape)) { BufferedTensor(this.shape, this.buffer, 0) } else { this.copyToBufferedTensor() 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 553ed6add..5a1fe9d57 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 @@ -85,7 +85,7 @@ internal fun format(value: Double, digits: Int = 4): String = buildString { internal fun DoubleTensor.toPrettyString(): String = buildString { var offset = 0 val shape = this@toPrettyString.shape - val linearStructure = this@toPrettyString.indices + val linearStructure = this@toPrettyString.shapeIndices val vectorSize = shape.last() append("DoubleTensor(\n") var charOffset = 3 diff --git a/settings.gradle.kts b/settings.gradle.kts index e73381bf2..889855eef 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,8 +5,8 @@ pluginManagement { gradlePluginPortal() } - val kotlinVersion = "1.6.0-RC" - val toolsVersion = "0.10.5" + val kotlinVersion = "1.6.0-RC2" + val toolsVersion = "0.10.6" plugins { id("org.jetbrains.kotlinx.benchmark") version "0.3.1"