[WIP] moving from features to attributes

This commit is contained in:
Alexander Nozik 2023-06-20 19:45:21 +03:00
parent c0a7cff1d8
commit d3893ab7e6
17 changed files with 219 additions and 127 deletions

View File

@ -0,0 +1,17 @@
plugins {
id("space.kscience.gradle.mpp")
}
kscience {
jvm()
js()
native()
wasm()
}
readme {
maturity = space.kscience.gradle.Maturity.DEVELOPMENT
description = """
An API and basic implementation for arranging objects in a continuous memory block.
""".trimIndent()
}

View File

@ -0,0 +1,28 @@
/*
* 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.attributes
import kotlin.reflect.KType
public interface Attribute<T>
public interface AttributeWithDefault<T> : Attribute<T> {
public val default: T
}
public interface SetAttribute<V> : Attribute<Set<V>>
/**
* An attribute that has a type parameter for value
*/
public abstract class PolymorphicAttribute<T>(public val type: KType) : Attribute<T> {
override fun equals(other: Any?): Boolean = (other as? PolymorphicAttribute<*>)?.type == this.type
override fun hashCode(): Int {
return type.hashCode()
}
}

View File

@ -0,0 +1,13 @@
/*
* 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.attributes
/**
* A container for attributes. [attributes] could be made mutable by implementation
*/
public interface AttributeContainer {
public val attributes: Attributes
}

View File

@ -0,0 +1,71 @@
/*
* 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.attributes
import kotlin.jvm.JvmInline
@JvmInline
public value class Attributes internal constructor(public val content: Map<out Attribute<*>, Any>) {
public val keys: Set<Attribute<*>> get() = content.keys
@Suppress("UNCHECKED_CAST")
public operator fun <T> get(attribute: Attribute<T>): T? = content[attribute] as? T
override fun toString(): String = "Attributes(value=${content.entries})"
public companion object {
public val EMPTY: Attributes = Attributes(emptyMap())
}
}
public fun Attributes.isEmpty(): Boolean = content.isEmpty()
public fun <T> Attributes.getOrDefault(attribute: AttributeWithDefault<T>): T = get(attribute) ?: attribute.default
public fun <T, A : Attribute<T>> Attributes.withAttribute(
attribute: A,
attrValue: T?,
): Attributes = Attributes(
if (attrValue == null) {
content - attribute
} else {
content + (attribute to attrValue)
}
)
/**
* Add an element to a [SetAttribute]
*/
public fun <T, A : SetAttribute<T>> Attributes.withAttributeElement(
attribute: A,
attrValue: T,
): Attributes {
val currentSet: Set<T> = get(attribute) ?: emptySet()
return Attributes(
content + (attribute to (currentSet + attrValue))
)
}
/**
* Remove an element from [SetAttribute]
*/
public fun <T, A : SetAttribute<T>> Attributes.withoutAttributeElement(
attribute: A,
attrValue: T,
): Attributes {
val currentSet: Set<T> = get(attribute) ?: emptySet()
return Attributes(
content + (attribute to (currentSet - attrValue))
)
}
public fun <T : Any, A : Attribute<T>> Attributes(
attribute: A,
attrValue: T,
): Attributes = Attributes(mapOf(attribute to attrValue))
public operator fun Attributes.plus(other: Attributes): Attributes = Attributes(content + other.content)

View File

@ -0,0 +1,52 @@
/*
* 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.attributes
/**
* A safe builder for [Attributes]
*/
public class AttributesBuilder internal constructor(private val map: MutableMap<Attribute<*>, Any> = mutableMapOf()) {
@Suppress("UNCHECKED_CAST")
public operator fun <T> get(attribute: Attribute<T>): T? = map[attribute] as? T
public operator fun <V> Attribute<V>.invoke(value: V?) {
if (value == null) {
map.remove(this)
} else {
map[this] = value
}
}
public fun from(attributes: Attributes) {
map.putAll(attributes.content)
}
public fun <V> SetAttribute<V>.add(
attrValue: V,
) {
val currentSet: Set<V> = get(this) ?: emptySet()
map[this] = currentSet + attrValue
}
/**
* Remove an element from [SetAttribute]
*/
public fun <V> SetAttribute<V>.remove(
attrValue: V,
) {
val currentSet: Set<V> = get(this) ?: emptySet()
map[this] = currentSet - attrValue
}
public fun build(): Attributes = Attributes(map)
}
public fun AttributesBuilder(
attributes: Attributes,
): AttributesBuilder = AttributesBuilder(attributes.content.toMutableMap())
public fun Attributes(builder: AttributesBuilder.() -> Unit): Attributes = AttributesBuilder().apply(builder).build()

