This commit is contained in:
Gleb Minaev 2022-06-11 16:09:24 +03:00
parent 89cdbf4d71
commit 9b51062bf7
19 changed files with 0 additions and 4715 deletions

View File

@ -1,539 +0,0 @@
/*
* 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 space.kscience.kmath.operations.ScaleOperations
import kotlin.math.max
/**
* Represents multivariate polynomials with labeled variables.
*
* @param C Ring in which the polynomial is considered.
*/
public data class LabeledPolynomial<C>
internal constructor(
/**
* Map that collects coefficients of the polynomial. Every non-zero monomial
* `a x_1^{d_1} ... x_n^{d_n}` is represented as pair "key-value" in the map, where value is coefficients `a` and
* key is map that associates variables in the monomial with multiplicity of them occurring in the monomial.
* For example polynomial
* ```
* 5 a^2 c^3 - 6 b + 0 b c
* ```
* has coefficients represented as
* ```
* mapOf(
* mapOf(
* a to 2,
* c to 3
* ) to 5,
* mapOf(
* b to 1
* ) to (-6)
* )
* ```
* 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"
}
/**
* Space of polynomials.
*
* @param C the type of operated polynomials.
* @param A the intersection of [Ring] of [C] and [ScaleOperations] of [C].
* @param ring the [A] instance.
*/
public class LabeledPolynomialSpace<C, A : Ring<C>>(
public override val ring: A,
) : MultivariatePolynomialSpace<C, Symbol, LabeledPolynomial<C>>, PolynomialSpaceOverRing<C, LabeledPolynomial<C>, A> {
public override operator fun Symbol.plus(other: Int): LabeledPolynomial<C> =
if (other == 0) LabeledPolynomial<C>(mapOf(
mapOf(this@plus to 1U) to constantOne,
))
else LabeledPolynomial<C>(mapOf(
mapOf(this@plus to 1U) to constantOne,
emptyMap<Symbol, UInt>() to constantOne * other,
))
public override operator fun Symbol.minus(other: Int): LabeledPolynomial<C> =
if (other == 0) LabeledPolynomial<C>(mapOf(
mapOf(this@minus to 1U) to -constantOne,
))
else LabeledPolynomial<C>(mapOf(
mapOf(this@minus to 1U) to -constantOne,
emptyMap<Symbol, UInt>() to constantOne * other,
))
public override operator fun Symbol.times(other: Int): LabeledPolynomial<C> =
if (other == 0) zero
else LabeledPolynomial<C>(mapOf(
mapOf(this to 1U) to constantOne * other,
))
public override operator fun Int.plus(other: Symbol): LabeledPolynomial<C> =
if (this == 0) LabeledPolynomial<C>(mapOf(
mapOf(other to 1U) to constantOne,
))
else LabeledPolynomial<C>(mapOf(
mapOf(other to 1U) to constantOne,
emptyMap<Symbol, UInt>() to constantOne * this@plus,
))
public override operator fun Int.minus(other: Symbol): LabeledPolynomial<C> =
if (this == 0) LabeledPolynomial<C>(mapOf(
mapOf(other to 1U) to -constantOne,
))
else LabeledPolynomial<C>(mapOf(
mapOf(other to 1U) to -constantOne,
emptyMap<Symbol, UInt>() to constantOne * this@minus,
))
public override operator fun Int.times(other: Symbol): LabeledPolynomial<C> =
if (this == 0) zero
else LabeledPolynomial<C>(mapOf(
mapOf(other to 1U) to constantOne * this@times,
))
/**
* Returns sum of the polynomial and the integer represented as 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()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to other.asConstant()))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
this[degs] = getOrElse(degs) { constantZero } + other
}
)
}
/**
* Returns difference between the polynomial and the integer represented as 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()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to (-other).asConstant()))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
this[degs] = getOrElse(degs) { constantZero } - other
}
)
}
/**
* Returns product of the polynomial and the integer represented as 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 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()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this@plus.asConstant()))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
this[degs] = this@plus + getOrElse(degs) { constantZero }
}
)
}
/**
* Returns difference between the integer represented as 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()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this@minus.asConstant()))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
this[degs] = this@minus - getOrElse(degs) { constantZero }
}
)
}
/**
* Returns product of the integer represented as 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))
public override operator fun C.plus(other: Symbol): LabeledPolynomial<C> =
LabeledPolynomial<C>(mapOf(
mapOf(other to 1U) to constantOne,
emptyMap<Symbol, UInt>() to this@plus,
))
public override operator fun C.minus(other: Symbol): LabeledPolynomial<C> =
LabeledPolynomial<C>(mapOf(
mapOf(other to 1U) to -constantOne,
emptyMap<Symbol, UInt>() to this@minus,
))
public override operator fun C.times(other: Symbol): LabeledPolynomial<C> =
LabeledPolynomial<C>(mapOf(
mapOf(other to 1U) to this@times,
))
public override operator fun Symbol.plus(other: C): LabeledPolynomial<C> =
LabeledPolynomial<C>(mapOf(
mapOf(this@plus to 1U) to constantOne,
emptyMap<Symbol, UInt>() to other,
))
public override operator fun Symbol.minus(other: C): LabeledPolynomial<C> =
LabeledPolynomial<C>(mapOf(
mapOf(this@minus to 1U) to -constantOne,
emptyMap<Symbol, UInt>() to other,
))
public override operator fun Symbol.times(other: C): LabeledPolynomial<C> =
LabeledPolynomial<C>(mapOf(
mapOf(this@times to 1U) to other,
))
/**
* Returns sum of the constant represented as polynomial and the polynomial.
*/
override operator fun C.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this@plus))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
this[degs] = this@plus + getOrElse(degs) { constantZero }
}
)
}
/**
* Returns difference between the constant represented as polynomial and the polynomial.
*/
override operator fun C.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this@minus))
else LabeledPolynomial<C>(
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 polynomial and the polynomial.
*/
override operator fun C.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
LabeledPolynomial<C>(
other.coefficients
.toMutableMap()
.apply {
for (degs in keys) this[degs] = this@times * this[degs]!!
}
)
/**
* Returns sum of the constant represented as polynomial and the polynomial.
*/
override operator fun LabeledPolynomial<C>.plus(other: C): LabeledPolynomial<C> =
with(coefficients) {
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to other))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
val degs = emptyMap<Symbol, UInt>()
this[degs] = getOrElse(degs) { constantZero } + other
}
)
}
/**
* Returns difference between the constant represented as polynomial and the polynomial.
*/
override operator fun LabeledPolynomial<C>.minus(other: C): LabeledPolynomial<C> =
with(coefficients) {
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to other))
else LabeledPolynomial<C>(
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 polynomial and the polynomial.
*/
override operator fun LabeledPolynomial<C>.times(other: C): LabeledPolynomial<C> =
LabeledPolynomial<C>(
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))
public override operator fun Symbol.unaryPlus(): LabeledPolynomial<C> =
LabeledPolynomial<C>(mapOf(
mapOf(this to 1U) to constantOne,
))
public override operator fun Symbol.unaryMinus(): LabeledPolynomial<C> =
LabeledPolynomial<C>(mapOf(
mapOf(this to 1U) to -constantOne,
))
public override operator fun Symbol.plus(other: Symbol): LabeledPolynomial<C> =
if (this == other) LabeledPolynomial<C>(mapOf(
mapOf(this to 1U) to constantOne * 2
))
else LabeledPolynomial<C>(mapOf(
mapOf(this to 1U) to constantOne,
mapOf(other to 1U) to constantOne,
))
public override operator fun Symbol.minus(other: Symbol): LabeledPolynomial<C> =
if (this == other) zero
else LabeledPolynomial<C>(mapOf(
mapOf(this to 1U) to constantOne,
mapOf(other to 1U) to -constantOne,
))
public override operator fun Symbol.times(other: Symbol): LabeledPolynomial<C> =
if (this == other) LabeledPolynomial<C>(mapOf(
mapOf(this to 2U) to constantOne
))
else LabeledPolynomial<C>(mapOf(
mapOf(this to 1U, other to 1U) to constantOne,
))
public override operator fun Symbol.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) LabeledPolynomial<C>(mapOf(mapOf(this@plus to 1u) to constantOne))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
val degs = mapOf(this@plus to 1U)
this[degs] = constantOne + getOrElse(degs) { constantZero }
}
)
}
public override operator fun Symbol.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) LabeledPolynomial<C>(mapOf(mapOf(this@minus to 1u) to constantOne))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c }
val degs = mapOf(this@minus to 1U)
this[degs] = constantOne - getOrElse(degs) { constantZero }
}
)
}
public override operator fun Symbol.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
LabeledPolynomial<C>(
other.coefficients
.mapKeys { (degs, _) -> degs.toMutableMap().also{ it[this] = if (this in it) it[this]!! + 1U else 1U } }
)
public override operator fun LabeledPolynomial<C>.plus(other: Symbol): LabeledPolynomial<C> =
with(coefficients) {
if (isEmpty()) LabeledPolynomial<C>(mapOf(mapOf(other to 1u) to constantOne))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
val degs = mapOf(other to 1U)
this[degs] = constantOne + getOrElse(degs) { constantZero }
}
)
}
public override operator fun LabeledPolynomial<C>.minus(other: Symbol): LabeledPolynomial<C> =
with(coefficients) {
if (isEmpty()) LabeledPolynomial<C>(mapOf(mapOf(other to 1u) to constantOne))
else LabeledPolynomial<C>(
toMutableMap()
.apply {
val degs = mapOf(other to 1U)
this[degs] = constantOne - getOrElse(degs) { constantZero }
}
)
}
public override operator fun LabeledPolynomial<C>.times(other: Symbol): LabeledPolynomial<C> =
LabeledPolynomial<C>(
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> =
LabeledPolynomial<C>(
coefficients.mapValues { -it.value }
)
/**
* Returns sum of the polynomials.
*/
override operator fun LabeledPolynomial<C>.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
LabeledPolynomial<C>(
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> =
LabeledPolynomial<C>(
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> =
LabeledPolynomial<C>(
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> = LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to constantZero))
/**
* Instance of unit polynomial (unit of the polynomial ring).
*/
override val one: LabeledPolynomial<C> = LabeledPolynomial<C>(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, c) -> 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
// @Suppress("NOTHING_TO_INLINE")
// public inline fun LabeledPolynomial<C>.substitute(argument: Map<Symbol, C>): LabeledPolynomial<C> = this.substitute(ring, argument)
// @Suppress("NOTHING_TO_INLINE")
// @JvmName("substitutePolynomial")
// public inline fun LabeledPolynomial<C>.substitute(argument: Map<Symbol, LabeledPolynomial<C>>): LabeledPolynomial<C> = this.substitute(ring, argument)
//
// @Suppress("NOTHING_TO_INLINE")
// public inline fun LabeledPolynomial<C>.asFunction(): (Map<Symbol, C>) -> LabeledPolynomial<C> = { this.substitute(ring, it) }
// @Suppress("NOTHING_TO_INLINE")
// public inline fun LabeledPolynomial<C>.asFunctionOnConstants(): (Map<Symbol, C>) -> LabeledPolynomial<C> = { this.substitute(ring, it) }
// @Suppress("NOTHING_TO_INLINE")
// public inline fun LabeledPolynomial<C>.asFunctionOnPolynomials(): (Map<Symbol, LabeledPolynomial<C>>) -> LabeledPolynomial<C> = { this.substitute(ring, it) }
//
// @Suppress("NOTHING_TO_INLINE")
// public inline operator fun LabeledPolynomial<C>.invoke(argument: Map<Symbol, C>): LabeledPolynomial<C> = this.substitute(ring, argument)
// @Suppress("NOTHING_TO_INLINE")
// @JvmName("invokePolynomial")
// public inline operator fun LabeledPolynomial<C>.invoke(argument: Map<Symbol, LabeledPolynomial<C>>): LabeledPolynomial<C> = this.substitute(ring, argument)
}

View File

@ -1,139 +0,0 @@
/*
* 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 space.kscience.kmath.operations.invoke
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}"
}
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>,
>() {
override val polynomialRing : LabeledPolynomialSpace<C, A> = LabeledPolynomialSpace(ring)
override fun constructRationalFunction(
numerator: LabeledPolynomial<C>,
denominator: LabeledPolynomial<C>
): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(numerator, denominator)
/**
* Instance of zero rational function (zero of the rational functions ring).
*/
public override val zero: LabeledRationalFunction<C> = LabeledRationalFunction<C>(polynomialZero, polynomialOne)
/**
* Instance of unit polynomial (unit of the rational functions ring).
*/
public override val one: LabeledRationalFunction<C> = LabeledRationalFunction<C>(polynomialOne, polynomialOne)
// TODO: Разобрать
// operator fun invoke(arg: Map<Symbol, C>): LabeledRationalFunction<C> =
// LabeledRationalFunction(
// numerator(arg),
// denominator(arg)
// )
//
// @JvmName("invokeLabeledPolynomial")
// operator fun invoke(arg: Map<Symbol, LabeledPolynomial<C>>): LabeledRationalFunction<C> =
// LabeledRationalFunction(
// numerator(arg),
// denominator(arg)
// )
//
// @JvmName("invokeLabeledRationalFunction")
// operator fun invoke(arg: Map<Symbol, LabeledRationalFunction<C>>): LabeledRationalFunction<C> {
// var num = numerator invokeRFTakeNumerator arg
// var den = denominator invokeRFTakeNumerator arg
// for (variable in variables) if (variable in arg) {
// val degreeDif = degrees[variable]!!
// if (degreeDif > 0)
// den = multiplyByPower(den, arg[variable]!!.denominator, degreeDif)
// else
// num = multiplyByPower(num, arg[variable]!!.denominator, -degreeDif)
// }
// return LabeledRationalFunction(num, den)
// }
//
// override fun toString(): String = toString(emptyMap())
//
// fun toString(names: Map<Symbol, String> = emptyMap()): String =
// when (true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toString(names)
// else -> "${numerator.toStringWithBrackets(names)}/${denominator.toStringWithBrackets(names)}"
// }
//
// fun toString(namer: (Symbol) -> String): String =
// when (true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toString(namer)
// else -> "${numerator.toStringWithBrackets(namer)}/${denominator.toStringWithBrackets(namer)}"
// }
//
// fun toStringWithBrackets(names: Map<Symbol, String> = emptyMap()): String =
// when (true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toStringWithBrackets(names)
// else -> "(${numerator.toStringWithBrackets(names)}/${denominator.toStringWithBrackets(names)})"
// }
//
// fun toStringWithBrackets(namer: (Symbol) -> String): String =
// when (true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toStringWithBrackets(namer)
// else -> "(${numerator.toStringWithBrackets(namer)}/${denominator.toStringWithBrackets(namer)})"
// }
//
// fun toReversedString(names: Map<Symbol, String> = emptyMap()): String =
// when (true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedString(names)
// else -> "${numerator.toReversedStringWithBrackets(names)}/${denominator.toReversedStringWithBrackets(names)}"
// }
//
// fun toReversedString(namer: (Symbol) -> String): String =
// when (true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedString(namer)
// else -> "${numerator.toReversedStringWithBrackets(namer)}/${denominator.toReversedStringWithBrackets(namer)}"
// }
//
// fun toReversedStringWithBrackets(names: Map<Symbol, String> = emptyMap()): String =
// when (true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedStringWithBrackets(names)
// else -> "(${numerator.toReversedStringWithBrackets(names)}/${denominator.toReversedStringWithBrackets(names)})"
// }
//
// fun toReversedStringWithBrackets(namer: (Symbol) -> String): String =
// when (true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedStringWithBrackets(namer)
// else -> "(${numerator.toReversedStringWithBrackets(namer)}/${denominator.toReversedStringWithBrackets(namer)})"
// }
}

View File

