Explicit mutability for StructureND builders

This commit is contained in:
Alexander Nozik 2023-04-21 12:41:46 +03:00
parent 875e32679b
commit cdfddb7551
25 changed files with 159 additions and 43 deletions

View File

@ -3,6 +3,7 @@
## Unreleased
### Added
- Explicit `mutableStructureND` builders for mutable stucures
### Changed

View File

@ -3,8 +3,6 @@ plugins {
`version-catalog`
}
java.targetCompatibility = JavaVersion.VERSION_11
repositories {
mavenLocal()
maven("https://repo.kotlin.link")
@ -26,6 +24,11 @@ dependencies {
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.+")
}
kotlin.sourceSets.all {
kotlin{
jvmToolchain{
languageVersion.set(JavaLanguageVersion.of(11))
}
sourceSets.all {
languageSettings.optIn("kotlin.OptIn")
}
}

View File

@ -52,7 +52,7 @@ dependencies {
implementation("org.slf4j:slf4j-simple:1.7.32")
// plotting
implementation("space.kscience:plotlykt-server:0.5.0")
implementation("space.kscience:plotlykt-server:0.5.3-dev-1")
}
kotlin {

View File

@ -8,14 +8,15 @@ package space.kscience.kmath.operations
import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.linear.matrix
import space.kscience.kmath.nd.DoubleBufferND
import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.mutableStructureND
import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.viktor.ViktorStructureND
import space.kscience.kmath.viktor.viktorAlgebra
import kotlin.collections.component1
import kotlin.collections.component2
fun main() {
val viktorStructure: ViktorStructureND = DoubleField.viktorAlgebra.structureND(ShapeND(2, 2)) { (i, j) ->
val viktorStructure = DoubleField.viktorAlgebra.mutableStructureND(2, 2) { (i, j) ->
if (i == j) 2.0 else 0.0
}

View File

@ -13,7 +13,10 @@ import space.kscience.kmath.structures.slice
import space.kscience.plotly.*
import kotlin.math.PI
fun main() = with(Double.algebra.bufferAlgebra.seriesAlgebra()) {
fun Double.Companion.seriesAlgebra() = Double.algebra.bufferAlgebra.seriesAlgebra()
fun main() = with(Double.seriesAlgebra()) {
fun Plot.plotSeries(name: String, buffer: Buffer<Double>) {

View File

@ -0,0 +1,50 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.series
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.structures.toDoubleArray
import space.kscience.plotly.*
import space.kscience.plotly.models.Scatter
import space.kscience.plotly.models.ScatterMode
import kotlin.random.Random
fun main(): Unit = with(Double.seriesAlgebra()) {
val random = Random(1234)
val arrayOfRandoms = DoubleArray(20) { random.nextDouble() }
val series1: DoubleBuffer = arrayOfRandoms.asBuffer()
val series2: Series<Double> = series1.moveBy(3)
val res = series2 - series1
println(res.size)
println(res)
fun Plot.series(name: String, buffer: Buffer<Double>, block: Scatter.() -> Unit = {}) {
scatter {
this.name = name
x.numbers = buffer.offsetIndices
y.doubles = buffer.toDoubleArray()
block()
}
}
Plotly.plot {
series("series1", series1)
series("series2", series2)
series("dif", res){
mode = ScatterMode.lines
line.color("magenta")
}
}.makeFile(resourceLocation = ResourceLocation.REMOTE)
}

View File

@ -52,6 +52,15 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND<Double, DoubleF
return BufferND(strides, array.asBuffer())
}
override fun mutableStructureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): MutableBufferND<Double> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
val index = strides.index(offset)
DoubleField.initializer(index)
}.toArray()
return MutableBufferND(strides, array.asBuffer())
}
@OptIn(PerformancePitfall::class)
override fun StructureND<Double>.map(
transform: DoubleField.(Double) -> Double,

View File

@ -0,0 +1,26 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.structures
import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.algebra
@OptIn(PerformancePitfall::class)
fun main(): Unit = with(Double.algebra.ndAlgebra) {
val structure: MutableStructure2D<Double> = mutableStructureND(ShapeND(2, 2)) { (i, j) ->
i.toDouble() + j.toDouble()
}.as2D()
structure[0, 1] = -2.0
val structure2 = mutableStructureND(2, 2) { (i, j) -> i.toDouble() + j.toDouble() }.as2D()
structure2[0, 1] = 2.0
println(structure + structure2)
}

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -37,8 +37,8 @@ kotlin.sourceSets {
filter { it.name.contains("test", true) }
.map(org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet::languageSettings)
.forEach {
it.optIn("space.kscience.kmath.misc.PerformancePitfall")
it.optIn("space.kscience.kmath.misc.UnstableKMathAPI")
it.optIn("space.kscience.kmath.PerformancePitfall")
it.optIn("space.kscience.kmath.UnstableKMathAPI")
}
}

View File

@ -22,10 +22,16 @@ public interface AlgebraND<T, out C : Algebra<T>>: Algebra<StructureND<T>> {
*/
public val elementAlgebra: C
/**
* Produces a new [MutableStructureND] using given initializer function.
*/
public fun mutableStructureND(shape: ShapeND, initializer: C.(IntArray) -> T): MutableStructureND<T>
/**
* Produces a new [StructureND] using given initializer function.
*/
public fun structureND(shape: ShapeND, initializer: C.(IntArray) -> T): StructureND<T>
public fun structureND(shape: ShapeND, initializer: C.(IntArray) -> T): StructureND<T> =
mutableStructureND(shape, initializer)
/**
* Maps elements from one structure to another one by applying [transform] to them.

View File

@ -16,9 +16,10 @@ public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
public val bufferAlgebra: BufferAlgebra<T, A>
override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): BufferND<T> {
//TODO change AlgebraND contract to include this
override fun mutableStructureND(shape: ShapeND, initializer: A.(IntArray) -> T): MutableBufferND<T> {
val indexer = indexerBuilder(shape)
return BufferND(
return MutableBufferND(
indexer,
bufferAlgebra.buffer(indexer.linearSize) { offset ->
elementAlgebra.initializer(indexer.index(offset))
@ -26,6 +27,9 @@ public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
)
}
override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): BufferND<T> =
mutableStructureND(shape, initializer)
@OptIn(PerformancePitfall::class)
public fun StructureND<T>.toBufferND(): BufferND<T> = when (this) {
is BufferND -> this
@ -133,6 +137,11 @@ public fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.structureND(
initializer: A.(IntArray) -> T,
): BufferND<T> = structureND(ShapeND(shape), initializer)
public fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.mutableStructureND(
vararg shape: Int,
initializer: A.(IntArray) -> T,
): MutableBufferND<T> = mutableStructureND(ShapeND(shape), initializer)
public fun <T, EA : Algebra<T>, A> A.structureND(
initializer: EA.(IntArray) -> T,
): BufferND<T> where A : BufferAlgebraND<T, EA>, A : WithShape = structureND(shape, initializer)

View File

@ -74,7 +74,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
transform: DoubleField.(Double, Double) -> Double,
): BufferND<Double> = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> DoubleField.transform(l, r) }
override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND {
override fun mutableStructureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND {
val indexer = indexerBuilder(shape)
return DoubleBufferND(
indexer,
@ -225,7 +225,7 @@ public class DoubleFieldND(override val shape: ShapeND) :
override fun number(value: Number): DoubleBufferND {
val d = value.toDouble() // minimize conversions
return structureND(shape) { d }
return mutableStructureND(shape) { d }
}
}

View File

@ -244,6 +244,7 @@ public interface MutableStructureND<T> : StructureND<T> {
* Set value at specified indices
*/
@PerformancePitfall
@Deprecated("")
public operator fun <T> MutableStructureND<T>.set(vararg index: Int, value: T) {
set(index, value)
}

View File

@ -17,6 +17,12 @@ public fun <T, A : Algebra<T>> AlgebraND<T, A>.structureND(
initializer: A.(IntArray) -> T
): StructureND<T> = structureND(ShapeND(shapeFirst, *shapeRest), initializer)
public fun <T, A : Algebra<T>> AlgebraND<T, A>.mutableStructureND(
shapeFirst: Int,
vararg shapeRest: Int,
initializer: A.(IntArray) -> T
): MutableStructureND<T> = mutableStructureND(ShapeND(shapeFirst, *shapeRest), initializer)
public fun <T, A : Group<T>> AlgebraND<T, A>.zero(shape: ShapeND): StructureND<T> = structureND(shape) { zero }
@JvmName("zeroVarArg")

View File

@ -6,7 +6,8 @@
package space.kscience.kmath.operations
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.MutableBufferFactory
public interface WithSize {
public val size: Int
@ -17,7 +18,7 @@ public interface WithSize {
*/
public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
public val elementAlgebra: A
public val elementBufferFactory: BufferFactory<T> get() = elementAlgebra.bufferFactory
public val elementBufferFactory: MutableBufferFactory<T> get() = elementAlgebra.bufferFactory
public fun buffer(size: Int, vararg elements: T): Buffer<T> {
require(elements.size == size) { "Expected $size elements but found ${elements.size}" }
@ -73,11 +74,11 @@ private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.zipInline(
return elementBufferFactory(l.size) { elementAlgebra.block(l[it], r[it]) }
}
public fun <T> BufferAlgebra<T, *>.buffer(size: Int, initializer: (Int) -> T): Buffer<T> {
public fun <T> BufferAlgebra<T, *>.buffer(size: Int, initializer: (Int) -> T): MutableBuffer<T> {
return elementBufferFactory(size, initializer)
}
public fun <T, A> A.buffer(initializer: (Int) -> T): Buffer<T> where A : BufferAlgebra<T, *>, A : WithSize {
public fun <T, A> A.buffer(initializer: (Int) -> T): MutableBuffer<T> where A : BufferAlgebra<T, *>, A : WithSize {
return elementBufferFactory(size, initializer)
}

View File

@ -33,7 +33,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
protected val multikStat: Statistics = multikEngine.getStatistics()
@OptIn(UnsafeKMathAPI::class)
override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): MultikTensor<T> {
override fun mutableStructureND(shape: ShapeND, initializer: A.(IntArray) -> T): MultikTensor<T> {
val strides = ColumnStrides(shape)
val memoryView = initMemoryView<T>(strides.linearSize, type)
strides.asSequence().forEachIndexed { linearIndex, tensorIndex ->
@ -49,7 +49,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
for (el in array) data[count++] = elementAlgebra.transform(el)
NDArray(data, shape = shape.asArray(), dim = array.dim).wrap()
} else {
structureND(shape) { index ->
mutableStructureND(shape) { index ->
transform(get(index))
}
}
@ -70,7 +70,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
}
NDArray(data, shape = array.shape, dim = array.dim).wrap()
} else {
structureND(shape) { index ->
mutableStructureND(shape) { index ->
transform(index, get(index))
}
}

View File

@ -34,7 +34,7 @@ public sealed interface Nd4jArrayAlgebra<T, out C : Algebra<T>> : AlgebraND<T, C
public val StructureND<T>.ndArray: INDArray
@OptIn(PerformancePitfall::class)
override fun structureND(shape: ShapeND, initializer: C.(IntArray) -> T): Nd4jArrayStructure<T> {
override fun mutableStructureND(shape: ShapeND, initializer: C.(IntArray) -> T): Nd4jArrayStructure<T> {
@OptIn(UnsafeKMathAPI::class)
val struct: Nd4jArrayStructure<T> = Nd4j.create(*shape.asArray())!!.wrap()
struct.indicesIterator().forEach { struct[it] = elementAlgebra.initializer(it) }

View File

@ -37,20 +37,20 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
*/
public val StructureND<T>.ndArray: INDArray
override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): Nd4jArrayStructure<T>
override fun mutableStructureND(shape: ShapeND, initializer: A.(IntArray) -> T): Nd4jArrayStructure<T>
@OptIn(PerformancePitfall::class)
override fun StructureND<T>.map(transform: A.(T) -> T): Nd4jArrayStructure<T> =
structureND(shape) { index -> elementAlgebra.transform(get(index)) }
mutableStructureND(shape) { index -> elementAlgebra.transform(get(index)) }
@OptIn(PerformancePitfall::class)
override fun StructureND<T>.mapIndexed(transform: A.(index: IntArray, T) -> T): Nd4jArrayStructure<T> =
structureND(shape) { index -> elementAlgebra.transform(index, get(index)) }
mutableStructureND(shape) { index -> elementAlgebra.transform(index, get(index)) }
@OptIn(PerformancePitfall::class)
override fun zip(left: StructureND<T>, right: StructureND<T>, transform: A.(T, T) -> T): Nd4jArrayStructure<T> {
require(left.shape.contentEquals(right.shape))
return structureND(left.shape) { index -> elementAlgebra.transform(left[index], right[index]) }
return mutableStructureND(left.shape) { index -> elementAlgebra.transform(left[index], right[index]) }
}
override fun T.plus(arg: StructureND<T>): Nd4jArrayStructure<T> = arg.ndArray.add(this).wrap()
@ -178,7 +178,7 @@ public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra<Double, DoubleField> {
override fun INDArray.wrap(): Nd4jArrayStructure<Double> = asDoubleStructure()
@OptIn(UnsafeKMathAPI::class)
override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): Nd4jArrayStructure<Double> {
override fun mutableStructureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): Nd4jArrayStructure<Double> {
val array: INDArray = Nd4j.zeros(*shape.asArray())
val indices = ColumnStrides(shape)
indices.asSequence().forEach { index ->

View File

@ -14,6 +14,7 @@ import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.nd.ColumnStrides
import space.kscience.kmath.nd.MutableStructureND
import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.DoubleField
@ -36,10 +37,10 @@ public class DoubleTensorFlowAlgebra internal constructor(
override val elementAlgebra: DoubleField get() = DoubleField
override fun structureND(
override fun mutableStructureND(
shape: ShapeND,
initializer: DoubleField.(IntArray) -> Double,
): StructureND<Double> {
): MutableStructureND<Double> {
val res = TFloat64.tensorOf(org.tensorflow.ndarray.Shape.of(*shape.toLongArray())) { array ->
ColumnStrides(shape).forEach { index ->
array.setDouble(elementAlgebra.initializer(index), *index.toLongArray())

View File

@ -15,12 +15,12 @@ kscience{
kotlin.sourceSets {
all {
languageSettings.optIn("space.kscience.kmath.misc.UnstableKMathAPI")
languageSettings.optIn("space.kscience.kmath.UnstableKMathAPI")
}
filter { it.name.contains("test", true) }
.map(org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet::languageSettings)
.forEach { it.optIn("space.kscience.kmath.misc.PerformancePitfall") }
.forEach { it.optIn("space.kscience.kmath.PerformancePitfall") }
commonMain {
dependencies {

View File

@ -127,7 +127,7 @@ public open class DoubleTensorAlgebra :
* @param initializer mapping tensor indices to values.
* @return tensor with the [shape] shape and data generated by the [initializer].
*/
override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): DoubleTensor = fromArray(
override fun mutableStructureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): DoubleTensor = fromArray(
shape,
RowStrides(shape).asSequence().map { DoubleField.initializer(it) }.toMutableList().toDoubleArray()
)

View File

@ -118,7 +118,7 @@ public open class IntTensorAlgebra : TensorAlgebra<Int, IntRing> {
* @param initializer mapping tensor indices to values.
* @return tensor with the [shape] shape and data generated by the [initializer].
*/
override fun structureND(shape: ShapeND, initializer: IntRing.(IntArray) -> Int): IntTensor = fromArray(
override fun mutableStructureND(shape: ShapeND, initializer: IntRing.(IntArray) -> Int): IntTensor = fromArray(
shape,
RowStrides(shape).asSequence().map { IntRing.initializer(it) }.toMutableList().toIntArray()
)

View File

@ -92,7 +92,7 @@ internal class TestDoubleTensor {
@Test
fun test2D() = with(DoubleTensorAlgebra) {
val tensor: DoubleTensor = structureND(ShapeND(3, 3)) { (i, j) -> (i - j).toDouble() }
val tensor: DoubleTensor = mutableStructureND(ShapeND(3, 3)) { (i, j) -> (i - j).toDouble() }
//println(tensor.toPrettyString())
val tensor2d = tensor.asDoubleTensor2D()
assertBufferEquals(DoubleBuffer(1.0, 0.0, -1.0), tensor2d.rows[1])
@ -101,7 +101,7 @@ internal class TestDoubleTensor {
@Test
fun testMatrixIteration() = with(DoubleTensorAlgebra) {
val tensor = structureND(ShapeND(3, 3, 3, 3)) { index -> index.sum().toDouble() }
val tensor = mutableStructureND(ShapeND(3, 3, 3, 3)) { index -> index.sum().toDouble() }
tensor.forEachMatrix { index, matrix ->
println(index.joinToString { it.toString() })
println(matrix)

View File

@ -17,8 +17,7 @@ import space.kscience.kmath.operations.ExtendedFieldOps
import space.kscience.kmath.operations.NumbersAddOps
import space.kscience.kmath.operations.PowerOperations
@OptIn(UnstableKMathAPI::class, PerformancePitfall::class)
@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
@OptIn(PerformancePitfall::class)
public open class ViktorFieldOpsND :
FieldOpsND<Double, DoubleField>,
ExtendedFieldOps<StructureND<Double>>,
@ -27,13 +26,13 @@ public open class ViktorFieldOpsND :
public val StructureND<Double>.f64Buffer: F64Array
get() = when (this) {
is ViktorStructureND -> this.f64Buffer
else -> structureND(shape) { this@f64Buffer[it] }.f64Buffer
else -> mutableStructureND(shape) { this@f64Buffer[it] }.f64Buffer
}
override val elementAlgebra: DoubleField get() = DoubleField
@OptIn(UnsafeKMathAPI::class)
override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND =
override fun mutableStructureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND =
F64Array(*shape.asArray()).apply {
ColumnStrides(shape).asSequence().forEach { index ->
set(value = DoubleField.initializer(index), indices = index)