Feature: Polynomials and rational functions #469

Merged
lounres merged 132 commits from feature/polynomials into dev 2022-07-28 18:04:06 +03:00
8 changed files with 1584 additions and 49 deletions
Showing only changes of commit 680d23ddcb - Show all commits

View File

@ -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<C>
@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<Map<Symbol, UInt>, C>
) : Polynomial<C> {
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<C, A : Ring<C>>(
public override val ring: A,
) : MultivariatePolynomialSpace<C, Symbol, LabeledPolynomial<C>>, PolynomialSpaceOverRing<C, LabeledPolynomial<C>, 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<C> =
if (other == 0) LabeledPolynomialAsIs(mapOf(
mapOf(this@plus to 1U) to constantOne,
))
else LabeledPolynomialAsIs(mapOf(
mapOf(this@plus to 1U) to constantOne,
emptyMap<Symbol, UInt>() 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<C> =
if (other == 0) LabeledPolynomialAsIs(mapOf(
mapOf(this@minus to 1U) to -constantOne,
))
else LabeledPolynomialAsIs(mapOf(
mapOf(this@minus to 1U) to -constantOne,
emptyMap<Symbol, UInt>() 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<C> =
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<C> =
if (this == 0) LabeledPolynomialAsIs(mapOf(
mapOf(other to 1U) to constantOne,
))
else LabeledPolynomialAsIs(mapOf(
mapOf(other to 1U) to constantOne,
emptyMap<Symbol, UInt>() 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<C> =
if (this == 0) LabeledPolynomialAsIs(mapOf(
mapOf(other to 1U) to -constantOne,
))
else LabeledPolynomialAsIs(mapOf(
mapOf(other to 1U) to -constantOne,
emptyMap<Symbol, UInt>() 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<C> =
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<C>.plus(other: Int): LabeledPolynomial<C> =
if (other == 0) this
else with(coefficients) {
if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to other.asConstant()))
else LabeledPolynomialAsIs(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
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<C>.minus(other: Int): LabeledPolynomial<C> =
if (other == 0) this
else with(coefficients) {
if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to (-other).asConstant()))
else LabeledPolynomialAsIs(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
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<C>.times(other: Int): LabeledPolynomial<C> =
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<C>): LabeledPolynomial<C> =
if (this == 0) other
else with(other.coefficients) {
if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this@plus.asConstant()))
else LabeledPolynomialAsIs(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
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<C>): LabeledPolynomial<C> =
if (this == 0) other
else with(other.coefficients) {
if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this@minus.asConstant()))
else LabeledPolynomialAsIs(
toMutableMap()
.apply {
forEach { (key, value) -> if (key.isNotEmpty()) this[key] = -value }
val degs = emptyMap<Symbol, UInt>()
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<C>): LabeledPolynomial<C> =
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<C> = 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<C> =
LabeledPolynomialAsIs(mapOf(
mapOf(this@plus to 1U) to constantOne,
emptyMap<Symbol, UInt>() 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<C> =
LabeledPolynomialAsIs(mapOf(
mapOf(this@minus to 1U) to -constantOne,
emptyMap<Symbol, UInt>() 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<C> =
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<C> =
LabeledPolynomialAsIs(mapOf(
mapOf(other to 1U) to constantOne,
emptyMap<Symbol, UInt>() 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<C> =
LabeledPolynomialAsIs(mapOf(
mapOf(other to 1U) to -constantOne,
emptyMap<Symbol, UInt>() 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<C> =
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<C>): LabeledPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this@plus))
else LabeledPolynomialAsIs(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
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<C>): LabeledPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this@minus))
else LabeledPolynomialAsIs(
toMutableMap()
.apply {
forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c }
val degs = emptyMap<Symbol, UInt>()
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<C>): LabeledPolynomial<C> =
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<C>.plus(other: C): LabeledPolynomial<C> =
with(coefficients) {
if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to other))
else LabeledPolynomialAsIs(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
this[degs] = getOrElse(degs) { constantZero } + other
}
)
}
/**
* Returns difference between the constant represented as a polynomial and the polynomial.
*/
override operator fun LabeledPolynomial<C>.minus(other: C): LabeledPolynomial<C> =
with(coefficients) {
if (isEmpty()) LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to other))
else LabeledPolynomialAsIs(
toMutableMap()
.apply {
forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c }
val degs = emptyMap<Symbol, UInt>()
this[degs] = getOrElse(degs) { constantZero } - other
}
)
}
/**
* Returns product of the constant represented as a polynomial and the polynomial.
*/
override operator fun LabeledPolynomial<C>.times(other: C): LabeledPolynomial<C> =
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<C> =
LabeledPolynomial(mapOf(emptyMap<Symbol, UInt>() to value))
/**
* Represents the variable as a monic monomial.
*/
public override operator fun Symbol.unaryPlus(): LabeledPolynomial<C> =
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<C> =
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<C> =
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<C> =
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<C> =
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<C>): LabeledPolynomial<C> =
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<C>): LabeledPolynomial<C> =
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<C>): LabeledPolynomial<C> =
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<C>.plus(other: Symbol): LabeledPolynomial<C> =
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<C>.minus(other: Symbol): LabeledPolynomial<C> =
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<C>.times(other: Symbol): LabeledPolynomial<C> =
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<C>.unaryMinus(): LabeledPolynomial<C> =
LabeledPolynomialAsIs(
coefficients.mapValues { -it.value }
)
/**
* Returns sum of the polynomials.
*/
override operator fun LabeledPolynomial<C>.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
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<C>.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
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<C>.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
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<C> = LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to constantZero))
/**
* Instance of unit polynomial (unit of the polynomial ring).
*/
override val one: LabeledPolynomial<C> = LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() 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<C>.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<C>.degrees: Map<Symbol, UInt>
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<C>.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<C>.degreeBy(variables: Collection<Symbol>): 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<C>.variables: Set<Symbol>
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<C>.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<C>.substitute(arguments: Map<Symbol, C>): LabeledPolynomial<C> = substitute(ring, arguments)
/**
* Substitutes provided arguments [arguments] into [this] polynomial.
*/
@Suppress("NOTHING_TO_INLINE")
@JvmName("substitutePolynomial")
public inline fun LabeledPolynomial<C>.substitute(arguments: Map<Symbol, LabeledPolynomial<C>>) : LabeledPolynomial<C> = substitute(ring, arguments)
}