@ -1,367 +0,0 @@
/*
* 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.operations.Ring
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.experimental.ExperimentalTypeInference
import kotlin.jvm.JvmName
import kotlin.math.max
import kotlin.math.min
/**
* Polynomial model without fixation on specific context they are applied to.
*
* @param coefficients constant is the leftmost coefficient.
*/
public data class ListPolynomial<C>(
/**
* List that collects coefficients of the polynomial. Every monomial `a x^d` is represented as a coefficients
* `a` placed into the list with index `d`. For example coefficients of polynomial `5 x^2 - 6` can be represented as
* ```
* listOf(
* -6, // -6 +
* 0, // 0 x +
* 5, // 5 x^2
* )
* ```
* and also as
* ```
* listOf(
* -6, // -6 +
* 0, // 0 x +
* 5, // 5 x^2
* 0, // 0 x^3
* 0, // 0 x^4
* )
* ```
* It is recommended not to put extra zeros at end of the list (as for `0x^3` and `0x^4` in the example), but is not
* prohibited.
*/
public val coefficients: List<C>
) : Polynomial<C> {
override fun toString(): String = "Polynomial$coefficients"
}
/**
* Space of univariate polynomials constructed over ring.
*
* @param C the type of constants. Polynomials have them as a coefficients in their terms.
* @param A type of underlying ring of constants. It's [Ring] of [C].
* @param ring underlying ring of constants of type [A].
*/
public open class ListPolynomialSpace<C, A : Ring<C>>(
public override val ring: A,
) : PolynomialSpaceOverRing<C, ListPolynomial<C>, A> {
/**
* Returns sum of the polynomial and the integer represented as polynomial.
*
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
*/
public override operator fun ListPolynomial<C>.plus(other: Int): ListPolynomial<C> =
if (other == 0) this
else
ListPolynomial(
coefficients
.toMutableList()
.apply {
val result = getOrElse(0) { constantZero } + other
if(size == 0) add(result)
else this[0] = result
}
)
/**
* Returns difference between the polynomial and the integer represented as polynomial.
*
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
*/
public override operator fun ListPolynomial<C>.minus(other: Int): ListPolynomial<C> =
if (other == 0) this
else
ListPolynomial(
coefficients
.toMutableList()
.apply {
val result = getOrElse(0) { constantZero } - other
if(size == 0) add(result)
else this[0] = result
}
)
/**
* Returns product of the polynomial and the integer represented as polynomial.
*
* The operation is equivalent to sum of [other] copies of [this].
*/
public override operator fun ListPolynomial<C>.times(other: Int): ListPolynomial<C> =
if (other == 0) zero
else ListPolynomial(
coefficients
.toMutableList()
.apply {
for (deg in indices) this[deg] = this[deg] * other
}
)
/**
* Returns sum of the integer represented as polynomial and the polynomial.
*
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
*/
public override operator fun Int.plus(other: ListPolynomial<C>): ListPolynomial<C> =
if (this == 0) other
else
ListPolynomial(
other.coefficients
.toMutableList()
.apply {
val result = this@plus + getOrElse(0) { constantZero }
if(size == 0) add(result)
else this[0] = result
}
)
/**
* Returns difference between the integer represented as polynomial and the polynomial.
*
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
*/
public override operator fun Int.minus(other: ListPolynomial<C>): ListPolynomial<C> =
if (this == 0) other
else
ListPolynomial(
other.coefficients
.toMutableList()
.apply {
forEachIndexed { index, c -> if (index != 0) this[index] = -c }
val result = this@minus - getOrElse(0) { constantZero }
if(size == 0) add(result)
else this[0] = result
}
)
/**
* Returns product of the integer represented as polynomial and the polynomial.
*
* The operation is equivalent to sum of [this] copies of [other].
*/
public override operator fun Int.times(other: ListPolynomial<C>): ListPolynomial<C> =
if (this == 0) zero
else ListPolynomial(
other.coefficients
.toMutableList()
.apply {
for (deg in indices) this[deg] = this@times * this[deg]
}
)
/**
* Converts the integer [value] to polynomial.
*/
public override fun number(value: Int): ListPolynomial<C> = number(constantNumber(value))
/**
* Returns sum of the constant represented as polynomial and the polynomial.
*/
public override operator fun C.plus(other: ListPolynomial<C>): ListPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) ListPolynomial(listOf(this@plus))
else ListPolynomial(
toMutableList()
.apply {
val result = if (size == 0) this@plus else this@plus + get(0)
if(size == 0) add(result)
else this[0] = result
}
)
}
/**
* Returns difference between the constant represented as polynomial and the polynomial.
*/
public override operator fun C.minus(other: ListPolynomial<C>): ListPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) ListPolynomial(listOf(this@minus))
else ListPolynomial(
toMutableList()
.apply {
forEachIndexed { index, c -> if (index != 0) this[index] = -c }
val result = if (size == 0) this@minus else this@minus - get(0)
if(size == 0) add(result)
else this[0] = result
}
)
}
/**
* Returns product of the constant represented as polynomial and the polynomial.
*/
public override operator fun C.times(other: ListPolynomial<C>): ListPolynomial<C> =
ListPolynomial(
other.coefficients
.toMutableList()
.apply {
for (deg in indices) this[deg] = this@times * this[deg]
}
)
/**
* Returns sum of the constant represented as polynomial and the polynomial.
*/
public override operator fun ListPolynomial<C>.plus(other: C): ListPolynomial<C> =
with(coefficients) {
if (isEmpty()) ListPolynomial(listOf(other))
else ListPolynomial(
toMutableList()
.apply {
val result = if (size == 0) other else get(0) + other
if(size == 0) add(result)
else this[0] = result
}
)
}
/**
* Returns difference between the constant represented as polynomial and the polynomial.
*/
public override operator fun ListPolynomial<C>.minus(other: C): ListPolynomial<C> =
with(coefficients) {
if (isEmpty()) ListPolynomial(listOf(-other))
else ListPolynomial(
toMutableList()
.apply {
val result = if (size == 0) other else get(0) - other
if(size == 0) add(result)
else this[0] = result
}
)
}
/**
* Returns product of the constant represented as polynomial and the polynomial.
*/
public override operator fun ListPolynomial<C>.times(other: C): ListPolynomial<C> =
ListPolynomial(
coefficients
.toMutableList()
.apply {
for (deg in indices) this[deg] = this[deg] * other
}
)
/**
* Converts the constant [value] to polynomial.
*/
public override fun number(value: C): ListPolynomial<C> = ListPolynomial(value)
/**
* Returns negation of the polynomial.
*/
public override operator fun ListPolynomial<C>.unaryMinus(): ListPolynomial<C> =
ListPolynomial(coefficients.map { -it })
/**
* Returns sum of the polynomials.
*/
public override operator fun ListPolynomial<C>.plus(other: ListPolynomial<C>): ListPolynomial<C> {
val thisDegree = degree
val otherDegree = other.degree
return ListPolynomial(
List(max(thisDegree, otherDegree) + 1) {
when {
it > thisDegree -> other.coefficients[it]
it > otherDegree -> coefficients[it]
else -> coefficients[it] + other.coefficients[it]
}
}
)
}
/**
* Returns difference of the polynomials.
*/
public override operator fun ListPolynomial<C>.minus(other: ListPolynomial<C>): ListPolynomial<C> {
val thisDegree = degree
val otherDegree = other.degree
return ListPolynomial(
List(max(thisDegree, otherDegree) + 1) {
when {
it > thisDegree -> -other.coefficients[it]
it > otherDegree -> coefficients[it]
else -> coefficients[it] - other.coefficients[it]
}
}
)
}
/**
* Returns product of the polynomials.
*/
public override operator fun ListPolynomial<C>.times(other: ListPolynomial<C>): ListPolynomial<C> {
val thisDegree = degree
val otherDegree = other.degree
return ListPolynomial(
List(thisDegree + otherDegree + 1) { d ->
(max(0, d - otherDegree)..min(thisDegree, d))
.map { coefficients[it] * other.coefficients[d - it] }
.reduce { acc, rational -> acc + rational }
}
)
}
/**
* Instance of zero polynomial (zero of the polynomial ring).
*/
override val zero: ListPolynomial<C> = ListPolynomial(emptyList())
/**
* Instance of unit constant (unit of the underlying ring).
*/
override val one: ListPolynomial<C> = ListPolynomial(listOf(constantOne))
/**
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
* zero, degree is -1.
*/
public override val ListPolynomial<C>.degree: Int get() = coefficients.lastIndex
@Suppress("NOTHING_TO_INLINE")
public inline fun ListPolynomial<C>.substitute(argument: C): C = this.substitute(ring, argument)
@Suppress("NOTHING_TO_INLINE")
public inline fun ListPolynomial<C>.substitute(argument: ListPolynomial<C>): ListPolynomial<C> = this.substitute(ring, argument)
@Suppress("NOTHING_TO_INLINE")
public inline fun ListPolynomial<C>.asFunction(): (C) -> C = { this.substitute(ring, it) }
@Suppress("NOTHING_TO_INLINE")
public inline fun ListPolynomial<C>.asFunctionOnConstants(): (C) -> C = { this.substitute(ring, it) }
@Suppress("NOTHING_TO_INLINE")
public inline fun ListPolynomial<C>.asFunctionOnPolynomials(): (ListPolynomial<C>) -> ListPolynomial<C> = { this.substitute(ring, it) }
/**
* Evaluates the polynomial for the given value [argument].
*/
@Suppress("NOTHING_TO_INLINE")
public inline operator fun ListPolynomial<C>.invoke(argument: C): C = this.substitute(ring, argument)
@Suppress("NOTHING_TO_INLINE")
public inline operator fun ListPolynomial<C>.invoke(argument: ListPolynomial<C>): ListPolynomial<C> = this.substitute(ring, argument)
}
/**
* Space of polynomials constructed over ring.
*
* @param C the type of constants. Polynomials have them as a coefficients in their terms.
* @param A type of underlying ring of constants. It's [Ring] of [C].
* @param ring underlying ring of constants of type [A].
*/
public class ScalableListPolynomialSpace<C, A>(
ring: A,
) : ListPolynomialSpace<C, A>(ring), ScaleOperations<ListPolynomial<C>> where A : Ring<C>, A : ScaleOperations<C> {
override fun scale(a: ListPolynomial<C>, value: Double): ListPolynomial<C> =
ring { ListPolynomial(a.coefficients.map { scale(it, value) }) }
}

View File

@ -1,105 +0,0 @@
/*
* 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.operations.Ring
public data class ListRationalFunction<C>(
public override val numerator: ListPolynomial<C>,
public override val denominator: ListPolynomial<C>
) : RationalFunction<C, ListPolynomial<C>> {
override fun toString(): String = "RationalFunction${numerator.coefficients}/${denominator.coefficients}"
}
public class ListRationalFunctionSpace<C, A : Ring<C>> (
public val ring: A,
) :
RationalFunctionalSpaceOverPolynomialSpace<
C,
ListPolynomial<C>,
ListRationalFunction<C>,
ListPolynomialSpace<C, A>,
>,
PolynomialSpaceOfFractions<
C,
ListPolynomial<C>,
ListRationalFunction<C>,
>() {
override val polynomialRing : ListPolynomialSpace<C, A> = ListPolynomialSpace(ring)
override fun constructRationalFunction(numerator: ListPolynomial<C>, denominator: ListPolynomial<C>): ListRationalFunction<C> =
ListRationalFunction(numerator, denominator)
/**
* Instance of zero rational function (zero of the rational functions ring).
*/
public override val zero: ListRationalFunction<C> = ListRationalFunction(polynomialZero, polynomialOne)
/**
* Instance of unit polynomial (unit of the rational functions ring).
*/
public override val one: ListRationalFunction<C> = ListRationalFunction(polynomialOne, polynomialOne)
// TODO: Разобрать
// operator fun invoke(arg: UnivariatePolynomial<T>): RationalFunction<T> =
// RationalFunction(
// numerator(arg),
// denominator(arg)
// )
//
// operator fun invoke(arg: RationalFunction<T>): RationalFunction<T> {
// val num = numerator invokeRFTakeNumerator arg
// val den = denominator invokeRFTakeNumerator arg
// val degreeDif = numeratorDegree - denominatorDegree
// return if (degreeDif > 0)
// RationalFunction(
// num,
// multiplyByPower(den, arg.denominator, degreeDif)
// )
// else
// RationalFunction(
// multiplyByPower(num, arg.denominator, -degreeDif),
// den
// )
// }
//
// override fun toString(): String = toString(UnivariatePolynomial.variableName)
//
// fun toString(withVariableName: String = UnivariatePolynomial.variableName): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toString(withVariableName)
// else -> "${numerator.toStringWithBrackets(withVariableName)}/${denominator.toStringWithBrackets(withVariableName)}"
// }
//
// fun toStringWithBrackets(withVariableName: String = UnivariatePolynomial.variableName): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toStringWithBrackets(withVariableName)
// else -> "(${numerator.toStringWithBrackets(withVariableName)}/${denominator.toStringWithBrackets(withVariableName)})"
// }
//
// fun toReversedString(withVariableName: String = UnivariatePolynomial.variableName): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedString(withVariableName)
// else -> "${numerator.toReversedStringWithBrackets(withVariableName)}/${denominator.toReversedStringWithBrackets(withVariableName)}"
// }
//
// fun toReversedStringWithBrackets(withVariableName: String = UnivariatePolynomial.variableName): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedStringWithBrackets(withVariableName)
// else -> "(${numerator.toReversedStringWithBrackets(withVariableName)}/${denominator.toReversedStringWithBrackets(withVariableName)})"
// }
//
// fun removeZeros() =
// RationalFunction(
// numerator.removeZeros(),
// denominator.removeZeros()
// )
}

View File

