Basic series

This commit is contained in:
Alexander Nozik 2021-11-05 16:58:13 +03:00
parent 0e1e97a3ff
commit bf504ae6c5
28 changed files with 291 additions and 84 deletions

View File

@ -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")

View File

@ -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<Double>.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))}
}

View File

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

View File

@ -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

View File

@ -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
/**
*

View File

@ -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
/**

View File

@ -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<T, out A : Ring<T>>(
private val bufferAlgebra: BufferAlgebra<T, A>
private val bufferAlgebra: BufferAlgebra<T, A>,
) : LinearSpace<T, A> {
override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra

View File

@ -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)
}
/**

View File

@ -13,7 +13,7 @@ import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.BufferFactory
public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
public val indexerBuilder: (IntArray) -> ShapeIndexer
public val indexerBuilder: (IntArray) -> ShapeIndices
public val bufferAlgebra: BufferAlgebra<T, A>
override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
@ -47,7 +47,7 @@ public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
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 <T, A : Algebra<T>> BufferAlgebraND<T, A>.mapInline(
arg: BufferND<T>,
crossinline transform: A.(T) -> T,
): BufferND<T> {
val indexes = arg.indices
val indexes = arg.shapeIndices
val buffer = arg.buffer
return BufferND(
indexes,
@ -69,7 +69,7 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.mapIndexedInline(
arg: BufferND<T>,
crossinline transform: A.(index: IntArray, arg: T) -> T,
): BufferND<T> {
val indexes = arg.indices
val indexes = arg.shapeIndices
val buffer = arg.buffer
return BufferND(
indexes,
@ -84,8 +84,8 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.zipInline(
r: BufferND<T>,
crossinline block: A.(l: T, r: T) -> T,
): BufferND<T> {
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 <T, A : Algebra<T>> BufferAlgebraND<T, A>.zipInline(
@OptIn(PerformancePitfall::class)
public open class BufferedGroupNDOps<T, out A : Group<T>>(
override val bufferAlgebra: BufferAlgebra<T, A>,
override val indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
override val indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder,
) : GroupOpsND<T, A>, BufferAlgebraND<T, A> {
override fun StructureND<T>.unaryMinus(): StructureND<T> = map { -it }
}
public open class BufferedRingOpsND<T, out A : Ring<T>>(
bufferAlgebra: BufferAlgebra<T, A>,
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder,
) : BufferedGroupNDOps<T, A>(bufferAlgebra, indexerBuilder), RingOpsND<T, A>
public open class BufferedFieldOpsND<T, out A : Field<T>>(
bufferAlgebra: BufferAlgebra<T, A>,
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder,
) : BufferedRingOpsND<T, A>(bufferAlgebra, indexerBuilder), FieldOpsND<T, A> {
public constructor(
elementAlgebra: A,
bufferFactory: BufferFactory<T>,
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
indexerBuilder: (IntArray) -> ShapeIndices = BufferAlgebraND.defaultIndexerBuilder,
) : this(BufferFieldOps(elementAlgebra, bufferFactory), indexerBuilder)
@OptIn(PerformancePitfall::class)

View File

@ -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<out T>(
override val indices: ShapeIndexer,
override val shapeIndices: ShapeIndices,
public open val buffer: Buffer<T>,
) : StructureND<T> {
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 <T, reified R : Any> StructureND<T>.mapToBuffer(
crossinline transform: (T) -> R,
): BufferND<R> {
return if (this is BufferND<T>)
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 <T, reified R : Any> StructureND<T>.mapToBuffer(
* @param buffer The underlying buffer.
*/
public class MutableBufferND<T>(
strides: ShapeIndexer,
strides: ShapeIndices,
override val buffer: MutableBuffer<T>,
) : MutableStructureND<T>, BufferND<T>(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 <T, reified R : Any> MutableStructureND<T>.mapToMutableBuffer(
crossinline transform: (T) -> R,
): MutableBufferND<R> {
return if (this is MutableBufferND<T>)
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))) })

View File

@ -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<Double>(indexes, buffer)
@ -34,7 +34,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(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<Double, DoubleField>(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]) })

View File

