From d3893ab7e6b38970c5761f4c14c949272331ebc7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 20 Jun 2023 19:45:21 +0300 Subject: [PATCH] [WIP] moving from features to attributes --- attributes-kt/build.gradle.kts | 17 +++++ .../space/kscience/attributes/Attribute.kt | 28 ++++++++ .../kscience/attributes/AttributeContainer.kt | 13 ++++ .../space/kscience/attributes/Attributes.kt | 71 +++++++++++++++++++ .../kscience/attributes/AttributesBuilder.kt | 52 ++++++++++++++ build.gradle.kts | 2 +- buildSrc/settings.gradle.kts | 6 -- gradle.properties | 9 ++- .../kscience/kmath/commons/linear/CMMatrix.kt | 2 +- kmath-core/build.gradle.kts | 1 + .../kscience/kmath/linear/MatrixFeatures.kt | 37 +++++----- .../kscience/kmath/linear/MatrixWrapper.kt | 25 +++---- .../space/kscience/kmath/misc/Featured.kt | 61 ---------------- .../space/kscience/kmath/nd/AlgebraND.kt | 5 +- .../space/kscience/kmath/nd/StructureND.kt | 14 ++-- kmath-symja/build.gradle.kts | 2 +- settings.gradle.kts | 1 + 17 files changed, 219 insertions(+), 127 deletions(-) create mode 100644 attributes-kt/build.gradle.kts create mode 100644 attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attribute.kt create mode 100644 attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributeContainer.kt create mode 100644 attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt create mode 100644 attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributesBuilder.kt delete mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Featured.kt diff --git a/attributes-kt/build.gradle.kts b/attributes-kt/build.gradle.kts new file mode 100644 index 000000000..fe422f751 --- /dev/null +++ b/attributes-kt/build.gradle.kts @@ -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() +} diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attribute.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attribute.kt new file mode 100644 index 000000000..4b9e27655 --- /dev/null +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attribute.kt @@ -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 + + +public interface AttributeWithDefault : Attribute { + public val default: T +} + +public interface SetAttribute : Attribute> + +/** + * An attribute that has a type parameter for value + */ +public abstract class PolymorphicAttribute(public val type: KType) : Attribute { + override fun equals(other: Any?): Boolean = (other as? PolymorphicAttribute<*>)?.type == this.type + + override fun hashCode(): Int { + return type.hashCode() + } +} diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributeContainer.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributeContainer.kt new file mode 100644 index 000000000..69b050649 --- /dev/null +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributeContainer.kt @@ -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 +} \ No newline at end of file diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt new file mode 100644 index 000000000..25767a686 --- /dev/null +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt @@ -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, Any>) { + + public val keys: Set> get() = content.keys + + @Suppress("UNCHECKED_CAST") + public operator fun get(attribute: Attribute): 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 Attributes.getOrDefault(attribute: AttributeWithDefault): T = get(attribute) ?: attribute.default + +public fun > 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 > Attributes.withAttributeElement( + attribute: A, + attrValue: T, +): Attributes { + val currentSet: Set = get(attribute) ?: emptySet() + return Attributes( + content + (attribute to (currentSet + attrValue)) + ) +} + +/** + * Remove an element from [SetAttribute] + */ +public fun > Attributes.withoutAttributeElement( + attribute: A, + attrValue: T, +): Attributes { + val currentSet: Set = get(attribute) ?: emptySet() + return Attributes( + content + (attribute to (currentSet - attrValue)) + ) +} + +public fun > Attributes( + attribute: A, + attrValue: T, +): Attributes = Attributes(mapOf(attribute to attrValue)) + +public operator fun Attributes.plus(other: Attributes): Attributes = Attributes(content + other.content) \ No newline at end of file diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributesBuilder.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributesBuilder.kt new file mode 100644 index 000000000..588c789f5 --- /dev/null +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/AttributesBuilder.kt @@ -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, Any> = mutableMapOf()) { + + @Suppress("UNCHECKED_CAST") + public operator fun get(attribute: Attribute): T? = map[attribute] as? T + + public operator fun Attribute.invoke(value: V?) { + if (value == null) { + map.remove(this) + } else { + map[this] = value + } + } + + public fun from(attributes: Attributes) { + map.putAll(attributes.content) + } + + public fun SetAttribute.add( + attrValue: V, + ) { + val currentSet: Set = get(this) ?: emptySet() + map[this] = currentSet + attrValue + } + + /** + * Remove an element from [SetAttribute] + */ + public fun SetAttribute.remove( + attrValue: V, + ) { + val currentSet: Set = 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() \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 043420577..a132f091c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ allprojects { } group = "space.kscience" - version = "0.3.2-dev-1" + version = "0.4.0-dev-1" } subprojects { diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index e6b69b0b3..38eebeecc 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -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") dependencyResolutionManagement { diff --git a/gradle.properties b/gradle.properties index fee75d428..162720167 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,11 +6,10 @@ kotlin.code.style=official kotlin.mpp.stability.nowarn=true kotlin.native.ignoreDisabledTargets=true -org.gradle.configureondemand=true -org.gradle.jvmargs=-Xmx4096m - -toolsVersion=0.14.8-kotlin-1.8.20 - +toolsVersion=0.14.9-kotlin-1.8.20 org.gradle.parallel=true org.gradle.workers.max=4 +org.gradle.configureondemand=true +org.gradle.jvmargs=-Xmx4096m + diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt index d19bd1be0..e369effdf 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt @@ -106,7 +106,7 @@ public object CMLinearSpace : LinearSpace { val origin = structure.toCM().origin 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, diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 4ecff77aa..398465fbe 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -10,6 +10,7 @@ kscience{ dependencies { api(projects.kmathMemory) + api(projects.attributesKt) } testDependencies { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixFeatures.kt index ce7acdcba..62325b39d 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixFeatures.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixFeatures.kt @@ -5,54 +5,49 @@ 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 * to optimize matrix operations performance in some cases or retrieve the APIs. */ -public interface MatrixFeature: StructureFeature +public interface MatrixFeature : Attribute /** - * 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 companion object : DiagonalFeature +public interface IsDiagonal : MatrixFeature { + public companion object : IsDiagonal } /** * 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. */ -public object UnitFeature : DiagonalFeature +public object IsUnit : IsDiagonal /** * Matrices with this feature can be inverted: *[inverse] = a−1* where *a* is the owning matrix. * * @param T the type of matrices' items. */ -public interface InverseMatrixFeature : MatrixFeature { - /** - * The inverse matrix of the matrix that owns this feature. - */ - public val inverse: Matrix +public class Inverted private constructor() : MatrixFeature> { + internal val instance: Inverted = Inverted() } +@Suppress("UNCHECKED_CAST") +public val LinearSpace.Inverted: Inverted get() = Inverted.instance as Inverted + /** * Matrices with this feature can compute their determinant. * * @param T the type of matrices' items. */ -public interface DeterminantFeature : MatrixFeature { - /** - * The determinant of the matrix that owns this feature. - */ - public val determinant: T -} +public class DeterminantFeature : MatrixFeature /** * Produces a [DeterminantFeature] where the [DeterminantFeature.determinant] is [determinant]. @@ -68,12 +63,12 @@ public fun DeterminantFeature(determinant: T): DeterminantFeature = /** * Matrices with this feature are lower triangular ones. */ -public object LFeature : MatrixFeature +public object LFeature : MatrixFeature /** * Matrices with this feature are upper triangular ones. */ -public object UFeature : MatrixFeature +public object UFeature : MatrixFeature /** * Matrices with this feature support LU factorization: *a = [l] · [u]* where *a* is the owning matrix. @@ -117,7 +112,7 @@ public interface LupDecompositionFeature : MatrixFeature { /** * Matrices with this feature are orthogonal ones: *a · aT = u* where *a* is the owning matrix, *u* - * is the unit matrix ([UnitFeature]). + * is the unit matrix ([IsUnit]). */ public object OrthogonalFeature : MatrixFeature diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt index 46454a584..b510cc697 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt @@ -5,11 +5,10 @@ package space.kscience.kmath.linear +import space.kscience.attributes.Attributes import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.misc.FeatureSet -import space.kscience.kmath.nd.StructureFeature import space.kscience.kmath.operations.Ring -import kotlin.reflect.KClass /** * A [Matrix] that holds [MatrixFeature] objects. @@ -18,18 +17,10 @@ import kotlin.reflect.KClass */ public class MatrixWrapper internal constructor( public val origin: Matrix, - public val features: FeatureSet, + public val attributes: Attributes, ) : Matrix by origin { - /** - * Get the first feature matching given class. Does not guarantee that matrix has only one feature matching the - * criteria. - */ - @Suppress("UNCHECKED_CAST") - override fun getFeature(type: KClass): F? = - features.getFeature(type) ?: origin.getFeature(type) - - override fun toString(): String = "MatrixWrapper(matrix=$origin, features=$features)" + override fun toString(): String = "MatrixWrapper(matrix=$origin, features=$attributes)" } /** @@ -44,7 +35,7 @@ public val Matrix.origin: Matrix * Add a single feature to a [Matrix] */ public fun Matrix.withFeature(newFeature: MatrixFeature): MatrixWrapper = if (this is MatrixWrapper) { - MatrixWrapper(origin, features.with(newFeature)) + MatrixWrapper(origin, attributes.with(newFeature)) } else { MatrixWrapper(this, FeatureSet.of(newFeature)) } @@ -57,20 +48,20 @@ public operator fun Matrix.plus(newFeature: MatrixFeature): MatrixW */ public fun Matrix.withFeatures(newFeatures: Iterable): MatrixWrapper = if (this is MatrixWrapper) { - MatrixWrapper(origin, features.with(newFeatures)) + MatrixWrapper(origin, attributes.with(newFeatures)) } else { 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 LinearSpace>.one( rows: Int, columns: Int, ): Matrix = VirtualMatrix(rows, columns) { i, j -> if (i == j) elementAlgebra.one else elementAlgebra.zero -}.withFeature(UnitFeature) +}.withFeature(IsUnit) /** @@ -81,7 +72,7 @@ public fun LinearSpace>.zero( columns: Int, ): Matrix = VirtualMatrix(rows, columns) { _, _ -> elementAlgebra.zero -}.withFeature(ZeroFeature) +}.withFeature(IsZero) public class TransposedFeature(public val original: Matrix) : MatrixFeature diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Featured.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Featured.kt deleted file mode 100644 index bdda674dc..000000000 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Featured.kt +++ /dev/null @@ -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 { - public fun getFeature(type: FeatureKey): T? -} - -public typealias FeatureKey = KClass - -public interface Feature> { - - /** - * A key used for extraction - */ - @Suppress("UNCHECKED_CAST") - public val key: FeatureKey - get() = this::class as FeatureKey -} - -/** - * A container for a set of features - */ -@JvmInline -public value class FeatureSet> private constructor(public val features: Map, F>) : Featured { - @Suppress("UNCHECKED_CAST") - override fun getFeature(type: FeatureKey): T? = features[type]?.let { it as T } - - public inline fun getFeature(): T? = getFeature(T::class) - - public fun with(feature: T, type: FeatureKey = feature.key): FeatureSet = - FeatureSet(features + (type to feature)) - - public fun with(other: FeatureSet): FeatureSet = FeatureSet(features + other.features) - - public fun with(vararg otherFeatures: F): FeatureSet = - FeatureSet(features + otherFeatures.associateBy { it.key }) - - public fun with(otherFeatures: Iterable): FeatureSet = - FeatureSet(features + otherFeatures.associateBy { it.key }) - - public operator fun iterator(): Iterator = features.values.iterator() - - override fun toString(): String = features.values.joinToString(prefix = "[ ", postfix = " ]") - - - public companion object { - public fun > of(vararg features: F): FeatureSet = FeatureSet(features.associateBy { it.key }) - public fun > of(features: Iterable): FeatureSet = - FeatureSet(features.associateBy { it.key }) - } -} diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt index f6626432d..a75807bad 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt @@ -114,7 +114,7 @@ public interface GroupOpsND> : GroupOps>, override fun add(left: StructureND, right: StructureND): StructureND = 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. @@ -181,8 +181,6 @@ public interface RingOpsND> : RingOps>, Gro override fun multiply(left: StructureND, right: StructureND): StructureND = zip(left, right) { aValue, bValue -> multiply(aValue, bValue) } - //TODO move to extensions with context receivers - /** * Multiplies an ND structure by an element of it. * @@ -232,7 +230,6 @@ public interface FieldOpsND> : override fun divide(left: StructureND, right: StructureND): StructureND = 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. * diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt index f0a258389..e9814acbf 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt @@ -5,10 +5,10 @@ package space.kscience.kmath.nd +import space.kscience.attributes.Attribute +import space.kscience.attributes.AttributeContainer import space.kscience.kmath.PerformancePitfall 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.invoke import space.kscience.kmath.structures.Buffer @@ -17,7 +17,7 @@ import kotlin.jvm.JvmName import kotlin.math.abs import kotlin.reflect.KClass -public interface StructureFeature : Feature +public interface StructureFeature : Attribute /** * 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 * * @param T the type of items. */ -public interface StructureND : Featured, WithShape { +public interface StructureND : AttributeContainer, WithShape { /** * The shape of structure i.e., non-empty sequence of non-negative integers that specify sizes of dimensions of * this structure. @@ -57,12 +57,6 @@ public interface StructureND : Featured, WithShape { @PerformancePitfall public fun elements(): Sequence> = 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 getFeature(type: KClass): F? = null - public companion object { /** * Indicates whether some [StructureND] is equal to another one. diff --git a/kmath-symja/build.gradle.kts b/kmath-symja/build.gradle.kts index 8741de2ae..a996f3bec 100644 --- a/kmath-symja/build.gradle.kts +++ b/kmath-symja/build.gradle.kts @@ -10,7 +10,7 @@ plugins { description = "Symja integration module" dependencies { - api("org.matheclipse:matheclipse-core:2.0.0-SNAPSHOT") { + api("org.matheclipse:matheclipse-core:2.0.0") { // Incorrect transitive dependencies exclude("org.apfloat", "apfloat") exclude("org.hipparchus", "hipparchus-clustering") diff --git a/settings.gradle.kts b/settings.gradle.kts index f158f3444..e660bef85 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,6 +21,7 @@ dependencyResolutionManagement { include( ":test-utils", + ":attributes-kt", ":kmath-memory", ":kmath-complex", ":kmath-core",