@ -1,389 +0,0 @@
/*
* 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.operations.invoke
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.ScaleOperations
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.experimental.ExperimentalTypeInference
import kotlin.jvm.JvmName
import kotlin.math.max
/**
* Polynomial model without fixation on specific context they are applied to.
*
* @param C the type of constants.
*/
public data class NumberedPolynomial<C>
internal constructor(
/**
* Map that collects coefficients of the polynomial. Every monomial `a x_1^{d_1} ... x_n^{d_n}` is represented as
* pair "key-value" in the map, where value is coefficients `a` and
* key is list that associates index of every variable in the monomial with multiplicity of the variable occurring
* in the monomial. For example coefficients of polynomial `5 x_1^2 x_3^3 - 6 x_2` can be represented as
* ```
* mapOf(
* listOf(2, 0, 3) to 5,
* listOf(0, 1) to (-6),
* )
* ```
* and also as
* ```
* mapOf(
* listOf(2, 0, 3) to 5,
* listOf(0, 1) to (-6),
* listOf(0, 1, 1) to 0,
* )
* ```
* It is recommended not to put zero monomials into the map, but is not prohibited. Lists of degrees always do not
* contain any zeros on end, but can contain zeros on start or anywhere in middle.
*/
public val coefficients: Map<List<UInt>, C>
) : Polynomial<C> {
override fun toString(): String = "NumberedPolynomial$coefficients"
}
/**
* Space of polynomials.
*
* @param C the type of operated polynomials.
* @param A the intersection of [Ring] of [C] and [ScaleOperations] of [C].
* @param ring the [A] instance.
*/
public open class NumberedPolynomialSpace<C, A : Ring<C>>(
public final override val ring: A,
) : PolynomialSpaceOverRing<C, NumberedPolynomial<C>, A> {
/**
* Returns sum of the polynomial and the integer represented as polynomial.
*
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
*/
public override operator fun NumberedPolynomial<C>.plus(other: Int): NumberedPolynomial<C> =
if (other == 0) this
else
NumberedPolynomial(
coefficients
.toMutableMap()
.apply {
val degs = emptyList<UInt>()
this[degs] = getOrElse(degs) { constantZero } + other
}
)
/**
* Returns difference between the polynomial and the integer represented as polynomial.
*
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
*/
public override operator fun NumberedPolynomial<C>.minus(other: Int): NumberedPolynomial<C> =
if (other == 0) this
else
NumberedPolynomial(
coefficients
.toMutableMap()
.apply {
val degs = emptyList<UInt>()
this[degs] = getOrElse(degs) { constantZero } - other
}
)
/**
* Returns product of the polynomial and the integer represented as polynomial.
*
* The operation is equivalent to sum of [other] copies of [this].
*/
public override operator fun NumberedPolynomial<C>.times(other: Int): NumberedPolynomial<C> =
if (other == 0) zero
else NumberedPolynomial<C>(
coefficients
.toMutableMap()
.apply {
for (degs in keys) this[degs] = this[degs]!! * other
}
)
/**
* Returns sum of the integer represented as polynomial and the polynomial.
*
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
*/
public override operator fun Int.plus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
if (this == 0) other
else
NumberedPolynomial(
other.coefficients
.toMutableMap()
.apply {
val degs = emptyList<UInt>()
this[degs] = this@plus + getOrElse(degs) { constantZero }
}
)
/**
* Returns difference between the integer represented as polynomial and the polynomial.
*
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
*/
public override operator fun Int.minus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
if (this == 0) other
else
NumberedPolynomial(
other.coefficients
.toMutableMap()
.apply {
val degs = emptyList<UInt>()
this[degs] = this@minus - getOrElse(degs) { constantZero }
}
)
/**
* Returns product of the integer represented as polynomial and the polynomial.
*
* The operation is equivalent to sum of [this] copies of [other].
*/
public override operator fun Int.times(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
if (this == 0) zero
else NumberedPolynomial(
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): NumberedPolynomial<C> = number(constantNumber(value))
/**
* Returns sum of the constant represented as polynomial and the polynomial.
*/
override operator fun C.plus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) NumberedPolynomial<C>(mapOf(emptyList<UInt>() to this@plus))
else NumberedPolynomial<C>(
toMutableMap()
.apply {
val degs = emptyList<UInt>()
this[degs] = this@plus + getOrElse(degs) { constantZero }
}
)
}
/**
* Returns difference between the constant represented as polynomial and the polynomial.
*/
override operator fun C.minus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
with(other.coefficients) {
if (isEmpty()) NumberedPolynomial<C>(mapOf(emptyList<UInt>() to this@minus))
else NumberedPolynomial<C>(
toMutableMap()
.apply {
forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c }
val degs = emptyList<UInt>()
this[degs] = this@minus - getOrElse(degs) { constantZero }
}
)
}
/**
* Returns product of the constant represented as polynomial and the polynomial.
*/
override operator fun C.times(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
NumberedPolynomial<C>(
other.coefficients
.toMutableMap()
.apply {
for (degs in keys) this[degs] = this@times * this[degs]!!
}
)
/**
* Returns sum of the constant represented as polynomial and the polynomial.
*/
override operator fun NumberedPolynomial<C>.plus(other: C): NumberedPolynomial<C> =
with(coefficients) {
if (isEmpty()) NumberedPolynomial<C>(mapOf(emptyList<UInt>() to other))
else NumberedPolynomial<C>(
toMutableMap()
.apply {
val degs = emptyList<UInt>()
this[degs] = getOrElse(degs) { constantZero } + other
}
)
}
/**
* Returns difference between the constant represented as polynomial and the polynomial.
*/
override operator fun NumberedPolynomial<C>.minus(other: C): NumberedPolynomial<C> =
with(coefficients) {
if (isEmpty()) NumberedPolynomial<C>(mapOf(emptyList<UInt>() to other))
else NumberedPolynomial<C>(
toMutableMap()
.apply {
val degs = emptyList<UInt>()
this[degs] = getOrElse(degs) { constantZero } - other
}
)
}
/**
* Returns product of the constant represented as polynomial and the polynomial.
*/
override operator fun NumberedPolynomial<C>.times(other: C): NumberedPolynomial<C> =
NumberedPolynomial<C>(
coefficients
.toMutableMap()
.apply {
for (degs in keys) this[degs] = this[degs]!! * other
}
)
/**
* Converts the constant [value] to polynomial.
*/
public override fun number(value: C): NumberedPolynomial<C> =
NumberedPolynomial(mapOf(emptyList<UInt>() to value))
/**
* Returns negation of the polynomial.
*/
override fun NumberedPolynomial<C>.unaryMinus(): NumberedPolynomial<C> =
NumberedPolynomial<C>(
coefficients.mapValues { -it.value }
)
/**
* Returns sum of the polynomials.
*/
override operator fun NumberedPolynomial<C>.plus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
NumberedPolynomial<C>(
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 NumberedPolynomial<C>.minus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
NumberedPolynomial<C>(
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 NumberedPolynomial<C>.times(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
NumberedPolynomial<C>(
buildMap(coefficients.size * other.coefficients.size) {
for ((degs1, c1) in coefficients) for ((degs2, c2) in other.coefficients) {
val degs =
(0..max(degs1.lastIndex, degs2.lastIndex))
.map { degs1.getOrElse(it) { 0U } + degs2.getOrElse(it) { 0U } }
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: NumberedPolynomial<C> = NumberedPolynomial<C>(emptyMap())
/**
* Instance of unit polynomial (unit of the polynomial ring).
*/
override val one: NumberedPolynomial<C> =
NumberedPolynomial<C>(
mapOf(
emptyList<UInt>() to constantOne // 1 * x_1^0 * x_2^0 * ...
)
)
/**
* Maximal index (ID) of variable occurring in the polynomial with positive power. If there is no such variable,
* the result is `-1`.
*/
public val NumberedPolynomial<C>.lastVariable: Int
get() = coefficients.entries.maxOfOrNull { (degs, _) -> degs.lastIndex } ?: -1
/**
* 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 NumberedPolynomial<C>.degree: Int
get() = coefficients.entries.maxOfOrNull { (degs, _) -> degs.sum().toInt() } ?: -1
/**
* List that associates indices of variables (that appear in the polynomial in positive exponents) with their most
* exponents in which the variables are appeared in the polynomial.
*
* As consequence all values in the list are non-negative integers. Also, if the polynomial is constant, the list is empty.
* And last index of the list is [lastVariable].
*/
public val NumberedPolynomial<C>.degrees: List<UInt>
get() =
MutableList(lastVariable + 1) { 0u }.apply {
coefficients.entries.forEach { (degs, _) ->
degs.forEachIndexed { index, deg ->
this[index] = max(this[index], deg)
}
}
}
/**
* Counts degree of the polynomial by the specified [variable].
*/
public fun NumberedPolynomial<C>.degreeBy(variable: Int): UInt =
coefficients.entries.maxOfOrNull { (degs, _) -> degs.getOrElse(variable) { 0u } } ?: 0u
/**
* Counts degree of the polynomial by the specified [variables].
*/
public fun NumberedPolynomial<C>.degreeBy(variables: Collection<Int>): UInt =
coefficients.entries.maxOfOrNull { (degs, _) ->
degs.withIndex().filter { (index, _) -> index in variables }.sumOf { it.value }
} ?: 0u
/**
* Count of variables occurring in the polynomial with positive power. If there is no such variable,
* the result is `0`.
*/
public val NumberedPolynomial<C>.countOfVariables: Int
get() =
MutableList(lastVariable + 1) { false }.apply {
coefficients.entries.forEach { (degs, _) ->
degs.forEachIndexed { index, deg ->
if (deg != 0u) this[index] = true
}
}
}.count { it }
@Suppress("NOTHING_TO_INLINE")
public inline fun NumberedPolynomial<C>.substitute(argument: Map<Int, C>): NumberedPolynomial<C> = this.substitute(ring, argument)
@Suppress("NOTHING_TO_INLINE")
@JvmName("substitutePolynomial")
public inline fun NumberedPolynomial<C>.substitute(argument: Map<Int, NumberedPolynomial<C>>): NumberedPolynomial<C> = this.substitute(ring, argument)
@Suppress("NOTHING_TO_INLINE")
public inline fun NumberedPolynomial<C>.asFunction(): (Map<Int, C>) -> NumberedPolynomial<C> = { this.substitute(ring, it) }
@Suppress("NOTHING_TO_INLINE")
public inline fun NumberedPolynomial<C>.asFunctionOnConstants(): (Map<Int, C>) -> NumberedPolynomial<C> = { this.substitute(ring, it) }
@Suppress("NOTHING_TO_INLINE")
public inline fun NumberedPolynomial<C>.asFunctionOnPolynomials(): (Map<Int, NumberedPolynomial<C>>) -> NumberedPolynomial<C> = { this.substitute(ring, it) }
@Suppress("NOTHING_TO_INLINE")
public inline operator fun NumberedPolynomial<C>.invoke(argument: Map<Int, C>): NumberedPolynomial<C> = this.substitute(ring, argument)
@Suppress("NOTHING_TO_INLINE")
@JvmName("invokePolynomial")
public inline operator fun NumberedPolynomial<C>.invoke(argument: Map<Int, NumberedPolynomial<C>>): NumberedPolynomial<C> = this.substitute(ring, argument)
// FIXME: Move to other constructors with context receiver
public fun C.asNumberedPolynomial() : NumberedPolynomial<C> = NumberedPolynomial<C>(mapOf(emptyList<UInt>() to this))
}

View File

@ -1,188 +0,0 @@
/*
* 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.operations.Ring
import space.kscience.kmath.operations.invoke
import kotlin.math.max
public class NumberedRationalFunction<C> internal constructor(
public override val numerator: NumberedPolynomial<C>,
public override val denominator: NumberedPolynomial<C>
) : RationalFunction<C, NumberedPolynomial<C>> {
override fun toString(): String = "NumberedRationalFunction${numerator.coefficients}/${denominator.coefficients}"
}
public class NumberedRationalFunctionSpace<C, A: Ring<C>> (
public val ring: A,
) :
RationalFunctionalSpaceOverPolynomialSpace<
C,
NumberedPolynomial<C>,
NumberedRationalFunction<C>,
NumberedPolynomialSpace<C, A>,
>,
PolynomialSpaceOfFractions<
C,
NumberedPolynomial<C>,
NumberedRationalFunction<C>,
>() {
override val polynomialRing : NumberedPolynomialSpace<C, A> = NumberedPolynomialSpace(ring)
override fun constructRationalFunction(
numerator: NumberedPolynomial<C>,
denominator: NumberedPolynomial<C>
): NumberedRationalFunction<C> =
NumberedRationalFunction(numerator, denominator)
/**
* Instance of zero rational function (zero of the rational functions ring).
*/
public override val zero: NumberedRationalFunction<C> = NumberedRationalFunction(polynomialZero, polynomialOne)
/**
* Instance of unit polynomial (unit of the rational functions ring).
*/
public override val one: NumberedRationalFunction<C> = NumberedRationalFunction(polynomialOne, polynomialOne)
/**
* Maximal index (ID) of variable occurring in the polynomial with positive power. If there is no such variable,
* the result is `-1`.
*/
public val NumberedPolynomial<C>.lastVariable: Int get() = polynomialRing { lastVariable }
/**
* List that associates indices of variables (that appear in the polynomial in positive exponents) with their most
* exponents in which the variables are appeared in the polynomial.
*
* As consequence all values in the list are non-negative integers. Also, if the polynomial is constant, the list is empty.
* And last index of the list is [lastVariable].
*/
public val NumberedPolynomial<C>.degrees: List<UInt> get() = polynomialRing { degrees }
/**
* Counts degree of the polynomial by the specified [variable].
*/
public fun NumberedPolynomial<C>.degreeBy(variable: Int): UInt = polynomialRing { degreeBy(variable) }
/**
* Counts degree of the polynomial by the specified [variables].
*/
public fun NumberedPolynomial<C>.degreeBy(variables: Collection<Int>): UInt = polynomialRing { degreeBy(variables) }
/**
* Count of variables occurring in the polynomial with positive power. If there is no such variable,
* the result is `0`.
*/
public val NumberedPolynomial<C>.countOfVariables: Int get() = polynomialRing { countOfVariables }
/**
* Count of all variables that appear in the polynomial in positive exponents.
*/
public val NumberedRationalFunction<C>.lastVariable: Int
get() = polynomialRing { max(numerator.lastVariable, denominator.lastVariable) }
/**
* Count of variables occurring in the rational function with positive power. If there is no such variable,
* the result is `0`.
*/
public val NumberedRationalFunction<C>.countOfVariables: Int
get() =
MutableList(lastVariable + 1) { false }.apply {
numerator.coefficients.entries.forEach { (degs, _) ->
degs.forEachIndexed { index, deg ->
if (deg != 0u) this[index] = true
}
}
denominator.coefficients.entries.forEach { (degs, _) ->
degs.forEachIndexed { index, deg ->
if (deg != 0u) this[index] = true
}
}
}.count { it }
// TODO: Разобрать
// operator fun invoke(arg: Map<Int, C>): NumberedRationalFunction<C> =
// NumberedRationalFunction(
// numerator(arg),
// denominator(arg)
// )
//
// @JvmName("invokePolynomial")
// operator fun invoke(arg: Map<Int, Polynomial<C>>): NumberedRationalFunction<C> =
// NumberedRationalFunction(
// numerator(arg),
// denominator(arg)
// )
//
// @JvmName("invokeRationalFunction")
// operator fun invoke(arg: Map<Int, NumberedRationalFunction<C>>): NumberedRationalFunction<C> {
// var num = numerator invokeRFTakeNumerator arg
// var den = denominator invokeRFTakeNumerator arg
// for (variable in 0 until max(numerator.countOfVariables, denominator.countOfVariables)) if (variable in arg) {
// val degreeDif = numerator.degrees.getOrElse(variable) { 0 } - denominator.degrees.getOrElse(variable) { 0 }
// if (degreeDif > 0)
// den = multiplyByPower(den, arg[variable]!!.denominator, degreeDif)
// else
// num = multiplyByPower(num, arg[variable]!!.denominator, -degreeDif)
// }
// return NumberedRationalFunction(num, den)
// }
//
// override fun toString(): String = toString(Polynomial.variableName)
//
// fun toString(withVariableName: String = Polynomial.variableName): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toString(withVariableName)
// else -> "${numerator.toStringWithBrackets(withVariableName)}/${denominator.toStringWithBrackets(withVariableName)}"
// }
//
// fun toString(namer: (Int) -> String): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toString(namer)
// else -> "${numerator.toStringWithBrackets(namer)}/${denominator.toStringWithBrackets(namer)}"
// }
//
// fun toStringWithBrackets(withVariableName: String = Polynomial.variableName): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toStringWithBrackets(withVariableName)
// else -> "(${numerator.toStringWithBrackets(withVariableName)}/${denominator.toStringWithBrackets(withVariableName)})"
// }
//
// fun toStringWithBrackets(namer: (Int) -> String): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toStringWithBrackets(namer)
// else -> "(${numerator.toStringWithBrackets(namer)}/${denominator.toStringWithBrackets(namer)})"
// }
//
// fun toReversedString(withVariableName: String = Polynomial.variableName): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedString(withVariableName)
// else -> "${numerator.toReversedStringWithBrackets(withVariableName)}/${denominator.toReversedStringWithBrackets(withVariableName)}"
// }
//
// fun toReversedString(namer: (Int) -> String): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedString(namer)
// else -> "${numerator.toReversedStringWithBrackets(namer)}/${denominator.toReversedStringWithBrackets(namer)}"
// }
//
// fun toReversedStringWithBrackets(withVariableName: String = Polynomial.variableName): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedStringWithBrackets(withVariableName)
// else -> "(${numerator.toReversedStringWithBrackets(withVariableName)}/${denominator.toReversedStringWithBrackets(withVariableName)})"
// }
//
// fun toReversedStringWithBrackets(namer: (Int) -> String): String =
// when(true) {
// numerator.isZero() -> "0"
// denominator.isOne() -> numerator.toReversedStringWithBrackets(namer)
// else -> "(${numerator.toReversedStringWithBrackets(namer)}/${denominator.toReversedStringWithBrackets(namer)})"
// }
}

View File

@ -1,132 +0,0 @@
/*
* 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.misc.PerformancePitfall
import space.kscience.kmath.operations.Ring
/**
* Represents piecewise-defined function.
*
* @param T the piece key type.
* @param R the sub-function type.
*/
public fun interface Piecewise<in T, out R> {
/**
* Returns the appropriate sub-function for given piece key.
*/
public fun findPiece(arg: T): R?
}
/**
* Represents piecewise-defined function where all the sub-functions are polynomials.
*
* @property pieces An ordered list of range-polynomial pairs. The list does not in general guarantee that there are no
* "holes" in it.
*/
public interface PiecewisePolynomial<T : Comparable<T>> : Piecewise<T, ListPolynomial<T>> {
public val pieces: Collection<Pair<ClosedRange<T>, ListPolynomial<T>>>
override fun findPiece(arg: T): ListPolynomial<T>?
}
/**
* A generic piecewise without constraints on how pieces are placed
*/
@PerformancePitfall("findPiece method of resulting piecewise is slow")
public fun <T : Comparable<T>> PiecewisePolynomial(
pieces: Collection<Pair<ClosedRange<T>, ListPolynomial<T>>>,
): PiecewisePolynomial<T> = object : PiecewisePolynomial<T> {
override val pieces: Collection<Pair<ClosedRange<T>, ListPolynomial<T>>> = pieces
override fun findPiece(arg: T): ListPolynomial<T>? = pieces.firstOrNull { arg in it.first }?.second
}
/**
* An optimized piecewise that uses not separate pieces, but a range separated by delimiters.
* The pieces search is logarithmic.
*/
private class OrderedPiecewisePolynomial<T : Comparable<T>>(
override val pieces: List<Pair<ClosedRange<T>, ListPolynomial<T>>>,
) : PiecewisePolynomial<T> {
override fun findPiece(arg: T): ListPolynomial<T>? {
val index = pieces.binarySearch { (range, _) ->
when {
arg >= range.endInclusive -> -1
arg < range.start -> +1
else -> 0
}
}
return if (index < 0) null else pieces[index].second
}
}
/**
* A [Piecewise] builder where all the pieces are ordered by the [Comparable] type instances.
*
* @param T the comparable piece key type.
* @param delimiter the initial piecewise separator
*/
public class PiecewiseBuilder<T : Comparable<T>>(delimiter: T) {
private val delimiters: MutableList<T> = arrayListOf(delimiter)
private val pieces: MutableList<ListPolynomial<T>> = arrayListOf()
/**
* Dynamically adds a piece to the right side (beyond maximum argument value of previous piece)
*
* @param right new rightmost position. If is less than current rightmost position, an error is thrown.
* @param piece the sub-function.
*/
public fun putRight(right: T, piece: ListPolynomial<T>) {
require(right > delimiters.last()) { "New delimiter should be to the right of old one" }
delimiters += right
pieces += piece
}
/**
* Dynamically adds a piece to the left side (beyond maximum argument value of previous piece)
*
* @param left the new leftmost position. If is less than current rightmost position, an error is thrown.
* @param piece the sub-function.
*/
public fun putLeft(left: T, piece: ListPolynomial<T>) {
require(left < delimiters.first()) { "New delimiter should be to the left of old one" }
delimiters.add(0, left)
pieces.add(0, piece)
}
public fun build(): PiecewisePolynomial<T> = OrderedPiecewisePolynomial(delimiters.zipWithNext { l, r ->
l..r
}.zip(pieces))
}
/**
* A builder for [PiecewisePolynomial]
*/
public fun <T : Comparable<T>> PiecewisePolynomial(
startingPoint: T,
builder: PiecewiseBuilder<T>.() -> Unit,
): PiecewisePolynomial<T> = PiecewiseBuilder(startingPoint).apply(builder).build()
/**
* Return a value of polynomial function with given [ring] a given [arg] or null if argument is outside piecewise
* definition.
*/
public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.substitute(ring: C, arg: T): T? =
findPiece(arg)?.substitute(ring, arg)
/**
* Convert this polynomial to a function returning nullable value (null if argument is outside piecewise range).
*/
public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.asFunction(ring: C): (T) -> T? = { substitute(ring, it) }
/**
* Convert this polynomial to a function using [defaultValue] for arguments outside the piecewise range.
*/
public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.asFunction(ring: C, defaultValue: T): (T) -> T =
{ substitute(ring, it) ?: defaultValue }

View File

@ -1,203 +0,0 @@
/*
* 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.Ring
/**
* Returns the same degrees' description of the monomial, but without zero degrees.
*/
internal fun Map<Symbol, UInt>.cleanUp() = filterValues { it > 0U }
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(coefs, toCheckInput)
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(coefs, toCheckInput)
@Suppress("FunctionName")
internal fun <C, A: Ring<C>> A.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> {
if (!toCheckInput) return LabeledPolynomial<C>(coefs)
val fixedCoefs = LinkedHashMap<Map<Symbol, UInt>, C>(coefs.size)
for (entry in coefs) {
val key = entry.key.cleanUp()
val value = entry.value
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
}
return LabeledPolynomial<C>(fixedCoefs)
}
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(pairs, toCheckInput)
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(pairs, toCheckInput)
@Suppress("FunctionName")
internal fun <C, A: Ring<C>> A.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>, toCheckInput: Boolean = true) : LabeledPolynomial<C> {
if (!toCheckInput) return LabeledPolynomial<C>(pairs.toMap())
val fixedCoefs = LinkedHashMap<Map<Symbol, UInt>, C>(pairs.size)
for (entry in pairs) {
val key = entry.first.cleanUp()
val value = entry.second
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
}
return LabeledPolynomial<C>(fixedCoefs)
}
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(pairs = pairs, toCheckInput = toCheckInput)
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(pairs = pairs, toCheckInput = toCheckInput)
@Suppress("FunctionName")
internal fun <C, A: Ring<C>> A.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> {
if (!toCheckInput) return LabeledPolynomial<C>(pairs.toMap())
val fixedCoefs = LinkedHashMap<Map<Symbol, UInt>, C>(pairs.size)
for (entry in pairs) {
val key = entry.first.cleanUp()
val value = entry.second
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
}
return LabeledPolynomial<C>(fixedCoefs)
}
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs, toCheckInput = true)
//context(A)
//public fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to one))
//context(LabeledPolynomialSpace<C, A>)
//public fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to constantOne))
//context(LabeledRationalFunctionSpace<C, A>)
//public fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to constantOne))
public fun <C> C.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this))
@DslMarker
@UnstableKMathAPI
internal annotation class LabeledPolynomialConstructorDSL
@UnstableKMathAPI
@LabeledPolynomialConstructorDSL
public class LabeledPolynomialTermSignatureBuilder {
private val signature: MutableMap<Symbol, UInt> = LinkedHashMap()
public fun build(): Map<Symbol, UInt> = signature
public infix fun Symbol.inPowerOf(deg: UInt) {
signature[this] = deg
}
@Suppress("NOTHING_TO_INLINE")
public inline infix fun Symbol.pow(deg: UInt): Unit = this inPowerOf deg
@Suppress("NOTHING_TO_INLINE")
public inline infix fun Symbol.`in`(deg: UInt): Unit = this inPowerOf deg
@Suppress("NOTHING_TO_INLINE")
public inline infix fun Symbol.of(deg: UInt): Unit = this inPowerOf deg
}
@UnstableKMathAPI
public class LabeledPolynomialBuilder<C>(private val zero: C, private val add: (C, C) -> C, capacity: Int = 0) {
private val coefficients: MutableMap<Map<Symbol, UInt>, C> = LinkedHashMap(capacity)
public fun build(): LabeledPolynomial<C> = LabeledPolynomial<C>(coefficients)
public operator fun C.invoke(block: LabeledPolynomialTermSignatureBuilder.() -> Unit) {
val signature = LabeledPolynomialTermSignatureBuilder().apply(block).build()
coefficients[signature] = add(coefficients.getOrElse(signature) { zero }, this@invoke)
}
@Suppress("NOTHING_TO_INLINE")
public inline infix fun C.with(noinline block: LabeledPolynomialTermSignatureBuilder.() -> Unit): Unit = this.invoke(block)
@Suppress("NOTHING_TO_INLINE")
public inline infix fun (LabeledPolynomialTermSignatureBuilder.() -> Unit).with(coef: C): Unit = coef.invoke(this)
@Suppress("NOTHING_TO_INLINE")
public infix fun sig(block: LabeledPolynomialTermSignatureBuilder.() -> Unit): LabeledPolynomialTermSignatureBuilder.() -> Unit = block
}
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
@UnstableKMathAPI
@LabeledPolynomialConstructorDSL
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> A.LabeledPolynomial(block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = LabeledPolynomialBuilder(zero, ::add).apply(block).build()
@UnstableKMathAPI
@LabeledPolynomialConstructorDSL
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> A.LabeledPolynomial(capacity: Int, block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = LabeledPolynomialBuilder(zero, ::add, capacity).apply(block).build()
@UnstableKMathAPI
@LabeledPolynomialConstructorDSL
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = LabeledPolynomialBuilder(constantZero, { left: C, right: C -> left + right}).apply(block).build()
@UnstableKMathAPI
@LabeledPolynomialConstructorDSL
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(capacity: Int, block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = LabeledPolynomialBuilder(constantZero, { left: C, right: C -> left + right}, capacity).apply(block).build()
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
@Suppress("FunctionName")
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, toCheckInput = true),
LabeledPolynomial(denominatorCoefficients, toCheckInput = true)
)
@Suppress("FunctionName")
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, toCheckInput = true),
LabeledPolynomial(denominatorCoefficients, toCheckInput = true)
)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numerator: LabeledPolynomial<C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(numerator, polynomialOne)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.LabeledRationalFunction(numerator: LabeledPolynomial<C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(numerator, LabeledPolynomial(mapOf(emptyMap<Symbol, UInt>() to one), toCheckInput = false))
@Suppress("FunctionName")
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(
LabeledPolynomial(numeratorCoefficients, toCheckInput = true),
polynomialOne
)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
LabeledRationalFunction<C>(
LabeledPolynomial(numeratorCoefficients, toCheckInput = true),
LabeledPolynomial(mapOf(emptyMap<Symbol, UInt>() to one), toCheckInput = false)
)
//context(A)
//public fun <C, A: Ring<C>> Symbol.asLabeledRationalFunction() : LabeledRationalFunction<C> = LabeledRationalFunction(asLabeledPolynomial())
//context(LabeledRationalFunctionSpace<C, A>)
//public fun <C, A: Ring<C>> Symbol.asLabeledRationalFunction() : LabeledRationalFunction<C> = LabeledRationalFunction(asLabeledPolynomial())
//context(A)
//public fun <C, A: Ring<C>> C.asLabeledRationalFunction() : LabeledRationalFunction<C> = LabeledRationalFunction(asLabeledPolynomial())
//context(LabeledRationalFunctionSpace<C, A>)
//public fun <C, A: Ring<C>> C.asLabeledRationalFunction() : LabeledRationalFunction<C> = LabeledRationalFunction(asLabeledPolynomial())

View File

@ -1,495 +0,0 @@
/*
* 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.Ring
import space.kscience.kmath.operations.invoke
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
// TODO: Docs
/**
* Creates a [LabeledPolynomialSpace] over a received ring.
*/
public fun <C, A : Ring<C>> A.labeledPolynomial(): LabeledPolynomialSpace<C, A> =
LabeledPolynomialSpace(this)
/**
* Creates a [LabeledPolynomialSpace]'s scope over a received ring.
*/
@OptIn(ExperimentalContracts::class)
public inline fun <C, A : Ring<C>, R> A.labeledPolynomial(block: LabeledPolynomialSpace<C, A>.() -> R): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return LabeledPolynomialSpace(this).block()
}
///**
// * Represents the polynomial as a [String] with names of variables substituted with names from [names].
// * Consider that monomials are sorted in lexicographic order.
// */
//context(LabeledPolynomialSpace<C, A>)
//fun <C, A: Ring<C>> LabeledPolynomial<C>.represent(names: Map<Symbol, String> = emptyMap()): String =
// coefficients.entries
// .sortedWith { o1, o2 -> LabeledPolynomial.monomialComparator.compare(o1.key, o2.key) }
// .asSequence()
// .map { (degs, t) ->
// if (degs.isEmpty()) "$t"
// else {
// when {
// t.isOne() -> ""
// t.isMinusOne() -> "-"
// else -> "$t "
// } +
// degs
// .toSortedMap()
// .filter { it.value > 0U }
// .map { (variable, deg) ->
// val variableName = names.getOrDefault(variable, variable.toString())
// when (deg) {
// 1U -> variableName
// else -> "$variableName^$deg"
// }
// }
// .joinToString(separator = " ") { it }
// }
// }
// .joinToString(separator = " + ") { it }
// .ifEmpty { "0" }
//
///**
// * Represents the polynomial as a [String] naming variables by [namer].
// * Consider that monomials are sorted in lexicographic order.
// */
//context(LabeledPolynomialSpace<C, A>)
//fun <C, A: Ring<C>> LabeledPolynomial<C>.represent(namer: (Symbol) -> String): String =
// coefficients.entries
// .sortedWith { o1, o2 -> LabeledPolynomial.monomialComparator.compare(o1.key, o2.key) }
// .asSequence()
// .map { (degs, t) ->
// if (degs.isEmpty()) "$t"
// else {
// when {
// t.isOne() -> ""
// t.isMinusOne() -> "-"
// else -> "$t "
// } +
// degs
// .toSortedMap()
// .filter { it.value > 0U }
// .map { (variable, deg) ->
// when (deg) {
// 1U -> namer(variable)
// else -> "${namer(variable)}^$deg"
// }
// }
// .joinToString(separator = " ") { it }
// }
// }
// .joinToString(separator = " + ") { it }
// .ifEmpty { "0" }
//
///**
// * Represents the polynomial as a [String] with names of variables substituted with names from [names] and with
// * brackets around the string if needed (i.e. when there are at least two addends in the representation).
// * Consider that monomials are sorted in lexicographic order.
// */
//context(LabeledPolynomialSpace<C, A>)
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representWithBrackets(names: Map<Symbol, String> = emptyMap()): String =
// with(represent(names)) { if (coefficients.count() == 1) this else "($this)" }
//
///**
// * Represents the polynomial as a [String] naming variables by [namer] and with brackets around the string if needed
// * (i.e. when there are at least two addends in the representation).
// * Consider that monomials are sorted in lexicographic order.
// */
//context(LabeledPolynomialSpace<C, A>)
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representWithBrackets(namer: (Symbol) -> String): String =
// with(represent(namer)) { if (coefficients.count() == 1) this else "($this)" }
//
///**
// * Represents the polynomial as a [String] with names of variables substituted with names from [names].
// * Consider that monomials are sorted in **reversed** lexicographic order.
// */
//context(LabeledPolynomialSpace<C, A>)
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representReversed(names: Map<Symbol, String> = emptyMap()): String =
// coefficients.entries
// .sortedWith { o1, o2 -> -LabeledPolynomial.monomialComparator.compare(o1.key, o2.key) }
// .asSequence()
// .map { (degs, t) ->
// if (degs.isEmpty()) "$t"
// else {
// when {
// t.isOne() -> ""
// t.isMinusOne() -> "-"
// else -> "$t "
// } +
// degs
// .toSortedMap()
// .filter { it.value > 0U }
// .map { (variable, deg) ->
// val variableName = names.getOrDefault(variable, variable.toString())
// when (deg) {
// 1U -> variableName
// else -> "$variableName^$deg"
// }
// }
// .joinToString(separator = " ") { it }
// }
// }
// .joinToString(separator = " + ") { it }
// .ifEmpty { "0" }
//
///**
// * Represents the polynomial as a [String] naming variables by [namer].
// * Consider that monomials are sorted in **reversed** lexicographic order.
// */
//context(LabeledPolynomialSpace<C, A>)
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representReversed(namer: (Symbol) -> String): String =
// coefficients.entries
// .sortedWith { o1, o2 -> -LabeledPolynomial.monomialComparator.compare(o1.key, o2.key) }
// .asSequence()
// .map { (degs, t) ->
// if (degs.isEmpty()) "$t"
// else {
// when {
// t.isOne() -> ""
// t.isMinusOne() -> "-"
// else -> "$t "
// } +
// degs
// .toSortedMap()
// .filter { it.value > 0U }
// .map { (variable, deg) ->
// when (deg) {
// 1U -> namer(variable)
// else -> "${namer(variable)}^$deg"
// }
// }
// .joinToString(separator = " ") { it }
// }
// }
// .joinToString(separator = " + ") { it }
// .ifEmpty { "0" }
//
///**
// * Represents the polynomial as a [String] with names of variables substituted with names from [names] and with
// * brackets around the string if needed (i.e. when there are at least two addends in the representation).
// * Consider that monomials are sorted in **reversed** lexicographic order.
// */
//context(LabeledPolynomialSpace<C, A>)
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representReversedWithBrackets(names: Map<Symbol, String> = emptyMap()): String =
// with(representReversed(names)) { if (coefficients.count() == 1) this else "($this)" }
//
///**
// * Represents the polynomial as a [String] naming variables by [namer] and with brackets around the string if needed
// * (i.e. when there are at least two addends in the representation).
// * Consider that monomials are sorted in **reversed** lexicographic order.
// */
//context(LabeledPolynomialSpace<C, A>)
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representReversedWithBrackets(namer: (Symbol) -> String): String =
// with(representReversed(namer)) { if (coefficients.count() == 1) this else "($this)" }
//operator fun <T: Field<T>> Polynomial<T>.div(other: T): Polynomial<T> =
// if (other.isZero()) throw ArithmeticException("/ by zero")
// else
// Polynomial(
// coefficients
// .mapValues { it.value / other },
// toCheckInput = false
// )
//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 = degs.entries.asSequence().filter { it.key in args }.fold(c) { acc, (variable, deg) ->
// multiplyWithPower(acc, args[variable]!!, deg)
// }
// this[newDegs] = if (newDegs in this) this[newDegs]!! + newC else newC
// }
// }
// )
//}
//
//// TODO: Replace with optimisation: the [result] may be unboxed, and all operations may be performed as soon as
//// possible on it
//@JvmName("substitutePolynomial")
//fun <C> LabeledPolynomial<C>.substitute(ring: Ring<C>, arg: Map<Symbol, LabeledPolynomial<C>>) : LabeledPolynomial<C> =
// ring.labeledPolynomial {
// if (coefficients.isEmpty()) return zero
// coefficients
// .asSequence()
// .map { (degs, c) ->
// degs.entries
// .asSequence()
// .filter { it.key in arg }
// .fold(LabeledPolynomial(mapOf(degs.filterKeys { it !in arg } to c))) { acc, (index, deg) ->
// multiplyWithPower(acc, arg[index]!!, deg)
// }
// }
// .reduce { acc, polynomial -> acc + polynomial } // TODO: Rewrite. Might be slow.
// }
//
//// TODO: Substitute rational function
//
//fun <C, A : Ring<C>> LabeledPolynomial<C>.asFunctionOver(ring: A): (Map<Symbol, C>) -> LabeledPolynomial<C> =
// { substitute(ring, it) }
//
//fun <C, A : Ring<C>> LabeledPolynomial<C>.asPolynomialFunctionOver(ring: A): (Map<Symbol, LabeledPolynomial<C>>) -> LabeledPolynomial<C> =
// { substitute(ring, it) }
/**
* Returns algebraic derivative of received polynomial.
*/
@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.
*/
@UnstableKMathAPI
public fun <C, A : Ring<C>> LabeledPolynomial<C>.derivativeWithRespectTo(
algebra: A,
variables: Collection<Symbol>,
): LabeledPolynomial<C> = algebra {
val cleanedVariables = variables.toSet()
if (cleanedVariables.isEmpty()) return this@derivativeWithRespectTo
LabeledPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
if (!degs.keys.containsAll(cleanedVariables)) return@forEach
put(
buildMap {
degs.forEach { (vari, deg) ->
when {
vari !in cleanedVariables -> put(vari, deg)
deg > 1u -> put(vari, deg - 1u)
}
}
},
cleanedVariables.fold(c) { acc, variable -> multiplyByDoubling(acc, degs[variable]!!) }
)
}
}
)
}
/**
* Returns algebraic derivative of received polynomial.
*/
@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.
*/
@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.
*/
@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.
*/
@UnstableKMathAPI
public fun <C, A : Field<C>> LabeledPolynomial<C>.antiderivativeWithRespectTo(
algebra: A,
variables: Collection<Symbol>,
): LabeledPolynomial<C> = algebra {
val cleanedVariables = variables.toSet()
if (cleanedVariables.isEmpty()) return this@antiderivativeWithRespectTo
LabeledPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
val newDegs = buildMap<Symbol, UInt>(degs.size + 1) {
for (variable in cleanedVariables) put(variable, 1u)
for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u })
}
put(
newDegs,
cleanedVariables.fold(c) { acc, variable -> acc / multiplyByDoubling(one, newDegs[variable]!!) }
)
}
}
)
}
/**
* Returns algebraic derivative of received polynomial.
*/
@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.
*/
@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

