This commit is contained in:
Alexander Nozik 2023-11-11 10:19:09 +03:00
parent 2386ecba41
commit 2f2f552648
11 changed files with 121 additions and 58 deletions

View File

@ -5,8 +5,6 @@
package space.kscience.attributes package space.kscience.attributes
import kotlin.reflect.KType
public interface Attribute<T> public interface Attribute<T>
/** /**

View File

@ -8,7 +8,7 @@ package space.kscience.attributes
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@JvmInline @JvmInline
public value class Attributes internal constructor(public val content: Map<out Attribute<*>, Any>) { public value class Attributes internal constructor(public val content: Map<out Attribute<*>, Any?>) {
public val keys: Set<Attribute<*>> get() = content.keys public val keys: Set<Attribute<*>> get() = content.keys
@ -51,7 +51,7 @@ public inline fun <reified A : FlagAttribute> Attributes.has(): Boolean =
/** /**
* Create [Attributes] with an added or replaced attribute key. * Create [Attributes] with an added or replaced attribute key.
*/ */
public fun <T : Any, A : Attribute<T>> Attributes.withAttribute( public fun <T, A : Attribute<T>> Attributes.withAttribute(
attribute: A, attribute: A,
attrValue: T, attrValue: T,
): Attributes = Attributes(content + (attribute to attrValue)) ): Attributes = Attributes(content + (attribute to attrValue))
@ -101,7 +101,7 @@ public fun <T, A : SetAttribute<T>> Attributes.withoutAttributeElement(
/** /**
* Create [Attributes] with a single key * Create [Attributes] with a single key
*/ */
public fun <T : Any, A : Attribute<T>> Attributes( public fun <T, A : Attribute<T>> Attributes(
attribute: A, attribute: A,
attrValue: T, attrValue: T,
): Attributes = Attributes(mapOf(attribute to attrValue)) ): Attributes = Attributes(mapOf(attribute to attrValue))

View File

@ -10,7 +10,7 @@ package space.kscience.attributes
* *
* @param O type marker of an owner object, for which these attributes are made * @param O type marker of an owner object, for which these attributes are made
*/ */
public class TypedAttributesBuilder<in O> internal constructor(private val map: MutableMap<Attribute<*>, Any>) { public class TypedAttributesBuilder<in O> internal constructor(private val map: MutableMap<Attribute<*>, Any?>) {
public constructor() : this(mutableMapOf()) public constructor() : this(mutableMapOf())

View File

@ -5,8 +5,10 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.attributes.Attributes
import space.kscience.attributes.SafeType import space.kscience.attributes.SafeType
import space.kscience.attributes.WithType import space.kscience.attributes.WithType
import space.kscience.attributes.withAttribute
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.nd.* import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.BufferRingOps import space.kscience.kmath.operations.BufferRingOps
@ -173,15 +175,36 @@ public interface LinearSpace<T, out A : Ring<T>> : MatrixOperations<T> {
public operator fun T.times(v: Point<T>): Point<T> = v * this public operator fun T.times(v: Point<T>): Point<T> = 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 <V, A : StructureAttribute<V>> computeAttribute(structure: StructureND<*>, attribute: A): V? = null
@UnstableKMathAPI
public fun <V, A : StructureAttribute<V>> 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. * This method is used to compute and cache attribute inside the structure. If one needs an attribute only once,
* @param attribute to be computed. * better use [StructureND.getOrComputeAttribute].
* @return a feature object or `null` if it isn't present.
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public fun <T, A : StructureAttribute<T>> attributeFor(structure: StructureND<*>, attribute: A): T = public fun <V : Any, A : StructureAttribute<V>> Matrix<T>.compute(
structure.attributes[attribute] ?: error("Can't compute attribute $attribute for $structure") attribute: A,
): Matrix<T>? {
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 { public companion object {

View File

@ -209,8 +209,8 @@ public fun <T : Comparable<T>, F : Field<T>> LinearSpace<T, F>.lupSolver(
singularityCheck: (T) -> Boolean, singularityCheck: (T) -> Boolean,
): LinearSolver<T> = object : LinearSolver<T> { ): LinearSolver<T> = object : LinearSolver<T> {
override fun solve(a: Matrix<T>, b: Matrix<T>): Matrix<T> { override fun solve(a: Matrix<T>, b: Matrix<T>): Matrix<T> {
// Use existing decomposition if it is provided by matrix // Use existing decomposition if it is provided by matrix or linear space itself
val decomposition = attributeFor(a, LUP) ?: lup(a, singularityCheck) val decomposition = a.getOrComputeAttribute(LUP) ?: lup(a, singularityCheck)
return solve(decomposition, b) return solve(decomposition, b)
} }

View File

@ -39,5 +39,5 @@ public abstract class EjmlLinearSpace<T : Any, out A : Ring<T>, out M : org.ejml
@UnstableKMathAPI @UnstableKMathAPI
public fun EjmlMatrix<T, *>.inverted(): Matrix<Double> = public fun EjmlMatrix<T, *>.inverted(): Matrix<Double> =
attributeFor(this, Float64Field.linearSpace.Inverted) attributeForOrNull(this, Float64Field.linearSpace.Inverted)
} }

View File

@ -61,9 +61,9 @@ internal class EjmlMatrixTest {
fun features() { fun features() {
val m = randomMatrix val m = randomMatrix
val w = EjmlDoubleMatrix(m) val w = EjmlDoubleMatrix(m)
val det: Determinant<Double> = EjmlLinearSpaceDDRM.attributeFor(w) ?: fail() val det: Determinant<Double> = EjmlLinearSpaceDDRM.attributeForOrNull(w) ?: fail()
assertEquals(CommonOps_DDRM.det(m), det.determinant) assertEquals(CommonOps_DDRM.det(m), det.determinant)
val lup: LupDecompositionAttribute<Double> = EjmlLinearSpaceDDRM.attributeFor(w) ?: fail() val lup: LupDecompositionAttribute<Double> = EjmlLinearSpaceDDRM.attributeForOrNull(w) ?: fail()
val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows, m.numCols) val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows, m.numCols)
.also { it.decompose(m.copy()) } .also { it.decompose(m.copy()) }

View File

@ -12,8 +12,6 @@ import space.kscience.kmath.chains.combine
import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.Float64Buffer
import space.kscience.kmath.structures.Int32Buffer
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
/** /**
@ -36,7 +34,7 @@ public fun interface Sampler<out T : Any> {
public fun <T : Any> Sampler<T>.sampleBuffer( public fun <T : Any> Sampler<T>.sampleBuffer(
generator: RandomGenerator, generator: RandomGenerator,
size: Int, size: Int,
bufferFactory: BufferFactory<T> = BufferFactory.boxing(), bufferFactory: BufferFactory<T>
): Chain<Buffer<T>> { ): Chain<Buffer<T>> {
require(size > 1) require(size > 1)
//creating temporary storage once //creating temporary storage once
@ -58,18 +56,11 @@ public fun <T : Any> Sampler<T>.sampleBuffer(
public suspend fun <T : Any> Sampler<T>.next(generator: RandomGenerator): T = sample(generator).first() public suspend fun <T : Any> Sampler<T>.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") @JvmName("sampleRealBuffer")
public fun Sampler<Double>.sampleBuffer(generator: RandomGenerator, size: Int): Chain<Buffer<Double>> = public inline fun <reified T:Any> Sampler<T>.sampleBuffer(generator: RandomGenerator, size: Int): Chain<Buffer<T>> =
sampleBuffer(generator, size, ::Float64Buffer) sampleBuffer(generator, size, BufferFactory())
/**
* Generates [size] integer samples and chunks them into some buffers.
*/
@JvmName("sampleIntBuffer")
public fun Sampler<Int>.sampleBuffer(generator: RandomGenerator, size: Int): Chain<Buffer<Int>> =
sampleBuffer(generator, size, ::Int32Buffer)
/** /**

View File

@ -14,6 +14,7 @@ import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.stat.Sampler import space.kscience.kmath.stat.Sampler
import space.kscience.kmath.structures.MutableBufferFactory
/** /**
* Implements [Sampler] by sampling only certain [value]. * Implements [Sampler] by sampling only certain [value].
@ -41,6 +42,8 @@ public class BasicSampler<out T : Any>(public val chainBuilder: (RandomGenerator
public class SamplerSpace<T : Any, out S>(public val algebra: S) : Group<Sampler<T>>, public class SamplerSpace<T : Any, out S>(public val algebra: S) : Group<Sampler<T>>,
ScaleOperations<Sampler<T>> where S : Group<T>, S : ScaleOperations<T> { ScaleOperations<Sampler<T>> where S : Group<T>, S : ScaleOperations<T> {
override val bufferFactory: MutableBufferFactory<Sampler<T>> = MutableBufferFactory()
override val zero: Sampler<T> = ConstantSampler(algebra.zero) override val zero: Sampler<T> = ConstantSampler(algebra.zero)
override fun add(left: Sampler<T>, right: Sampler<T>): Sampler<T> = BasicSampler { generator -> override fun add(left: Sampler<T>, right: Sampler<T>): Sampler<T> = BasicSampler { generator ->

View File

@ -5,10 +5,14 @@
package space.kscience.kmath.stat 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.operations.Field
import space.kscience.kmath.structures.*
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.sqrt import kotlin.math.sqrt
/** /**
* A combination of a random [value] and its [dispersion]. * A combination of a random [value] and its [dispersion].
* *
@ -53,4 +57,48 @@ public object ValueAndErrorField : Field<ValueAndError> {
override fun scale(a: ValueAndError, value: Double): ValueAndError = override fun scale(a: ValueAndError, value: Double): ValueAndError =
ValueAndError(a.value * value, a.dispersion * value.pow(2)) ValueAndError(a.value * value, a.dispersion * value.pow(2))
private class ValueAndErrorBuffer(val values: DoubleBuffer, val ds: DoubleBuffer) : MutableBuffer<ValueAndError> {
init {
require(values.size == ds.size)
}
override val type: SafeType<ValueAndError> 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<ValueAndError> = ValueAndErrorBuffer(values.copy(), ds.copy())
}
override val bufferFactory: MutableBufferFactory<ValueAndError> = object : MutableBufferFactory<ValueAndError> {
override fun invoke(
size: Int,
builder: (Int) -> ValueAndError,
): MutableBuffer<ValueAndError> {
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<ValueAndError> get() = safeTypeOf()
}
} }