View File

@ -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<C>(
public override val numerator: LabeledPolynomial<C>,
public override val denominator: LabeledPolynomial<C>
) : RationalFunction<C, LabeledPolynomial<C>> {
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<C, A: Ring<C>>(
public val ring: A,
) :
MultivariateRationalFunctionalSpaceOverMultivariatePolynomialSpace<
C,
Symbol,
LabeledPolynomial<C>,
LabeledRationalFunction<C>,
LabeledPolynomialSpace<C, A>,
>,
MultivariatePolynomialSpaceOfFractions<
C,
Symbol,
LabeledPolynomial<C>,
LabeledRationalFunction<C>,
>() {
/**
* Underlying polynomial ring. Its polynomial operations are inherited by local polynomial operations.
*/
override val polynomialRing : LabeledPolynomialSpace<C, A> = LabeledPolynomialSpace(ring)
/**
* Constructor of rational functions (of type [LabeledRationalFunction]) from numerator and denominator (of type [LabeledPolynomial]).
*/
override fun constructRationalFunction(
numerator: LabeledPolynomial<C>,
denominator: LabeledPolynomial<C>
): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(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<C>.substitute(argument: Map<Symbol, C>): LabeledPolynomial<C> = substitute(ring, argument)
/**
* Substitutes provided polynomial [argument] into [this] polynomial.
*/
@Suppress("NOTHING_TO_INLINE")
@JvmName("substitutePolynomial")
public inline fun LabeledPolynomial<C>.substitute(argument: Map<Symbol, LabeledPolynomial<C>>): LabeledPolynomial<C> = substitute(ring, argument)
/**
* Substitutes provided rational function [argument] into [this] polynomial.
*/
@Suppress("NOTHING_TO_INLINE")
@JvmName("substituteRationalFunction")
public inline fun LabeledPolynomial<C>.substitute(argument: Map<Symbol, LabeledRationalFunction<C>>): LabeledRationalFunction<C> = substitute(ring, argument)
/**
* Substitutes provided constant [argument] into [this] rational function.
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun LabeledRationalFunction<C>.substitute(argument: Map<Symbol, C>): LabeledRationalFunction<C> = substitute(ring, argument)
/**
* Substitutes provided polynomial [argument] into [this] rational function.
*/
@Suppress("NOTHING_TO_INLINE")
@JvmName("substitutePolynomial")
public inline fun LabeledRationalFunction<C>.substitute(argument: Map<Symbol, LabeledPolynomial<C>>): LabeledRationalFunction<C> = substitute(ring, argument)
/**
* Substitutes provided rational function [argument] into [this] rational function.
*/
@Suppress("NOTHING_TO_INLINE")
@JvmName("substituteRationalFunction")
public inline fun LabeledRationalFunction<C>.substitute(argument: Map<Symbol, LabeledRationalFunction<C>>): LabeledRationalFunction<C> = substitute(ring, argument)
}

View File

@ -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<C>(
/**

View File

@ -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<C>
@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 +

View File

@ -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<Symbol, UInt>.cleanUp() = filterValues { it > 0U }
/**
* Constructs [LabeledPolynomial] with provided coefficients map [coefs]. The map is used as is.
*/
@PublishedApi
internal inline fun <C> LabeledPolynomialAsIs(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial<C>(coefs)
/**
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature &mdash; term's coefficient".
* The collections will be transformed to map with [toMap] and then will be used as is.
*/
@PublishedApi
internal inline fun <C> LabeledPolynomialAsIs(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial<C>(pairs.toMap())
/**
* Constructs [LabeledPolynomial] with provided array of [pairs] of pairs "term's signature &mdash; term's coefficient".
* The array will be transformed to map with [toMap] and then will be used as is.
*/
@PublishedApi
internal inline fun <C> LabeledPolynomialAsIs(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial<C>(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 <C> LabeledPolynomialWithoutCheck(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial<C>(coefs)
/**
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature &mdash; 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 <C> LabeledPolynomialWithoutCheck(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial<C>(pairs.toMap())
/**
* Constructs [LabeledPolynomial] with provided array of [pairs] of pairs "term's signature &mdash; 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 <C> LabeledPolynomialWithoutCheck(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial<C>(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 <C> LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>, add: (C, C) -> C) : LabeledPolynomial<C> {
val fixedCoefs = mutableMapOf<Map<Symbol, UInt>, 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<C>(fixedCoefs)
}
/**
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature &mdash; 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 <C> LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>, add: (C, C) -> C) : LabeledPolynomial<C> {
val fixedCoefs = mutableMapOf<Map<Symbol, UInt>, 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<C>(fixedCoefs)
}
/**
* Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature &mdash; 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 <C> LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>, add: (C, C) -> C) : LabeledPolynomial<C> {
val fixedCoefs = mutableMapOf<Map<Symbol, UInt>, 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<C>(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 <C, A: Ring<C>> A.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = 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 <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = 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 <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs) { left: C, right: C -> left + right }
/**
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature &mdash; 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 <C, A: Ring<C>> A.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs, ::add)
/**
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature &mdash; 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 <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs) { left: C, right: C -> left + right }
/**
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature &mdash; 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 <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs) { left: C, right: C -> left + right }
/**
* Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature &mdash; 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 <C, A: Ring<C>> A.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs) { left: C, right: C -> left + right }
/**
* Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature &mdash; 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 <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs) { left: C, right: C -> left + right }
/**
* Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature &mdash; 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 <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs) { left: C, right: C -> left + right }
/**
* Converts [this] constant to [LabeledPolynomial].
*/
public inline fun <C> C.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this))
///**
//// * Converts [this] variable to [LabeledPolynomial].
//// */
//context(A)
//public inline fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to one))
///**
// * Converts [this] variable to [LabeledPolynomial].
// */
//context(LabeledPolynomialSpace<C, A>)
//public inline fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to constantOne))
///**
// * Converts [this] variable to [LabeledPolynomial].
// */
//context(LabeledRationalFunctionSpace<C, A>)
//public inline fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(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<Int> = 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<Symbol, UInt> = LinkedHashMap()
/**
* Builds the resulting signature.
*
* In fact, it just returns [signature] as regular signature of type `List<UInt>`.
*/
@PublishedApi
internal fun build(): Map<Symbol, UInt> = 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<C>(
/**
* 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<Map<Symbol, UInt>, C> = LinkedHashMap(initialCapacity)
/**
* Builds the resulting coefficients map.
*
* In fact, it just returns [coefficients] as regular coefficients map of type `Map<List<UInt>, C>`.
*/
@PublishedApi
internal fun build(): LabeledPolynomial<C> = LabeledPolynomial<C>(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<Symbol, UInt>) {
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<Int> = 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 <C, A: Ring<C>> A.LabeledPolynomial(initialCapacity: Int = 0, block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = 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<Int> = 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 <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(initialCapacity: Int = 0, block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = 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<Int> = 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 <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(initialCapacity: Int = 0, block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = 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 <C, A: Ring<C>> A.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>, denominatorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(
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 <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>, denominatorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(
LabeledPolynomial(numeratorCoefficients),
LabeledPolynomial(denominatorCoefficients)
)
/**
* Constructs [LabeledRationalFunction] with provided [numerator] and unit denominator.
*/
public fun <C, A: Ring<C>> A.LabeledRationalFunction(numerator: LabeledPolynomial<C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(numerator, LabeledPolynomial(mapOf(emptyMap<Symbol, UInt>() to one)))
/**
* Constructs [LabeledRationalFunction] with provided [numerator] and unit denominator.
*/
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numerator: LabeledPolynomial<C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(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 <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(
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 <C, A: Ring<C>> A.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(
LabeledPolynomial(numeratorCoefficients),
LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to one))
)
///**
// * Converts [this] constant to [LabeledRationalFunction].
// */
//context(A)
//public fun <C, A: Ring<C>> C.asLabeledRationalFunction() : LabeledRationalFunction<C> =
// LabeledRationalFunction(
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this)),
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to one))
// )
///**
// * Converts [this] constant to [LabeledRationalFunction].
// */
//context(LabeledRationalFunctionSpace<C, A>)
//public fun <C, A: Ring<C>> C.asLabeledRationalFunction() : LabeledRationalFunction<C> =
// LabeledRationalFunction(
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this)),
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to constantOne))
// )
///**
// * Converts [this] variable to [LabeledRationalFunction].
// */
//context(A)
//public fun <C, A: Ring<C>> Symbol.asLabeledRationalFunction() : LabeledRationalFunction<C> =
// LabeledRationalFunction(
// LabeledPolynomialAsIs(mapOf(mapOf(this to 1u) to one)),
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to one))
// )
///**
// * Converts [this] variable to [LabeledRationalFunction].
// */
//context(LabeledRationalFunctionSpace<C, A>)
//public fun <C, A: Ring<C>> Symbol.asLabeledRationalFunction() : LabeledRationalFunction<C> =
// LabeledRationalFunction(
// LabeledPolynomialAsIs(mapOf(mapOf(this to 1u) to constantOne)),
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to constantOne))
// )

View File

@ -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 <C, A : Ring<C>> A.labeledPolynomialSpace(): LabeledPolynomialSpace<C, A> =
LabeledPolynomialSpace(this)
/**
* Creates a [LabeledPolynomialSpace]'s scope over a received ring.
*/
public inline fun <C, A : Ring<C>, R> A.labeledPolynomialSpace(block: LabeledPolynomialSpace<C, A>.() -> R): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return LabeledPolynomialSpace(this).block()
}
/**
* Creates a [LabeledRationalFunctionSpace] over a received ring.
*/
public fun <C, A : Ring<C>> A.labeledRationalFunctionSpace(): LabeledRationalFunctionSpace<C, A> =
LabeledRationalFunctionSpace(this)
/**
* Creates a [LabeledRationalFunctionSpace]'s scope over a received ring.
*/
public inline fun <C, A : Ring<C>, R> A.labeledRationalFunctionSpace(block: LabeledRationalFunctionSpace<C, A>.() -> R): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return LabeledRationalFunctionSpace(this).block()
}
/**
* Substitutes provided Double arguments [args] into [this] Double polynomial.
*/
public fun LabeledPolynomial<Double>.substitute(args: Map<Symbol, Double>): LabeledPolynomial<Double> = Double.algebra {
if (coefficients.isEmpty()) return this@substitute
LabeledPolynomial<Double>(
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 <C> LabeledPolynomial<C>.substitute(ring: Ring<C>, args: Map<Symbol, C>): LabeledPolynomial<C> = ring {
if (coefficients.isEmpty()) return this@substitute
LabeledPolynomial<C>(
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 <C> LabeledPolynomial<C>.substitute(ring: Ring<C>, args: Map<Symbol, LabeledPolynomial<C>>) : LabeledPolynomial<C> =
ring.labeledPolynomialSpace {
coefficients.entries.fold(zero) { acc, (degs, c) ->
val newDegs = degs.filterKeys { it !in args }
acc + args.entries.fold(LabeledPolynomial<C>(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 <C> LabeledPolynomial<C>.substitute(ring: Ring<C>, args: Map<Symbol, LabeledRationalFunction<C>>) : LabeledRationalFunction<C> =
ring.labeledRationalFunctionSpace {
coefficients.entries.fold(zero) { acc, (degs, c) ->
val newDegs = degs.filterKeys { it !in args }
acc + args.entries.fold(LabeledRationalFunction(LabeledPolynomial<C>(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<Double>.substitute(args: Map<Symbol, Double>): LabeledRationalFunction<Double> =
LabeledRationalFunction(numerator.substitute(args), denominator.substitute(args))
/**
* Substitutes provided arguments [args] into [this] rational function.
*/
public fun <C> LabeledRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Symbol, C>): LabeledRationalFunction<C> =
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 <C> LabeledRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Symbol, LabeledPolynomial<C>>) : LabeledRationalFunction<C> =
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 <C> LabeledRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Symbol, LabeledRationalFunction<C>>) : LabeledRationalFunction<C> =
ring.labeledRationalFunctionSpace {
numerator.substitute(ring, args) / denominator.substitute(ring, args)
}
/**
* Returns algebraic derivative of received polynomial with respect to provided variable.
*/
@UnstableKMathAPI
public fun <C, A : Ring<C>> LabeledPolynomial<C>.derivativeWithRespectTo(
algebra: A,
variable: Symbol,
): LabeledPolynomial<C> = algebra {
LabeledPolynomial<C>(
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 <C, A : Ring<C>> LabeledPolynomial<C>.nthDerivativeWithRespectTo(
algebra: A,
variable: Symbol,
order: UInt
): LabeledPolynomial<C> = algebra {
if (order == 0u) return this@nthDerivativeWithRespectTo
LabeledPolynomial<C>(
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 <C, A : Ring<C>> LabeledPolynomial<C>.nthDerivativeWithRespectTo(
algebra: A,
variablesAndOrders: Map<Symbol, UInt>,
): LabeledPolynomial<C> = algebra {
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
if (filteredVariablesAndOrders.isEmpty()) return this@nthDerivativeWithRespectTo
LabeledPolynomial<C>(
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 <C, A : Field<C>> LabeledPolynomial<C>.antiderivativeWithRespectTo(
algebra: A,
variable: Symbol,
): LabeledPolynomial<C> = algebra {
LabeledPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
val newDegs = buildMap<Symbol, UInt>(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 <C, A : Field<C>> LabeledPolynomial<C>.nthAntiderivativeWithRespectTo(
algebra: A,
variable: Symbol,
order: UInt
): LabeledPolynomial<C> = algebra {
if (order == 0u) return this@nthAntiderivativeWithRespectTo
LabeledPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
val newDegs = buildMap<Symbol, UInt>(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 <C, A : Field<C>> LabeledPolynomial<C>.nthAntiderivativeWithRespectTo(
algebra: A,
variablesAndOrders: Map<Symbol, UInt>,
): LabeledPolynomial<C> = algebra {
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
if (filteredVariablesAndOrders.isEmpty()) return this@nthAntiderivativeWithRespectTo
LabeledPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
val newDegs = buildMap<Symbol, UInt>(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) }
}
}
)
}
}
)
}

View File

@ -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<UInt>.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 <C> NumberedPolynomialAsIs(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial<C>(coefs)
@ -25,7 +26,6 @@ internal inline fun <C> NumberedPolynomialAsIs(coefs: Map<List<UInt>, C>) : Numb
* Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature &mdash; 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 <C> NumberedPolynomialAsIs(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial<C>(pairs.toMap())
@ -33,7 +33,6 @@ internal inline fun <C> NumberedPolynomialAsIs(pairs: Collection<Pair<List<UInt>
* Constructs [NumberedPolynomial] with provided array of [pairs] of pairs "term's signature &mdash; 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 <C> NumberedPolynomialAsIs(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial<C>(pairs.toMap())
@ -43,7 +42,6 @@ internal inline fun <C> NumberedPolynomialAsIs(vararg pairs: Pair<List<UInt>, 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 <C> NumberedPolynomialWithoutCheck(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial<C>(coefs)
@ -54,7 +52,6 @@ public inline fun <C> NumberedPolynomialWithoutCheck(coefs: Map<List<UInt>, 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 <C> NumberedPolynomialWithoutCheck(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial<C>(pairs.toMap())
@ -65,7 +62,6 @@ public inline fun <C> NumberedPolynomialWithoutCheck(pairs: Collection<Pair<List
* **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 <C> NumberedPolynomialWithoutCheck(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial<C>(pairs.toMap())
@ -77,7 +73,6 @@ public inline fun <C> NumberedPolynomialWithoutCheck(vararg pairs: Pair<List<UIn
* 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 <C> NumberedPolynomial(coefs: Map<List<UInt>, C>, add: (C, C) -> C) : NumberedPolynomial<C> {
val fixedCoefs = mutableMapOf<List<UInt>, C>()
@ -98,7 +93,6 @@ public fun <C> NumberedPolynomial(coefs: Map<List<UInt>, 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 <C> NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>, add: (C, C) -> C) : NumberedPolynomial<C> {
val fixedCoefs = mutableMapOf<List<UInt>, C>()
@ -119,7 +113,6 @@ public fun <C> NumberedPolynomial(pairs: Collection<Pair<List<UInt>, 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 <C> NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>, add: (C, C) -> C) : NumberedPolynomial<C> {
val fixedCoefs = mutableMapOf<List<UInt>, C>()
@ -142,7 +135,6 @@ public fun <C> NumberedPolynomial(vararg pairs: Pair<List<UInt>, 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 <C, A: Ring<C>> A.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs, ::add)
/**
* Constructs [NumberedPolynomial] with provided coefficients map [coefs].
@ -152,7 +144,6 @@ public inline fun <C, A: Ring<C>> A.NumberedPolynomial(coefs: Map<List<UInt>, 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 <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs) { left: C, right: C -> left + right }
/**
@ -163,7 +154,6 @@ public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.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 <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs) { left: C, right: C -> left + right }
/**
@ -174,7 +164,6 @@ public inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.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 <C, A: Ring<C>> A.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs, ::add)
/**
@ -185,7 +174,6 @@ public inline fun <C, A: Ring<C>> A.NumberedPolynomial(pairs: Collection<Pair<Li
* 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 <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs) { left: C, right: C -> left + right }
/**
* Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature &mdash; term's coefficient".
@ -195,7 +183,6 @@ public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.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 <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs) { left: C, right: C -> left + right }
/**
@ -206,7 +193,6 @@ public inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.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 <C, A: Ring<C>> A.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs) { left: C, right: C -> left + right }
/**
* Constructs [NumberedPolynomial] with provided array [pairs] of pairs "term's signature &mdash; term's coefficient".
@ -216,7 +202,6 @@ public inline fun <C, A: Ring<C>> A.NumberedPolynomial(vararg pairs: Pair<List<U
* 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 <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs) { left: C, right: C -> left + right }
/**
* Constructs [NumberedPolynomial] with provided array [pairs] of pairs "term's signature &mdash; term's coefficient".
@ -226,13 +211,11 @@ public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.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 <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs) { left: C, right: C -> left + right }
/**
* Converts [this] constant to [NumberedPolynomial].
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun <C> C.asNumberedPolynomial() : NumberedPolynomial<C> = NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to this))
/**
@ -269,6 +252,7 @@ public class NumberedPolynomialTermSignatureBuilder {
*
* In fact, it just returns [signature] as regular signature of type `List<UInt>`.
*/
@PublishedApi
internal fun build(): List<UInt> = signature
/**
@ -344,8 +328,7 @@ public class NumberedPolynomialBuilder<C>(
* 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<UInt>) {
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<C>(
* 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<C>(
// 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 <C, A: Ring<C>> A.NumberedPolynomial(initialCapacity: Int = 0, block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = 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<C>(
* ```
*/
@UnstableKMathAPI
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(initialCapacity: Int = 0, block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = 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 <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomi
* ```
*/
@UnstableKMathAPI
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(initialCapacity: Int = 0, block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = 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 <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.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 <C, A: Ring<C>> A.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>, denominatorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(
NumberedPolynomial(numeratorCoefficients),
@ -444,7 +423,6 @@ public fun <C, A: Ring<C>> 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 <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>, denominatorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(
NumberedPolynomial(numeratorCoefficients),
@ -454,13 +432,11 @@ public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalF
/**
* Constructs [NumberedRationalFunction] with provided [numerator] and unit denominator.
*/
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.NumberedRationalFunction(numerator: NumberedPolynomial<C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(numerator, NumberedPolynomial(mapOf(emptyList<UInt>() to one)))
/**
* Constructs [NumberedRationalFunction] with provided [numerator] and unit denominator.
*/
@Suppress("FunctionName")
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numerator: NumberedPolynomial<C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(numerator, polynomialOne)
@ -473,7 +449,6 @@ public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.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 <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(
NumberedPolynomial(numeratorCoefficients),
@ -488,7 +463,6 @@ public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.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 <C, A: Ring<C>> A.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(
NumberedPolynomial(numeratorCoefficients),
@ -496,7 +470,7 @@ public fun <C, A: Ring<C>> A.NumberedRationalFunction(numeratorCoefficients: Map
)
///**
// * Converts [this] coefficient to [NumberedRationalFunction].
// * Converts [this] constant to [NumberedRationalFunction].
// */
//context(A)
//public fun <C, A: Ring<C>> C.asNumberedRationalFunction() : NumberedRationalFunction<C> =
@ -505,7 +479,7 @@ public fun <C, A: Ring<C>> A.NumberedRationalFunction(numeratorCoefficients: Map
// NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to one))
// )
///**
// * Converts [this] coefficient to [NumberedRationalFunction].
// * Converts [this] constant to [NumberedRationalFunction].
// */
//context(NumberedRationalFunctionSpace<C, A>)
//public fun <C, A: Ring<C>> C.asNumberedRationalFunction() : NumberedRationalFunction<C> =

View File

@ -51,15 +51,14 @@ public inline fun <C, A : Ring<C>, R> A.numberedRationalFunctionSpace(block: Num
*/
public fun NumberedPolynomial<Double>.substitute(args: Map<Int, Double>): NumberedPolynomial<Double> = Double.algebra {
NumberedPolynomial<Double>(
buildMap<List<UInt>, 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<Double>.substitute(args: Map<Int, Double>): Number
*/
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, C>): NumberedPolynomial<C> = ring {
NumberedPolynomial<C>(
buildMap<List<UInt>, 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 <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Map<I
/**
* Substitutes provided arguments [args] into [this] rational function.
*/ // TODO: To optimize boxing
*/ // TODO: To optimize calculation
@JvmName("substitutePolynomial")
public fun <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Int, NumberedPolynomial<C>>) : NumberedRationalFunction<C> =
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 <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Int, NumberedRationalFunction<C>>) : NumberedRationalFunction<C> =
ring.numberedRationalFunctionSpace {
@ -250,14 +248,14 @@ public fun <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Buffe
/**
* Substitutes provided arguments [args] into [this] rational function.
*/ // TODO: To optimize boxing
*/ // TODO: To optimize calculation
@JvmName("substitutePolynomial")
public fun <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Buffer<NumberedPolynomial<C>>) : NumberedRationalFunction<C> =
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 <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Buffer<NumberedRationalFunction<C>>) : NumberedRationalFunction<C> =
ring.numberedRationalFunctionSpace {