@ -1,33 +0,0 @@
/*
* 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.operations.Ring
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Creates a [LabeledRationalFunctionSpace] over a received ring.
*/
public fun <C, A : Ring<C>> A.labeledRationalFunction(): LabeledRationalFunctionSpace<C, A> =
LabeledRationalFunctionSpace(this)
/**
* Creates a [LabeledRationalFunctionSpace]'s scope over a received ring.
*/
public inline fun <C, A : Ring<C>, R> A.labeledRationalFunction(block: LabeledRationalFunctionSpace<C, A>.() -> R): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return LabeledRationalFunctionSpace(this).block()
}
//fun <T: Field<T>> LabeledRationalFunction<T>.reduced(): LabeledRationalFunction<T> {
// val greatestCommonDivider = polynomialGCD(numerator, denominator)
// return LabeledRationalFunction(
// numerator / greatestCommonDivider,
// denominator / greatestCommonDivider
// )
//}

View File

@ -1,60 +0,0 @@
/*
* 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.operations.Ring
/**
* Returns a [ListPolynomial] instance with given [coefficients]. The collection of coefficients will be reversed if
* [reverse] parameter is true.
*/
@Suppress("FunctionName")
public fun <C> ListPolynomial(coefficients: List<C>, reverse: Boolean = false): ListPolynomial<C> =
ListPolynomial(with(coefficients) { if (reverse) reversed() else this })
/**
* Returns a [ListPolynomial] instance with given [coefficients]. The collection of coefficients will be reversed if
* [reverse] parameter is true.
*/
@Suppress("FunctionName")
public fun <C> ListPolynomial(vararg coefficients: C, reverse: Boolean = false): ListPolynomial<C> =
ListPolynomial(with(coefficients) { if (reverse) reversed() else toList() })
public fun <C> C.asListPolynomial() : ListPolynomial<C> = ListPolynomial(listOf(this))
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
@Suppress("FunctionName")
public fun <C> ListRationalFunction(numeratorCoefficients: List<C>, denominatorCoefficients: List<C>, reverse: Boolean = false): ListRationalFunction<C> =
ListRationalFunction<C>(
ListPolynomial( with(numeratorCoefficients) { if (reverse) reversed() else this } ),
ListPolynomial( with(denominatorCoefficients) { if (reverse) reversed() else this } )
)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> ListRationalFunctionSpace<C, A>.ListRationalFunction(numerator: ListPolynomial<C>): ListRationalFunction<C> =
ListRationalFunction<C>(numerator, polynomialOne)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.ListRationalFunction(numerator: ListPolynomial<C>): ListRationalFunction<C> =
ListRationalFunction<C>(numerator, ListPolynomial(listOf(one)))
@Suppress("FunctionName")
public fun <C, A: Ring<C>> ListRationalFunctionSpace<C, A>.ListRationalFunction(numeratorCoefficients: List<C>, reverse: Boolean = false): ListRationalFunction<C> =
ListRationalFunction<C>(
ListPolynomial( with(numeratorCoefficients) { if (reverse) reversed() else this } ),
polynomialOne
)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.ListRationalFunction(numeratorCoefficients: List<C>, reverse: Boolean = false): ListRationalFunction<C> =
ListRationalFunction<C>(
ListPolynomial( with(numeratorCoefficients) { if (reverse) reversed() else this } ),
ListPolynomial(listOf(one))
)
//context(A)
//public fun <C, A: Ring<C>> C.asListRationalFunction() : ListRationalFunction<C> = ListRationalFunction(asListPolynomial())
//context(ListRationalFunctionSpace<C, A>)
//public fun <C, A: Ring<C>> C.asListRationalFunction() : ListRationalFunction<C> = ListRationalFunction(asListPolynomial())