View File

@ -15,7 +15,7 @@ allprojects {
} }
group = "space.kscience" group = "space.kscience"
version = "0.3.2-dev-1" version = "0.4.0-dev-1"
} }
subprojects { subprojects {

View File

@ -1,9 +1,3 @@
/*
* Copyright 2018-2021 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.
*/
rootProject.name = "kmath"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
dependencyResolutionManagement { dependencyResolutionManagement {

View File

@ -6,11 +6,10 @@ kotlin.code.style=official
kotlin.mpp.stability.nowarn=true kotlin.mpp.stability.nowarn=true
kotlin.native.ignoreDisabledTargets=true kotlin.native.ignoreDisabledTargets=true
org.gradle.configureondemand=true toolsVersion=0.14.9-kotlin-1.8.20
org.gradle.jvmargs=-Xmx4096m
toolsVersion=0.14.8-kotlin-1.8.20
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.workers.max=4 org.gradle.workers.max=4
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4096m

View File

@ -106,7 +106,7 @@ public object CMLinearSpace : LinearSpace<Double, DoubleField> {
val origin = structure.toCM().origin val origin = structure.toCM().origin
return when (type) { return when (type) {
DiagonalFeature::class -> if (origin is DiagonalMatrix) DiagonalFeature else null IsDiagonal::class -> if (origin is DiagonalMatrix) IsDiagonal else null
DeterminantFeature::class, LupDecompositionFeature::class -> object : DeterminantFeature::class, LupDecompositionFeature::class -> object :
DeterminantFeature<Double>, DeterminantFeature<Double>,

View File

@ -10,6 +10,7 @@ kscience{
dependencies { dependencies {
api(projects.kmathMemory) api(projects.kmathMemory)
api(projects.attributesKt)
} }
testDependencies { testDependencies {

View File

@ -5,54 +5,49 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.kmath.nd.StructureFeature import space.kscience.attributes.Attribute
/** /**
* A marker interface representing some properties of matrices or additional transformations of them. Features are used * A marker interface representing some properties of matrices or additional transformations of them. Features are used
* to optimize matrix operations performance in some cases or retrieve the APIs. * to optimize matrix operations performance in some cases or retrieve the APIs.
*/ */
public interface MatrixFeature: StructureFeature public interface MatrixFeature<T> : Attribute<T>
/** /**
* Matrices with this feature are considered to have only diagonal non-null elements. * Matrices with this feature are considered to have only diagonal non-zero elements.
*/ */
public interface DiagonalFeature : MatrixFeature { public interface IsDiagonal : MatrixFeature<Unit> {
public companion object : DiagonalFeature public companion object : IsDiagonal
} }
/** /**
* Matrices with this feature have all zero elements. * Matrices with this feature have all zero elements.
*/ */
public object ZeroFeature : DiagonalFeature public object IsZero : IsDiagonal
/** /**
* Matrices with this feature have unit elements on diagonal and zero elements in all other places. * Matrices with this feature have unit elements on diagonal and zero elements in all other places.
*/ */
public object UnitFeature : DiagonalFeature public object IsUnit : IsDiagonal
/** /**
* Matrices with this feature can be inverted: *[inverse] = a<sup>&minus;1</sup>* where *a* is the owning matrix. * Matrices with this feature can be inverted: *[inverse] = a<sup>&minus;1</sup>* where *a* is the owning matrix.
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
*/ */
public interface InverseMatrixFeature<out T : Any> : MatrixFeature { public class Inverted<T> private constructor() : MatrixFeature<Matrix<T>> {
/** internal val instance: Inverted<Nothing> = Inverted()
* The inverse matrix of the matrix that owns this feature.
*/
public val inverse: Matrix<T>
} }
@Suppress("UNCHECKED_CAST")
public val <T> LinearSpace<T, *>.Inverted: Inverted<T> get() = Inverted.instance as Inverted<T>
/** /**
* Matrices with this feature can compute their determinant. * Matrices with this feature can compute their determinant.
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
*/ */
public interface DeterminantFeature<out T : Any> : MatrixFeature { public class DeterminantFeature<T : Any> : MatrixFeature<T>
/**
* The determinant of the matrix that owns this feature.
*/
public val determinant: T
}
/** /**
* Produces a [DeterminantFeature] where the [DeterminantFeature.determinant] is [determinant]. * Produces a [DeterminantFeature] where the [DeterminantFeature.determinant] is [determinant].
@ -68,12 +63,12 @@ public fun <T : Any> DeterminantFeature(determinant: T): DeterminantFeature<T> =
/** /**
* Matrices with this feature are lower triangular ones. * Matrices with this feature are lower triangular ones.
*/ */
public object LFeature : MatrixFeature public object LFeature : MatrixFeature<Unit>
/** /**
* Matrices with this feature are upper triangular ones. * Matrices with this feature are upper triangular ones.
*/ */
public object UFeature : MatrixFeature public object UFeature : MatrixFeature<Unit>
/** /**
* Matrices with this feature support LU factorization: *a = [l] &middot; [u]* where *a* is the owning matrix. * Matrices with this feature support LU factorization: *a = [l] &middot; [u]* where *a* is the owning matrix.
@ -117,7 +112,7 @@ public interface LupDecompositionFeature<out T : Any> : MatrixFeature {
/** /**
* Matrices with this feature are orthogonal ones: *a &middot; a<sup>T</sup> = u* where *a* is the owning matrix, *u* * Matrices with this feature are orthogonal ones: *a &middot; a<sup>T</sup> = u* where *a* is the owning matrix, *u*
* is the unit matrix ([UnitFeature]). * is the unit matrix ([IsUnit]).
*/ */
public object OrthogonalFeature : MatrixFeature public object OrthogonalFeature : MatrixFeature

View File

@ -5,11 +5,10 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.attributes.Attributes
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.misc.FeatureSet import space.kscience.kmath.misc.FeatureSet
import space.kscience.kmath.nd.StructureFeature
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
import kotlin.reflect.KClass
/** /**
* A [Matrix] that holds [MatrixFeature] objects. * A [Matrix] that holds [MatrixFeature] objects.
@ -18,18 +17,10 @@ import kotlin.reflect.KClass
*/ */
public class MatrixWrapper<out T : Any> internal constructor( public class MatrixWrapper<out T : Any> internal constructor(
public val origin: Matrix<T>, public val origin: Matrix<T>,
public val features: FeatureSet<StructureFeature>, public val attributes: Attributes,
) : Matrix<T> by origin { ) : Matrix<T> by origin {
/** override fun toString(): String = "MatrixWrapper(matrix=$origin, features=$attributes)"
* Get the first feature matching given class. Does not guarantee that matrix has only one feature matching the
* criteria.
*/
@Suppress("UNCHECKED_CAST")
override fun <F : StructureFeature> getFeature(type: KClass<out F>): F? =
features.getFeature(type) ?: origin.getFeature(type)
override fun toString(): String = "MatrixWrapper(matrix=$origin, features=$features)"
} }
/** /**
@ -44,7 +35,7 @@ public val <T : Any> Matrix<T>.origin: Matrix<T>
* Add a single feature to a [Matrix] * Add a single feature to a [Matrix]
*/ */
public fun <T : Any> Matrix<T>.withFeature(newFeature: MatrixFeature): MatrixWrapper<T> = if (this is MatrixWrapper) { public fun <T : Any> Matrix<T>.withFeature(newFeature: MatrixFeature): MatrixWrapper<T> = if (this is MatrixWrapper) {
MatrixWrapper(origin, features.with(newFeature)) MatrixWrapper(origin, attributes.with(newFeature))
} else { } else {
MatrixWrapper(this, FeatureSet.of(newFeature)) MatrixWrapper(this, FeatureSet.of(newFeature))
} }
@ -57,20 +48,20 @@ public operator fun <T : Any> Matrix<T>.plus(newFeature: MatrixFeature): MatrixW
*/ */
public fun <T : Any> Matrix<T>.withFeatures(newFeatures: Iterable<MatrixFeature>): MatrixWrapper<T> = public fun <T : Any> Matrix<T>.withFeatures(newFeatures: Iterable<MatrixFeature>): MatrixWrapper<T> =
if (this is MatrixWrapper) { if (this is MatrixWrapper) {
MatrixWrapper(origin, features.with(newFeatures)) MatrixWrapper(origin, attributes.with(newFeatures))
} else { } else {
MatrixWrapper(this, FeatureSet.of(newFeatures)) MatrixWrapper(this, FeatureSet.of(newFeatures))
} }
/** /**
* Diagonal matrix of ones. The matrix is virtual no actual matrix is created. * Diagonal matrix of ones. The matrix is virtual, no actual matrix is created.
*/ */
public fun <T : Any> LinearSpace<T, Ring<T>>.one( public fun <T : Any> LinearSpace<T, Ring<T>>.one(
rows: Int, rows: Int,
columns: Int, columns: Int,
): Matrix<T> = VirtualMatrix(rows, columns) { i, j -> ): Matrix<T> = VirtualMatrix(rows, columns) { i, j ->
if (i == j) elementAlgebra.one else elementAlgebra.zero if (i == j) elementAlgebra.one else elementAlgebra.zero
}.withFeature(UnitFeature) }.withFeature(IsUnit)
/** /**
@ -81,7 +72,7 @@ public fun <T : Any> LinearSpace<T, Ring<T>>.zero(
columns: Int, columns: Int,
): Matrix<T> = VirtualMatrix(rows, columns) { _, _ -> ): Matrix<T> = VirtualMatrix(rows, columns) { _, _ ->
elementAlgebra.zero elementAlgebra.zero
}.withFeature(ZeroFeature) }.withFeature(IsZero)
public class TransposedFeature<out T : Any>(public val original: Matrix<T>) : MatrixFeature public class TransposedFeature<out T : Any>(public val original: Matrix<T>) : MatrixFeature

View File

@ -1,61 +0,0 @@
/*
* Copyright 2018-2022 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.misc
import kotlin.jvm.JvmInline
import kotlin.reflect.KClass
/**
* An entity that contains a set of features defined by their types
*/
public interface Featured<F : Any> {
public fun <T : F> getFeature(type: FeatureKey<T>): T?
}
public typealias FeatureKey<T> = KClass<out T>
public interface Feature<F : Feature<F>> {
/**
* A key used for extraction
*/
@Suppress("UNCHECKED_CAST")
public val key: FeatureKey<F>
get() = this::class as FeatureKey<F>
}
/**
* A container for a set of features
*/
@JvmInline
public value class FeatureSet<F : Feature<F>> private constructor(public val features: Map<FeatureKey<F>, F>) : Featured<F> {
@Suppress("UNCHECKED_CAST")
override fun <T : F> getFeature(type: FeatureKey<T>): T? = features[type]?.let { it as T }
public inline fun <reified T : F> getFeature(): T? = getFeature(T::class)
public fun <T : F> with(feature: T, type: FeatureKey<F> = feature.key): FeatureSet<F> =
FeatureSet(features + (type to feature))
public fun with(other: FeatureSet<F>): FeatureSet<F> = FeatureSet(features + other.features)
public fun with(vararg otherFeatures: F): FeatureSet<F> =
FeatureSet(features + otherFeatures.associateBy { it.key })
public fun with(otherFeatures: Iterable<F>): FeatureSet<F> =
FeatureSet(features + otherFeatures.associateBy { it.key })
public operator fun iterator(): Iterator<F> = features.values.iterator()
override fun toString(): String = features.values.joinToString(prefix = "[ ", postfix = " ]")
public companion object {
public fun <F : Feature<F>> of(vararg features: F): FeatureSet<F> = FeatureSet(features.associateBy { it.key })
public fun <F : Feature<F>> of(features: Iterable<F>): FeatureSet<F> =
FeatureSet(features.associateBy { it.key })
}
}

View File

@ -114,7 +114,7 @@ public interface GroupOpsND<T, out A : GroupOps<T>> : GroupOps<StructureND<T>>,
override fun add(left: StructureND<T>, right: StructureND<T>): StructureND<T> = override fun add(left: StructureND<T>, right: StructureND<T>): StructureND<T> =
zip(left, right) { aValue, bValue -> add(aValue, bValue) } zip(left, right) { aValue, bValue -> add(aValue, bValue) }
// TODO move to extensions after KEEP-176 // TODO implement using context receivers
/** /**
* Adds an ND structure to an element of it. * Adds an ND structure to an element of it.
@ -181,8 +181,6 @@ public interface RingOpsND<T, out A : RingOps<T>> : RingOps<StructureND<T>>, Gro
override fun multiply(left: StructureND<T>, right: StructureND<T>): StructureND<T> = override fun multiply(left: StructureND<T>, right: StructureND<T>): StructureND<T> =
zip(left, right) { aValue, bValue -> multiply(aValue, bValue) } zip(left, right) { aValue, bValue -> multiply(aValue, bValue) }
//TODO move to extensions with context receivers
/** /**
* Multiplies an ND structure by an element of it. * Multiplies an ND structure by an element of it.
* *
@ -232,7 +230,6 @@ public interface FieldOpsND<T, out A : Field<T>> :
override fun divide(left: StructureND<T>, right: StructureND<T>): StructureND<T> = override fun divide(left: StructureND<T>, right: StructureND<T>): StructureND<T> =
zip(left, right) { aValue, bValue -> divide(aValue, bValue) } zip(left, right) { aValue, bValue -> divide(aValue, bValue) }
//TODO move to extensions after https://github.com/Kotlin/KEEP/blob/master/proposals/context-receivers.md
/** /**
* Divides an ND structure by an element of it. * Divides an ND structure by an element of it.
* *

View File

@ -5,10 +5,10 @@
package space.kscience.kmath.nd package space.kscience.kmath.nd
import space.kscience.attributes.Attribute
import space.kscience.attributes.AttributeContainer
import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.misc.Feature
import space.kscience.kmath.misc.Featured
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
@ -17,7 +17,7 @@ import kotlin.jvm.JvmName
import kotlin.math.abs import kotlin.math.abs
import kotlin.reflect.KClass import kotlin.reflect.KClass
public interface StructureFeature : Feature<StructureFeature> public interface StructureFeature<T> : Attribute<T>
/** /**
* Represents n-dimensional structure i.e., multidimensional container of items of the same type and size. The number * Represents n-dimensional structure i.e., multidimensional container of items of the same type and size. The number
@ -28,7 +28,7 @@ public interface StructureFeature : Feature<StructureFeature>
* *
* @param T the type of items. * @param T the type of items.
*/ */
public interface StructureND<out T> : Featured<StructureFeature>, WithShape { public interface StructureND<out T> : AttributeContainer, WithShape {
/** /**
* The shape of structure i.e., non-empty sequence of non-negative integers that specify sizes of dimensions of * The shape of structure i.e., non-empty sequence of non-negative integers that specify sizes of dimensions of
* this structure. * this structure.
@ -57,12 +57,6 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
@PerformancePitfall @PerformancePitfall
public fun elements(): Sequence<Pair<IntArray, T>> = indices.asSequence().map { it to get(it) } public fun elements(): Sequence<Pair<IntArray, T>> = indices.asSequence().map { it to get(it) }
/**
* Feature is some additional structure information that allows to access it special properties or hints.
* If the feature is not present, `null` is returned.
*/
override fun <F : StructureFeature> getFeature(type: KClass<out F>): F? = null
public companion object { public companion object {
/** /**
* Indicates whether some [StructureND] is equal to another one. * Indicates whether some [StructureND] is equal to another one.

View File

@ -10,7 +10,7 @@ plugins {
description = "Symja integration module" description = "Symja integration module"
dependencies { dependencies {
api("org.matheclipse:matheclipse-core:2.0.0-SNAPSHOT") { api("org.matheclipse:matheclipse-core:2.0.0") {
// Incorrect transitive dependencies // Incorrect transitive dependencies
exclude("org.apfloat", "apfloat") exclude("org.apfloat", "apfloat")
exclude("org.hipparchus", "hipparchus-clustering") exclude("org.hipparchus", "hipparchus-clustering")

View File

@ -21,6 +21,7 @@ dependencyResolutionManagement {
include( include(
":test-utils", ":test-utils",
":attributes-kt",
":kmath-memory", ":kmath-memory",
":kmath-complex", ":kmath-complex",
":kmath-core", ":kmath-core",