From 3a91cb2579862e7ef9d617a46d6c8b44c8e61438 Mon Sep 17 00:00:00 2001 From: Gleb Minaev <43728100+lounres@users.noreply.github.com> Date: Sat, 16 Jul 2022 18:46:40 +0300 Subject: [PATCH] Draft another DSL for labeled polynomials. Add variance. --- .../LabeledPolynomial.kt | 2 +- .../ListPolynomial.kt | 4 +- .../NumberedPolynomial.kt | 2 +- .../Polynomial.kt | 2 +- .../RationalFunction.kt | 6 +- .../labeledConstructors.kt | 255 ++++++++++++++++++ 6 files changed, 263 insertions(+), 8 deletions(-) diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt index 7df51930e..e2320114b 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt @@ -75,7 +75,7 @@ internal constructor( * @param A type of provided underlying ring of constants. It's [Ring] of [C]. * @param ring underlying ring of constants of type [A]. */ -public class LabeledPolynomialSpace>( +public class LabeledPolynomialSpace>( public override val ring: A, ) : MultivariatePolynomialSpace>, PolynomialSpaceOverRing, A> { /** diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt index 17c42ac8c..d0e58c0d6 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt @@ -60,7 +60,7 @@ public data class ListPolynomial( * @param A type of provided underlying ring of constants. It's [Ring] of [C]. * @param ring underlying ring of constants of type [A]. */ -public open class ListPolynomialSpace>( +public open class ListPolynomialSpace>( public override val ring: A, ) : PolynomialSpaceOverRing, A> { /** @@ -366,7 +366,7 @@ public open class ListPolynomialSpace>( * @param A type of underlying ring of constants. It's [Ring] of [C]. * @param ring underlying ring of constants of type [A]. */ -public class ScalableListPolynomialSpace( +public class ScalableListPolynomialSpace( ring: A, ) : ListPolynomialSpace(ring), ScaleOperations> where A : Ring, A : ScaleOperations { override fun scale(a: ListPolynomial, value: Double): ListPolynomial = diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt index 037419cfc..d2fc5ffa1 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt @@ -59,7 +59,7 @@ internal constructor( * @param A type of provided underlying ring of constants. It's [Ring] of [C]. * @param ring underlying ring of constants of type [A]. */ -public class NumberedPolynomialSpace>( +public class NumberedPolynomialSpace>( public override val ring: A, ) : PolynomialSpaceOverRing, A> { /** diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/Polynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/Polynomial.kt index 61ea5a342..66308a7bc 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/Polynomial.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/Polynomial.kt @@ -252,7 +252,7 @@ public interface PolynomialSpace> : Ring

{ * @param A the type of algebraic structure (precisely, of ring) provided for constants. */ @Suppress("INAPPLICABLE_JVM_NAME") // FIXME: Waiting for KT-31420 -public interface PolynomialSpaceOverRing, A: Ring> : PolynomialSpace { +public interface PolynomialSpaceOverRing, out A: Ring> : PolynomialSpace { /** * Underlying ring of constants. Its operations on constants are inherited by local operations on constants. diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/RationalFunction.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/RationalFunction.kt index f664ae9db..da91c8d61 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/RationalFunction.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/RationalFunction.kt @@ -464,7 +464,7 @@ public interface RationalFunctionSpaceOverRing< C, P: Polynomial, R: RationalFunction, - A: Ring + out A: Ring > : RationalFunctionSpace { /** @@ -566,7 +566,7 @@ public interface RationalFunctionSpaceOverPolynomialSpace< C, P: Polynomial, R: RationalFunction, - AP: PolynomialSpace, + out AP: PolynomialSpace, > : RationalFunctionSpace { /** @@ -1341,7 +1341,7 @@ public interface MultivariateRationalFunctionSpaceOverMultivariatePolynomialSpac V, P: Polynomial, R: RationalFunction, - AP: MultivariatePolynomialSpace, + out AP: MultivariatePolynomialSpace, > : RationalFunctionSpaceOverPolynomialSpace, MultivariateRationalFunctionSpace { /** * Returns sum of the variable represented as a monic monomial and the integer represented as a constant polynomial. diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt index 43048089e..d74c0e1fb 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt @@ -416,6 +416,261 @@ public inline fun > LabeledPolynomialSpace.LabeledPolynomial @UnstableKMathAPI public inline fun > LabeledRationalFunctionSpace.LabeledPolynomialDSL1(initialCapacity: Int? = null, block: DSL1LabeledPolynomialBuilder.() -> Unit) : LabeledPolynomial = DSL1LabeledPolynomialBuilder({ left: C, right: C -> left + right }, initialCapacity).apply(block).build() +/** + * Marks DSL that allows to more simply create [LabeledPolynomial]s with good performance. + * + * For example, polynomial \(5 a^2 c^3 - 6 b\) can be described as + * ``` + * Int.algebra { + * val numberedPolynomial : NumberedPolynomial = NumberedPolynomial { + * 5 { a inPowerOf 2u; c inPowerOf 3u } // 5 a^2 c^3 + + * (-6) { b inPowerOf 1u } // (-6) b^1 + * } + * } + * ``` + * @usesMathJax + */ +@DslMarker +@UnstableKMathAPI +internal annotation class LabeledPolynomialBuilderDSL2 + +/** + * Builder of [LabeledPolynomial]. It should be used as an implicit context for lambdas that describe [LabeledPolynomial]. + */ +@UnstableKMathAPI +@LabeledPolynomialBuilderDSL2 +public class DSL2LabeledPolynomialBuilder( + private val ring: Ring, + /** + * Initial capacity of coefficients map. + */ + initialCapacity: Int? = null +) { + /** + * Coefficients storage. Any declaration of any monomial updates the storage. + * Afterward the storage will be used as a resulting coefficients map. + */ + private val coefficients: MutableMap, C> = if (initialCapacity != null) LinkedHashMap(initialCapacity) else LinkedHashMap() + + /** + * Builds the resulting coefficients map. + * + * In fact, it just returns [coefficients] as regular coefficients map of type `Map, C>`. + */ + @PublishedApi + internal fun build(): LabeledPolynomial = LabeledPolynomial(coefficients) + + public inner class Term internal constructor( + internal val signature: Map = HashMap(), + internal val coefficient: C + ) + + private inline fun submit(signature: Map, onPut: Ring.() -> C, onChange: Ring.(C) -> C) { + coefficients.putOrChange<_, C>(signature, { ring.onPut() }, { ring.onChange(it) }) + } + + private inline fun submit(signature: Map, lazyCoefficient: Ring.() -> C) { + submit(signature, lazyCoefficient, { it + lazyCoefficient() }) + } + + private fun submit(signature: Map, coefficient: C) { + submit(signature) { coefficient } + } + + // TODO: `@submit` will be resolved differently. Change it to `@C`. + private fun C.submit() = submit(emptyMap(), { this@submit }) + + private fun Symbol.submit() = submit(mapOf(this to 1u), { one }) + + private fun Term.submit(): Submit { + submit(signature, coefficient) + return Submit + } + + public object Submit + + public operator fun C.unaryPlus(): Submit { + submit() + return Submit + } + + public operator fun C.unaryMinus(): Submit { + submit(emptyMap(), { -this@unaryMinus }, { it - this@unaryMinus }) + return Submit + } + + public operator fun C.plus(other: C): Submit { + submit(emptyMap(), { this@plus + other }) + return Submit + } + + public operator fun C.minus(other: C): Submit { + submit(emptyMap(), { this@minus - other }) + return Submit + } + + public operator fun C.times(other: C): C = ring { this@times * other } + + public operator fun C.plus(other: Symbol): Submit { + submit(emptyMap(), this) + submit(mapOf(other to 1u), ring.one) + return Submit + } + + public operator fun C.minus(other: Symbol): Submit { + submit(emptyMap(), this) + submit(mapOf(other to 1u), { -one }, { it - one }) + return Submit + } + + public operator fun C.times(other: Symbol): Term = Term(mapOf(other to 1u), this) + + public operator fun C.plus(other: Term): Submit { + submit(emptyMap(), this) + other.submit() + return Submit + } + + public operator fun C.minus(other: Term): Submit { + submit(emptyMap(), this) + submit(other.signature, { -other.coefficient }, { it - other.coefficient }) + return Submit + } + + public operator fun C.times(other: Term): Term = Term(other.signature, ring { this@times * other.coefficient }) + + public operator fun Symbol.plus(other: C): Submit { + this.submit() + other.submit() + return Submit + } + + public operator fun Symbol.minus(other: C): Submit { + this.submit() + submit(emptyMap(), { -other }, { it - other }) + return Submit + } + + public operator fun Symbol.times(other: C): Term = Term(mapOf(this to 1u), other) + + public operator fun Symbol.unaryPlus(): Submit { + this.submit() + return Submit + } + + public operator fun Symbol.unaryMinus(): Submit { + submit(mapOf(this to 1u), { -one }, { it - one }) + return Submit + } + + public operator fun Symbol.plus(other: Symbol): Submit { + this.submit() + other.submit() + return Submit + } + + public operator fun Symbol.minus(other: Symbol): Submit { + this.submit() + submit(mapOf(other to 1u), { -one }, { it - one }) + return Submit + } + + public operator fun Symbol.times(other: Symbol): Term = + if (this == other) Term(mapOf(this to 2u), ring.one) + else Term(mapOf(this to 1u, other to 1u), ring.one) + + public operator fun Symbol.plus(other: Term): Submit { + this.submit() + other.submit() + return Submit + } + + public operator fun Symbol.minus(other: Term): Submit { + this.submit() + submit(other.signature, { -other.coefficient }, { it - other.coefficient }) + return Submit + } + + public operator fun Symbol.times(other: Term): Term = + Term( + other.signature.withPutOrChanged(this, 1u) { it -> it + 1u }, + other.coefficient + ) + + public operator fun Term.plus(other: C): Submit { + this.submit() + other.submit() + return Submit + } + + public operator fun Term.minus(other: C): Submit { + this.submit() + submit(emptyMap(), { -other }, { it - other }) + return Submit + } + + public operator fun Term.times(other: C): Term = + Term( + signature, + ring { coefficient * other } + ) + + public operator fun Term.plus(other: Symbol): Submit { + this.submit() + other.submit() + return Submit + } + + public operator fun Term.minus(other: Symbol): Submit { + this.submit() + submit(mapOf(other to 1u), { -one }, { it - one }) + return Submit + } + + public operator fun Term.times(other: Symbol): Term = + Term( + signature.withPutOrChanged(other, 1u) { it -> it + 1u }, + coefficient + ) + + public operator fun Term.unaryPlus(): Submit { + this.submit() + return Submit + } + + public operator fun Term.unaryMinus(): Submit { + submit(signature, { -coefficient }, { it - coefficient }) + return Submit + } + + public operator fun Term.plus(other: Term): Submit { + this.submit() + other.submit() + return Submit + } + + public operator fun Term.minus(other: Term): Submit { + this.submit() + submit(other.signature, { -other.coefficient }, { it - other.coefficient }) + return Submit + } + + public operator fun Term.times(other: Term): Term = + Term( + mergeBy(signature, other.signature) { deg1, deg2 -> deg1 + deg2 }, + ring { coefficient * other.coefficient } + ) +} + +//@UnstableKMathAPI +//public fun Ring.LabeledPolynomialDSL2(initialCapacity: Int? = null, block: DSL2LabeledPolynomialBuilder.() -> Unit): LabeledPolynomial = DSL2LabeledPolynomialBuilder(this, initialCapacity).apply(block).build() + +@UnstableKMathAPI +public fun > LabeledPolynomialSpace.LabeledPolynomialDSL2(initialCapacity: Int? = null, block: DSL2LabeledPolynomialBuilder.() -> Unit): LabeledPolynomial = DSL2LabeledPolynomialBuilder(ring, initialCapacity).apply(block).build() + +@UnstableKMathAPI +public fun > LabeledRationalFunctionSpace.LabeledPolynomialDSL2(initialCapacity: Int? = null, block: DSL2LabeledPolynomialBuilder.() -> Unit): LabeledPolynomial = DSL2LabeledPolynomialBuilder(ring, initialCapacity).apply(block).build() + // Waiting for context receivers :( FIXME: Replace with context receivers when they will be available /**