View File

@ -1,233 +0,0 @@
/*
* 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.misc.UnstableKMathAPI
import space.kscience.kmath.operations.*
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
/**
* Removes zeros on the end of the coefficient list of polynomial.
*/
//context(PolynomialSpace<C, A>)
//fun <C, A: Ring<C>> Polynomial<C>.removeZeros() : Polynomial<C> =
// if (degree > -1) Polynomial(coefficients.subList(0, degree + 1)) else zero
/**
* Creates a [ListPolynomialSpace] over a received ring.
*/
public fun <C, A : Ring<C>> A.listPolynomial(): ListPolynomialSpace<C, A> =
ListPolynomialSpace(this)
/**
* Creates a [ListPolynomialSpace]'s scope over a received ring.
*/
public inline fun <C, A : Ring<C>, R> A.listPolynomial(block: ListPolynomialSpace<C, A>.() -> R): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ListPolynomialSpace(this).block()
}
/**
* Creates a [ScalableListPolynomialSpace] over a received scalable ring.
*/
public fun <C, A> A.scalableListPolynomial(): ScalableListPolynomialSpace<C, A> where A : Ring<C>, A : ScaleOperations<C> =
ScalableListPolynomialSpace(this)
/**
* Creates a [ScalableListPolynomialSpace]'s scope over a received scalable ring.
*/
public inline fun <C, A, R> A.scalableListPolynomial(block: ScalableListPolynomialSpace<C, A>.() -> R): R where A : Ring<C>, A : ScaleOperations<C> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ScalableListPolynomialSpace(this).block()
}
@Suppress("NOTHING_TO_INLINE")
internal inline fun <C> copyTo(
origin: List<C>,
originDegree: Int,
target: MutableList<C>,
) {
for (deg in 0 .. originDegree) target[deg] = origin[deg]
}
@Suppress("NOTHING_TO_INLINE")
internal inline fun <C> multiplyAddingToUpdater(
ring: Ring<C>,
multiplicand: MutableList<C>,
multiplicandDegree: Int,
multiplier: List<C>,
multiplierDegree: Int,
updater: MutableList<C>,
zero: C,
) {
multiplyAddingTo(
ring = ring,
multiplicand = multiplicand,
multiplicandDegree = multiplicandDegree,
multiplier = multiplier,
multiplierDegree = multiplierDegree,
target = updater
)
for (updateDeg in 0 .. multiplicandDegree + multiplierDegree) {
multiplicand[updateDeg] = updater[updateDeg]
updater[updateDeg] = zero
}
}
@Suppress("NOTHING_TO_INLINE")
internal inline fun <C> multiplyAddingTo(
ring: Ring<C>,
multiplicand: List<C>,
multiplicandDegree: Int,
multiplier: List<C>,
multiplierDegree: Int,
target: MutableList<C>
) = ring {
for (d in 0 .. multiplicandDegree + multiplierDegree)
for (k in max(0, d - multiplierDegree)..min(multiplicandDegree, d))
target[d] += multiplicand[k] * multiplier[d - k]
}
/**
* Evaluates the value of the given double polynomial for given double argument.
*/
public fun ListPolynomial<Double>.substitute(arg: Double): Double =
coefficients.reduceIndexedOrNull { index, acc, c ->
acc + c * arg.pow(index)
} ?: .0
/**
* Evaluates the value of the given polynomial for given argument.
*
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
*/
public fun <C> ListPolynomial<C>.substitute(ring: Ring<C>, arg: C): C = ring {
if (coefficients.isEmpty()) return@ring zero
var result: C = coefficients.last()
for (j in coefficients.size - 2 downTo 0) {
result = (arg * result) + coefficients[j]
}
return result
}
public fun <C> ListPolynomial<C>.substitute(ring: Ring<C>, arg: ListPolynomial<C>) : ListPolynomial<C> = ring {
if (coefficients.isEmpty()) return ListPolynomial(emptyList())
val thisDegree = coefficients.lastIndex
if (thisDegree == -1) return ListPolynomial(emptyList())
val argDegree = arg.coefficients.lastIndex
if (argDegree == -1) return coefficients[0].asListPolynomial()
val constantZero = zero
val resultCoefs: MutableList<C> = MutableList(thisDegree * argDegree + 1) { constantZero }
resultCoefs[0] = coefficients[thisDegree]
val resultCoefsUpdate: MutableList<C> = MutableList(thisDegree * argDegree + 1) { constantZero }
var resultDegree = 0
for (deg in thisDegree - 1 downTo 0) {
resultCoefsUpdate[0] = coefficients[deg]
multiplyAddingToUpdater(
ring = ring,
multiplicand = resultCoefs,
multiplicandDegree = resultDegree,
multiplier = arg.coefficients,
multiplierDegree = argDegree,
updater = resultCoefsUpdate,
zero = constantZero
)
resultDegree += argDegree
}
return ListPolynomial<C>(resultCoefs)
}
/**
* Represent the polynomial as a regular context-less function.
*/
public fun <C, A : Ring<C>> ListPolynomial<C>.asFunction(ring: A): (C) -> C = { substitute(ring, it) }
/**
* Represent the polynomial as a regular context-less function.
*/
public fun <C, A : Ring<C>> ListPolynomial<C>.asPolynomialFunctionOver(ring: A): (ListPolynomial<C>) -> ListPolynomial<C> = { substitute(ring, it) }
/**
* Returns algebraic derivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A> ListPolynomial<C>.derivative(
algebra: A,
): ListPolynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = algebra {
ListPolynomial(
buildList(max(0, coefficients.size - 1)) {
for (deg in 1 .. coefficients.lastIndex) add(number(deg) * coefficients[deg])
}
)
}
/**
* Returns algebraic derivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A> ListPolynomial<C>.nthDerivative(
algebra: A,
order: Int,
): ListPolynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = algebra {
require(order >= 0) { "Order of derivative must be non-negative" }
ListPolynomial(
buildList(max(0, coefficients.size - order)) {
for (deg in order.. coefficients.lastIndex)
add((deg - order + 1 .. deg).fold(coefficients[deg]) { acc, d -> acc * number(d) })
}
)
}
/**
* Returns algebraic antiderivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A> ListPolynomial<C>.antiderivative(
algebra: A,
): ListPolynomial<C> where A : Field<C>, A : NumericAlgebra<C> = algebra {
ListPolynomial(
buildList(coefficients.size + 1) {
add(zero)
coefficients.mapIndexedTo(this) { index, t -> t / number(index + 1) }
}
)
}
/**
* Returns algebraic antiderivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A> ListPolynomial<C>.nthAntiderivative(
algebra: A,
order: Int,
): ListPolynomial<C> where A : Field<C>, A : NumericAlgebra<C> = algebra {
require(order >= 0) { "Order of antiderivative must be non-negative" }
ListPolynomial(
buildList(coefficients.size + order) {
repeat(order) { add(zero) }
coefficients.mapIndexedTo(this) { index, c -> (1..order).fold(c) { acc, i -> acc / number(index + i) } }
}
)
}
/**
* Compute a definite integral of a given polynomial in a [range]
*/
@UnstableKMathAPI
public fun <C : Comparable<C>> ListPolynomial<C>.integrate(
algebra: Field<C>,
range: ClosedRange<C>,
): C = algebra {
val integral = antiderivative(algebra)
integral.substitute(algebra, range.endInclusive) - integral.substitute(algebra, range.start)
}

View File

@ -1,221 +0,0 @@
/*
* 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.operations.Field
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.math.max
/**
* Creates a [ListRationalFunctionSpace] over a received ring.
*/
public fun <C, A : Ring<C>> A.listRationalFunction(): ListRationalFunctionSpace<C, A> =
ListRationalFunctionSpace(this)
/**
* Creates a [ListRationalFunctionSpace]'s scope over a received ring.
*/
public inline fun <C, A : Ring<C>, R> A.listRationalFunction(block: ListRationalFunctionSpace<C, A>.() -> R): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ListRationalFunctionSpace(this).block()
}
/**
* Evaluates the value of the given double polynomial for given double argument.
*/
public fun ListRationalFunction<Double>.substitute(arg: Double): Double =
numerator.substitute(arg) / denominator.substitute(arg)
/**
* Evaluates the value of the given polynomial for given argument.
*
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
*/
public fun <C> ListRationalFunction<C>.substitute(ring: Field<C>, arg: C): C = ring {
numerator.substitute(ring, arg) / denominator.substitute(ring, arg)
}
/**
* Returns numerator (polynomial) of rational function gotten by substitution rational function [arg] to the polynomial instance.
* More concrete, if [arg] is a fraction `f(x)/g(x)` and the receiving instance is `p(x)`, then
* ```
* p(f/g) * g^deg(p)
* ```
* is returned.
*
* Used in [ListPolynomial.substitute] and [ListRationalFunction.substitute] for performance optimisation.
*/ // TODO: Дописать
internal fun <C> ListPolynomial<C>.substituteRationalFunctionTakeNumerator(ring: Ring<C>, arg: ListRationalFunction<C>): ListPolynomial<C> = ring {
if (coefficients.isEmpty()) return ListPolynomial(emptyList())
val thisDegree = coefficients.lastIndex
if (thisDegree == -1) return ListPolynomial(emptyList())
val thisDegreeLog2 = 31 - thisDegree.countLeadingZeroBits()
val numeratorDegree = arg.numerator.coefficients.lastIndex
val denominatorDegree = arg.denominator.coefficients.lastIndex
val argDegree = max(numeratorDegree, denominatorDegree)
val constantZero = zero
val powersOf2 = buildList<Int>(thisDegreeLog2 + 1) {
var result = 1
for (exp in 0 .. thisDegreeLog2) {
add(result)
result = result shl 1
}
}
val hashes = powersOf2.runningReduce { acc, i -> acc + i }
val numeratorPowers = buildList<List<C>>(thisDegreeLog2 + 1) {
add(arg.numerator.coefficients)
repeat(thisDegreeLog2) {
val next = MutableList<C>(powersOf2[it + 1] * numeratorDegree + 1) { constantZero }
add(next)
val last = last()
multiplyAddingTo(
ring = ring,
multiplicand = last,
multiplicandDegree = powersOf2[it] * numeratorDegree + 1,
multiplier = last,
multiplierDegree = powersOf2[it] * numeratorDegree + 1,
target = next,
)
}
}
val denominatorPowers = buildList<List<C>>(thisDegreeLog2 + 1) {
add(arg.denominator.coefficients)
repeat(thisDegreeLog2) {
val next = MutableList<C>(powersOf2[it + 1] * denominatorDegree + 1) { constantZero }
add(next)
val last = last()
multiplyAddingTo(
ring = ring,
multiplicand = last,
multiplicandDegree = powersOf2[it] * denominatorDegree + 1,
multiplier = last,
multiplierDegree = powersOf2[it] * denominatorDegree + 1,
target = next,
)
}
}
val levelResultCoefsPool = buildList<MutableList<C>>(thisDegreeLog2 + 1) {
repeat(thisDegreeLog2 + 1) {
add(MutableList(hashes[it] * argDegree) { constantZero })
}
}
val edgedMultiplier = MutableList<C>(0) { TODO() }
val edgedMultiplierUpdater = MutableList<C>(0) { TODO() }
fun MutableList<C>.reset() {
for (i in indices) set(i, constantZero)
}
fun processLevel(level: Int, start: Int, end: Int) : List<C> {
val levelResultCoefs = levelResultCoefsPool[level + 1]
if (level == -1) {
levelResultCoefs[0] = coefficients[start]
} else {
levelResultCoefs.reset()
multiplyAddingTo(
ring = ring,
multiplicand = processLevel(level = level - 1, start = start, end = (start + end) / 2),
multiplicandDegree = hashes[level] * argDegree,
multiplier = denominatorPowers[level],
multiplierDegree = powersOf2[level] * denominatorDegree,
target = levelResultCoefs
)
multiplyAddingTo(
ring = ring,
multiplicand = processLevel(level = level - 1, start = (start + end) / 2, end = end),
multiplicandDegree = hashes[level] * argDegree,
multiplier = numeratorPowers[level],
multiplierDegree = powersOf2[level] * numeratorDegree,
target = levelResultCoefs
)
}
return levelResultCoefs
}
fun processLevelEdged(level: Int, start: Int, end: Int) : List<C> {
val levelResultCoefs = levelResultCoefsPool[level + 1]
if (level == -1) {
levelResultCoefs[0] = coefficients[start]
} else {
val levelsPowerOf2 = powersOf2[level]
if (end - start >= levelsPowerOf2) {
multiplyAddingTo(
ring = ring,
multiplicand = processLevelEdged(level = level - 1, start = start + levelsPowerOf2, end = end),
multiplicandDegree = hashes[level] * argDegree, // TODO: Ввести переменную
multiplier = numeratorPowers[level],
multiplierDegree = powersOf2[level] * numeratorDegree,
target = levelResultCoefs
)
multiplyAddingTo(
ring = ring,
multiplicand = processLevel(level = level - 1, start = start, end = start + levelsPowerOf2),
multiplicandDegree = hashes[level] * argDegree,
multiplier = edgedMultiplier,
multiplierDegree = max((hashes[level] and thisDegree) - powersOf2[level] + 1, 0) * denominatorDegree, // TODO: Ввести переменную
target = levelResultCoefs
)
if (level != thisDegreeLog2) {
multiplyAddingToUpdater(
ring = ring,
multiplicand = edgedMultiplier,
multiplicandDegree = max((hashes[level] and thisDegree) - powersOf2[level] + 1, 0) * denominatorDegree, // TODO: Ввести переменную
multiplier = denominatorPowers[level],
multiplierDegree = powersOf2[level] * denominatorDegree,
updater = edgedMultiplierUpdater,
zero = constantZero
)
}
} else {
copyTo(
origin = processLevelEdged(level = level - 1, start = start + levelsPowerOf2, end = end),
originDegree = hashes[level] * argDegree, // TODO: Ввести переменную
target = levelResultCoefs
)
}
}
return levelResultCoefs
}
return ListPolynomial(
processLevelEdged(
level = thisDegreeLog2,
start = 0,
end = thisDegree + 1
)
)
}
//operator fun <T: Field<T>> RationalFunction<T>.invoke(arg: T): T = numerator(arg) / denominator(arg)
//
//fun <T: Field<T>> RationalFunction<T>.reduced(): RationalFunction<T> =
// polynomialGCD(numerator, denominator).let {
// RationalFunction(
// numerator / it,
// denominator / it
// )
// }
///**
// * Returns result of applying formal derivative to the polynomial.
// *
// * @param T Field where we are working now.
// * @return Result of the operator.
// */
//fun <T: Ring<T>> RationalFunction<T>.derivative() =
// RationalFunction(
// numerator.derivative() * denominator - denominator.derivative() * numerator,
// denominator * denominator
// )

