[WIP] Features to Attributes refactoring

This commit is contained in:
Alexander Nozik 2023-07-18 10:13:36 +03:00
parent 6da51b7794
commit 4abe25c188
24 changed files with 84 additions and 86 deletions

View File

@ -8,6 +8,7 @@
### Changed
- Features replaced with Attributes.
- Transposed refactored.
### Deprecated

View File

@ -5,6 +5,7 @@
package space.kscience.attributes
import kotlin.jvm.JvmInline
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.typeOf
@ -14,7 +15,8 @@ import kotlin.reflect.typeOf
*
* @param kType raw [KType]
*/
public class SafeType<T> @PublishedApi internal constructor(public val kType: KType)
@JvmInline
public value class SafeType<T> @PublishedApi internal constructor(public val kType: KType)
public inline fun <reified T> safeTypeOf(): SafeType<T> = SafeType(typeOf<T>())

View File

@ -15,7 +15,7 @@ private fun DMatrixContext<Double, *>.simple() {
val m2 = produce<D3, D2> { i, j -> (i + j).toDouble() }
//Dimension-safe addition
m1.transpose() + m2
m1.transposed() + m2
}
private object D5 : Dimension {

View File

@ -125,7 +125,7 @@ public object CMOptimizer : Optimizer<Double, FunctionOptimization<Double>> {
val logger = problem.getFeature<OptimizationLog>()
for (feature in problem.features) {
for (feature in problem.attributes) {
when (feature) {
is CMOptimizerData -> feature.data.forEach { dataBuilder ->
addOptimizationData(dataBuilder())

View File

@ -174,9 +174,8 @@ public interface LinearSpace<T, out A : Ring<T>> : MatrixOperations<T> {
/**
* Get an attribute value for the structure in this scope. Structure features take precedence other context features.
*
* @param A the type of feature.
* @param structure the structure.
* @param attribute to be computed
* @param attribute to be computed.
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI

View File

@ -21,12 +21,11 @@ import space.kscience.kmath.structures.*
* @param T the type of matrices' items.
* @param l The lower triangular matrix in this decomposition. It may have [LowerTriangular].
* @param u The upper triangular matrix in this decomposition. It may have [UpperTriangular].
* @param p he permutation matrix in this decomposition. May have [Determinant] attribute
*/
public data class LupDecomposition<T>(
public val l: Matrix<T>,
public val u: Matrix<T>,
public val p: Matrix<T>,
public val pivot: IntBuffer,
)
@ -180,12 +179,12 @@ public fun <T : Comparable<T>> LinearSpace<T, Field<T>>.lup(
val u = VirtualMatrix(rowNum, colNum) { i, j ->
if (j >= i) lu[i, j] else zero
}.withAttribute(UpperTriangular)
//
// val p = VirtualMatrix(rowNum, colNum) { i, j ->
// if (j == pivot[i]) one else zero
// }.withAttribute(Determinant, if (even) one else -one)
val p = VirtualMatrix(rowNum, colNum) { i, j ->
if (j == pivot[i]) one else zero
}.withAttribute(Determinant, if (even) one else -one)
return LupDecomposition(l, u, p)
return LupDecomposition(l, u, pivot.asBuffer())
}
}
}
@ -209,7 +208,7 @@ internal fun <T : Any, A : Field<T>> LinearSpace<T, A>.solve(
for (row in 0 until rowNum) {
val bpRow = bp.row(row)
val pRow = pivot[row]
val pRow = lup.pivot[row]
for (col in 0 until matrix.colNum) bpRow[col] = matrix[pRow, col]
}

View File

@ -11,8 +11,6 @@ package space.kscience.kmath.linear
import space.kscience.attributes.*
import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.nd.StructureAttribute
import kotlin.reflect.KType
import kotlin.reflect.typeOf
/**
* A marker interface representing some properties of matrices or additional transformations of them. Features are used
@ -53,11 +51,9 @@ public val <T> MatrixOperations<T>.Inverted: Inverted<T> get() = Inverted(safeTy
*
* @param T the type of matrices' items.
*/
public class Determinant<T>(type: SafeType<T>) :
PolymorphicAttribute<T>(type),
MatrixAttribute<T>
public class Determinant<T> : MatrixAttribute<T>
public inline val <reified T> MatrixOperations<T>.Determinant: Determinant<T> get() = Determinant(safeTypeOf())
public val <T> MatrixOperations<T>.Determinant: Determinant<T> get() = Determinant()
/**
* Matrices with this feature are lower triangular ones.
@ -174,4 +170,5 @@ public class SingularValueDecompositionAttribute<T>(type: SafeType<SingularValue
public val <T> MatrixOperations<T>.SVD: SingularValueDecompositionAttribute<T>
get() = SingularValueDecompositionAttribute(safeTypeOf())
//TODO add sparse matrix feature

View File

@ -82,13 +82,3 @@ public fun <T : Any> LinearSpace<T, Ring<T>>.zero(
): MatrixWrapper<T> = VirtualMatrix(rows, columns) { _, _ ->
elementAlgebra.zero
}.withAttribute(IsZero)
public class TransposedAttribute<out T : Any>(public val original: Matrix<T>) : MatrixAttribute
/**
* Create a virtual transposed matrix without copying anything. `A.transpose().transpose() === A`
*/
@Suppress("UNCHECKED_CAST")
@OptIn(UnstableKMathAPI::class)
public fun <T : Any> Matrix<T>.transpose(): Matrix<T> = getFeature(TransposedAttribute::class)?.original as? Matrix<T>
?: VirtualMatrix(colNum, rowNum) { i, j -> get(j, i) }.withAttribute(TransposedAttribute(this))

View File

@ -0,0 +1,25 @@
/*
* 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.linear
import space.kscience.attributes.Attributes
public class TransposedMatrix<T>(public val origin: Matrix<T>) : Matrix<T> {
override val rowNum: Int get() = origin.colNum
override val colNum: Int get() = origin.rowNum
override fun get(i: Int, j: Int): T = origin[j, i]
override val attributes: Attributes get() = Attributes.EMPTY
}
/**
* Create a virtual transposed matrix without copying anything. `A.transpose().transpose() === A`
*/
public val <T> Matrix<T>.transposed: Matrix<T>
get() = (this as? TransposedMatrix<T>)?.origin ?: TransposedMatrix(this)

View File

@ -29,5 +29,4 @@ public class VirtualMatrix<out T : Any>(
public fun <T : Any> MatrixBuilder<T, *>.virtual(
attributes: Attributes = Attributes.EMPTY,
generator: (i: Int, j: Int) -> T,
): VirtualMatrix<T> =
VirtualMatrix(rows, columns, attributes, generator)
): VirtualMatrix<T> = VirtualMatrix(rows, columns, attributes, generator)

View File

@ -71,31 +71,19 @@ public interface AlgebraND<T, out C : Algebra<T>> : Algebra<StructureND<T>> {
structure.map { value -> this@invoke(value) }
/**
* Get a feature of the structure in this scope. Structure features take precedence other context features.
* Get an attribute value for the structure in this scope. Structure features take precedence other context features.
*
* @param F the type of feature.
* @param structure the structure.
* @param type the [KClass] instance of [F].
* @param attribute to be computed.
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI
public fun <F : StructureAttribute> getFeature(structure: StructureND<T>, type: KClass<out F>): F? =
structure.getFeature(type)
public fun <T, A : StructureAttribute<T>> attributeFor(structure: StructureND<*>, attribute: A): T? =
structure.attributes[attribute]
public companion object
}
/**
* Get a feature of the structure in this scope. Structure features take precedence other context features.
*
* @param T the type of items in the matrices.
* @param F the type of feature.
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI
public inline fun <T : Any, reified F : StructureAttribute> AlgebraND<T, *>.getFeature(structure: StructureND<T>): F? =
getFeature(structure, F::class)
/**
* Space of [StructureND].
*

View File

@ -36,8 +36,8 @@ public open class BufferND<out T>(
*/
public inline fun <reified T> BufferND(
shape: ShapeND,
bufferFactory: BufferFactory<T> = BufferFactory.auto(),
initializer: (IntArray) -> T,
bufferFactory: BufferFactory<T> = BufferFactory.auto<T>(),
crossinline initializer: (IntArray) -> T,
): BufferND<T> {
val strides = Strides(shape)
return BufferND(strides, bufferFactory(strides.linearSize) { initializer(strides.index(it)) })

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.nd
import space.kscience.attributes.Attributes
import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBuffer
@ -110,7 +111,8 @@ private value class Structure2DWrapper<out T>(val structure: StructureND<T>) : S
@PerformancePitfall
override operator fun get(i: Int, j: Int): T = structure[i, j]
override fun <F : StructureAttribute> getFeature(type: KClass<out F>): F? = structure.getFeature(type)
override val attributes: Attributes
get() = structure.attributes
@PerformancePitfall
override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements()

View File

@ -7,13 +7,13 @@ package space.kscience.kmath.nd
import space.kscience.attributes.Attribute
import space.kscience.attributes.AttributeContainer
import space.kscience.attributes.Attributes
import space.kscience.attributes.SafeType
import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import kotlin.jvm.JvmName
import kotlin.math.abs
@ -57,6 +57,8 @@ public interface StructureND<out T> : AttributeContainer, WithShape {
@PerformancePitfall
public fun elements(): Sequence<Pair<IntArray, T>> = indices.asSequence().map { it to get(it) }
override val attributes: Attributes get() = Attributes.EMPTY
public companion object {
/**
* Indicates whether some [StructureND] is equal to another one.

View File

@ -20,8 +20,6 @@ public interface WithSize {
public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
public val elementAlgebra: A
public val elementType: KType
public val elementBufferFactory: MutableBufferFactory<T> get() = elementAlgebra.bufferFactory
public fun buffer(size: Int, vararg elements: T): Buffer<T> {

View File

@ -21,7 +21,7 @@ public fun interface BufferFactory<T> {
public operator fun invoke(size: Int, builder: (Int) -> T): Buffer<T>
public companion object{
public inline fun <reified T : Any> auto(): BufferFactory<T> =
public inline fun <reified T> auto(): BufferFactory<T> =
BufferFactory(Buffer.Companion::auto)
public fun <T> boxing(): BufferFactory<T> =

View File

@ -146,8 +146,8 @@ public value class DMatrixContext<T : Any, out A : Ring<T>>(public val context:
public inline operator fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.unaryMinus(): DMatrix<T, C, R> =
context.run { this@unaryMinus.unaryMinus() }.coerce()
public inline fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.transpose(): DMatrix<T, R, C> =
context.run { (this@transpose as Matrix<T>).transpose() }.coerce()
public inline fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.transposed(): DMatrix<T, R, C> =
context.run { (this@transposed as Matrix<T>).transposed }.coerce()
public companion object {
public val real: DMatrixContext<Double, DoubleField> = DMatrixContext(Double.algebra.linearSpace)

View File

@ -30,7 +30,7 @@ internal class DMatrixContextTest {
val m2 = produce<D3, D2> { i, j -> (i + j).toDouble() }
//Dimension-safe addition
m1.transpose() + m2
m1.transposed() + m2
}
}
}

View File

@ -9,8 +9,6 @@ kscience{
wasm()
dependencies {
api(projects.kmathCore)
}

View File

@ -68,9 +68,6 @@ public open class PolynomialSpace<C, A>(
public val ring: A,
) : Ring<Polynomial<C>>, ScaleOperations<Polynomial<C>> where A : Ring<C>, A : ScaleOperations<C> {
@UnstableKMathAPI
override val elementType: KType get() = typeOf<Polynomial<C>>()
/**
* Instance of zero constant (zero of the underlying ring).
*/

View File

@ -5,21 +5,21 @@
package space.kscience.kmath.optimization
import space.kscience.attributes.*
import space.kscience.kmath.expressions.DifferentiableExpression
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.misc.FeatureSet
public class OptimizationValue<T>(public val value: T) : OptimizationFeature {
override fun toString(): String = "Value($value)"
}
public enum class FunctionOptimizationTarget : OptimizationFeature {
public enum class FunctionOptimizationTarget {
MAXIMIZE,
MINIMIZE
}
public class FunctionOptimization<T>(
override val features: FeatureSet<OptimizationFeature>,
override val attributes: Attributes,
public val expression: DifferentiableExpression<T>,
) : OptimizationProblem<T> {
@ -30,25 +30,36 @@ public class FunctionOptimization<T>(
other as FunctionOptimization<*>
if (features != other.features) return false
if (attributes != other.attributes) return false
if (expression != other.expression) return false
return true
}
override fun hashCode(): Int {
var result = features.hashCode()
var result = attributes.hashCode()
result = 31 * result + expression.hashCode()
return result
}
override fun toString(): String = "FunctionOptimization(features=$features)"
override fun toString(): String = "FunctionOptimization(features=$attributes)"
public companion object
}
public class OptimizationPrior<T>(type: SafeType<T>):
PolymorphicAttribute<DifferentiableExpression<T>>(safeTypeOf()),
Attribute<DifferentiableExpression<T>>
public val <T> FunctionOptimization.Companion.Optimization get() =
public fun <T> FunctionOptimization<T>.withFeatures(
vararg newFeature: OptimizationFeature,
): FunctionOptimization<T> = FunctionOptimization(
features.with(*newFeature),
attributes.with(*newFeature),
expression,
)

View File

@ -5,21 +5,16 @@
package space.kscience.kmath.optimization
import space.kscience.attributes.*
import space.kscience.kmath.expressions.DifferentiableExpression
import space.kscience.kmath.expressions.NamedMatrix
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.misc.*
import kotlin.reflect.KClass
public interface OptimizationFeature : Feature<OptimizationFeature> {
// enforce toString override
override fun toString(): String
}
public interface OptimizationAttribute<T>: Attribute<T>
public interface OptimizationProblem<T> : Featured<OptimizationFeature> {
public val features: FeatureSet<OptimizationFeature>
override fun <F : OptimizationFeature> getFeature(type: KClass<out F>): F? = features.getFeature(type)
}
public interface OptimizationProblem<T> : AttributeContainer
public inline fun <reified F : OptimizationFeature> OptimizationProblem<*>.getFeature(): F? = getFeature(F::class)
@ -27,11 +22,6 @@ public open class OptimizationStartPoint<T>(public val point: Map<Symbol, T>) :
override fun toString(): String = "StartPoint($point)"
}
public interface OptimizationPrior<T> : OptimizationFeature, DifferentiableExpression<T> {
override val key: FeatureKey<OptimizationFeature> get() = OptimizationPrior::class
}
/**
* Covariance matrix for
*/

View File

@ -79,7 +79,7 @@ public interface PointWeight : OptimizationFeature {
public class XYFit(
public val data: XYColumnarData<Double, Double, Double>,
public val model: DifferentiableExpression<Double>,
override val features: FeatureSet<OptimizationFeature>,
override val attributes: FeatureSet<OptimizationFeature>,
internal val pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY,
internal val pointWeight: PointWeight = PointWeight.byYSigma,
public val xSymbol: Symbol = Symbol.x,
@ -90,7 +90,7 @@ public class XYFit(
}
public fun XYFit.withFeature(vararg features: OptimizationFeature): XYFit {
return XYFit(data, model, this.features.with(*features), pointToCurveDistance, pointWeight)
return XYFit(data, model, this.attributes.with(*features), pointToCurveDistance, pointWeight)
}
public suspend fun XYColumnarData<Double, Double, Double>.fitWith(

View File

@ -53,9 +53,9 @@ internal fun XYFit.logLikelihood(): DifferentiableExpression<Double> = object :
*/
@UnstableKMathAPI
public suspend fun Optimizer<Double, FunctionOptimization<Double>>.maximumLogLikelihood(problem: XYFit): XYFit {
val functionOptimization = FunctionOptimization(problem.features, problem.logLikelihood())
val functionOptimization = FunctionOptimization(problem.attributes, problem.logLikelihood())
val result = optimize(functionOptimization.withFeatures(FunctionOptimizationTarget.MAXIMIZE))
return XYFit(problem.data, problem.model, result.features)
return XYFit(problem.data, problem.model, result.attributes)
}
@UnstableKMathAPI