From 2f2f5526488b0606832fdb50e143ea8202583329 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 11 Nov 2023 10:19:09 +0300 Subject: [PATCH] 0.4 WIP --- .../space/kscience/attributes/Attribute.kt | 2 - .../space/kscience/attributes/Attributes.kt | 6 +- .../kscience/attributes/AttributesBuilder.kt | 2 +- .../kscience/kmath/linear/LinearSpace.kt | 35 ++++++++++-- .../kscience/kmath/linear/LupDecomposition.kt | 4 +- .../kscience/kmath/ejml/EjmlLinearSpace.kt | 2 +- .../space/kscience/kmath/ejml/_generated.kt | 56 +++++++++---------- .../kscience/kmath/ejml/EjmlMatrixTest.kt | 4 +- .../space/kscience/kmath/samplers/Sampler.kt | 17 ++---- .../kscience/kmath/samplers/SamplerAlgebra.kt | 3 + .../kscience/kmath/stat/ValueAndErrorField.kt | 48 ++++++++++++++++ 11 files changed, 121 insertions(+), 58 deletions(-) diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attribute.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attribute.kt index 6fa142180..a507cd698 100644 --- a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attribute.kt +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attribute.kt @@ -5,8 +5,6 @@ package space.kscience.attributes -import kotlin.reflect.KType - public interface Attribute /** diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt index a1bccc211..b50436dd2 100644 --- a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt @@ -8,7 +8,7 @@ package space.kscience.attributes import kotlin.jvm.JvmInline @JvmInline -public value class Attributes internal constructor(public val content: Map, Any>) { +public value class Attributes internal constructor(public val content: Map, Any?>) { public val keys: Set> get() = content.keys @@ -51,7 +51,7 @@ public inline fun Attributes.has(): Boolean = /** * Create [Attributes] with an added or replaced attribute key. */ -public fun > Attributes.withAttribute( +public fun > Attributes.withAttribute( attribute: A, attrValue: T, ): Attributes = Attributes(content + (attribute to attrValue)) @@ -101,7 +101,7 @@ public fun > Attributes.withoutAttributeElement( /** * Create [Attributes] with a single key */ -public fun > Attributes( +public fun > Attributes( attribute: A, attrValue: T, ): Attributes = Attributes(mapOf(attribute to attrValue)) diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributesBuilder.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributesBuilder.kt index 79df91c3e..6d74b90c1 100644 --- a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributesBuilder.kt +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributesBuilder.kt @@ -10,7 +10,7 @@ package space.kscience.attributes * * @param O type marker of an owner object, for which these attributes are made */ -public class TypedAttributesBuilder internal constructor(private val map: MutableMap, Any>) { +public class TypedAttributesBuilder internal constructor(private val map: MutableMap, Any?>) { public constructor() : this(mutableMapOf()) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt index 00ebad5ee..6b547e2c5 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt @@ -5,8 +5,10 @@ package space.kscience.kmath.linear +import space.kscience.attributes.Attributes import space.kscience.attributes.SafeType import space.kscience.attributes.WithType +import space.kscience.attributes.withAttribute import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.* import space.kscience.kmath.operations.BufferRingOps @@ -173,15 +175,36 @@ public interface LinearSpace> : MatrixOperations { public operator fun T.times(v: Point): Point = v * this /** - * Get an attribute value for the structure in this scope. Structure attributes are preferred to computed attributes. + * Compute an [attribute] value for given [structure]. Return null if the attribute could not be computed. + */ + public fun > computeAttribute(structure: StructureND<*>, attribute: A): V? = null + + @UnstableKMathAPI + public fun > StructureND<*>.getOrComputeAttribute(attribute: A): V? { + return attributes[attribute] ?: computeAttribute(this, attribute) + } + + /** + * If the structure holds given [attribute] return itself. Otherwise, return a new [Matrix] that contains a computed attribute. * - * @param structure the structure. - * @param attribute to be computed. - * @return a feature object or `null` if it isn't present. + * This method is used to compute and cache attribute inside the structure. If one needs an attribute only once, + * better use [StructureND.getOrComputeAttribute]. */ @UnstableKMathAPI - public fun > attributeFor(structure: StructureND<*>, attribute: A): T = - structure.attributes[attribute] ?: error("Can't compute attribute $attribute for $structure") + public fun > Matrix.compute( + attribute: A, + ): Matrix? { + return if (attributes[attribute] != null) { + this + } else { + val value = computeAttribute(this, attribute) ?: return null + if (this is MatrixWrapper) { + MatrixWrapper(this, attributes.withAttribute(attribute, value)) + } else { + MatrixWrapper(this, Attributes(attribute, value)) + } + } + } public companion object { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt index 6592a3da4..fab4ef3db 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt @@ -209,8 +209,8 @@ public fun , F : Field> LinearSpace.lupSolver( singularityCheck: (T) -> Boolean, ): LinearSolver = object : LinearSolver { override fun solve(a: Matrix, b: Matrix): Matrix { - // Use existing decomposition if it is provided by matrix - val decomposition = attributeFor(a, LUP) ?: lup(a, singularityCheck) + // Use existing decomposition if it is provided by matrix or linear space itself + val decomposition = a.getOrComputeAttribute(LUP) ?: lup(a, singularityCheck) return solve(decomposition, b) } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt index a8f316650..37b14f8d1 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt @@ -39,5 +39,5 @@ public abstract class EjmlLinearSpace, out M : org.ejml @UnstableKMathAPI public fun EjmlMatrix.inverted(): Matrix = - attributeFor(this, Float64Field.linearSpace.Inverted) + attributeForOrNull(this, Float64Field.linearSpace.Inverted) } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt index bf797fed4..881649d01 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt @@ -131,10 +131,10 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace.plus(other: Matrix): EjmlDoubleMatrix { val out = DMatrixRMaj(1, 1) - + CommonOps_DDRM.add( elementAlgebra.one, toEjml().origin, elementAlgebra.one, - other.toEjml().origin, + other.toEjml().origin, out, ) @@ -369,10 +369,10 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace.plus(other: Matrix): EjmlFloatMatrix { val out = FMatrixRMaj(1, 1) - + CommonOps_FDRM.add( elementAlgebra.one, toEjml().origin, elementAlgebra.one, - other.toEjml().origin, + other.toEjml().origin, out, ) @@ -607,12 +607,12 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.plus(other: Matrix): EjmlDoubleMatrix { val out = DMatrixSparseCSC(1, 1) - + CommonOps_DSCC.add( elementAlgebra.one, toEjml().origin, elementAlgebra.one, - other.toEjml().origin, + other.toEjml().origin, out, - null, + null, null, ) @@ -656,7 +656,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.plus(other: Matrix): EjmlFloatMatrix { val out = FMatrixSparseCSC(1, 1) - + CommonOps_FSCC.add( elementAlgebra.one, toEjml().origin, elementAlgebra.one, - other.toEjml().origin, + other.toEjml().origin, out, - null, + null, null, ) @@ -889,7 +889,7 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace = EjmlLinearSpaceDDRM.attributeFor(w) ?: fail() + val det: Determinant = EjmlLinearSpaceDDRM.attributeForOrNull(w) ?: fail() assertEquals(CommonOps_DDRM.det(m), det.determinant) - val lup: LupDecompositionAttribute = EjmlLinearSpaceDDRM.attributeFor(w) ?: fail() + val lup: LupDecompositionAttribute = EjmlLinearSpaceDDRM.attributeForOrNull(w) ?: fail() val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows, m.numCols) .also { it.decompose(m.copy()) } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt index 34355dca7..c064641a9 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt @@ -12,8 +12,6 @@ import space.kscience.kmath.chains.combine import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory -import space.kscience.kmath.structures.Float64Buffer -import space.kscience.kmath.structures.Int32Buffer import kotlin.jvm.JvmName /** @@ -36,7 +34,7 @@ public fun interface Sampler { public fun Sampler.sampleBuffer( generator: RandomGenerator, size: Int, - bufferFactory: BufferFactory = BufferFactory.boxing(), + bufferFactory: BufferFactory ): Chain> { require(size > 1) //creating temporary storage once @@ -58,18 +56,11 @@ public fun Sampler.sampleBuffer( public suspend fun Sampler.next(generator: RandomGenerator): T = sample(generator).first() /** - * Generates [size] real samples and chunks them into some buffers. + * Generates [size] samples and chunks them into some buffers. */ @JvmName("sampleRealBuffer") -public fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = - sampleBuffer(generator, size, ::Float64Buffer) - -/** - * Generates [size] integer samples and chunks them into some buffers. - */ -@JvmName("sampleIntBuffer") -public fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = - sampleBuffer(generator, size, ::Int32Buffer) +public inline fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = + sampleBuffer(generator, size, BufferFactory()) /** diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/SamplerAlgebra.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/SamplerAlgebra.kt index 44b87a431..e23b5928e 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/SamplerAlgebra.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/SamplerAlgebra.kt @@ -14,6 +14,7 @@ import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.invoke import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.structures.MutableBufferFactory /** * Implements [Sampler] by sampling only certain [value]. @@ -41,6 +42,8 @@ public class BasicSampler(public val chainBuilder: (RandomGenerator public class SamplerSpace(public val algebra: S) : Group>, ScaleOperations> where S : Group, S : ScaleOperations { + override val bufferFactory: MutableBufferFactory> = MutableBufferFactory() + override val zero: Sampler = ConstantSampler(algebra.zero) override fun add(left: Sampler, right: Sampler): Sampler = BasicSampler { generator -> diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/ValueAndErrorField.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/ValueAndErrorField.kt index 38cd5f900..53df87947 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/ValueAndErrorField.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/ValueAndErrorField.kt @@ -5,10 +5,14 @@ package space.kscience.kmath.stat +import space.kscience.attributes.SafeType +import space.kscience.attributes.safeTypeOf import space.kscience.kmath.operations.Field +import space.kscience.kmath.structures.* import kotlin.math.pow import kotlin.math.sqrt + /** * A combination of a random [value] and its [dispersion]. * @@ -53,4 +57,48 @@ public object ValueAndErrorField : Field { override fun scale(a: ValueAndError, value: Double): ValueAndError = ValueAndError(a.value * value, a.dispersion * value.pow(2)) + + + private class ValueAndErrorBuffer(val values: DoubleBuffer, val ds: DoubleBuffer) : MutableBuffer { + init { + require(values.size == ds.size) + } + + override val type: SafeType get() = safeTypeOf() + override val size: Int + get() = values.size + + override fun get(index: Int): ValueAndError = ValueAndError(values[index], ds[index]) + + override fun toString(): String = Buffer.toString(this) + + override fun set(index: Int, value: ValueAndError) { + values[index] = value.value + values[index] = value.dispersion + } + + override fun copy(): MutableBuffer = ValueAndErrorBuffer(values.copy(), ds.copy()) + } + + override val bufferFactory: MutableBufferFactory = object : MutableBufferFactory { + override fun invoke( + size: Int, + builder: (Int) -> ValueAndError, + ): MutableBuffer { + val values: DoubleArray = DoubleArray(size) + val ds = DoubleArray(size) + repeat(size){ + val (v, d) = builder(it) + values[it] = v + ds[it] = d + } + return ValueAndErrorBuffer( + values.asBuffer(), + ds.asBuffer() + ) + } + + override val type: SafeType get() = safeTypeOf() + + } } \ No newline at end of file