View File

@ -1,195 +0,0 @@
/*
* 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.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Ring
/**
* Returns the same degrees' description of the monomial, but without extra zero degrees on the end.
*/
internal fun List<UInt>.cleanUp() = subList(0, indexOfLast { it != 0U } + 1)
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(coefs, toCheckInput)
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(coefs, toCheckInput)
@Suppress("FunctionName")
internal fun <C, A: Ring<C>> A.NumberedPolynomial(coefs: Map<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> {
if (!toCheckInput) return NumberedPolynomial<C>(coefs)
val fixedCoefs = mutableMapOf<List<UInt>, C>()
for (entry in coefs) {
val key = entry.key.cleanUp()
val value = entry.value
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
}
return NumberedPolynomial<C>(fixedCoefs)
}
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(pairs, toCheckInput)
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(pairs, toCheckInput)
@Suppress("FunctionName")
internal fun <C, A: Ring<C>> A.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>, toCheckInput: Boolean = true) : NumberedPolynomial<C> {
if (!toCheckInput) return NumberedPolynomial<C>(pairs.toMap())
val fixedCoefs = mutableMapOf<List<UInt>, C>()
for (entry in pairs) {
val key = entry.first.cleanUp()
val value = entry.second
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
}
return NumberedPolynomial<C>(fixedCoefs)
}
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(pairs = pairs, toCheckInput = toCheckInput)
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(pairs = pairs, toCheckInput = toCheckInput)
@Suppress("FunctionName")
internal fun <C, A: Ring<C>> A.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> {
if (!toCheckInput) return NumberedPolynomial<C>(pairs.toMap())
val fixedCoefs = mutableMapOf<List<UInt>, C>()
for (entry in pairs) {
val key = entry.first.cleanUp()
val value = entry.second
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
}
return NumberedPolynomial<C>(fixedCoefs)
}
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs, toCheckInput = true)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs, toCheckInput = true)
public fun <C> C.asNumberedPolynomial() : NumberedPolynomial<C> = NumberedPolynomial<C>(mapOf(emptyList<UInt>() to this))
@DslMarker
@UnstableKMathAPI
internal annotation class NumberedPolynomialConstructorDSL
@UnstableKMathAPI
@NumberedPolynomialConstructorDSL
public class NumberedPolynomialTermSignatureBuilder {
private val signature: MutableList<UInt> = ArrayList()
public fun build(): List<UInt> = signature
public infix fun Int.inPowerOf(deg: UInt) {
if (this > signature.lastIndex) {
signature.addAll(List(this - signature.lastIndex - 1) { 0u })
signature.add(deg)
} else {
signature[this] = deg
}
}
@Suppress("NOTHING_TO_INLINE")
public inline infix fun Int.pow(deg: UInt): Unit = this inPowerOf deg
@Suppress("NOTHING_TO_INLINE")
public inline infix fun Int.`in`(deg: UInt): Unit = this inPowerOf deg
@Suppress("NOTHING_TO_INLINE")
public inline infix fun Int.of(deg: UInt): Unit = this inPowerOf deg
}
@UnstableKMathAPI
public class NumberedPolynomialBuilder<C>(private val zero: C, private val add: (C, C) -> C, capacity: Int = 0) {
private val coefficients: MutableMap<List<UInt>, C> = LinkedHashMap(capacity)
public fun build(): NumberedPolynomial<C> = NumberedPolynomial<C>(coefficients)
public operator fun C.invoke(block: NumberedPolynomialTermSignatureBuilder.() -> Unit) {
val signature = NumberedPolynomialTermSignatureBuilder().apply(block).build()
coefficients[signature] = add(coefficients.getOrElse(signature) { zero }, this@invoke)
}
@Suppress("NOTHING_TO_INLINE")
public inline infix fun C.with(noinline block: NumberedPolynomialTermSignatureBuilder.() -> Unit): Unit = this.invoke(block)
@Suppress("NOTHING_TO_INLINE")
public inline infix fun (NumberedPolynomialTermSignatureBuilder.() -> Unit).with(coef: C): Unit = coef.invoke(this)
@Suppress("NOTHING_TO_INLINE")
public infix fun sig(block: NumberedPolynomialTermSignatureBuilder.() -> Unit): NumberedPolynomialTermSignatureBuilder.() -> Unit = block
}
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
@UnstableKMathAPI
@NumberedPolynomialConstructorDSL
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> A.NumberedPolynomial(block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = NumberedPolynomialBuilder(zero, ::add).apply(block).build()
@UnstableKMathAPI
@NumberedPolynomialConstructorDSL
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> A.NumberedPolynomial(capacity: Int, block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = NumberedPolynomialBuilder(zero, ::add, capacity).apply(block).build()
@UnstableKMathAPI
@NumberedPolynomialConstructorDSL
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = NumberedPolynomialBuilder(constantZero, { left: C, right: C -> left + right}).apply(block).build()
@UnstableKMathAPI
@NumberedPolynomialConstructorDSL
@Suppress("FunctionName")
public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(capacity: Int, block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = NumberedPolynomialBuilder(constantZero, { left: C, right: C -> left + right}, capacity).apply(block).build()
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
@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, toCheckInput = true),
NumberedPolynomial(denominatorCoefficients, toCheckInput = true)
)
@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, toCheckInput = true),
NumberedPolynomial(denominatorCoefficients, toCheckInput = true)
)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numerator: NumberedPolynomial<C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(numerator, polynomialOne)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.NumberedRationalFunction(numerator: NumberedPolynomial<C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(numerator, NumberedPolynomial(mapOf(emptyList<UInt>() to one), toCheckInput = false))
@Suppress("FunctionName")
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(
NumberedPolynomial(numeratorCoefficients, toCheckInput = true),
polynomialOne
)
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
NumberedRationalFunction<C>(
NumberedPolynomial(numeratorCoefficients, toCheckInput = true),
NumberedPolynomial(mapOf(emptyList<UInt>() to one), toCheckInput = false)
)
//context(A)
//public fun <C, A: Ring<C>> C.asNumberedRationalFunction() : NumberedRationalFunction<C> = NumberedRationalFunction(asLabeledPolynomial())
//context(NumberedRationalFunctionSpace<C, A>)
//public fun <C, A: Ring<C>> C.asNumberedRationalFunction() : NumberedRationalFunction<C> = NumberedRationalFunction(asLabeledPolynomial())

View File

@ -1,528 +0,0 @@
package space.kscience.kmath.functions
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.*
import kotlin.contracts.*
import kotlin.jvm.JvmName
import kotlin.math.max
// TODO: Docs
/**
* Creates a [NumberedPolynomialSpace] over a received ring.
*/
public fun <C, A : Ring<C>> A.numberedPolynomial(): NumberedPolynomialSpace<C, A> =
NumberedPolynomialSpace(this)
/**
* Creates a [NumberedPolynomialSpace]'s scope over a received ring.
*/
@OptIn(ExperimentalContracts::class)
public inline fun <C, A : Ring<C>, R> A.numberedPolynomial(block: NumberedPolynomialSpace<C, A>.() -> R): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return NumberedPolynomialSpace(this).block()
}
///**
// * Represents the polynomial as a [String] where name of variable with index `i` is [withVariableName] + `"_${i+1}"`.
// * Consider that monomials are sorted in lexicographic order.
// */
//context(NumberedPolynomialSpace<C, A>)
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.represent(withVariableName: String = NumberedPolynomial.defaultVariableName): String =
// coefficients.entries
// .sortedWith { o1, o2 -> NumberedPolynomial.monomialComparator.compare(o1.key, o2.key) }
// .asSequence()
// .map { (degs, t) ->
// if (degs.isEmpty()) "$t"
// else {
// when {
// t.isOne() -> ""
// t.isMinusOne() -> "-"
// else -> "$t "
// } +
// degs
// .mapIndexed { index, deg ->
// when (deg) {
// 0U -> ""
// 1U -> "${withVariableName}_${index+1}"
// else -> "${withVariableName}_${index+1}^$deg"
// }
// }
// .filter { it.isNotEmpty() }
// .joinToString(separator = " ") { it }
// }
// }
// .joinToString(separator = " + ") { it }
// .ifEmpty { "0" }
//
///**
// * Represents the polynomial as a [String] naming variables by [namer].
// * Consider that monomials are sorted in lexicographic order.
// */
//context(NumberedPolynomialSpace<C, A>)
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.represent(namer: (Int) -> String): String =
// coefficients.entries
// .sortedWith { o1, o2 -> NumberedPolynomial.monomialComparator.compare(o1.key, o2.key) }
// .asSequence()
// .map { (degs, t) ->
// if (degs.isEmpty()) "$t"
// else {
// when {
// t.isOne() -> ""
// t.isMinusOne() -> "-"
// else -> "$t "
// } +
// degs
// .mapIndexed { index, deg ->
// when (deg) {
// 0U -> ""
// 1U -> namer(index)
// else -> "${namer(index)}^$deg"
// }
// }
// .filter { it.isNotEmpty() }
// .joinToString(separator = " ") { it }
// }
// }
// .joinToString(separator = " + ") { it }
// .ifEmpty { "0" }
//
///**
// * Represents the polynomial as a [String] where name of variable with index `i` is [withVariableName] + `"_${i+1}"`
// * and with brackets around the string if needed (i.e. when there are at least two addends in the representation).
// * Consider that monomials are sorted in lexicographic order.
// */
//context(NumberedPolynomialSpace<C, A>)
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representWithBrackets(withVariableName: String = NumberedPolynomial.defaultVariableName): String =
// with(represent(withVariableName)) { if (coefficients.count() == 1) this else "($this)" }
//
///**
// * Represents the polynomial as a [String] naming variables by [namer] and with brackets around the string if needed
// * (i.e. when there are at least two addends in the representation).
// * Consider that monomials are sorted in lexicographic order.
// */
//context(NumberedPolynomialSpace<C, A>)
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representWithBrackets(namer: (Int) -> String): String =
// with(represent(namer)) { if (coefficients.count() == 1) this else "($this)" }
//
///**
// * Represents the polynomial as a [String] where name of variable with index `i` is [withVariableName] + `"_${i+1}"`.
// * Consider that monomials are sorted in **reversed** lexicographic order.
// */
//context(NumberedPolynomialSpace<C, A>)
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representReversed(withVariableName: String = NumberedPolynomial.defaultVariableName): String =
// coefficients.entries
// .sortedWith { o1, o2 -> -NumberedPolynomial.monomialComparator.compare(o1.key, o2.key) }
// .asSequence()
// .map { (degs, t) ->
// if (degs.isEmpty()) "$t"
// else {
// when {
// t.isOne() -> ""
// t.isMinusOne() -> "-"
// else -> "$t "
// } +
// degs
// .mapIndexed { index, deg ->
// when (deg) {
// 0U -> ""
// 1U -> "${withVariableName}_${index+1}"
// else -> "${withVariableName}_${index+1}^$deg"
// }
// }
// .filter { it.isNotEmpty() }
// .joinToString(separator = " ") { it }
// }
// }
// .joinToString(separator = " + ") { it }
// .ifEmpty { "0" }
//
///**
// * Represents the polynomial as a [String] naming variables by [namer].
// * Consider that monomials are sorted in **reversed** lexicographic order.
// */
//context(NumberedPolynomialSpace<C, A>)
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representReversed(namer: (Int) -> String): String =
// coefficients.entries
// .sortedWith { o1, o2 -> -NumberedPolynomial.monomialComparator.compare(o1.key, o2.key) }
// .asSequence()
// .map { (degs, t) ->
// if (degs.isEmpty()) "$t"
// else {
// when {
// t.isOne() -> ""
// t.isMinusOne() -> "-"
// else -> "$t "
// } +
// degs
// .mapIndexed { index, deg ->
// when (deg) {
// 0U -> ""
// 1U -> namer(index)
// else -> "${namer(index)}^$deg"
// }
// }
// .filter { it.isNotEmpty() }
// .joinToString(separator = " ") { it }
// }
// }
// .joinToString(separator = " + ") { it }
// .ifEmpty { "0" }
//
///**
// * Represents the polynomial as a [String] where name of variable with index `i` is [withVariableName] + `"_${i+1}"`
// * and with brackets around the string if needed (i.e. when there are at least two addends in the representation).
// * Consider that monomials are sorted in **reversed** lexicographic order.
// */
//context(NumberedPolynomialSpace<C, A>)
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representReversedWithBrackets(withVariableName: String = NumberedPolynomial.defaultVariableName): String =
// with(representReversed(withVariableName)) { if (coefficients.count() == 1) this else "($this)" }
//
///**
// * Represents the polynomial as a [String] naming variables by [namer] and with brackets around the string if needed
// * (i.e. when there are at least two addends in the representation).
// * Consider that monomials are sorted in **reversed** lexicographic order.
// */
//context(NumberedPolynomialSpace<C, A>)
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representReversedWithBrackets(namer: (Int) -> String): String =
// with(representReversed(namer)) { if (coefficients.count() == 1) this else "($this)" }
//public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, C>): NumberedPolynomial<C> = ring {
// if (coefficients.isEmpty()) return this@substitute
// NumberedPolynomial<C>(
// buildMap {
// coefficients.forEach { (degs, c) ->
// val newDegs = degs.mapIndexed { index, deg -> if (index in args) 0U else deg }.cleanUp()
// val newC = degs.foldIndexed(c) { index, acc, deg ->
// if (index in args) multiplyWithPower(acc, args[index]!!, deg)
// else acc
// }
// this[newDegs] = if (newDegs in this) this[newDegs]!! + newC else newC
// }
// }
// )
//}
//
//// TODO: Replace with optimisation: the [result] may be unboxed, and all operations may be performed as soon as
//// possible on it
//@JvmName("substitutePolynomial")
//public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, arg: Map<Int, NumberedPolynomial<C>>) : NumberedPolynomial<C> =
// ring.numberedPolynomialSpace {
// if (coefficients.isEmpty()) return zero
// coefficients
// .asSequence()
// .map { (degs, c) ->
// degs.foldIndexed(
// NumberedPolynomial(
// degs.mapIndexed { index, deg -> if (index in arg) 0U else deg } to c
// )
// ) { index, acc, deg -> if (index in arg) multiplyWithPower(acc, arg[index]!!, deg) else acc }
// }
// .reduce { acc, polynomial -> acc + polynomial } // TODO: Rewrite. Might be slow.
// }
//
//// TODO: Substitute rational function
//
//public fun <C, A : Ring<C>> NumberedPolynomial<C>.asFunctionOver(ring: A): (Map<Int, C>) -> NumberedPolynomial<C> =
// { substitute(ring, it) }
//
//public fun <C, A : Ring<C>> NumberedPolynomial<C>.asPolynomialFunctionOver(ring: A): (Map<Int, NumberedPolynomial<C>>) -> NumberedPolynomial<C> =
// { substitute(ring, it) }
//operator fun <T: Field<T>> Polynomial<T>.div(other: T): Polynomial<T> =
// if (other.isZero()) throw ArithmeticException("/ by zero")
// else
// Polynomial(
// coefficients
// .mapValues { it.value / other },
// toCheckInput = false
// )
/**
* Evaluates the value of the given double polynomial for given double argument.
*/
public fun NumberedPolynomial<Double>.substitute(args: Map<Int, Double>): NumberedPolynomial<Double> = Double.algebra {
val acc = LinkedHashMap<List<UInt>, Double>(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 acc) acc[newDegs] = newC
else acc[newDegs] = acc[newDegs]!! + newC
}
return NumberedPolynomial<Double>(acc)
}
/**
* Evaluates the value of the given polynomial for given argument.
*
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
*/
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, C>): NumberedPolynomial<C> = ring {
val acc = LinkedHashMap<List<UInt>, C>(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 acc) acc[newDegs] = newC
else acc[newDegs] = acc[newDegs]!! + newC
}
return NumberedPolynomial<C>(acc)
}
// TODO: (Waiting for hero) Replace with optimisation: the [result] may be unboxed, and all operations may be performed
// as soon as possible on it
@JvmName("substitutePolynomial")
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, NumberedPolynomial<C>>) : NumberedPolynomial<C> = TODO() /*ring.numberedPolynomial {
val acc = LinkedHashMap<List<UInt>, NumberedPolynomial<C>>(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.asNumberedPolynomial()) { product, (variable, substitution) ->
val deg = degs.getOrElse(variable) { 0u }
if (deg == 0u) product else product * power(substitution, deg)
}
if (newDegs !in acc) acc[newDegs] = c.asNumberedPolynomial()
else acc[newDegs] = acc[newDegs]!! + c
}
}*/
/**
* Represent the polynomial as a regular context-less function.
*/
public fun <C, A : Ring<C>> NumberedPolynomial<C>.asFunction(ring: A): (Map<Int, C>) -> NumberedPolynomial<C> = { substitute(ring, it) }
/**
* Represent the polynomial as a regular context-less function.
*/
public fun <C, A : Ring<C>> NumberedPolynomial<C>.asPolynomialFunctionOver(ring: A): (Map<Int, NumberedPolynomial<C>>) -> NumberedPolynomial<C> = { substitute(ring, it) }
/**
* Returns algebraic derivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A : Ring<C>> NumberedPolynomial<C>.derivativeWithRespectTo(
algebra: A,
variable: Int,
): NumberedPolynomial<C> = algebra {
NumberedPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
if (degs.size > variable) return@forEach
put(
degs.mapIndexed { index, deg ->
when {
index != variable -> deg
deg > 0u -> deg - 1u
else -> return@forEach
}
}.cleanUp(),
multiplyByDoubling(c, degs[variable])
)
}
}
)
}
/**
* Returns algebraic derivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A : Ring<C>> NumberedPolynomial<C>.derivativeWithRespectTo(
algebra: A,
variables: Collection<Int>,
): NumberedPolynomial<C> = algebra {
val cleanedVariables = variables.toSet()
if (cleanedVariables.isEmpty()) return this@derivativeWithRespectTo
val maxRespectedVariable = cleanedVariables.maxOrNull()!!
NumberedPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
if (degs.size > maxRespectedVariable) return@forEach
put(
degs.mapIndexed { index, deg ->
when {
index !in cleanedVariables -> deg
deg > 0u -> deg - 1u
else -> return@forEach
}
}.cleanUp(),
cleanedVariables.fold(c) { acc, variable -> multiplyByDoubling(acc, degs[variable]) }
)
}
}
)
}
/**
* Returns algebraic derivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A : Ring<C>> NumberedPolynomial<C>.nthDerivativeWithRespectTo(
algebra: A,
variable: Int,
order: UInt
): NumberedPolynomial<C> = algebra {
if (order == 0u) return this@nthDerivativeWithRespectTo
NumberedPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
if (degs.size > variable) return@forEach
put(
degs.mapIndexed { index, deg ->
when {
index != variable -> deg
deg >= order -> deg - order
else -> return@forEach
}
}.cleanUp(),
degs[variable].let { deg ->
(deg downTo deg - order + 1u)
.fold(c) { acc, ord -> multiplyByDoubling(acc, ord) }
}
)
}
}
)
}
/**
* Returns algebraic derivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A : Ring<C>> NumberedPolynomial<C>.nthDerivativeWithRespectTo(
algebra: A,
variablesAndOrders: Map<Int, UInt>,
): NumberedPolynomial<C> = algebra {
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
if (filteredVariablesAndOrders.isEmpty()) return this@nthDerivativeWithRespectTo
val maxRespectedVariable = filteredVariablesAndOrders.keys.maxOrNull()!!
NumberedPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
if (degs.size > maxRespectedVariable) return@forEach
put(
degs.mapIndexed { index, deg ->
if (index !in filteredVariablesAndOrders) return@mapIndexed deg
val order = filteredVariablesAndOrders[index]!!
if (deg >= order) deg - order else return@forEach
}.cleanUp(),
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.
*/
@UnstableKMathAPI
public fun <C, A : Field<C>> NumberedPolynomial<C>.antiderivativeWithRespectTo(
algebra: A,
variable: Int,
): NumberedPolynomial<C> = algebra {
NumberedPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
put(
List(max(variable + 1, degs.size)) { if (it != variable) degs[it] else degs[it] + 1u },
c / multiplyByDoubling(one, degs[variable])
)
}
}
)
}
/**
* Returns algebraic antiderivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A : Field<C>> NumberedPolynomial<C>.antiderivativeWithRespectTo(
algebra: A,
variables: Collection<Int>,
): NumberedPolynomial<C> = algebra {
val cleanedVariables = variables.toSet()
if (cleanedVariables.isEmpty()) return this@antiderivativeWithRespectTo
val maxRespectedVariable = cleanedVariables.maxOrNull()!!
NumberedPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
put(
List(max(maxRespectedVariable + 1, degs.size)) { if (it !in variables) degs[it] else degs[it] + 1u },
cleanedVariables.fold(c) { acc, variable -> acc / multiplyByDoubling(one, degs[variable]) }
)
}
}
)
}
/**
* Returns algebraic derivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A : Field<C>> NumberedPolynomial<C>.nthAntiderivativeWithRespectTo(
algebra: A,
variable: Int,
order: UInt
): NumberedPolynomial<C> = algebra {
if (order == 0u) return this@nthAntiderivativeWithRespectTo
NumberedPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
put(
List(max(variable + 1, degs.size)) { if (it != variable) degs[it] else degs[it] + order },
degs[variable].let { deg ->
(deg downTo deg - order + 1u)
.fold(c) { acc, ord -> acc / multiplyByDoubling(one, ord) }
}
)
}
}
)
}
/**
* Returns algebraic derivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A : Field<C>> NumberedPolynomial<C>.nthAntiderivativeWithRespectTo(
algebra: A,
variablesAndOrders: Map<Int, UInt>,
): NumberedPolynomial<C> = algebra {
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
if (filteredVariablesAndOrders.isEmpty()) return this@nthAntiderivativeWithRespectTo
val maxRespectedVariable = filteredVariablesAndOrders.keys.maxOrNull()!!
NumberedPolynomial<C>(
buildMap(coefficients.size) {
coefficients
.forEach { (degs, c) ->
put(
List(max(maxRespectedVariable + 1, degs.size)) { degs[it] + filteredVariablesAndOrders.getOrElse(it) { 0u } },
filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) ->
degs[index].let { deg ->
(deg downTo deg - order + 1u)
.fold(acc1) { acc2, ord -> acc2 / multiplyByDoubling(one, ord) }
}
}
)
}
}
)
}

View File

@ -1,33 +0,0 @@
/*
* 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.operations.Ring
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Creates a [NumberedRationalFunctionSpace] over a received ring.
*/
public fun <C, A : Ring<C>> A.numberedRationalFunction(): NumberedRationalFunctionSpace<C, A> =
NumberedRationalFunctionSpace(this)
/**
* Creates a [NumberedRationalFunctionSpace]'s scope over a received ring.
*/
public inline fun <C, A : Ring<C>, R> A.numberedRationalFunction(block: NumberedRationalFunctionSpace<C, A>.() -> R): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return NumberedRationalFunctionSpace(this).block()
}
//fun <T: Field<T>> NumberedRationalFunction<T>.reduced(): NumberedRationalFunction<T> {
// val greatestCommonDivider = polynomialGCD(numerator, denominator)
// return NumberedRationalFunction(
// numerator / greatestCommonDivider,
// denominator / greatestCommonDivider
// )
//}