@ -10,7 +10,7 @@ import kotlin.native.concurrent.ThreadLocal
/**
* A converter from linear index to multivariate index
*/
public interface ShapeIndexer: Iterable<IntArray>{
public interface ShapeIndices: Iterable<IntArray>{
public val shape: Shape
/**
@ -44,7 +44,7 @@ public interface ShapeIndexer: Iterable<IntArray>{
/**
* Linear transformation of indexes
*/
public abstract class Strides: ShapeIndexer {
public abstract class Strides: ShapeIndices {
/**
* Array strides
*/

View File

@ -54,7 +54,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
* @return the lazy sequence of pairs of indices to values.
*/
@PerformancePitfall
public fun elements(): Sequence<Pair<IntArray, T>> = indices.asSequence().map { it to get(it) }
public fun elements(): Sequence<Pair<IntArray, T>> = shapeIndices.asSequence().map { it to get(it) }
/**
* Feature is some additional structure information that allows to access it special properties or hints.
@ -71,7 +71,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, 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<out T> : Featured<StructureFeature>, 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

View File

@ -61,14 +61,14 @@ public inline fun <reified T> Buffer<T>.toTypedArray(): Array<T> = Array(size, :
/**
* Create a new buffer from this one with the given mapping function and using [Buffer.Companion.auto] buffer factory.
*/
public inline fun <T : Any, reified R : Any> Buffer<T>.map(block: (T) -> R): Buffer<R> =
public inline fun <T, reified R : Any> Buffer<T>.map(block: (T) -> R): Buffer<R> =
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 <T : Any, R : Any> Buffer<T>.map(
public inline fun <T, R> Buffer<T>.map(
bufferFactory: BufferFactory<R>,
crossinline block: (T) -> R,
): Buffer<R> = bufferFactory(size) { block(get(it)) }
@ -77,15 +77,16 @@ public inline fun <T : Any, R : Any> Buffer<T>.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 <T : Any, reified R : Any> Buffer<T>.mapIndexed(
public inline fun <T, reified R : Any> Buffer<T>.mapIndexed(
bufferFactory: BufferFactory<R> = Buffer.Companion::auto,
crossinline block: (index: Int, value: T) -> R,
): Buffer<R> = bufferFactory(size) { block(it, get(it)) }
/**
* Fold given buffer according to [operation]
* TODO add element algebra as fold receiver
*/
public inline fun <T : Any, R> Buffer<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
public inline fun <T, R> Buffer<T>.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 <T : Any, R> Buffer<T>.fold(initial: R, operation: (acc: R, T)
* Zip two buffers using given [transform].
*/
@UnstableKMathAPI
public inline fun <T1 : Any, T2 : Any, reified R : Any> Buffer<T1>.zip(
public inline fun <T1, T2, reified R : Any> Buffer<T1>.zip(
other: Buffer<T2>,
bufferFactory: BufferFactory<R> = Buffer.Companion::auto,
crossinline transform: (T1, T2) -> R,

View File

@ -48,6 +48,11 @@ public interface Buffer<out T> {
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<out T> {
}
}
public operator fun<T> Buffer<T>.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 <T> Buffer<T>.getOrNull(index: Int): T? = if (index in indices) get(index) else null
/**
* Immutable wrapper for [MutableBuffer].

View File

@ -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<Double>

View File

@ -13,8 +13,8 @@ import space.kscience.kmath.structures.DoubleBuffer
* Map one [BufferND] using function without indices.
*/
public inline fun BufferND<Double>.mapInline(crossinline transform: DoubleField.(Double) -> Double): BufferND<Double> {
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))
}
/**

View File

@ -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

View File

@ -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<T>(val buffer: Buffer<T>, val offset: Int, override val size: Int) : Buffer<T> {
init {
require(offset >= 0) { " Range offset must be positive" }
require(offset < buffer.size) { "Range offset is beyond the buffer size" }
require(size > 0) { "Size must be positive" }
require(size < buffer.size) { "Slice size is larger than the buffer" }
}
override fun get(index: Int): T = buffer[index - offset]
override fun iterator(): Iterator<T> = 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<T, A : Ring<T>, L>(
public val bufferAlgebra: BufferRingOps<T, A>,
private val labelResolver: (Int) -> L,
) : RingOps<Buffer<T>> {
public val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
public val bufferFactory: BufferFactory<T> get() = bufferAlgebra.bufferFactory
public val Buffer<T>.offset: UInt get() = indices.first.toUInt()
/**
* Build a new series
*/
public fun series(size: Int, fromIndex: Int = 0, block: A.(label: L) -> T): Buffer<T> {
return bufferFactory(size) {
val index = it + fromIndex
elementAlgebra.block(labelResolver(index))
}.moveTo(fromIndex)
}
/**
* Move a series starting to start at a given index
*/
public fun Buffer<T>.moveTo(index: Int): Buffer<T> = if (index == 0) {
this
} else if (this is BufferView) {
BufferView(buffer, index.toInt(), size)
} else {
BufferView(this, index.toInt(), size)
}
/**
* Create a buffer view using given range
*/
public fun Buffer<T>.get(range: IntRange): Buffer<T> {
val size = range.size
return if (this is BufferView) {
BufferView(this, indices.first + range.first, size)
} else {
BufferView(this, range.first, size)
}
}
/**
* Get a label buffer for given buffer.
*/
public val Buffer<T>.labels: List<L> get() = indices.map(labelResolver)
/**
* Try to resolve element by label and return null if element with a given label is not found
*/
public operator fun Buffer<T>.get(label: L): T? {
val index = labels.indexOf(label)
if (index == -1) return null
return get(index + offset.toInt())
}
override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = elementAlgebra.invoke {
val newRange = left.indices.intersect(right.indices)
//TODO optimize copy at BufferAlgebra level
bufferFactory(newRange.size) {
val offset = it + newRange.first
left[offset] + right[offset]
}.moveTo(newRange.first)
}
override fun Buffer<T>.unaryMinus(): Buffer<T> = map { -it }
override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = elementAlgebra.invoke {
val newRange = left.indices.intersect(right.indices)
bufferFactory(newRange.size) {
val offset = it + newRange.first
left[offset] * right[offset]
}
}
/**
* Map a series to another series of the same size
*/
public inline fun Buffer<T>.map(crossinline transform: A.(T) -> T): Buffer<T> {
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<T>.mapWithLabel(crossinline transform: A.(arg: T, label: L) -> T): Buffer<T> {
val labels = labels
val buf = bufferFactory(size) {
elementAlgebra.transform(get(it), labels[it])
}
return buf.moveTo(indices.first)
}
public inline fun <R> Buffer<T>.fold(initial: R, operation: A.(acc: R, T) -> R): R {
var accumulator = initial
for (index in this.indices) accumulator = elementAlgebra.operation(accumulator, get(index))
return accumulator
}
public inline fun <R> Buffer<T>.foldWithLabel(initial: R, operation: A.(acc: R, arg: T, label: L) -> R): R {
val labels = labels
var accumulator = initial
for (index in this.indices) accumulator = elementAlgebra.operation(accumulator, get(index), labels[index])
return accumulator
}
/**
* Zip two buffers replacing missing values with [defaultValue]
*/
public inline fun Buffer<T>.zip(
other: Buffer<T>,
defaultValue: T,
crossinline operation: A.(left: T?, right: T?) -> T?,
): Buffer<T> {
val start = min(indices.first, other.indices.first)
val size = max(indices.last, other.indices.last) - start
return bufferFactory(size) {
elementAlgebra.operation(
getOrNull(it) ?: defaultValue,
other.getOrNull(it) ?: defaultValue
) ?: defaultValue
}
}
}
public fun <T, A : Ring<T>, L> BufferRingOps<T, A>.seriesAlgebra(labels: Iterable<L>): SeriesAlgebra<T, A, L> {
val l = labels.toList()
return SeriesAlgebra(this) {
if (it in l.indices) l[it] else error("Index $it is outside of labels range ${l.indices}")
}
}

View File

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

View File

@ -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<Double>.plusAssign(arg: StructureND<Double>) {
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<Double>.minusAssign(arg: StructureND<Double>) {
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<Double>.timesAssign(arg: StructureND<Double>) {
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<Double>.divAssign(arg: StructureND<Double>) {
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]
}

View File

@ -22,22 +22,22 @@ public open class BufferedTensor<T> 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<Pair<IntArray, T>> = indices.asSequence().map {
override fun elements(): Sequence<Pair<IntArray, T>> = shapeIndices.asSequence().map {
it to get(it)
}
}

View File

@ -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 ->

View File

@ -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<DoubleTen
val resTensor = DoubleTensor(totalShape + matrixShape, DoubleArray(n * matrixSize))
for (linearIndex in 0 until n) {
val totalMultiIndex = outerTensor.indices.index(linearIndex)
val totalMultiIndex = outerTensor.shapeIndices.index(linearIndex)
var curMultiIndex = tensor.shape.sliceArray(0..tensor.shape.size - 3).copyOf()
curMultiIndex = IntArray(totalMultiIndex.size - curMultiIndex.size) { 1 } + curMultiIndex
@ -127,13 +127,13 @@ internal fun broadcastOuterTensors(vararg tensors: DoubleTensor): List<DoubleTen
}
for (i in 0 until matrixSize) {
val curLinearIndex = newTensor.indices.offset(
val curLinearIndex = newTensor.shapeIndices.offset(
curMultiIndex +
matrix.indices.index(i)
matrix.shapeIndices.index(i)
)
val newLinearIndex = resTensor.indices.offset(
val newLinearIndex = resTensor.shapeIndices.offset(
totalMultiIndex +
matrix.indices.index(i)
matrix.shapeIndices.index(i)
)
resTensor.mutableBuffer.array()[resTensor.bufferStart + newLinearIndex] =

View File

@ -3,8 +3,11 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
@file:OptIn(PerformancePitfall::class)
package space.kscience.kmath.tensors.core.internal
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.MutableStructure1D
import space.kscience.kmath.nd.MutableStructure2D
import space.kscience.kmath.nd.as1D

View File

@ -28,7 +28,7 @@ internal fun <T> StructureND<T>.copyToBufferedTensor(): BufferedTensor<T> =
internal fun <T> StructureND<T>.toBufferedTensor(): BufferedTensor<T> = when (this) {
is BufferedTensor<T> -> this
is MutableBufferND<T> -> if (this.indices == TensorLinearStructure(this.shape)) {
is MutableBufferND<T> -> if (this.shapeIndices == TensorLinearStructure(this.shape)) {
BufferedTensor(this.shape, this.buffer, 0)
} else {
this.copyToBufferedTensor()

View File

@ -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

View File

@ -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"