From 680d23ddcb0c3e11ea779146f3d2ced2d8c741be Mon Sep 17 00:00:00 2001 From: Gleb Minaev <43728100+lounres@users.noreply.github.com> Date: Sat, 18 Jun 2022 01:25:14 +0300 Subject: [PATCH] Last sift. Cleaned up labeled structures. --- .../kmath/functions/LabeledPolynomial.kt | 621 ++++++++++++++++++ .../functions/LabeledRationalFunction.kt | 96 +++ .../kmath/functions/ListPolynomial.kt | 2 +- .../kmath/functions/NumberedPolynomial.kt | 11 +- .../kmath/functions/labeledConstructors.kt | 518 +++++++++++++++ .../kscience/kmath/functions/labeledUtil.kt | 327 +++++++++ .../kmath/functions/numberedConstructors.kt | 40 +- .../kscience/kmath/functions/numberedUtil.kt | 18 +- 8 files changed, 1584 insertions(+), 49 deletions(-) create mode 100644 kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledPolynomial.kt create mode 100644 kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledRationalFunction.kt create mode 100644 kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/labeledConstructors.kt create mode 100644 kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/labeledUtil.kt diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledPolynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledPolynomial.kt new file mode 100644 index 000000000..b0c54502d --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledPolynomial.kt @@ -0,0 +1,621 @@ +/* + * 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. + */ + +package space.kscience.kmath.functions + +import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.operations.Ring +import kotlin.jvm.JvmName +import kotlin.math.max + + +/** + * Represents multivariate polynomial that stores its coefficients in a [Map] and terms' signatures in a [Map] that + * associates variables (of type [Symbol]) with their degree. + * + * @param C the type of constants. + */ +public data class LabeledPolynomial +@PublishedApi +internal constructor( + /** + * Map that contains coefficients of the polynomial. + * + * Every monomial `a x_1^{d_1} ... x_n^{d_n}` is stored as a pair "key-value" in the map, where the value is the + * coefficient `a` and the key is a map that associates variables in the monomial with their degree in the monomial. + * For example, coefficients of a polynomial `5 a^2 c^3 - 6 b` can be represented as + * ``` + * mapOf( + * mapOf( + * a to 2, + * c to 3 + * ) to 5, + * mapOf( + * b to 1 + * ) to (-6) + * ) + * ``` + * and also as + * ``` + * mapOf( + * mapOf( + * a to 2, + * c to 3 + * ) to 5, + * mapOf( + * b to 1 + * ) to (-6), + * mapOf( + * b to 1, + * c to 1 + * ) to 0 + * ) + * ``` + * where `a`, `b` and `c` are corresponding [Symbol] objects. + */ + public val coefficients: Map, C> +) : Polynomial { + override fun toString(): String = "LabeledPolynomial$coefficients" +} + +/** + * Arithmetic context for multivariate polynomials with coefficients stored as a [Map] and terms' signatures stored as a + * [Map] constructed with the provided [ring] of constants. + * + * @param C the type of constants. Polynomials have them a coefficients in their terms. + * @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 override val ring: A, +) : MultivariatePolynomialSpace>, PolynomialSpaceOverRing, A> { + /** + * Returns sum of the variable represented as a monic monomial and the integer represented as a constant polynomial. + */ + public override operator fun Symbol.plus(other: Int): LabeledPolynomial = + if (other == 0) LabeledPolynomialAsIs(mapOf( + mapOf(this@plus to 1U) to constantOne, + )) + else LabeledPolynomialAsIs(mapOf( + mapOf(this@plus to 1U) to constantOne, + emptyMap() to constantOne * other, + )) + /** + * Returns difference between the variable represented as a monic monomial and the integer represented as a constant polynomial. + */ + public override operator fun Symbol.minus(other: Int): LabeledPolynomial = + if (other == 0) LabeledPolynomialAsIs(mapOf( + mapOf(this@minus to 1U) to -constantOne, + )) + else LabeledPolynomialAsIs(mapOf( + mapOf(this@minus to 1U) to -constantOne, + emptyMap() to constantOne * other, + )) + /** + * Returns product of the variable represented as a monic monomial and the integer represented as a constant polynomial. + */ + public override operator fun Symbol.times(other: Int): LabeledPolynomial = + if (other == 0) zero + else LabeledPolynomialAsIs(mapOf( + mapOf(this to 1U) to constantOne * other, + )) + + /** + * Returns sum of the integer represented as a constant polynomial and the variable represented as a monic monomial. + */ + public override operator fun Int.plus(other: Symbol): LabeledPolynomial = + if (this == 0) LabeledPolynomialAsIs(mapOf( + mapOf(other to 1U) to constantOne, + )) + else LabeledPolynomialAsIs(mapOf( + mapOf(other to 1U) to constantOne, + emptyMap() to constantOne * this@plus, + )) + /** + * Returns difference between the integer represented as a constant polynomial and the variable represented as a monic monomial. + */ + public override operator fun Int.minus(other: Symbol): LabeledPolynomial = + if (this == 0) LabeledPolynomialAsIs(mapOf( + mapOf(other to 1U) to -constantOne, + )) + else LabeledPolynomialAsIs(mapOf( + mapOf(other to 1U) to -constantOne, + emptyMap() to constantOne * this@minus, + )) + /** + * Returns product of the integer represented as a constant polynomial and the variable represented as a monic monomial. + */ + public override operator fun Int.times(other: Symbol): LabeledPolynomial = + if (this == 0) zero + else LabeledPolynomialAsIs(mapOf( + mapOf(other to 1U) to constantOne * this@times, + )) + + /** + * Returns sum of the polynomial and the integer represented as a polynomial. + * + * The operation is equivalent to adding [other] copies of unit polynomial to [this]. + */ + public override operator fun LabeledPolynomial.plus(other: Int): LabeledPolynomial = + if (other == 0) this + else with(coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap() to other.asConstant())) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + val degs = emptyMap() + + this[degs] = getOrElse(degs) { constantZero } + other + } + ) + } + /** + * Returns difference between the polynomial and the integer represented as a polynomial. + * + * The operation is equivalent to subtraction [other] copies of unit polynomial from [this]. + */ + public override operator fun LabeledPolynomial.minus(other: Int): LabeledPolynomial = + if (other == 0) this + else with(coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap() to (-other).asConstant())) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + val degs = emptyMap() + + this[degs] = getOrElse(degs) { constantZero } - other + } + ) + } + /** + * Returns product of the polynomial and the integer represented as a polynomial. + * + * The operation is equivalent to sum of [other] copies of [this]. + */ + public override operator fun LabeledPolynomial.times(other: Int): LabeledPolynomial = + if (other == 0) zero + else LabeledPolynomial( + coefficients + .toMutableMap() + .apply { + for (degs in keys) this[degs] = this[degs]!! * other + } + ) + + /** + * Returns sum of the integer represented as a polynomial and the polynomial. + * + * The operation is equivalent to adding [this] copies of unit polynomial to [other]. + */ + public override operator fun Int.plus(other: LabeledPolynomial): LabeledPolynomial = + if (this == 0) other + else with(other.coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap() to this@plus.asConstant())) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + val degs = emptyMap() + + this[degs] = this@plus + getOrElse(degs) { constantZero } + } + ) + } + /** + * Returns difference between the integer represented as a polynomial and the polynomial. + * + * The operation is equivalent to subtraction [this] copies of unit polynomial from [other]. + */ + public override operator fun Int.minus(other: LabeledPolynomial): LabeledPolynomial = + if (this == 0) other + else with(other.coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap() to this@minus.asConstant())) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + forEach { (key, value) -> if (key.isNotEmpty()) this[key] = -value } + + val degs = emptyMap() + + this[degs] = this@minus - getOrElse(degs) { constantZero } + } + ) + } + /** + * Returns product of the integer represented as a polynomial and the polynomial. + * + * The operation is equivalent to sum of [this] copies of [other]. + */ + public override operator fun Int.times(other: LabeledPolynomial): LabeledPolynomial = + if (this == 0) zero + else LabeledPolynomial( + other.coefficients + .toMutableMap() + .apply { + for (degs in keys) this[degs] = this@times * this[degs]!! + } + ) + + /** + * Converts the integer [value] to polynomial. + */ + public override fun number(value: Int): LabeledPolynomial = number(constantNumber(value)) + + /** + * Returns sum of the variable represented as a monic monomial and the constant represented as a constant polynomial. + */ + public override operator fun Symbol.plus(other: C): LabeledPolynomial = + LabeledPolynomialAsIs(mapOf( + mapOf(this@plus to 1U) to constantOne, + emptyMap() to other, + )) + /** + * Returns difference between the variable represented as a monic monomial and the constant represented as a constant polynomial. + */ + public override operator fun Symbol.minus(other: C): LabeledPolynomial = + LabeledPolynomialAsIs(mapOf( + mapOf(this@minus to 1U) to -constantOne, + emptyMap() to other, + )) + /** + * Returns product of the variable represented as a monic monomial and the constant represented as a constant polynomial. + */ + public override operator fun Symbol.times(other: C): LabeledPolynomial = + LabeledPolynomialAsIs(mapOf( + mapOf(this@times to 1U) to other, + )) + + /** + * Returns sum of the constant represented as a constant polynomial and the variable represented as a monic monomial. + */ + public override operator fun C.plus(other: Symbol): LabeledPolynomial = + LabeledPolynomialAsIs(mapOf( + mapOf(other to 1U) to constantOne, + emptyMap() to this@plus, + )) + /** + * Returns difference between the constant represented as a constant polynomial and the variable represented as a monic monomial. + */ + public override operator fun C.minus(other: Symbol): LabeledPolynomial = + LabeledPolynomialAsIs(mapOf( + mapOf(other to 1U) to -constantOne, + emptyMap() to this@minus, + )) + /** + * Returns product of the constant represented as a constant polynomial and the variable represented as a monic monomial. + */ + public override operator fun C.times(other: Symbol): LabeledPolynomial = + LabeledPolynomialAsIs(mapOf( + mapOf(other to 1U) to this@times, + )) + + /** + * Returns sum of the constant represented as a polynomial and the polynomial. + */ + override operator fun C.plus(other: LabeledPolynomial): LabeledPolynomial = + with(other.coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap() to this@plus)) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + val degs = emptyMap() + + this[degs] = this@plus + getOrElse(degs) { constantZero } + } + ) + } + /** + * Returns difference between the constant represented as a polynomial and the polynomial. + */ + override operator fun C.minus(other: LabeledPolynomial): LabeledPolynomial = + with(other.coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap() to this@minus)) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c } + + val degs = emptyMap() + + this[degs] = this@minus - getOrElse(degs) { constantZero } + } + ) + } + /** + * Returns product of the constant represented as a polynomial and the polynomial. + */ + override operator fun C.times(other: LabeledPolynomial): LabeledPolynomial = + LabeledPolynomialAsIs( + other.coefficients + .toMutableMap() + .apply { + for (degs in keys) this[degs] = this@times * this[degs]!! + } + ) + + /** + * Returns sum of the constant represented as a polynomial and the polynomial. + */ + override operator fun LabeledPolynomial.plus(other: C): LabeledPolynomial = + with(coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap() to other)) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + val degs = emptyMap() + + this[degs] = getOrElse(degs) { constantZero } + other + } + ) + } + /** + * Returns difference between the constant represented as a polynomial and the polynomial. + */ + override operator fun LabeledPolynomial.minus(other: C): LabeledPolynomial = + with(coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap() to other)) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c } + + val degs = emptyMap() + + this[degs] = getOrElse(degs) { constantZero } - other + } + ) + } + /** + * Returns product of the constant represented as a polynomial and the polynomial. + */ + override operator fun LabeledPolynomial.times(other: C): LabeledPolynomial = + LabeledPolynomialAsIs( + coefficients + .toMutableMap() + .apply { + for (degs in keys) this[degs] = this[degs]!! * other + } + ) + + /** + * Converts the constant [value] to polynomial. + */ + public override fun number(value: C): LabeledPolynomial = + LabeledPolynomial(mapOf(emptyMap() to value)) + + /** + * Represents the variable as a monic monomial. + */ + public override operator fun Symbol.unaryPlus(): LabeledPolynomial = + LabeledPolynomialAsIs(mapOf( + mapOf(this to 1U) to constantOne, + )) + /** + * Returns negation of representation of the variable as a monic monomial. + */ + public override operator fun Symbol.unaryMinus(): LabeledPolynomial = + LabeledPolynomialAsIs(mapOf( + mapOf(this to 1U) to -constantOne, + )) + /** + * Returns sum of the variables represented as monic monomials. + */ + public override operator fun Symbol.plus(other: Symbol): LabeledPolynomial = + if (this == other) LabeledPolynomialAsIs(mapOf( + mapOf(this to 1U) to constantOne * 2 + )) + else LabeledPolynomialAsIs(mapOf( + mapOf(this to 1U) to constantOne, + mapOf(other to 1U) to constantOne, + )) + /** + * Returns difference between the variables represented as monic monomials. + */ + public override operator fun Symbol.minus(other: Symbol): LabeledPolynomial = + if (this == other) zero + else LabeledPolynomialAsIs(mapOf( + mapOf(this to 1U) to constantOne, + mapOf(other to 1U) to -constantOne, + )) + /** + * Returns product of the variables represented as monic monomials. + */ + public override operator fun Symbol.times(other: Symbol): LabeledPolynomial = + if (this == other) LabeledPolynomialAsIs(mapOf( + mapOf(this to 2U) to constantOne + )) + else LabeledPolynomialAsIs(mapOf( + mapOf(this to 1U, other to 1U) to constantOne, + )) + + /** + * Returns sum of the variable represented as a monic monomial and the polynomial. + */ + public override operator fun Symbol.plus(other: LabeledPolynomial): LabeledPolynomial = + with(other.coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(mapOf(this@plus to 1u) to constantOne)) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + val degs = mapOf(this@plus to 1U) + + this[degs] = constantOne + getOrElse(degs) { constantZero } + } + ) + } + /** + * Returns difference between the variable represented as a monic monomial and the polynomial. + */ + public override operator fun Symbol.minus(other: LabeledPolynomial): LabeledPolynomial = + with(other.coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(mapOf(this@minus to 1u) to constantOne)) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + val degs = mapOf(this@minus to 1U) + + forEach { (degs, c) -> if(degs != degs) this[degs] = -c } + + this[degs] = constantOne - getOrElse(degs) { constantZero } + } + ) + } + /** + * Returns product of the variable represented as a monic monomial and the polynomial. + */ + public override operator fun Symbol.times(other: LabeledPolynomial): LabeledPolynomial = + LabeledPolynomialAsIs( + other.coefficients + .mapKeys { (degs, _) -> degs.toMutableMap().also{ it[this] = if (this in it) it[this]!! + 1U else 1U } } + ) + + /** + * Returns sum of the polynomial and the variable represented as a monic monomial. + */ + public override operator fun LabeledPolynomial.plus(other: Symbol): LabeledPolynomial = + with(coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(mapOf(other to 1u) to constantOne)) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + val degs = mapOf(other to 1U) + + this[degs] = constantOne + getOrElse(degs) { constantZero } + } + ) + } + /** + * Returns difference between the polynomial and the variable represented as a monic monomial. + */ + public override operator fun LabeledPolynomial.minus(other: Symbol): LabeledPolynomial = + with(coefficients) { + if (isEmpty()) LabeledPolynomialAsIs(mapOf(mapOf(other to 1u) to constantOne)) + else LabeledPolynomialAsIs( + toMutableMap() + .apply { + val degs = mapOf(other to 1U) + + this[degs] = constantOne - getOrElse(degs) { constantZero } + } + ) + } + /** + * Returns product of the polynomial and the variable represented as a monic monomial. + */ + public override operator fun LabeledPolynomial.times(other: Symbol): LabeledPolynomial = + LabeledPolynomialAsIs( + coefficients + .mapKeys { (degs, _) -> degs.toMutableMap().also{ it[other] = if (other in it) it[other]!! + 1U else 1U } } + ) + + /** + * Returns negation of the polynomial. + */ + override fun LabeledPolynomial.unaryMinus(): LabeledPolynomial = + LabeledPolynomialAsIs( + coefficients.mapValues { -it.value } + ) + /** + * Returns sum of the polynomials. + */ + override operator fun LabeledPolynomial.plus(other: LabeledPolynomial): LabeledPolynomial = + LabeledPolynomialAsIs( + buildMap(coefficients.size + other.coefficients.size) { + other.coefficients.mapValuesTo(this) { it.value } + other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! + value else value } + } + ) + /** + * Returns difference of the polynomials. + */ + override operator fun LabeledPolynomial.minus(other: LabeledPolynomial): LabeledPolynomial = + LabeledPolynomialAsIs( + buildMap(coefficients.size + other.coefficients.size) { + other.coefficients.mapValuesTo(this) { it.value } + other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! - value else -value } + } + ) + /** + * Returns product of the polynomials. + */ + override operator fun LabeledPolynomial.times(other: LabeledPolynomial): LabeledPolynomial = + LabeledPolynomialAsIs( + buildMap(coefficients.size * other.coefficients.size) { + for ((degs1, c1) in coefficients) for ((degs2, c2) in other.coefficients) { + val degs = degs1.toMutableMap() + degs2.mapValuesTo(degs) { (variable, deg) -> degs.getOrElse(variable) { 0u } + deg } + val c = c1 * c2 + this[degs] = if (degs in this) this[degs]!! + c else c + } + } + ) + + /** + * Instance of zero polynomial (zero of the polynomial ring). + */ + override val zero: LabeledPolynomial = LabeledPolynomialAsIs(mapOf(emptyMap() to constantZero)) + /** + * Instance of unit polynomial (unit of the polynomial ring). + */ + override val one: LabeledPolynomial = LabeledPolynomialAsIs(mapOf(emptyMap() to constantOne)) + + /** + * Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is + * zero, degree is -1. + */ + override val LabeledPolynomial.degree: Int + get() = coefficients.entries.maxOfOrNull { (degs, _) -> degs.values.sum().toInt() } ?: -1 + /** + * Map that associates variables (that appear in the polynomial in positive exponents) with their most exponents + * in which they are appeared in the polynomial. + * + * As consequence all values in the map are positive integers. Also, if the polynomial is constant, the map is empty. + * And keys of the map is the same as in [variables]. + */ + public override val LabeledPolynomial.degrees: Map + get() = + buildMap { + coefficients.entries.forEach { (degs, _) -> + degs.mapValuesTo(this) { (variable, deg) -> + max(getOrElse(variable) { 0u }, deg) + } + } + } + /** + * Counts degree of the polynomial by the specified [variable]. + */ + public override fun LabeledPolynomial.degreeBy(variable: Symbol): UInt = + coefficients.entries.maxOfOrNull { (degs, _) -> degs.getOrElse(variable) { 0u } } ?: 0u + /** + * Counts degree of the polynomial by the specified [variables]. + */ + public override fun LabeledPolynomial.degreeBy(variables: Collection): UInt = + coefficients.entries.maxOfOrNull { (degs, _) -> degs.filterKeys { it in variables }.values.sum() } ?: 0u + /** + * Set of all variables that appear in the polynomial in positive exponents. + */ + public override val LabeledPolynomial.variables: Set + get() = + buildSet { + coefficients.entries.forEach { (degs, _) -> addAll(degs.keys) } + } + /** + * Count of all variables that appear in the polynomial in positive exponents. + */ + public override val LabeledPolynomial.countOfVariables: Int get() = variables.size + + // TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with + // [ListPolynomialSpace] as a context receiver + /** + * Substitutes provided arguments [arguments] into [this] polynomial. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun LabeledPolynomial.substitute(arguments: Map): LabeledPolynomial = substitute(ring, arguments) + /** + * Substitutes provided arguments [arguments] into [this] polynomial. + */ + @Suppress("NOTHING_TO_INLINE") + @JvmName("substitutePolynomial") + public inline fun LabeledPolynomial.substitute(arguments: Map>) : LabeledPolynomial = substitute(ring, arguments) +} \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledRationalFunction.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledRationalFunction.kt new file mode 100644 index 000000000..03f323813 --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledRationalFunction.kt @@ -0,0 +1,96 @@ +/* + * 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. + */ + +package space.kscience.kmath.functions + +import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.operations.Ring +import kotlin.jvm.JvmName + + +/** + * Represents multivariate rational function that stores its numerator and denominator as [LabeledPolynomial]s. + */ +public class LabeledRationalFunction( + public override val numerator: LabeledPolynomial, + public override val denominator: LabeledPolynomial +) : RationalFunction> { + override fun toString(): String = "LabeledRationalFunction${numerator.coefficients}/${denominator.coefficients}" +} + +/** + * Arithmetic context for univariate rational functions with numerator and denominator represented as [LabeledPolynomial]s. + * + * @param C the type of constants. Polynomials have them a coefficients in their terms. + * @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 LabeledRationalFunctionSpace>( + public val ring: A, +) : + MultivariateRationalFunctionalSpaceOverMultivariatePolynomialSpace< + C, + Symbol, + LabeledPolynomial, + LabeledRationalFunction, + LabeledPolynomialSpace, + >, + MultivariatePolynomialSpaceOfFractions< + C, + Symbol, + LabeledPolynomial, + LabeledRationalFunction, + >() { + + /** + * Underlying polynomial ring. Its polynomial operations are inherited by local polynomial operations. + */ + override val polynomialRing : LabeledPolynomialSpace = LabeledPolynomialSpace(ring) + /** + * Constructor of rational functions (of type [LabeledRationalFunction]) from numerator and denominator (of type [LabeledPolynomial]). + */ + override fun constructRationalFunction( + numerator: LabeledPolynomial, + denominator: LabeledPolynomial + ): LabeledRationalFunction = + LabeledRationalFunction(numerator, denominator) + + // TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with + // [ListPolynomialSpace] as a context receiver + /** + * Substitutes provided constant [argument] into [this] polynomial. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun LabeledPolynomial.substitute(argument: Map): LabeledPolynomial = substitute(ring, argument) + /** + * Substitutes provided polynomial [argument] into [this] polynomial. + */ + @Suppress("NOTHING_TO_INLINE") + @JvmName("substitutePolynomial") + public inline fun LabeledPolynomial.substitute(argument: Map>): LabeledPolynomial = substitute(ring, argument) + /** + * Substitutes provided rational function [argument] into [this] polynomial. + */ + @Suppress("NOTHING_TO_INLINE") + @JvmName("substituteRationalFunction") + public inline fun LabeledPolynomial.substitute(argument: Map>): LabeledRationalFunction = substitute(ring, argument) + /** + * Substitutes provided constant [argument] into [this] rational function. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun LabeledRationalFunction.substitute(argument: Map): LabeledRationalFunction = substitute(ring, argument) + /** + * Substitutes provided polynomial [argument] into [this] rational function. + */ + @Suppress("NOTHING_TO_INLINE") + @JvmName("substitutePolynomial") + public inline fun LabeledRationalFunction.substitute(argument: Map>): LabeledRationalFunction = substitute(ring, argument) + /** + * Substitutes provided rational function [argument] into [this] rational function. + */ + @Suppress("NOTHING_TO_INLINE") + @JvmName("substituteRationalFunction") + public inline fun LabeledRationalFunction.substitute(argument: Map>): LabeledRationalFunction = substitute(ring, argument) +} \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt index b3f1eb8f7..8db93cbb1 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt @@ -15,7 +15,7 @@ import kotlin.math.min /** * Represents univariate polynomial that stores its coefficients in a [List]. * - * @param coefficients constant is the leftmost coefficient. + * @param C the type of constants. */ public data class ListPolynomial( /** diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/NumberedPolynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/NumberedPolynomial.kt index 02a3af683..eadeb68ab 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/NumberedPolynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/NumberedPolynomial.kt @@ -12,7 +12,7 @@ import kotlin.math.max /** - * Represents univariate polynomial that stores its coefficients in a [Map] and terms' signatures in a [List]. + * Represents multivariate polynomial that stores its coefficients in a [Map] and terms' signatures in a [List]. * * @param C the type of constants. */ @@ -20,10 +20,11 @@ public data class NumberedPolynomial @PublishedApi internal constructor( /** - * Map that contains coefficients of the polynomial. Every monomial `a x_1^{d_1} ... x_n^{d_n}` is stored as pair - * "key-value" in the map, where the value is the coefficients `a` and the key is a list that associates index of - * every variable in the monomial with multiplicity of the variable occurring in the monomial. For example - * coefficients of a polynomial `5 x_1^2 x_3^3 - 6 x_2` can be represented as + * Map that contains coefficients of the polynomial. + * + * Every monomial `a x_1^{d_1} ... x_n^{d_n}` is stored as a pair "key-value" in the map, where the value is the + * coefficient `a` and the key is a list that associates index of every variable in the monomial with their degree + * in the monomial. For example, coefficients of a polynomial `5 x_1^2 x_3^3 - 6 x_2` can be represented as * ``` * mapOf( * listOf(2, 0, 3) to 5, // 5 x_1^2 x_3^3 + diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/labeledConstructors.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/labeledConstructors.kt new file mode 100644 index 000000000..47325c4bb --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/labeledConstructors.kt @@ -0,0 +1,518 @@ +/* + * 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. + */ + +@file:Suppress("FunctionName", "NOTHING_TO_INLINE") + +package space.kscience.kmath.functions + +import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.Ring + + +/** + * Returns the same degrees' description of the monomial, but without zero degrees. + */ +internal fun Map.cleanUp() = filterValues { it > 0U } + +/** + * Constructs [LabeledPolynomial] with provided coefficients map [coefs]. The map is used as is. + */ +@PublishedApi +internal inline fun LabeledPolynomialAsIs(coefs: Map, C>) : LabeledPolynomial = LabeledPolynomial(coefs) + +/** + * Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". + * The collections will be transformed to map with [toMap] and then will be used as is. + */ +@PublishedApi +internal inline fun LabeledPolynomialAsIs(pairs: Collection, C>>) : LabeledPolynomial = LabeledPolynomial(pairs.toMap()) + +/** + * Constructs [LabeledPolynomial] with provided array of [pairs] of pairs "term's signature — term's coefficient". + * The array will be transformed to map with [toMap] and then will be used as is. + */ +@PublishedApi +internal inline fun LabeledPolynomialAsIs(vararg pairs: Pair, C>) : LabeledPolynomial = LabeledPolynomial(pairs.toMap()) + +/** + * Constructs [LabeledPolynomial] with provided coefficients map [coefs]. The map is used as is. + * + * **Be sure you read description of [LabeledPolynomial.coefficients]. Otherwise, you may make a mistake that will + * cause wrong computation result or even runtime error.** + */ +@DelicatePolynomialAPI +public inline fun LabeledPolynomialWithoutCheck(coefs: Map, C>) : LabeledPolynomial = LabeledPolynomial(coefs) + +/** + * Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". + * The collections will be transformed to map with [toMap] and then will be used as is. + * + * **Be sure you read description of [LabeledPolynomial.coefficients]. Otherwise, you may make a mistake that will + * cause wrong computation result or even runtime error.** + */ +@DelicatePolynomialAPI +public inline fun LabeledPolynomialWithoutCheck(pairs: Collection, C>>) : LabeledPolynomial = LabeledPolynomial(pairs.toMap()) + +/** + * Constructs [LabeledPolynomial] with provided array of [pairs] of pairs "term's signature — term's coefficient". + * The array will be transformed to map with [toMap] and then will be used as is. + * + * **Be sure you read description of [LabeledPolynomial.coefficients]. Otherwise, you may make a mistake that will + * cause wrong computation result or even runtime error.** + */ +@DelicatePolynomialAPI +public inline fun LabeledPolynomialWithoutCheck(vararg pairs: Pair, C>) : LabeledPolynomial = LabeledPolynomial(pairs.toMap()) + +/** + * Constructs [LabeledPolynomial] with provided coefficients map [coefs]. + * + * [coefs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public fun LabeledPolynomial(coefs: Map, C>, add: (C, C) -> C) : LabeledPolynomial { + val fixedCoefs = mutableMapOf, C>() + + for (entry in coefs) { + val key = entry.key.cleanUp() + val value = entry.value + fixedCoefs[key] = if (key in fixedCoefs) add(fixedCoefs[key]!!, value) else value + } + + return LabeledPolynomial(fixedCoefs) +} + +/** + * Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". + * + * [pairs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public fun LabeledPolynomial(pairs: Collection, C>>, add: (C, C) -> C) : LabeledPolynomial { + val fixedCoefs = mutableMapOf, C>() + + for (entry in pairs) { + val key = entry.first.cleanUp() + val value = entry.second + fixedCoefs[key] = if (key in fixedCoefs) add(fixedCoefs[key]!!, value) else value + } + + return LabeledPolynomial(fixedCoefs) +} + +/** + * Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient". + * + * [pairs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public fun LabeledPolynomial(vararg pairs: Pair, C>, add: (C, C) -> C) : LabeledPolynomial { + val fixedCoefs = mutableMapOf, C>() + + for (entry in pairs) { + val key = entry.first.cleanUp() + val value = entry.second + fixedCoefs[key] = if (key in fixedCoefs) add(fixedCoefs[key]!!, value) else value + } + + return LabeledPolynomial(fixedCoefs) +} + +// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available + +/** + * Constructs [LabeledPolynomial] with provided coefficients map [coefs]. + * + * [coefs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public inline fun > A.LabeledPolynomial(coefs: Map, C>) : LabeledPolynomial = LabeledPolynomial(coefs, ::add) +/** + * Constructs [LabeledPolynomial] with provided coefficients map [coefs]. + * + * [coefs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public inline fun > LabeledPolynomialSpace.LabeledPolynomial(coefs: Map, C>) : LabeledPolynomial = LabeledPolynomial(coefs) { left: C, right: C -> left + right } + +/** + * Constructs [LabeledPolynomial] with provided coefficients map [coefs]. + * + * [coefs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public inline fun > LabeledRationalFunctionSpace.LabeledPolynomial(coefs: Map, C>) : LabeledPolynomial = LabeledPolynomial(coefs) { left: C, right: C -> left + right } + +/** + * Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". + * + * [pairs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public inline fun > A.LabeledPolynomial(pairs: Collection, C>>) : LabeledPolynomial = LabeledPolynomial(pairs, ::add) + +/** + * Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". + * + * [pairs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public inline fun > LabeledPolynomialSpace.LabeledPolynomial(pairs: Collection, C>>) : LabeledPolynomial = LabeledPolynomial(pairs) { left: C, right: C -> left + right } +/** + * Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". + * + * [pairs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public inline fun > LabeledRationalFunctionSpace.LabeledPolynomial(pairs: Collection, C>>) : LabeledPolynomial = LabeledPolynomial(pairs) { left: C, right: C -> left + right } + +/** + * Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient". + * + * [pairs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public inline fun > A.LabeledPolynomial(vararg pairs: Pair, C>) : LabeledPolynomial = LabeledPolynomial(*pairs) { left: C, right: C -> left + right } +/** + * Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient". + * + * [pairs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public inline fun > LabeledPolynomialSpace.LabeledPolynomial(vararg pairs: Pair, C>) : LabeledPolynomial = LabeledPolynomial(*pairs) { left: C, right: C -> left + right } +/** + * Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient". + * + * [pairs] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public inline fun > LabeledRationalFunctionSpace.LabeledPolynomial(vararg pairs: Pair, C>) : LabeledPolynomial = LabeledPolynomial(*pairs) { left: C, right: C -> left + right } + +/** + * Converts [this] constant to [LabeledPolynomial]. + */ +public inline fun C.asLabeledPolynomial() : LabeledPolynomial = LabeledPolynomialAsIs(mapOf(emptyMap() to this)) + +///** +//// * Converts [this] variable to [LabeledPolynomial]. +//// */ +//context(A) +//public inline fun > Symbol.asLabeledPolynomial() : LabeledPolynomial = LabeledPolynomial(mapOf(mapOf(this to 1u) to one)) +///** +// * Converts [this] variable to [LabeledPolynomial]. +// */ +//context(LabeledPolynomialSpace) +//public inline fun > Symbol.asLabeledPolynomial() : LabeledPolynomial = LabeledPolynomial(mapOf(mapOf(this to 1u) to constantOne)) +///** +// * Converts [this] variable to [LabeledPolynomial]. +// */ +//context(LabeledRationalFunctionSpace) +//public inline fun > Symbol.asLabeledPolynomial() : LabeledPolynomial = LabeledPolynomial(mapOf(mapOf(this to 1u) to constantOne)) + +/** + * 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 + * } + * } + * ``` + */ +@DslMarker +@UnstableKMathAPI +internal annotation class LabeledPolynomialConstructorDSL + +/** + * Builder of [LabeledPolynomial] signature. It should be used as an implicit context for lambdas that describe term signature. + */ +@UnstableKMathAPI +@LabeledPolynomialConstructorDSL +public class LabeledPolynomialTermSignatureBuilder { + /** + * Signature storage. Any declaration of any variable's power updates the storage by increasing corresponding value. + * Afterward the storage will be used as a resulting signature. + */ + private val signature: MutableMap = LinkedHashMap() + + /** + * Builds the resulting signature. + * + * In fact, it just returns [signature] as regular signature of type `List`. + */ + @PublishedApi + internal fun build(): Map = signature + + /** + * Declares power of [this] variable of degree [deg]. + * + * Declaring another power of the same variable will increase its degree by received degree. + */ + public infix fun Symbol.inPowerOf(deg: UInt) { + signature[this] = deg + } + /** + * Declares power of [this] variable of degree [deg]. + * + * Declaring another power of the same variable will increase its degree by received degree. + */ + @Suppress("NOTHING_TO_INLINE") + public inline infix fun Symbol.pow(deg: UInt): Unit = this inPowerOf deg + /** + * Declares power of [this] variable of degree [deg]. + * + * Declaring another power of the same variable will increase its degree by received degree. + */ + @Suppress("NOTHING_TO_INLINE") + public inline infix fun Symbol.`in`(deg: UInt): Unit = this inPowerOf deg + /** + * Declares power of [this] variable of degree [deg]. + * + * Declaring another power of the same variable will increase its degree by received degree. + */ + @Suppress("NOTHING_TO_INLINE") + public inline infix fun Symbol.of(deg: UInt): Unit = this inPowerOf deg +} + +/** + * Builder of [LabeledPolynomial]. It should be used as an implicit context for lambdas that describe [LabeledPolynomial]. + */ +@UnstableKMathAPI +public class LabeledPolynomialBuilder( + /** + * Summation operation that will be used to sum coefficients of monomials of same signatures. + */ + private val add: (C, C) -> C, + /** + * Initial capacity of coefficients map. + */ + initialCapacity: Int = 0 +) { + /** + * 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> = LinkedHashMap(initialCapacity) + + /** + * 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) + + /** + * Declares monomial with [this] coefficient and provided [signature]. + * + * Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such + * coefficients is zero at any moment the monomial won't be removed but will be left as it is. + */ + public infix fun C.with(signature: Map) { + coefficients[signature] = if (signature in coefficients) add(coefficients[signature]!!, this@with) else this@with + } + /** + * Declares monomial with [this] coefficient and signature constructed by [block]. + * + * Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such + * coefficients is zero at any moment the monomial won't be removed but will be left as it is. + */ + @Suppress("NOTHING_TO_INLINE") + public inline infix fun C.with(noinline block: LabeledPolynomialTermSignatureBuilder.() -> Unit): Unit = this.invoke(block) + /** + * Declares monomial with [this] coefficient and signature constructed by [block]. + * + * Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such + * coefficients is zero at any moment the monomial won't be removed but will be left as it is. + */ + public inline operator fun C.invoke(block: LabeledPolynomialTermSignatureBuilder.() -> Unit): Unit = + this with LabeledPolynomialTermSignatureBuilder().apply(block).build() +} + +// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available + +///** +// * Creates [LabeledPolynomial] with lambda [block] in context of [this] ring of constants. +// * +// * For example, polynomial `5 x_1^2 x_3^3 - 6 x_2` can be described as +// * ``` +// * Int.algebra { +// * val LabeledPolynomial : LabeledPolynomial = LabeledPolynomial { +// * 5 { 1 inPowerOf 2u; 3 inPowerOf 3u } // 5 x_1^2 x_3^3 + +// * (-6) { 2 inPowerOf 1u } // (-6) x_2^1 +// * } +// * } +// * ``` +// */ +// FIXME: For now this fabric does not let next two fabrics work. (See KT-52803.) Possible feature solutions: +// 1. `LowPriorityInOverloadResolution` becomes public. Then it should be applied to this function. +// 2. Union types are implemented. Then all three functions should be rewritten +// as one with single union type as a (context) receiver. +//@UnstableKMathAPI +//public inline fun > A.LabeledPolynomial(initialCapacity: Int = 0, block: LabeledPolynomialBuilder.() -> Unit) : LabeledPolynomial = LabeledPolynomialBuilder(::add, initialCapacity).apply(block).build() +/** + * Creates [LabeledPolynomial] with lambda [block] in context of [this] ring of [LabeledPolynomial]s. + * + * For example, polynomial `5 x_1^2 x_3^3 - 6 x_2` can be described as + * ``` + * Int.algebra { + * val LabeledPolynomial : LabeledPolynomial = LabeledPolynomial { + * 5 { 1 inPowerOf 2u; 3 inPowerOf 3u } // 5 x_1^2 x_3^3 + + * (-6) { 2 inPowerOf 1u } // (-6) x_2^1 + * } + * } + * ``` + */ +@UnstableKMathAPI +public inline fun > LabeledPolynomialSpace.LabeledPolynomial(initialCapacity: Int = 0, block: LabeledPolynomialBuilder.() -> Unit) : LabeledPolynomial = LabeledPolynomialBuilder({ left: C, right: C -> left + right }, initialCapacity).apply(block).build() +/** + * Creates [LabeledPolynomial] with lambda [block] in context of [this] field of [LabeledRationalFunction]s. + * + * For example, polynomial `5 x_1^2 x_3^3 - 6 x_2` can be described as + * ``` + * Int.algebra { + * val LabeledPolynomial : LabeledPolynomial = LabeledPolynomial { + * 5 { 1 inPowerOf 2u; 3 inPowerOf 3u } // 5 x_1^2 x_3^3 + + * (-6) { 2 inPowerOf 1u } // (-6) x_2^1 + * } + * } + * ``` + */ +@UnstableKMathAPI +public inline fun > LabeledRationalFunctionSpace.LabeledPolynomial(initialCapacity: Int = 0, block: LabeledPolynomialBuilder.() -> Unit) : LabeledPolynomial = LabeledPolynomialBuilder({ left: C, right: C -> left + right }, initialCapacity).apply(block).build() + +// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available + +/** + * Constructs [LabeledRationalFunction] with provided coefficients maps [numeratorCoefficients] and [denominatorCoefficients]. + * + * The maps will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. the maps' keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public fun > A.LabeledRationalFunction(numeratorCoefficients: Map, C>, denominatorCoefficients: Map, C>): LabeledRationalFunction = + LabeledRationalFunction( + LabeledPolynomial(numeratorCoefficients), + LabeledPolynomial(denominatorCoefficients) + ) +/** + * Constructs [LabeledRationalFunction] with provided coefficients maps [numeratorCoefficients] and [denominatorCoefficients]. + * + * The maps will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. the maps' keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public fun > LabeledRationalFunctionSpace.LabeledRationalFunction(numeratorCoefficients: Map, C>, denominatorCoefficients: Map, C>): LabeledRationalFunction = + LabeledRationalFunction( + LabeledPolynomial(numeratorCoefficients), + LabeledPolynomial(denominatorCoefficients) + ) + +/** + * Constructs [LabeledRationalFunction] with provided [numerator] and unit denominator. + */ +public fun > A.LabeledRationalFunction(numerator: LabeledPolynomial): LabeledRationalFunction = + LabeledRationalFunction(numerator, LabeledPolynomial(mapOf(emptyMap() to one))) +/** + * Constructs [LabeledRationalFunction] with provided [numerator] and unit denominator. + */ +public fun > LabeledRationalFunctionSpace.LabeledRationalFunction(numerator: LabeledPolynomial): LabeledRationalFunction = + LabeledRationalFunction(numerator, polynomialOne) + +/** + * Constructs [LabeledRationalFunction] with provided coefficients map [numeratorCoefficients] for numerator and unit + * denominator. + * + * [numeratorCoefficients] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [numeratorCoefficients]'s keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public fun > LabeledRationalFunctionSpace.LabeledRationalFunction(numeratorCoefficients: Map, C>): LabeledRationalFunction = + LabeledRationalFunction( + LabeledPolynomial(numeratorCoefficients), + polynomialOne + ) +/** + * Constructs [LabeledRationalFunction] with provided coefficients map [numeratorCoefficients] for numerator and unit + * denominator. + * + * [numeratorCoefficients] will be "cleaned up": + * 1. Zeros at the ends of terms' signatures (e.g. [numeratorCoefficients]'s keys) will be removed. (See [cleanUp].) + * 1. Terms that happen to have the same signature will be summed up. + * 1. New map will be formed of resulting terms. + */ +public fun > A.LabeledRationalFunction(numeratorCoefficients: Map, C>): LabeledRationalFunction = + LabeledRationalFunction( + LabeledPolynomial(numeratorCoefficients), + LabeledPolynomialAsIs(mapOf(emptyMap() to one)) + ) + +///** +// * Converts [this] constant to [LabeledRationalFunction]. +// */ +//context(A) +//public fun > C.asLabeledRationalFunction() : LabeledRationalFunction = +// LabeledRationalFunction( +// LabeledPolynomialAsIs(mapOf(emptyMap() to this)), +// LabeledPolynomialAsIs(mapOf(emptyMap() to one)) +// ) +///** +// * Converts [this] constant to [LabeledRationalFunction]. +// */ +//context(LabeledRationalFunctionSpace) +//public fun > C.asLabeledRationalFunction() : LabeledRationalFunction = +// LabeledRationalFunction( +// LabeledPolynomialAsIs(mapOf(emptyMap() to this)), +// LabeledPolynomialAsIs(mapOf(emptyMap() to constantOne)) +// ) + +///** +// * Converts [this] variable to [LabeledRationalFunction]. +// */ +//context(A) +//public fun > Symbol.asLabeledRationalFunction() : LabeledRationalFunction = +// LabeledRationalFunction( +// LabeledPolynomialAsIs(mapOf(mapOf(this to 1u) to one)), +// LabeledPolynomialAsIs(mapOf(emptyMap() to one)) +// ) +///** +// * Converts [this] variable to [LabeledRationalFunction]. +// */ +//context(LabeledRationalFunctionSpace) +//public fun > Symbol.asLabeledRationalFunction() : LabeledRationalFunction = +// LabeledRationalFunction( +// LabeledPolynomialAsIs(mapOf(mapOf(this to 1u) to constantOne)), +// LabeledPolynomialAsIs(mapOf(emptyMap() to constantOne)) +// ) \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/labeledUtil.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/labeledUtil.kt new file mode 100644 index 000000000..39c781a14 --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/labeledUtil.kt @@ -0,0 +1,327 @@ +/* + * 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. + */ + +package space.kscience.kmath.functions + +import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.Field +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.operations.Ring +import space.kscience.kmath.operations.algebra +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.jvm.JvmName + + +/** + * Creates a [LabeledPolynomialSpace] over a received ring. + */ +public fun > A.labeledPolynomialSpace(): LabeledPolynomialSpace = + LabeledPolynomialSpace(this) + +/** + * Creates a [LabeledPolynomialSpace]'s scope over a received ring. + */ +public inline fun , R> A.labeledPolynomialSpace(block: LabeledPolynomialSpace.() -> R): R { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return LabeledPolynomialSpace(this).block() +} +/** + * Creates a [LabeledRationalFunctionSpace] over a received ring. + */ +public fun > A.labeledRationalFunctionSpace(): LabeledRationalFunctionSpace = + LabeledRationalFunctionSpace(this) + +/** + * Creates a [LabeledRationalFunctionSpace]'s scope over a received ring. + */ +public inline fun , R> A.labeledRationalFunctionSpace(block: LabeledRationalFunctionSpace.() -> R): R { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return LabeledRationalFunctionSpace(this).block() +} + +/** + * Substitutes provided Double arguments [args] into [this] Double polynomial. + */ +public fun LabeledPolynomial.substitute(args: Map): LabeledPolynomial = Double.algebra { + if (coefficients.isEmpty()) return this@substitute + LabeledPolynomial( + buildMap { + coefficients.forEach { (degs, c) -> + val newDegs = degs.filterKeys { it !in args } + val newC = args.entries.fold(c) { product, (variable, substitution) -> + val deg = degs.getOrElse(variable) { 0u } + if (deg == 0u) product else product * power(substitution, deg) + } + this[newDegs] = if (newDegs in this) this[newDegs]!! + newC else newC + } + } + ) +} + +/** + * Substitutes provided arguments [args] into [this] polynomial. + */ +public fun LabeledPolynomial.substitute(ring: Ring, args: Map): LabeledPolynomial = ring { + if (coefficients.isEmpty()) return this@substitute + LabeledPolynomial( + buildMap { + coefficients.forEach { (degs, c) -> + val newDegs = degs.filterKeys { it !in args } + val newC = args.entries.fold(c) { product, (variable, substitution) -> + val deg = degs.getOrElse(variable) { 0u } + if (deg == 0u) product else product * power(substitution, deg) + } + this[newDegs] = if (newDegs in this) this[newDegs]!! + newC else newC + } + } + ) +} + +/** + * Substitutes provided arguments [args] into [this] polynomial. + */ // TODO: To optimize boxing +@JvmName("substitutePolynomial") +public fun LabeledPolynomial.substitute(ring: Ring, args: Map>) : LabeledPolynomial = + ring.labeledPolynomialSpace { + coefficients.entries.fold(zero) { acc, (degs, c) -> + val newDegs = degs.filterKeys { it !in args } + acc + args.entries.fold(LabeledPolynomial(mapOf(newDegs to c))) { product, (variable, substitution) -> + val deg = degs.getOrElse(variable) { 0u } + if (deg == 0u) product else product * power(substitution, deg) + } + } + } + +/** + * Substitutes provided arguments [args] into [this] polynomial. + */ // TODO: To optimize boxing +@JvmName("substituteRationalFunction") +public fun LabeledPolynomial.substitute(ring: Ring, args: Map>) : LabeledRationalFunction = + ring.labeledRationalFunctionSpace { + coefficients.entries.fold(zero) { acc, (degs, c) -> + val newDegs = degs.filterKeys { it !in args } + acc + args.entries.fold(LabeledRationalFunction(LabeledPolynomial(mapOf(newDegs to c)))) { product, (variable, substitution) -> + val deg = degs.getOrElse(variable) { 0u } + if (deg == 0u) product else product * power(substitution, deg) + } + } + } + +/** + * Substitutes provided Double arguments [args] into [this] Double rational function. + */ +public fun LabeledRationalFunction.substitute(args: Map): LabeledRationalFunction = + LabeledRationalFunction(numerator.substitute(args), denominator.substitute(args)) + +/** + * Substitutes provided arguments [args] into [this] rational function. + */ +public fun LabeledRationalFunction.substitute(ring: Ring, args: Map): LabeledRationalFunction = + LabeledRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args)) + +/** + * Substitutes provided arguments [args] into [this] rational function. + */ // TODO: To optimize calculation +@JvmName("substitutePolynomial") +public fun LabeledRationalFunction.substitute(ring: Ring, args: Map>) : LabeledRationalFunction = + LabeledRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args)) + +/** + * Substitutes provided arguments [args] into [this] rational function. + */ // TODO: To optimize calculation +@JvmName("substituteRationalFunction") +public fun LabeledRationalFunction.substitute(ring: Ring, args: Map>) : LabeledRationalFunction = + ring.labeledRationalFunctionSpace { + numerator.substitute(ring, args) / denominator.substitute(ring, args) + } + +/** + * Returns algebraic derivative of received polynomial with respect to provided variable. + */ +@UnstableKMathAPI +public fun > LabeledPolynomial.derivativeWithRespectTo( + algebra: A, + variable: Symbol, +): LabeledPolynomial = algebra { + LabeledPolynomial( + buildMap(coefficients.size) { + coefficients + .forEach { (degs, c) -> + if (variable !in degs) return@forEach + put( + buildMap { + degs.forEach { (vari, deg) -> + when { + vari != variable -> put(vari, deg) + deg > 1u -> put(vari, deg - 1u) + } + } + }, + multiplyByDoubling(c, degs[variable]!!) + ) + } + } + ) +} + +/** + * Returns algebraic derivative of received polynomial with respect to provided variable of specified order. + */ +@UnstableKMathAPI +public fun > LabeledPolynomial.nthDerivativeWithRespectTo( + algebra: A, + variable: Symbol, + order: UInt +): LabeledPolynomial = algebra { + if (order == 0u) return this@nthDerivativeWithRespectTo + LabeledPolynomial( + buildMap(coefficients.size) { + coefficients + .forEach { (degs, c) -> + if (degs.getOrElse(variable) { 0u } < order) return@forEach + put( + buildMap { + degs.forEach { (vari, deg) -> + when { + vari != variable -> put(vari, deg) + deg > order -> put(vari, deg - order) + } + } + }, + degs[variable]!!.let { deg -> + (deg downTo deg - order + 1u) + .fold(c) { acc, ord -> multiplyByDoubling(acc, ord) } + } + ) + } + } + ) +} + +/** + * Returns algebraic derivative of received polynomial with respect to provided variables of specified orders. + */ +@UnstableKMathAPI +public fun > LabeledPolynomial.nthDerivativeWithRespectTo( + algebra: A, + variablesAndOrders: Map, +): LabeledPolynomial = algebra { + val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u } + if (filteredVariablesAndOrders.isEmpty()) return this@nthDerivativeWithRespectTo + LabeledPolynomial( + buildMap(coefficients.size) { + coefficients + .forEach { (degs, c) -> + if (filteredVariablesAndOrders.any { (variable, order) -> degs.getOrElse(variable) { 0u } < order }) return@forEach + put( + buildMap { + degs.forEach { (vari, deg) -> + if (vari !in filteredVariablesAndOrders) put(vari, deg) + else { + val order = filteredVariablesAndOrders[vari]!! + if (deg > order) put(vari, deg - order) + } + } + }, + filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) -> + degs[index]!!.let { deg -> + (deg downTo deg - order + 1u) + .fold(acc1) { acc2, ord -> multiplyByDoubling(acc2, ord) } + } + } + ) + } + } + ) +} + +/** + * Returns algebraic antiderivative of received polynomial with respect to provided variable. + */ +@UnstableKMathAPI +public fun > LabeledPolynomial.antiderivativeWithRespectTo( + algebra: A, + variable: Symbol, +): LabeledPolynomial = algebra { + LabeledPolynomial( + buildMap(coefficients.size) { + coefficients + .forEach { (degs, c) -> + val newDegs = buildMap(degs.size + 1) { + put(variable, 1u) + for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u }) + } + put( + newDegs, + c / multiplyByDoubling(one, newDegs[variable]!!) + ) + } + } + ) +} + +/** + * Returns algebraic antiderivative of received polynomial with respect to provided variable of specified order. + */ +@UnstableKMathAPI +public fun > LabeledPolynomial.nthAntiderivativeWithRespectTo( + algebra: A, + variable: Symbol, + order: UInt +): LabeledPolynomial = algebra { + if (order == 0u) return this@nthAntiderivativeWithRespectTo + LabeledPolynomial( + buildMap(coefficients.size) { + coefficients + .forEach { (degs, c) -> + val newDegs = buildMap(degs.size + 1) { + put(variable, order) + for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u }) + } + put( + newDegs, + newDegs[variable]!!.let { deg -> + (deg downTo deg - order + 1u) + .fold(c) { acc, ord -> acc / multiplyByDoubling(one, ord) } + } + ) + } + } + ) +} + +/** + * Returns algebraic derivative of received polynomial with respect to provided variables of specified orders. + */ +@UnstableKMathAPI +public fun > LabeledPolynomial.nthAntiderivativeWithRespectTo( + algebra: A, + variablesAndOrders: Map, +): LabeledPolynomial = algebra { + val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u } + if (filteredVariablesAndOrders.isEmpty()) return this@nthAntiderivativeWithRespectTo + LabeledPolynomial( + buildMap(coefficients.size) { + coefficients + .forEach { (degs, c) -> + val newDegs = buildMap(degs.size + 1) { + for ((variable, order) in filteredVariablesAndOrders) put(variable, order) + for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u }) + } + put( + newDegs, + filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) -> + newDegs[index]!!.let { deg -> + (deg downTo deg - order + 1u) + .fold(acc1) { acc2, ord -> acc2 / multiplyByDoubling(one, ord) } + } + } + ) + } + } + ) +} \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/numberedConstructors.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/numberedConstructors.kt index 37d5d7fb6..4850e6cec 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/numberedConstructors.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/numberedConstructors.kt @@ -3,6 +3,8 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ +@file:Suppress("FunctionName", "NOTHING_TO_INLINE") + package space.kscience.kmath.functions import space.kscience.kmath.misc.UnstableKMathAPI @@ -17,7 +19,6 @@ internal fun List.cleanUp() = subList(0, indexOfLast { it != 0U } + 1) /** * Constructs [NumberedPolynomial] with provided coefficients map [coefs]. The map is used as is. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") @PublishedApi internal inline fun NumberedPolynomialAsIs(coefs: Map, C>) : NumberedPolynomial = NumberedPolynomial(coefs) @@ -25,7 +26,6 @@ internal inline fun NumberedPolynomialAsIs(coefs: Map, C>) : Numb * Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". * The collections will be transformed to map with [toMap] and then will be used as is. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") @PublishedApi internal inline fun NumberedPolynomialAsIs(pairs: Collection, C>>) : NumberedPolynomial = NumberedPolynomial(pairs.toMap()) @@ -33,7 +33,6 @@ internal inline fun NumberedPolynomialAsIs(pairs: Collection * Constructs [NumberedPolynomial] with provided array of [pairs] of pairs "term's signature — term's coefficient". * The array will be transformed to map with [toMap] and then will be used as is. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") @PublishedApi internal inline fun NumberedPolynomialAsIs(vararg pairs: Pair, C>) : NumberedPolynomial = NumberedPolynomial(pairs.toMap()) @@ -43,7 +42,6 @@ internal inline fun NumberedPolynomialAsIs(vararg pairs: Pair, C> * **Be sure you read description of [NumberedPolynomial.coefficients]. Otherwise, you may make a mistake that will * cause wrong computation result or even runtime error.** */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") @DelicatePolynomialAPI public inline fun NumberedPolynomialWithoutCheck(coefs: Map, C>) : NumberedPolynomial = NumberedPolynomial(coefs) @@ -54,7 +52,6 @@ public inline fun NumberedPolynomialWithoutCheck(coefs: Map, C>) * **Be sure you read description of [NumberedPolynomial.coefficients]. Otherwise, you may make a mistake that will * cause wrong computation result or even runtime error.** */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") @DelicatePolynomialAPI public inline fun NumberedPolynomialWithoutCheck(pairs: Collection, C>>) : NumberedPolynomial = NumberedPolynomial(pairs.toMap()) @@ -65,7 +62,6 @@ public inline fun NumberedPolynomialWithoutCheck(pairs: Collection NumberedPolynomialWithoutCheck(vararg pairs: Pair, C>) : NumberedPolynomial = NumberedPolynomial(pairs.toMap()) @@ -77,7 +73,6 @@ public inline fun NumberedPolynomialWithoutCheck(vararg pairs: Pair NumberedPolynomial(coefs: Map, C>, add: (C, C) -> C) : NumberedPolynomial { val fixedCoefs = mutableMapOf, C>() @@ -98,7 +93,6 @@ public fun NumberedPolynomial(coefs: Map, C>, add: (C, C) -> C) : * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName") public fun NumberedPolynomial(pairs: Collection, C>>, add: (C, C) -> C) : NumberedPolynomial { val fixedCoefs = mutableMapOf, C>() @@ -119,7 +113,6 @@ public fun NumberedPolynomial(pairs: Collection, C>>, add: ( * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName") public fun NumberedPolynomial(vararg pairs: Pair, C>, add: (C, C) -> C) : NumberedPolynomial { val fixedCoefs = mutableMapOf, C>() @@ -142,7 +135,6 @@ public fun NumberedPolynomial(vararg pairs: Pair, C>, add: (C, C) * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") public inline fun > A.NumberedPolynomial(coefs: Map, C>) : NumberedPolynomial = NumberedPolynomial(coefs, ::add) /** * Constructs [NumberedPolynomial] with provided coefficients map [coefs]. @@ -152,7 +144,6 @@ public inline fun > A.NumberedPolynomial(coefs: Map, C> * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") public inline fun > NumberedPolynomialSpace.NumberedPolynomial(coefs: Map, C>) : NumberedPolynomial = NumberedPolynomial(coefs) { left: C, right: C -> left + right } /** @@ -163,7 +154,6 @@ public inline fun > NumberedPolynomialSpace.NumberedPolynomi * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") public inline fun > NumberedRationalFunctionSpace.NumberedPolynomial(coefs: Map, C>) : NumberedPolynomial = NumberedPolynomial(coefs) { left: C, right: C -> left + right } /** @@ -174,7 +164,6 @@ public inline fun > NumberedRationalFunctionSpace.NumberedPo * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") public inline fun > A.NumberedPolynomial(pairs: Collection, C>>) : NumberedPolynomial = NumberedPolynomial(pairs, ::add) /** @@ -185,7 +174,6 @@ public inline fun > A.NumberedPolynomial(pairs: Collection> NumberedPolynomialSpace.NumberedPolynomial(pairs: Collection, C>>) : NumberedPolynomial = NumberedPolynomial(pairs) { left: C, right: C -> left + right } /** * Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". @@ -195,7 +183,6 @@ public inline fun > NumberedPolynomialSpace.NumberedPolynomi * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") public inline fun > NumberedRationalFunctionSpace.NumberedPolynomial(pairs: Collection, C>>) : NumberedPolynomial = NumberedPolynomial(pairs) { left: C, right: C -> left + right } /** @@ -206,7 +193,6 @@ public inline fun > NumberedRationalFunctionSpace.NumberedPo * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") public inline fun > A.NumberedPolynomial(vararg pairs: Pair, C>) : NumberedPolynomial = NumberedPolynomial(*pairs) { left: C, right: C -> left + right } /** * Constructs [NumberedPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient". @@ -216,7 +202,6 @@ public inline fun > A.NumberedPolynomial(vararg pairs: Pair> NumberedPolynomialSpace.NumberedPolynomial(vararg pairs: Pair, C>) : NumberedPolynomial = NumberedPolynomial(*pairs) { left: C, right: C -> left + right } /** * Constructs [NumberedPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient". @@ -226,13 +211,11 @@ public inline fun > NumberedPolynomialSpace.NumberedPolynomi * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName", "NOTHING_TO_INLINE") public inline fun > NumberedRationalFunctionSpace.NumberedPolynomial(vararg pairs: Pair, C>) : NumberedPolynomial = NumberedPolynomial(*pairs) { left: C, right: C -> left + right } /** * Converts [this] constant to [NumberedPolynomial]. */ -@Suppress("NOTHING_TO_INLINE") public inline fun C.asNumberedPolynomial() : NumberedPolynomial = NumberedPolynomialAsIs(mapOf(emptyList() to this)) /** @@ -269,6 +252,7 @@ public class NumberedPolynomialTermSignatureBuilder { * * In fact, it just returns [signature] as regular signature of type `List`. */ + @PublishedApi internal fun build(): List = signature /** @@ -344,8 +328,7 @@ public class NumberedPolynomialBuilder( * coefficients is zero at any moment the monomial won't be removed but will be left as it is. */ public infix fun C.with(signature: List) { - if (signature in coefficients) coefficients[signature] = add(coefficients[signature]!!, this@with) - else coefficients[signature] = this@with + coefficients[signature] = if (signature in coefficients) add(coefficients[signature]!!, this@with) else this@with } /** * Declares monomial with [this] coefficient and signature constructed by [block]. @@ -361,7 +344,7 @@ public class NumberedPolynomialBuilder( * Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such * coefficients is zero at any moment the monomial won't be removed but will be left as it is. */ - public operator fun C.invoke(block: NumberedPolynomialTermSignatureBuilder.() -> Unit): Unit = + public inline operator fun C.invoke(block: NumberedPolynomialTermSignatureBuilder.() -> Unit): Unit = this with NumberedPolynomialTermSignatureBuilder().apply(block).build() } @@ -385,7 +368,6 @@ public class NumberedPolynomialBuilder( // 2. Union types are implemented. Then all three functions should be rewritten // as one with single union type as a (context) receiver. //@UnstableKMathAPI -//@Suppress("FunctionName") //public inline fun > A.NumberedPolynomial(initialCapacity: Int = 0, block: NumberedPolynomialBuilder.() -> Unit) : NumberedPolynomial = NumberedPolynomialBuilder(::add, initialCapacity).apply(block).build() /** * Creates [NumberedPolynomial] with lambda [block] in context of [this] ring of [NumberedPolynomial]s. @@ -401,7 +383,6 @@ public class NumberedPolynomialBuilder( * ``` */ @UnstableKMathAPI -@Suppress("FunctionName") public inline fun > NumberedPolynomialSpace.NumberedPolynomial(initialCapacity: Int = 0, block: NumberedPolynomialBuilder.() -> Unit) : NumberedPolynomial = NumberedPolynomialBuilder({ left: C, right: C -> left + right }, initialCapacity).apply(block).build() /** * Creates [NumberedPolynomial] with lambda [block] in context of [this] field of [NumberedRationalFunction]s. @@ -417,7 +398,6 @@ public inline fun > NumberedPolynomialSpace.NumberedPolynomi * ``` */ @UnstableKMathAPI -@Suppress("FunctionName") public inline fun > NumberedRationalFunctionSpace.NumberedPolynomial(initialCapacity: Int = 0, block: NumberedPolynomialBuilder.() -> Unit) : NumberedPolynomial = NumberedPolynomialBuilder({ left: C, right: C -> left + right }, initialCapacity).apply(block).build() // Waiting for context receivers :( FIXME: Replace with context receivers when they will be available @@ -430,7 +410,6 @@ public inline fun > NumberedRationalFunctionSpace.NumberedPo * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName") public fun > A.NumberedRationalFunction(numeratorCoefficients: Map, C>, denominatorCoefficients: Map, C>): NumberedRationalFunction = NumberedRationalFunction( NumberedPolynomial(numeratorCoefficients), @@ -444,7 +423,6 @@ public fun > A.NumberedRationalFunction(numeratorCoefficients: Map * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName") public fun > NumberedRationalFunctionSpace.NumberedRationalFunction(numeratorCoefficients: Map, C>, denominatorCoefficients: Map, C>): NumberedRationalFunction = NumberedRationalFunction( NumberedPolynomial(numeratorCoefficients), @@ -454,13 +432,11 @@ public fun > NumberedRationalFunctionSpace.NumberedRationalF /** * Constructs [NumberedRationalFunction] with provided [numerator] and unit denominator. */ -@Suppress("FunctionName") public fun > A.NumberedRationalFunction(numerator: NumberedPolynomial): NumberedRationalFunction = NumberedRationalFunction(numerator, NumberedPolynomial(mapOf(emptyList() to one))) /** * Constructs [NumberedRationalFunction] with provided [numerator] and unit denominator. */ -@Suppress("FunctionName") public fun > NumberedRationalFunctionSpace.NumberedRationalFunction(numerator: NumberedPolynomial): NumberedRationalFunction = NumberedRationalFunction(numerator, polynomialOne) @@ -473,7 +449,6 @@ public fun > NumberedRationalFunctionSpace.NumberedRationalF * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName") public fun > NumberedRationalFunctionSpace.NumberedRationalFunction(numeratorCoefficients: Map, C>): NumberedRationalFunction = NumberedRationalFunction( NumberedPolynomial(numeratorCoefficients), @@ -488,7 +463,6 @@ public fun > NumberedRationalFunctionSpace.NumberedRationalF * 1. Terms that happen to have the same signature will be summed up. * 1. New map will be formed of resulting terms. */ -@Suppress("FunctionName") public fun > A.NumberedRationalFunction(numeratorCoefficients: Map, C>): NumberedRationalFunction = NumberedRationalFunction( NumberedPolynomial(numeratorCoefficients), @@ -496,7 +470,7 @@ public fun > A.NumberedRationalFunction(numeratorCoefficients: Map ) ///** -// * Converts [this] coefficient to [NumberedRationalFunction]. +// * Converts [this] constant to [NumberedRationalFunction]. // */ //context(A) //public fun > C.asNumberedRationalFunction() : NumberedRationalFunction = @@ -505,7 +479,7 @@ public fun > A.NumberedRationalFunction(numeratorCoefficients: Map // NumberedPolynomialAsIs(mapOf(emptyList() to one)) // ) ///** -// * Converts [this] coefficient to [NumberedRationalFunction]. +// * Converts [this] constant to [NumberedRationalFunction]. // */ //context(NumberedRationalFunctionSpace) //public fun > C.asNumberedRationalFunction() : NumberedRationalFunction = diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/numberedUtil.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/numberedUtil.kt index fca9a8ab8..06911feca 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/numberedUtil.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/numberedUtil.kt @@ -51,15 +51,14 @@ public inline fun , R> A.numberedRationalFunctionSpace(block: Num */ public fun NumberedPolynomial.substitute(args: Map): NumberedPolynomial = Double.algebra { NumberedPolynomial( - buildMap, Double>(coefficients.size) { + buildMap(coefficients.size) { for ((degs, c) in coefficients) { val newDegs = degs.mapIndexed { index, deg -> if (index !in args) deg else 0u }.cleanUp() val newC = args.entries.fold(c) { product, (variable, substitution) -> val deg = degs.getOrElse(variable) { 0u } if (deg == 0u) product else product * substitution.pow(deg.toInt()) } - if (newDegs !in this) this[newDegs] = newC - else this[newDegs] = this[newDegs]!! + newC + this[newDegs] = if (newDegs !in this) newC else this[newDegs]!! + newC } } ) @@ -70,15 +69,14 @@ public fun NumberedPolynomial.substitute(args: Map): Number */ public fun NumberedPolynomial.substitute(ring: Ring, args: Map): NumberedPolynomial = ring { NumberedPolynomial( - buildMap, C>(coefficients.size) { + buildMap(coefficients.size) { for ((degs, c) in coefficients) { val newDegs = degs.mapIndexed { index, deg -> if (index !in args) deg else 0u }.cleanUp() val newC = args.entries.fold(c) { product, (variable, substitution) -> val deg = degs.getOrElse(variable) { 0u } if (deg == 0u) product else product * power(substitution, deg) } - if (newDegs !in this) this[newDegs] = newC - else this[newDegs] = this[newDegs]!! + newC + this[newDegs] = if (newDegs !in this) newC else this[newDegs]!! + newC } } ) @@ -128,14 +126,14 @@ public fun NumberedRationalFunction.substitute(ring: Ring, args: Map NumberedRationalFunction.substitute(ring: Ring, args: Map>) : NumberedRationalFunction = NumberedRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args)) /** * Substitutes provided arguments [args] into [this] rational function. - */ // TODO: To optimize boxing + */ // TODO: To optimize calculation @JvmName("substituteRationalFunction") public fun NumberedRationalFunction.substitute(ring: Ring, args: Map>) : NumberedRationalFunction = ring.numberedRationalFunctionSpace { @@ -250,14 +248,14 @@ public fun NumberedRationalFunction.substitute(ring: Ring, args: Buffe /** * Substitutes provided arguments [args] into [this] rational function. - */ // TODO: To optimize boxing + */ // TODO: To optimize calculation @JvmName("substitutePolynomial") public fun NumberedRationalFunction.substitute(ring: Ring, args: Buffer>) : NumberedRationalFunction = NumberedRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args)) /** * Substitutes provided arguments [args] into [this] rational function. - */ // TODO: To optimize boxing + */ // TODO: To optimize calculation @JvmName("substituteRationalFunction") public fun NumberedRationalFunction.substitute(ring: Ring, args: Buffer>) : NumberedRationalFunction = ring.numberedRationalFunctionSpace {