View File

@ -1,107 +0,0 @@
/*
* 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.integration
import space.kscience.kmath.functions.PiecewisePolynomial
import space.kscience.kmath.functions.integrate
import space.kscience.kmath.functions.antiderivative
import space.kscience.kmath.interpolation.PolynomialInterpolator
import space.kscience.kmath.interpolation.SplineInterpolator
import space.kscience.kmath.interpolation.interpolatePolynomials
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.MutableBufferFactory
/**
* Compute analytical indefinite integral of this [PiecewisePolynomial], keeping all intervals intact
*/
@OptIn(PerformancePitfall::class)
@UnstableKMathAPI
public fun <T : Comparable<T>> PiecewisePolynomial<T>.integrate(algebra: Field<T>): PiecewisePolynomial<T> =
PiecewisePolynomial(pieces.map { it.first to it.second.antiderivative(algebra) })
/**
* Compute definite integral of given [PiecewisePolynomial] piece by piece in a given [range]
* Requires [UnivariateIntegrationNodes] or [IntegrationRange] and [IntegrandMaxCalls]
*
* TODO use context receiver for algebra
*/
@UnstableKMathAPI
public fun <T : Comparable<T>> PiecewisePolynomial<T>.integrate(
algebra: Field<T>, range: ClosedRange<T>,
): T = algebra.sum(
pieces.map { (region, poly) ->
val intersectedRange = maxOf(range.start, region.start)..minOf(range.endInclusive, region.endInclusive)
//Check if polynomial range is not used
if (intersectedRange.start == intersectedRange.endInclusive) algebra.zero
else poly.integrate(algebra, intersectedRange)
}
)
/**
* A generic spline-interpolation-based analytic integration
* * [IntegrationRange]&mdash;the univariate range of integration. By default, uses `0..1` interval.
* * [IntegrandMaxCalls]&mdash;the maximum number of function calls during integration. For non-iterative rules, always uses
* the maximum number of points. By default, uses 10 points.
*/
@UnstableKMathAPI
public class SplineIntegrator<T : Comparable<T>>(
public val algebra: Field<T>,
public val bufferFactory: MutableBufferFactory<T>,
) : UnivariateIntegrator<T> {
override fun process(integrand: UnivariateIntegrand<T>): UnivariateIntegrand<T> = algebra {
val range = integrand.getFeature<IntegrationRange>()?.range ?: 0.0..1.0
val interpolator: PolynomialInterpolator<T> = SplineInterpolator(algebra, bufferFactory)
val nodes: Buffer<Double> = integrand.getFeature<UnivariateIntegrationNodes>()?.nodes ?: run {
val numPoints = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: 100
val step = (range.endInclusive - range.start) / (numPoints - 1)
DoubleBuffer(numPoints) { i -> range.start + i * step }
}
val values = nodes.map(bufferFactory) { integrand.function(it) }
val polynomials = interpolator.interpolatePolynomials(
nodes.map(bufferFactory) { number(it) },
values
)
val res = polynomials.integrate(algebra, number(range.start)..number(range.endInclusive))
integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + nodes.size)
}
}
/**
* A simplified double-based spline-interpolation-based analytic integration
* * [IntegrationRange]&mdash;the univariate range of integration. By default, uses `0.0..1.0` interval.
* * [IntegrandMaxCalls]&mdash;the maximum number of function calls during integration. For non-iterative rules, always
* uses the maximum number of points. By default, uses 10 points.
*/
@UnstableKMathAPI
public object DoubleSplineIntegrator : UnivariateIntegrator<Double> {
override fun process(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
val range = integrand.getFeature<IntegrationRange>()?.range ?: 0.0..1.0
val interpolator: PolynomialInterpolator<Double> = SplineInterpolator(DoubleField, ::DoubleBuffer)
val nodes: Buffer<Double> = integrand.getFeature<UnivariateIntegrationNodes>()?.nodes ?: run {
val numPoints = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: 100
val step = (range.endInclusive - range.start) / (numPoints - 1)
DoubleBuffer(numPoints) { i -> range.start + i * step }
}
val values = nodes.map { integrand.function(it) }
val polynomials = interpolator.interpolatePolynomials(nodes, values)
val res = polynomials.integrate(DoubleField, range)
return integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + nodes.size)
}
}
@Suppress("unused")
@UnstableKMathAPI
public inline val DoubleField.splineIntegrator: UnivariateIntegrator<Double>
get() = DoubleSplineIntegrator

View File

@ -1,491 +0,0 @@
/*
* 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.test.misc.*
import kotlin.test.*
class ListPolynomialTest {
@Test
fun test_Polynomial_Int_plus() {
RationalField.listPolynomial {
assertEquals(
ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) + -3,
"test 1"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + 2,
"test 2"
)
assertEquals(
ListPolynomial(Rational(0)),
ListPolynomial(Rational(-2)) + 2,
"test 3"
)
assertEquals(
ListPolynomial(),
ListPolynomial<Rational>() + 0,
"test 4"
)
assertEquals(
ListPolynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + 1,
"test 5"
)
assertEquals(
ListPolynomial(Rational(-1)),
ListPolynomial(Rational(-2)) + 1,
"test 6"
)
assertEquals(
ListPolynomial(Rational(2)),
ListPolynomial<Rational>() + 2,
"test 7"
)
}
}
@Test
fun test_Polynomial_Int_minus() {
RationalField.listPolynomial {
assertEquals(
ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) - -3,
"test 1"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - 2,
"test 2"
)
assertEquals(
ListPolynomial(Rational(0)),
ListPolynomial(Rational(2)) - 2,
"test 3"
)
assertEquals(
ListPolynomial(),
ListPolynomial<Rational>() - 0,
"test 4"
)
assertEquals(
ListPolynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - 1,
"test 5"
)
assertEquals(
ListPolynomial(Rational(1)),
ListPolynomial(Rational(2)) - 1,
"test 6"
)
assertEquals(
ListPolynomial(Rational(-2)),
ListPolynomial<Rational>() - 2,
"test 7"
)
}
}
@Test
fun test_Polynomial_Int_times() {
IntModuloRing(35).listPolynomial {
assertEquals(
ListPolynomial(34, 2, 1, 20, 2),
ListPolynomial(22, 26, 13, 15, 26) * 27,
"test 1"
)
assertEquals(
ListPolynomial(0, 0, 0, 0, 0),
ListPolynomial(7, 0, 49, 21, 14) * 15,
"test 2"
)
}
}
@Test
fun test_Int_Polynomial_plus() {
RationalField.listPolynomial {
assertEquals(
ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
-3 + ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)),
"test 1"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
2 + ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
"test 2"
)
assertEquals(
ListPolynomial(Rational(0)),
2 + ListPolynomial(Rational(-2)),
"test 3"
)
assertEquals(
ListPolynomial(),
0 + ListPolynomial(),
"test 4"
)
assertEquals(
ListPolynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
1 + ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
"test 5"
)
assertEquals(
ListPolynomial(Rational(-1)),
1 + ListPolynomial(Rational(-2)),
"test 6"
)
assertEquals(
ListPolynomial(Rational(2)),
2 + ListPolynomial(),
"test 7"
)
}
}
@Test
fun test_Int_Polynomial_minus() {
RationalField.listPolynomial {
assertEquals(
ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
3 - ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)),
"test 1"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
-2 - ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
"test 2"
)
assertEquals(
ListPolynomial(Rational(0)),
-2 - ListPolynomial(Rational(-2)),
"test 3"
)
assertEquals(
ListPolynomial(),
0 - ListPolynomial(),
"test 4"
)
assertEquals(
ListPolynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
-1 - ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
"test 5"
)
assertEquals(
ListPolynomial(Rational(1)),
-1 - ListPolynomial(Rational(-2)),
"test 6"
)
assertEquals(
ListPolynomial(Rational(-2)),
-2 - ListPolynomial(),
"test 7"
)
}
}
@Test
fun test_Int_Polynomial_times() {
IntModuloRing(35).listPolynomial {
assertEquals(
ListPolynomial(34, 2, 1, 20, 2),
27 * ListPolynomial(22, 26, 13, 15, 26),
"test 1"
)
assertEquals(
ListPolynomial(0, 0, 0, 0, 0),
15 * ListPolynomial(7, 0, 49, 21, 14),
"test 2"
)
}
}
@Test
fun test_Polynomial_Constant_plus() {
RationalField.listPolynomial {
assertEquals(
ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) + Rational(-3),
"test 1"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + Rational(2),
"test 2"
)
assertEquals(
ListPolynomial(Rational(0)),
ListPolynomial(Rational(-2)) + Rational(2),
"test 3"
)
assertEquals(
ListPolynomial(Rational(0)),
ListPolynomial<Rational>() + Rational(0),
"test 4"
)
assertEquals(
ListPolynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + Rational(1),
"test 5"
)
assertEquals(
ListPolynomial(Rational(-1)),
ListPolynomial(Rational(-2)) + Rational(1),
"test 6"
)
assertEquals(
ListPolynomial(Rational(2)),
ListPolynomial<Rational>() + Rational(2),
"test 7"
)
}
}
@Test
fun test_Polynomial_Constant_minus() {
RationalField.listPolynomial {
assertEquals(
ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) - Rational(-3),
"test 1"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - Rational(2),
"test 2"
)
assertEquals(
ListPolynomial(Rational(0)),
ListPolynomial(Rational(2)) - Rational(2),
"test 3"
)
assertEquals(
ListPolynomial(Rational(0)),
ListPolynomial<Rational>() - Rational(0),
"test 4"
)
assertEquals(
ListPolynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - Rational(1),
"test 5"
)
assertEquals(
ListPolynomial(Rational(1)),
ListPolynomial(Rational(2)) - Rational(1),
"test 6"
)
assertEquals(
ListPolynomial(Rational(-2)),
ListPolynomial<Rational>() - Rational(2),
"test 7"
)
}
}
@Test
fun test_Polynomial_Constant_times() {
IntModuloRing(35).listPolynomial {
assertEquals(
ListPolynomial(34, 2, 1, 20, 2),
ListPolynomial(22, 26, 13, 15, 26) * 27.asConstant(),
"test 1"
)
assertEquals(
ListPolynomial(0, 0, 0, 0, 0),
ListPolynomial(7, 0, 49, 21, 14) * 15.asConstant(),
"test 2"
)
}
}
@Test
fun test_Constant_Polynomial_plus() {
RationalField.listPolynomial {
assertEquals(
ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
Rational(-3) + ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)),
"test 1"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
Rational(2) + ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
"test 2"
)
assertEquals(
ListPolynomial(Rational(0)),
Rational(2) + ListPolynomial(Rational(-2)),
"test 3"
)
assertEquals(
ListPolynomial(Rational(0)),
Rational(0) + ListPolynomial(),
"test 4"
)
assertEquals(
ListPolynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
Rational(1) + ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
"test 5"
)
assertEquals(
ListPolynomial(Rational(-1)),
Rational(1) + ListPolynomial(Rational(-2)),
"test 6"
)
assertEquals(
ListPolynomial(Rational(2)),
Rational(2) + ListPolynomial(),
"test 7"
)
}
}
@Test
fun test_Constant_Polynomial_minus() {
RationalField.listPolynomial {
assertEquals(
ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
Rational(3) - ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)),
"test 1"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
Rational(-2) - ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
"test 2"
)
assertEquals(
ListPolynomial(Rational(0)),
Rational(-2) - ListPolynomial(Rational(-2)),
"test 3"
)
assertEquals(
ListPolynomial(Rational(0)),
Rational(0) - ListPolynomial(),
"test 4"
)
assertEquals(
ListPolynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
Rational(-1) - ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
"test 5"
)
assertEquals(
ListPolynomial(Rational(1)),
Rational(-1) - ListPolynomial(Rational(-2)),
"test 6"
)
assertEquals(
ListPolynomial(Rational(-2)),
Rational(-2) - ListPolynomial(),
"test 7"
)
}
}
@Test
fun test_Constant_Polynomial_times() {
IntModuloRing(35).listPolynomial {
assertEquals(
ListPolynomial(34, 2, 1, 20, 2),
27 * ListPolynomial(22, 26, 13, 15, 26),
"test 1"
)
assertEquals(
ListPolynomial(0, 0, 0, 0, 0),
15 * ListPolynomial(7, 0, 49, 21, 14),
"test 2"
)
}
}
@Test
fun test_Polynomial_unaryMinus() {
RationalField.listPolynomial {
assertEquals(
ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)),
-ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)),
"test 1"
)
assertEquals(
ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7), Rational(0), Rational(0)),
-ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7), Rational(0), Rational(0)),
"test 2"
)
}
}
@Test
fun test_Polynomial_Polynomial_plus() {
RationalField.listPolynomial {
// (5/9 - 8/9 x - 8/7 x^2) + (-5/7 + 5/1 x + 5/8 x^2) ?= -10/63 + 37/9 x - 29/56 x^2
assertEquals(
ListPolynomial(Rational(-10, 63), Rational(37, 9), Rational(-29, 56)),
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) +
ListPolynomial(Rational(-5, 7), Rational(5, 1), Rational(5, 8)),
"test 1"
)
// (-2/9 - 8/3 x) + (0 + 9/4 x + 2/4 x^2) ?= -2/9 - 5/12 x + 2/4 x^2
assertEquals(
ListPolynomial(Rational(-2, 9), Rational(-5, 12), Rational(2, 4)),
ListPolynomial(Rational(-2, 9), Rational(-8, 3)) +
ListPolynomial(Rational(0), Rational(9, 4), Rational(2, 4)),
"test 2"
)
// (-4/7 - 2/6 x + 0 x^2 + 0 x^3) + (-6/3 - 7/2 x + 2/3 x^2) ?= -18/7 - 23/6 x + 2/3 x^2
assertEquals(
ListPolynomial(Rational(-18, 7), Rational(-23, 6), Rational(2, 3), Rational(0)),
ListPolynomial(Rational(-4, 7), Rational(-2, 6), Rational(0), Rational(0)) +
ListPolynomial(Rational(-6, 3), Rational(-7, 2), Rational(2, 3)),
"test 3"
)
// (-2/4 - 6/9 x - 4/9 x^2) + (2/4 + 6/9 x + 4/9 x^2) ?= 0
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(-2, 4), Rational(-6, 9), Rational(-4, 9)) +
ListPolynomial(Rational(2, 4), Rational(6, 9), Rational(4, 9)),
"test 4"
)
}
}
@Test
fun test_Polynomial_Polynomial_minus() {
RationalField.listPolynomial {
// (5/9 - 8/9 x - 8/7 x^2) - (-5/7 + 5/1 x + 5/8 x^2) ?= 80/63 - 53/9 x - 99/56 x^2
assertEquals(
ListPolynomial(Rational(80, 63), Rational(-53, 9), Rational(-99, 56)),
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) -
ListPolynomial(Rational(-5, 7), Rational(5, 1), Rational(5, 8)),
"test 1"
)
// (-2/9 - 8/3 x) - (0 + 9/4 x + 2/4 x^2) ?= -2/9 - 59/12 x - 2/4 x^2
assertEquals(
ListPolynomial(Rational(-2, 9), Rational(-59, 12), Rational(-2, 4)),
ListPolynomial(Rational(-2, 9), Rational(-8, 3)) -
ListPolynomial(Rational(0), Rational(9, 4), Rational(2, 4)),
"test 2"
)
// (-4/7 - 2/6 x + 0 x^2 + 0 x^3) - (-6/3 - 7/2 x + 2/3 x^2) ?= 10/7 + 19/6 x - 2/3 x^2
assertEquals(
ListPolynomial(Rational(10, 7), Rational(19, 6), Rational(-2, 3), Rational(0)),
ListPolynomial(Rational(-4, 7), Rational(-2, 6), Rational(0), Rational(0)) -
ListPolynomial(Rational(-6, 3), Rational(-7, 2), Rational(2, 3)),
"test 3"
)
// (-2/4 - 6/9 x - 4/9 x^2) - (-2/4 - 6/9 x - 4/9 x^2) ?= 0
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(-2, 4), Rational(-6, 9), Rational(-4, 9)) -
ListPolynomial(Rational(-2, 4), Rational(-6, 9), Rational(-4, 9)),
"test 4"
)
}
}
@Test
fun test_Polynomial_Polynomial_times() {
IntModuloRing(35).listPolynomial {
// (1 + x + x^2) * (1 - x + x^2) ?= 1 + x^2 + x^4
assertEquals(
ListPolynomial(1, 0, 1, 0, 1),
ListPolynomial(1, -1, 1) * ListPolynomial(1, 1, 1),
"test 1"
)
// Spoiler: 5 * 7 = 0
assertEquals(
ListPolynomial(0, 0, 0, 0, 0),
ListPolynomial(5, -25, 10) * ListPolynomial(21, 14, -7),
"test 2"
)
}
}
}

View File

@ -1,257 +0,0 @@
/*
* 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.test.misc.Rational
import space.kscience.kmath.test.misc.RationalField
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class ListPolynomialUtilTest {
@Test
fun test_substitute_Double() {
assertEquals(
0.0,
ListPolynomial(1.0, -2.0, 1.0).substitute(1.0),
0.001,
"test 1"
)
assertEquals(
1.1931904761904761,
ListPolynomial(0.625, 2.6666666666666665, 0.5714285714285714, 1.5).substitute(0.2),
0.001,
"test 2"
)
assertEquals(
0.5681904761904762,
ListPolynomial(0.0, 2.6666666666666665, 0.5714285714285714, 1.5).substitute(0.2),
0.001,
"test 3"
)
assertEquals(
1.1811904761904761,
ListPolynomial(0.625, 2.6666666666666665, 0.5714285714285714, 0.0).substitute(0.2),
0.001,
"test 4"
)
assertEquals(
1.1703333333333332,
ListPolynomial(0.625, 2.6666666666666665, 0.0, 1.5).substitute(0.2),
0.001,
"test 5"
)
}
@Test
fun test_substitute_Constant() {
assertEquals(
Rational(0),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).substitute(RationalField, Rational(1)),
"test 1"
)
assertEquals(
Rational(25057, 21000),
ListPolynomial(Rational(5,8), Rational(8, 3), Rational(4, 7), Rational(3, 2))
.substitute(RationalField, Rational(1, 5)),
"test 2"
)
assertEquals(
Rational(2983, 5250),
ListPolynomial(Rational(0), Rational(8, 3), Rational(4, 7), Rational(3, 2))
.substitute(RationalField, Rational(1, 5)),
"test 3"
)
assertEquals(
Rational(4961, 4200),
ListPolynomial(Rational(5,8), Rational(8, 3), Rational(4, 7), Rational(0))
.substitute(RationalField, Rational(1, 5)),
"test 4"
)
assertEquals(
Rational(3511, 3000),
ListPolynomial(Rational(5,8), Rational(8, 3), Rational(0), Rational(3, 2))
.substitute(RationalField, Rational(1, 5)),
"test 5"
)
}
@Test
fun test_substitute_Polynomial() {
assertEquals(
ListPolynomial(Rational(0)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).substitute(RationalField, ListPolynomial(Rational(1))),
"test 1"
)
assertEquals(
ListPolynomial(Rational(709, 378), Rational(155, 252), Rational(19, 525), Rational(2, 875)),
ListPolynomial(Rational(1, 7), Rational(9, 4), Rational(1, 3), Rational(2, 7))
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(1, 5))),
"test 2"
)
assertEquals(
ListPolynomial(Rational(655, 378), Rational(155, 252), Rational(19, 525), Rational(2, 875)),
ListPolynomial(Rational(0), Rational(9, 4), Rational(1, 3), Rational(2, 7))
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(1, 5))),
"test 3"
)
assertEquals(
ListPolynomial(Rational(677, 378), Rational(97, 180), Rational(1, 75), Rational(0)),
ListPolynomial(Rational(1, 7), Rational(9, 4), Rational(1, 3), Rational(0))
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(1, 5))),
"test 4"
)
assertEquals(
ListPolynomial(Rational(653, 378), Rational(221, 420), Rational(4, 175), Rational(2, 875)),
ListPolynomial(Rational(1, 7), Rational(9, 4), Rational(0), Rational(2, 7))
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(1, 5))),
"test 5"
)
assertEquals(
ListPolynomial(Rational(89, 54), Rational(0), Rational(0), Rational(0)),
ListPolynomial(Rational(0), Rational(9, 4), Rational(1, 3), Rational(0))
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(0))),
"test 6"
)
}
@Test
fun test_derivative() {
assertEquals(
ListPolynomial(Rational(-2), Rational(2)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).derivative(RationalField),
"test 1"
)
assertEquals(
ListPolynomial(Rational(-8, 3), Rational(8, 9), Rational(15, 7), Rational(-20, 9)),
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).derivative(RationalField),
"test 2"
)
assertEquals(
ListPolynomial(Rational(0), Rational(8, 9), Rational(15, 7), Rational(-20, 9)),
ListPolynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).derivative(RationalField),
"test 3"
)
assertEquals(
ListPolynomial(Rational(-8, 3), Rational(8, 9), Rational(15, 7), Rational(0)),
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).derivative(RationalField),
"test 4"
)
}
@Test
fun test_nthDerivative() {
assertEquals(
ListPolynomial(Rational(-2), Rational(2)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 1),
"test 1"
)
assertFailsWith<IllegalArgumentException>("test2") {
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, -1)
}
assertEquals(
ListPolynomial(Rational(1), Rational(-2), Rational(1)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 0),
"test 3"
)
assertEquals(
ListPolynomial(Rational(2)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 2),
"test 4"
)
assertEquals(
ListPolynomial(),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 3),
"test 5"
)
assertEquals(
ListPolynomial(),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 4),
"test 6"
)
assertEquals(
ListPolynomial(Rational(8, 9), Rational(30, 7), Rational(-20, 3)),
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthDerivative(RationalField, 2),
"test 7"
)
assertEquals(
ListPolynomial(Rational(8, 9), Rational(30, 7), Rational(-20, 3)),
ListPolynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthDerivative(RationalField, 2),
"test 8"
)
assertEquals(
ListPolynomial(Rational(8, 9), Rational(30, 7), Rational(0)),
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).nthDerivative(RationalField, 2),
"test 9"
)
}
@Test
fun test_antiderivative() {
assertEquals(
ListPolynomial(Rational(0), Rational(1), Rational(-1), Rational(1, 3)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).antiderivative(RationalField),
"test 1"
)
assertEquals(
ListPolynomial(Rational(0), Rational(1, 5), Rational(-4, 3), Rational(4, 27), Rational(5, 28), Rational(-1, 9)),
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).antiderivative(RationalField),
"test 2"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(4, 27), Rational(5, 28), Rational(-1, 9)),
ListPolynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).antiderivative(RationalField),
"test 3"
)
assertEquals(
ListPolynomial(Rational(0), Rational(1, 5), Rational(-4, 3), Rational(4, 27), Rational(5, 28), Rational(0)),
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).antiderivative(RationalField),
"test 4"
)
}
@Test
fun test_nthAntiderivative() {
assertEquals(
ListPolynomial(Rational(0), Rational(1), Rational(-1), Rational(1, 3)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 1),
"test 1"
)
assertFailsWith<IllegalArgumentException>("test2") {
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, -1)
}
assertEquals(
ListPolynomial(Rational(1), Rational(-2), Rational(1)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 0),
"test 3"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(1, 2), Rational(-1, 3), Rational(1, 12)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 2),
"test 4"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(1, 6), Rational(-1, 12), Rational(1, 60)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 3),
"test 5"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0), Rational(1, 24), Rational(-1, 60), Rational(1, 360)),
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 4),
"test 6"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(1, 10), Rational(-4, 9), Rational(1, 27), Rational(1, 28), Rational(-1, 54)),
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthAntiderivative(RationalField, 2),
"test 7"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0), Rational(1, 27), Rational(1, 28), Rational(-1, 54)),
ListPolynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthAntiderivative(RationalField, 2),
"test 8"
)
assertEquals(
ListPolynomial(Rational(0), Rational(0), Rational(1, 10), Rational(-4, 9), Rational(1, 27), Rational(1, 28), Rational(0)),
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).nthAntiderivative(RationalField, 2),
"test 9"
)
}
}