Feature: Polynomials and rational functions #469

Merged
lounres merged 132 commits from feature/polynomials into dev 2022-07-28 18:04:06 +03:00
29 changed files with 1278 additions and 24 deletions
Showing only changes of commit 51dd72e48f - Show all commits

View File

@ -16,7 +16,7 @@ dependencies {
implementation(project(":kmath-commons"))
implementation(project(":kmath-complex"))
implementation(project(":kmath-functions"))
implementation(project(":kmath-polynomialX"))
implementation(project(":kmath-polynomial"))
implementation(project(":kmath-optimization"))
implementation(project(":kmath-stat"))
implementation(project(":kmath-viktor"))

View File

@ -10,13 +10,11 @@ kotlin.sourceSets {
commonMain {
dependencies {
api(projects.kmathCore)
api(projects.kmathFunctions)
}
}
commonTest {
dependencies {
api(projects.testUtilsFunctions)
api(projects.testUtilsPolynomialX)
api(projects.testUtilsPolynomial)
api(kotlin("test"))
}
}

View File

@ -0,0 +1,387 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
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.math.max
import kotlin.math.min
/**
* Represents univariate polynomial that stores its coefficients in a [List].
*
* @param C the type of constants.
*/
public data class ListPolynomial<C>(
/**
* List that contains coefficients of the polynomial. Every monomial `a x^d` is stored as a coefficient `a` placed
* into the list at index `d`. For example, coefficients of a 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 not prohibited to put extra zeros at end of the list (as for `0x^3` and `0x^4` in the example). But the
* longer the coefficients list the worse performance of arithmetical operations performed on it. Thus, it is
* recommended not to put (or even to remove) extra (or useless) coefficients at the end of the coefficients list.
*/
public val coefficients: List<C>
) : Polynomial<C> {
override fun toString(): String = "ListPolynomial$coefficients"
}
/**
* Arithmetic context for univariate polynomials with coefficients stored as a [List] constructed with the provided
* [ring] of constants.
*
* @param C the type of constants. Polynomials have them a coefficients in their terms.
* @param A type of provided underlying ring of constants. It's [Ring] of [C].
* @param ring underlying ring of constants of type [A].
*/
public 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 a 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 a 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 a polynomial.
*
* The operation is equivalent to sum of [other] copies of [this].
*/
public override operator fun ListPolynomial<C>.times(other: Int): ListPolynomial<C> =
when (other) {
0 -> zero
1 -> this
else -> ListPolynomial(
coefficients
.toMutableList()
.apply {
for (deg in indices) this[deg] = this[deg] * other
}
)
}
/**
* Returns sum of the integer represented as a polynomial and the polynomial.
*
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
*/
public override operator fun Int.plus(other: 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 a polynomial and the polynomial.
*
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
*/
public override operator fun Int.minus(other: ListPolynomial<C>): ListPolynomial<C> =
ListPolynomial(
other.coefficients
.toMutableList()
.apply {
if (this@minus == 0) {
indices.forEach { this[it] = -this[it] }
} else {
(1..lastIndex).forEach { this[it] = -this[it] }
val result = this@minus - getOrElse(0) { constantZero }
if (size == 0) add(result)
else this[0] = result
}
}
)
/**
* Returns product of the integer represented as a polynomial and the polynomial.
*
* The operation is equivalent to sum of [this] copies of [other].
*/
public override operator fun Int.times(other: ListPolynomial<C>): ListPolynomial<C> =
when (this) {
0 -> zero
1 -> other
else -> ListPolynomial(
other.coefficients
.toMutableList()
.apply {
for (deg in indices) this[deg] = this@times * this[deg]
}
)
}
/**
* Returns sum of the constant represented as a 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 a 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 {
(1 .. lastIndex).forEach { this[it] = -this[it] }
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 a 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 a 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 a 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 a 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(listOf(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 }
}
)
}
/**
* Raises [arg] to the integer power [exponent].
*/ // TODO: To optimize boxing
override fun power(arg: ListPolynomial<C>, exponent: UInt): ListPolynomial<C> = super.power(arg, exponent)
/**
* Instance of zero polynomial (zero of the polynomial ring).
*/
override val zero: ListPolynomial<C> = ListPolynomial(emptyList())
/**
* Instance of unit polynomial (unit of the polynomial ring).
*/
override val one: ListPolynomial<C> by lazy { 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
// TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with
// [ListPolynomialSpace] as a context receiver
/**
* Evaluates value of [this] polynomial on provided [argument].
*/
public inline fun ListPolynomial<C>.substitute(argument: C): C = substitute(ring, argument)
/**
* Substitutes provided polynomial [argument] into [this] polynomial.
*/
public inline fun ListPolynomial<C>.substitute(argument: ListPolynomial<C>): ListPolynomial<C> = substitute(ring, argument)
/**
* Represent [this] polynomial as a regular context-less function.
*/
public inline fun ListPolynomial<C>.asFunction(): (C) -> C = asFunctionOver(ring)
/**
* Represent [this] polynomial as a regular context-less function.
*/
public inline fun ListPolynomial<C>.asFunctionOfConstant(): (C) -> C = asFunctionOfConstantOver(ring)
/**
* Represent [this] polynomial as a regular context-less function.
*/
public inline fun ListPolynomial<C>.asFunctionOfPolynomial(): (ListPolynomial<C>) -> ListPolynomial<C> = asFunctionOfPolynomialOver(ring)
/**
* Evaluates value of [this] polynomial on provided [argument].
*/
public inline operator fun ListPolynomial<C>.invoke(argument: C): C = substitute(ring, argument)
/**
* Evaluates value of [this] polynomial on provided [argument].
*/
public inline operator fun ListPolynomial<C>.invoke(argument: ListPolynomial<C>): ListPolynomial<C> = 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

@ -0,0 +1,502 @@
/*
* 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.js.JsName
import kotlin.jvm.JvmName
/**
* Abstraction of polynomials.
*/
public interface Polynomial<C>
/**
* Abstraction of ring of polynomials of type [P] over ring of constants of type [C].
*
* @param C the type of constants. Polynomials have them as coefficients in their terms.
* @param P the type of polynomials.
*/
@Suppress("INAPPLICABLE_JVM_NAME", "PARAMETER_NAME_CHANGED_ON_OVERRIDE") // FIXME: Waiting for KT-31420
public interface PolynomialSpace<C, P: Polynomial<C>> : Ring<P> {
/**
* Returns sum of the constant and the integer represented as a constant (member of underlying ring).
*
* The operation is equivalent to adding [other] copies of unit of underlying ring to [this].
*/
public operator fun C.plus(other: Int): C
/**
* Returns difference between the constant and the integer represented as a constant (member of underlying ring).
*
* The operation is equivalent to subtraction [other] copies of unit of underlying ring from [this].
*/
public operator fun C.minus(other: Int): C
/**
* Returns product of the constant and the integer represented as a constant (member of underlying ring).
*
* The operation is equivalent to sum of [other] copies of [this].
*/
public operator fun C.times(other: Int): C
/**
* Returns sum of the integer represented as a constant (member of underlying ring) and the constant.
*
* The operation is equivalent to adding [this] copies of unit of underlying ring to [other].
*/
public operator fun Int.plus(other: C): C
/**
* Returns difference between the integer represented as a constant (member of underlying ring) and the constant.
*
* The operation is equivalent to subtraction [this] copies of unit of underlying ring from [other].
*/
public operator fun Int.minus(other: C): C
/**
* Returns product of the integer represented as a constant (member of underlying ring) and the constant.
*
* The operation is equivalent to sum of [this] copies of [other].
*/
public operator fun Int.times(other: C): C
/**
* Converts the integer [value] to constant.
*/
public fun constantNumber(value: Int): C = constantOne * value
/**
* Converts the integer to constant.
*/
public fun Int.asConstant(): C = constantNumber(this)
/**
* Returns sum of the polynomial and the integer represented as a polynomial.
*
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
*/
public operator fun P.plus(other: Int): P = addMultipliedByDoubling(this, one, other)
/**
* Returns difference between the polynomial and the integer represented as a polynomial.
*
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
*/
public operator fun P.minus(other: Int): P = addMultipliedByDoubling(this, one, -other)
/**
* Returns product of the polynomial and the integer represented as a polynomial.
*
* The operation is equivalent to sum of [other] copies of [this].
*/
public operator fun P.times(other: Int): P = multiplyByDoubling(this, other)
/**
* Returns sum of the integer represented as a polynomial and the polynomial.
*
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
*/
public operator fun Int.plus(other: P): P = addMultipliedByDoubling(other, one, this)
/**
* Returns difference between the integer represented as a polynomial and the polynomial.
*
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
*/
public operator fun Int.minus(other: P): P = addMultipliedByDoubling(-other, one, this)
/**
* Returns product of the integer represented as a polynomial and the polynomial.
*
* The operation is equivalent to sum of [this] copies of [other].
*/
public operator fun Int.times(other: P): P = multiplyByDoubling(other, this)
/**
* Converts the integer [value] to polynomial.
*/
public fun number(value: Int): P = number(constantNumber(value))
/**
* Converts the integer to polynomial.
*/
public fun Int.asPolynomial(): P = number(this)
/**
* Returns the same constant.
*/
@JvmName("unaryPlusConstant")
@JsName("unaryPlusConstant")
public operator fun C.unaryPlus(): C = this
/**
* Returns negation of the constant.
*/
@JvmName("unaryMinusConstant")
@JsName("unaryMinusConstant")
public operator fun C.unaryMinus(): C
/**
* Returns sum of the constants.
*/
@JvmName("plusConstantConstant")
@JsName("plusConstantConstant")
public operator fun C.plus(other: C): C
/**
* Returns difference of the constants.
*/
@JvmName("minusConstantConstant")
@JsName("minusConstantConstant")
public operator fun C.minus(other: C): C
/**
* Returns product of the constants.
*/
@JvmName("timesConstantConstant")
@JsName("timesConstantConstant")
public operator fun C.times(other: C): C
/**
* Raises [arg] to the integer power [exponent].
*/
@JvmName("powerConstant")
@JsName("powerConstant")
public fun power(arg: C, exponent: UInt) : C
/**
* Instance of zero constant (zero of the underlying ring).
*/
public val constantZero: C
/**
* Instance of unit constant (unit of the underlying ring).
*/
public val constantOne: C
/**
* Returns sum of the constant represented as a polynomial and the polynomial.
*/
public operator fun C.plus(other: P): P
/**
* Returns difference between the constant represented as a polynomial and the polynomial.
*/
public operator fun C.minus(other: P): P
/**
* Returns product of the constant represented as a polynomial and the polynomial.
*/
public operator fun C.times(other: P): P
/**
* Returns sum of the constant represented as a polynomial and the polynomial.
*/
public operator fun P.plus(other: C): P
/**
* Returns difference between the constant represented as a polynomial and the polynomial.
*/
public operator fun P.minus(other: C): P
/**
* Returns product of the constant represented as a polynomial and the polynomial.
*/
public operator fun P.times(other: C): P
/**
* Converts the constant [value] to polynomial.
*/
public fun number(value: C): P = one * value
/**
* Converts the constant to polynomial.
*/
public fun C.asPolynomial(): P = number(this)
/**
* Returns the same polynomial.
*/
public override operator fun P.unaryPlus(): P = this
/**
* Returns negation of the polynomial.
*/
public override operator fun P.unaryMinus(): P
/**
* Returns sum of the polynomials.
*/
public override operator fun P.plus(other: P): P
/**
* Returns difference of the polynomials.
*/
public override operator fun P.minus(other: P): P
/**
* Returns product of the polynomials.
*/
public override operator fun P.times(other: P): P
/**
* Raises [arg] to the integer power [exponent].
*/
public override fun power(arg: P, exponent: UInt) : P = exponentiateBySquaring(arg, exponent)
/**
* Instance of zero polynomial (zero of the polynomial ring).
*/
public override val zero: P
/**
* Instance of unit polynomial (unit of the polynomial ring).
*/
public override val one: P
/**
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
* zero, degree is -1.
*/
public val P.degree: Int
override fun add(left: P, right: P): P = left + right
override fun multiply(left: P, right: P): P = left * right
}
/**
* Abstraction of ring of polynomials of type [P] over ring of constants of type [C]. It also assumes that there is
* provided [ring] (of type [A]), that provides constant-wise operations.
*
* @param C the type of constants. Polynomials have them as coefficients in their terms.
* @param P the type of polynomials.
* @param A the type of algebraic structure (precisely, of ring) provided for constants.
*/
@Suppress("INAPPLICABLE_JVM_NAME") // FIXME: Waiting for KT-31420
public interface PolynomialSpaceOverRing<C, P: Polynomial<C>, A: Ring<C>> : PolynomialSpace<C, P> {
/**
* Underlying ring of constants. Its operations on constants are inherited by local operations on constants.
*/
public val ring: A
/**
* Returns sum of the constant and the integer represented as a constant (member of underlying ring).
*
* The operation is equivalent to adding [other] copies of unit of underlying ring to [this].
*/
public override operator fun C.plus(other: Int): C = ring { addMultipliedByDoubling(this@plus, one, other) }
/**
* Returns difference between the constant and the integer represented as a constant (member of underlying ring).
*
* The operation is equivalent to subtraction [other] copies of unit of underlying ring from [this].
*/
public override operator fun C.minus(other: Int): C = ring { addMultipliedByDoubling(this@minus, one, -other) }
/**
* Returns product of the constant and the integer represented as a constant (member of underlying ring).
*
* The operation is equivalent to sum of [other] copies of [this].
*/
public override operator fun C.times(other: Int): C = ring { multiplyByDoubling(this@times, other) }
/**
* Returns sum of the integer represented as a constant (member of underlying ring) and the constant.
*
* The operation is equivalent to adding [this] copies of unit of underlying ring to [other].
*/
public override operator fun Int.plus(other: C): C = ring { addMultipliedByDoubling(other, one, this@plus) }
/**
* Returns difference between the integer represented as a constant (member of underlying ring) and the constant.
*
* The operation is equivalent to subtraction [this] copies of unit of underlying ring from [other].
*/
public override operator fun Int.minus(other: C): C = ring { addMultipliedByDoubling(-other, one, this@minus) }
/**
* Returns product of the integer represented as a constant (member of underlying ring) and the constant.
*
* The operation is equivalent to sum of [this] copies of [other].
*/
public override operator fun Int.times(other: C): C = ring { multiplyByDoubling(other, this@times) }
/**
* Returns negation of the constant.
*/
@JvmName("unaryMinusConstant")
public override operator fun C.unaryMinus(): C = ring { -this@unaryMinus }
/**
* Returns sum of the constants.
*/
@JvmName("plusConstantConstant")
public override operator fun C.plus(other: C): C = ring { this@plus + other }
/**
* Returns difference of the constants.
*/
@JvmName("minusConstantConstant")
public override operator fun C.minus(other: C): C = ring { this@minus - other }
/**
* Returns product of the constants.
*/
@JvmName("timesConstantConstant")
public override operator fun C.times(other: C): C = ring { this@times * other }
/**
* Raises [arg] to the integer power [exponent].
*/
@JvmName("powerConstant")
override fun power(arg: C, exponent: UInt): C = ring { power(arg, exponent) }
/**
* Instance of zero constant (zero of the underlying ring).
*/
public override val constantZero: C get() = ring.zero
/**
* Instance of unit constant (unit of the underlying ring).
*/
public override val constantOne: C get() = ring.one
}
/**
* Abstraction of ring of polynomials of type [P] of variables of type [V] and over ring of constants of type [C].
*
* @param C the type of constants. Polynomials have them as coefficients in their terms.
* @param V the type of variables. Polynomials have them in representations of terms.
* @param P the type of polynomials.
*/
@Suppress("INAPPLICABLE_JVM_NAME") // FIXME: Waiting for KT-31420
public interface MultivariatePolynomialSpace<C, V, P: Polynomial<C>>: PolynomialSpace<C, P> {
/**
* Returns sum of the variable represented as a monic monomial and the integer represented as a constant polynomial.
*/
@JvmName("plusVariableInt")
public operator fun V.plus(other: Int): P
/**
* Returns difference between the variable represented as a monic monomial and the integer represented as a constant polynomial.
*/
@JvmName("minusVariableInt")
public operator fun V.minus(other: Int): P
/**
* Returns product of the variable represented as a monic monomial and the integer represented as a constant polynomial.
*/
@JvmName("timesVariableInt")
public operator fun V.times(other: Int): P
/**
* Returns sum of the integer represented as a constant polynomial and the variable represented as a monic monomial.
*/
@JvmName("plusIntVariable")
public operator fun Int.plus(other: V): P
/**
* Returns difference between the integer represented as a constant polynomial and the variable represented as a monic monomial.
*/
@JvmName("minusIntVariable")
public operator fun Int.minus(other: V): P
/**
* Returns product of the integer represented as a constant polynomial and the variable represented as a monic monomial.
*/
@JvmName("timesIntVariable")
public operator fun Int.times(other: V): P
/**
* Returns sum of the variable represented as a monic monomial and the constant represented as a constant polynomial.
*/
@JvmName("plusVariableConstant")
public operator fun V.plus(other: C): P
/**
* Returns difference between the variable represented as a monic monomial and the constant represented as a constant polynomial.
*/
@JvmName("minusVariableConstant")
public operator fun V.minus(other: C): P
/**
* Returns product of the variable represented as a monic monomial and the constant represented as a constant polynomial.
*/
@JvmName("timesVariableConstant")
public operator fun V.times(other: C): P
/**
* Returns sum of the constant represented as a constant polynomial and the variable represented as a monic monomial.
*/
@JvmName("plusConstantVariable")
public operator fun C.plus(other: V): P
/**
* Returns difference between the constant represented as a constant polynomial and the variable represented as a monic monomial.
*/
@JvmName("minusConstantVariable")
public operator fun C.minus(other: V): P
/**
* Returns product of the constant represented as a constant polynomial and the variable represented as a monic monomial.
*/
@JvmName("timesConstantVariable")
public operator fun C.times(other: V): P
/**
* Represents the variable as a monic monomial.
*/
@JvmName("unaryPlusVariable")
public operator fun V.unaryPlus(): P
/**
* Returns negation of representation of the variable as a monic monomial.
*/
@JvmName("unaryMinusVariable")
public operator fun V.unaryMinus(): P
/**
* Returns sum of the variables represented as monic monomials.
*/
@JvmName("plusVariableVariable")
public operator fun V.plus(other: V): P
/**
* Returns difference between the variables represented as monic monomials.
*/
@JvmName("minusVariableVariable")
public operator fun V.minus(other: V): P
/**
* Returns product of the variables represented as monic monomials.
*/
@JvmName("timesVariableVariable")
public operator fun V.times(other: V): P
/**
* Represents the [variable] as a monic monomial.
*/
@JvmName("numberVariable")
public fun number(variable: V): P = +variable
/**
* Represents the variable as a monic monomial.
*/
@JvmName("asPolynomialVariable")
public fun V.asPolynomial(): P = number(this)
/**
* Returns sum of the variable represented as a monic monomial and the polynomial.
*/
@JvmName("plusVariablePolynomial")
public operator fun V.plus(other: P): P
/**
* Returns difference between the variable represented as a monic monomial and the polynomial.
*/
@JvmName("minusVariablePolynomial")
public operator fun V.minus(other: P): P
/**
* Returns product of the variable represented as a monic monomial and the polynomial.
*/
@JvmName("timesVariablePolynomial")
public operator fun V.times(other: P): P
/**
* Returns sum of the polynomial and the variable represented as a monic monomial.
*/
@JvmName("plusPolynomialVariable")
public operator fun P.plus(other: V): P
/**
* Returns difference between the polynomial and the variable represented as a monic monomial.
*/
@JvmName("minusPolynomialVariable")
public operator fun P.minus(other: V): P
/**
* Returns product of the polynomial and the variable represented as a monic monomial.
*/
@JvmName("timesPolynomialVariable")
public operator fun P.times(other: V): P
/**
* 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 val P.degrees: Map<V, UInt>
/**
* Counts degree of the polynomial by the specified [variable].
*/
public fun P.degreeBy(variable: V): UInt = degrees.getOrElse(variable) { 0u }
/**
* Counts degree of the polynomial by the specified [variables].
*/
public fun P.degreeBy(variables: Collection<V>): UInt
/**
* Set of all variables that appear in the polynomial in positive exponents.
*/
public val P.variables: Set<V> get() = degrees.keys
/**
* Count of all variables that appear in the polynomial in positive exponents.
*/
public val P.countOfVariables: Int get() = variables.size
}

View File

@ -5,7 +5,8 @@
package space.kscience.kmath.functions
import space.kscience.kmath.operations.*
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import kotlin.js.JsName
import kotlin.jvm.JvmName

View File

@ -8,9 +8,9 @@ package space.kscience.kmath.functions
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Field
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.operations.invoke
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmName

View File

@ -0,0 +1,92 @@
/*
* 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
/**
* Constructs a [ListPolynomial] instance with provided [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 })
/**
* Constructs a [ListPolynomial] instance with provided [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() })
/**
* Represents [this] constant as a [ListPolynomial].
*/
public fun <C> C.asListPolynomial() : ListPolynomial<C> = ListPolynomial(listOf(this))
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
/**
* Constructs [ListRationalFunction] instance with numerator and denominator constructed with provided
* [numeratorCoefficients] and [denominatorCoefficients]. The both collections of coefficients will be reversed if
* [reverse] parameter is true.
*/
@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 } )
)
/**
* Constructs [ListRationalFunction] instance with provided [numerator] and unit denominator.
*/
@Suppress("FunctionName")
public fun <C, A: Ring<C>> A.ListRationalFunction(numerator: ListPolynomial<C>): ListRationalFunction<C> =
ListRationalFunction<C>(numerator, ListPolynomial(listOf(one)))
/**
* Constructs [ListRationalFunction] instance with provided [numerator] and unit denominator.
*/
@Suppress("FunctionName")
public fun <C, A: Ring<C>> ListRationalFunctionSpace<C, A>.ListRationalFunction(numerator: ListPolynomial<C>): ListRationalFunction<C> =
ListRationalFunction<C>(numerator, polynomialOne)
/**
* Constructs [ListRationalFunction] instance with numerator constructed with provided [numeratorCoefficients] and unit
* denominator. The collection of numerator coefficients will be reversed if [reverse] parameter is true.
*/
@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))
)
/**
* Constructs [ListRationalFunction] instance with numerator constructed with provided [numeratorCoefficients] and unit
* denominator. The collection of numerator coefficients will be reversed if [reverse] parameter is true.
*/
@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
)
/**
* Represents [this] constant as a rational function.
*/ // FIXME: When context receivers will be ready, delete this function and uncomment the following two
public fun <C, A: Ring<C>> C.asListRationalFunction(ring: A) : ListRationalFunction<C> = ring.ListRationalFunction(asListPolynomial())
///**
// * Represents [this] constant as a rational function.
// */
//context(A)
//public fun <C, A: Ring<C>> C.asListRationalFunction() : ListRationalFunction<C> = ListRationalFunction(asListPolynomial())
///**
// * Represents [this] constant as a rational function.
// */
//context(ListRationalFunctionSpace<C, A>)
//public fun <C, A: Ring<C>> C.asListRationalFunction() : ListRationalFunction<C> = ListRationalFunction(asListPolynomial())

View File

@ -0,0 +1,255 @@
/*
* 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.pow
/**
* Creates a [ListPolynomialSpace] over a received ring.
*/
public inline val <C, A : Ring<C>> A.listPolynomialSpace: ListPolynomialSpace<C, A>
get() = ListPolynomialSpace(this)
/**
* Creates a [ListPolynomialSpace]'s scope over a received ring.
*/ // TODO: When context will be ready move [ListPolynomialSpace] and add [A] to context receivers of [block]
public inline fun <C, A : Ring<C>, R> A.listPolynomialSpace(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 inline val <C, A> A.scalableListPolynomialSpace: ScalableListPolynomialSpace<C, A> where A : Ring<C>, A : ScaleOperations<C>
get() = ScalableListPolynomialSpace(this)
/**
* Creates a [ScalableListPolynomialSpace]'s scope over a received scalable ring.
*/ // TODO: When context will be ready move [ListPolynomialSpace] and add [A] to context receivers of [block]
public inline fun <C, A, R> A.scalableListPolynomialSpace(block: ScalableListPolynomialSpace<C, A>.() -> R): R where A : Ring<C>, A : ScaleOperations<C> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ScalableListPolynomialSpace(this).block()
}
/**
* Creates a [ListRationalFunctionSpace] over a received ring.
*/
public inline val <C, A : Ring<C>> A.listRationalFunctionSpace: ListRationalFunctionSpace<C, A>
get() = ListRationalFunctionSpace(this)
/**
* Creates a [ListRationalFunctionSpace]'s scope over a received ring.
*/ // TODO: When context will be ready move [ListRationalFunctionSpace] and add [A] to context receivers of [block]
public inline fun <C, A : Ring<C>, R> A.listRationalFunctionSpace(block: ListRationalFunctionSpace<C, A>.() -> R): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return ListRationalFunctionSpace(this).block()
}
/**
* Evaluates value of [this] Double polynomial on provided Double argument.
*/
public fun ListPolynomial<Double>.substitute(arg: Double): Double =
coefficients.reduceIndexedOrNull { index, acc, c ->
acc + c * arg.pow(index)
} ?: .0
/**
* Evaluates value of [this] polynomial on provided 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 zero
var result: C = coefficients.last()
for (j in coefficients.size - 2 downTo 0) {
result = (arg * result) + coefficients[j]
}
return result
}
/**
* Substitutes provided polynomial [arg] into [this] polynomial.
*
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
*/ // TODO: To optimize boxing
public fun <C> ListPolynomial<C>.substitute(ring: Ring<C>, arg: ListPolynomial<C>) : ListPolynomial<C> =
ring.listPolynomialSpace {
if (coefficients.isEmpty()) return zero
var result: ListPolynomial<C> = coefficients.last().asPolynomial()
for (j in coefficients.size - 2 downTo 0) {
result = (arg * result) + coefficients[j]
}
return result
}
/**
* Substitutes provided rational function [arg] into [this] polynomial.
*
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
*/ // TODO: To optimize boxing
public fun <C> ListPolynomial<C>.substitute(ring: Ring<C>, arg: ListRationalFunction<C>) : ListRationalFunction<C> =
ring.listRationalFunctionSpace {
if (coefficients.isEmpty()) return zero
var result: ListRationalFunction<C> = coefficients.last().asRationalFunction()
for (j in coefficients.size - 2 downTo 0) {
result = (arg * result) + coefficients[j]
}
return result
}
/**
* Evaluates value of [this] Double rational function in provided Double argument.
*/
public fun ListRationalFunction<Double>.substitute(arg: Double): Double =
numerator.substitute(arg) / denominator.substitute(arg)
/**
* Evaluates value of [this] polynomial for provided 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)
}
/**
* Substitutes provided polynomial [arg] into [this] rational function.
*/ // TODO: To optimize boxing
public fun <C> ListRationalFunction<C>.substitute(ring: Ring<C>, arg: ListPolynomial<C>) : ListRationalFunction<C> =
ring.listRationalFunctionSpace {
numerator.substitute(ring, arg) / denominator.substitute(ring, arg)
}
/**
* Substitutes provided rational function [arg] into [this] rational function.
*/ // TODO: To optimize boxing
public fun <C> ListRationalFunction<C>.substitute(ring: Ring<C>, arg: ListRationalFunction<C>) : ListRationalFunction<C> =
ring.listRationalFunctionSpace {
numerator.substitute(ring, arg) / denominator.substitute(ring, arg)
}
/**
* Represent [this] polynomial as a regular context-less function.
*/
public fun <C, A : Ring<C>> ListPolynomial<C>.asFunctionOver(ring: A): (C) -> C = { substitute(ring, it) }
/**
* Represent [this] polynomial as a regular context-less function.
*/
public fun <C, A : Ring<C>> ListPolynomial<C>.asFunctionOfConstantOver(ring: A): (C) -> C = { substitute(ring, it) }
/**
* Represent [this] polynomial as a regular context-less function.
*/
public fun <C, A : Ring<C>> ListPolynomial<C>.asFunctionOfPolynomialOver(ring: A): (ListPolynomial<C>) -> ListPolynomial<C> = { substitute(ring, it) }
/**
* Represent [this] polynomial as a regular context-less function.
*/
public fun <C, A : Ring<C>> ListPolynomial<C>.asFunctionOfRationalFunctionOver(ring: A): (ListRationalFunction<C>) -> ListRationalFunction<C> = { substitute(ring, it) }
/**
* Represent [this] rational function as a regular context-less function.
*/
public fun <C, A : Field<C>> ListRationalFunction<C>.asFunctionOver(ring: A): (C) -> C = { substitute(ring, it) }
/**
* Represent [this] rational function as a regular context-less function.
*/
public fun <C, A : Field<C>> ListRationalFunction<C>.asFunctionOfConstantOver(ring: A): (C) -> C = { substitute(ring, it) }
/**
* Represent [this] rational function as a regular context-less function.
*/
public fun <C, A : Ring<C>> ListRationalFunction<C>.asFunctionOfPolynomialOver(ring: A): (ListPolynomial<C>) -> ListRationalFunction<C> = { substitute(ring, it) }
/**
* Represent [this] rational function as a regular context-less function.
*/
public fun <C, A : Ring<C>> ListRationalFunction<C>.asFunctionOfRationalFunctionOver(ring: A): (ListRationalFunction<C>) -> ListRationalFunction<C> = { substitute(ring, it) }
/**
* Returns algebraic derivative of received polynomial.
*/
@UnstableKMathAPI
public fun <C, A> ListPolynomial<C>.derivative(
ring: A,
): ListPolynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = ring {
ListPolynomial(
buildList(max(0, coefficients.size - 1)) {
for (deg in 1 .. coefficients.lastIndex) add(number(deg) * coefficients[deg])
}
)
}
/**
* Returns algebraic derivative of received polynomial of specified [order]. The [order] should be non-negative integer.
*/
@UnstableKMathAPI
public fun <C, A> ListPolynomial<C>.nthDerivative(
ring: A,
order: Int,
): ListPolynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = ring {
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(
ring: A,
): ListPolynomial<C> where A : Field<C>, A : NumericAlgebra<C> = ring {
ListPolynomial(
buildList(coefficients.size + 1) {
add(zero)
coefficients.mapIndexedTo(this) { index, t -> t / number(index + 1) }
}
)
}
/**
* Returns algebraic antiderivative of received polynomial of specified [order]. The [order] should be non-negative integer.
*/
@UnstableKMathAPI
public fun <C, A> ListPolynomial<C>.nthAntiderivative(
ring: A,
order: Int,
): ListPolynomial<C> where A : Field<C>, A : NumericAlgebra<C> = ring {
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) } }
}
)
}
/**
* Computes a definite integral of [this] polynomial in the specified [range].
*/
@UnstableKMathAPI
public fun <C : Comparable<C>> ListPolynomial<C>.integrate(
ring: Field<C>,
range: ClosedRange<C>,
): C = ring {
val antiderivative = antiderivative(ring)
antiderivative.substitute(ring, range.endInclusive) - antiderivative.substitute(ring, range.start)
}

View File

@ -0,0 +1,22 @@
/*
* 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
/**
* Marks declarations that give access to internal entities of polynomials delicate structure. Thus, it allows to
* optimize performance a bit by skipping standard steps, but such skips may cause critical errors if something is
* implemented badly. Make sure you fully read and understand documentation and don't break internal contracts.
*/
@RequiresOptIn(
message = "This declaration gives access to delicate internal structure of polynomials. " +
"It allows to optimize performance by skipping unnecessary arguments check. " +
"But at the same time makes it easy to make a mistake " +
"that will cause wrong computation result or even runtime error. " +
"Make sure you fully read and understand documentation.",
level = RequiresOptIn.Level.ERROR
)
public annotation class DelicatePolynomialAPI

View File

@ -6,13 +6,13 @@
package space.kscience.kmath.functions
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.functions.testUtils.t
import space.kscience.kmath.functions.testUtils.x
import space.kscience.kmath.functions.testUtils.y
import space.kscience.kmath.functions.testUtils.z
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.operations.invoke
import kotlin.test.Test
import kotlin.test.assertEquals

View File

@ -11,15 +11,15 @@ import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.functions.testUtils.IntModuloRing
import space.kscience.kmath.functions.testUtils.Rational
import space.kscience.kmath.functions.testUtils.RationalField
import space.kscience.kmath.functions.testUtils.o
import space.kscience.kmath.functions.testUtils.iota
import space.kscience.kmath.functions.testUtils.m
import kotlin.test.*
import space.kscience.kmath.functions.testUtils.o
import space.kscience.kmath.functions.testUtils.s
import space.kscience.kmath.functions.testUtils.t
import space.kscience.kmath.functions.testUtils.x
import space.kscience.kmath.functions.testUtils.y
import space.kscience.kmath.functions.testUtils.z
import space.kscience.kmath.functions.testUtils.t
import space.kscience.kmath.functions.testUtils.s
import space.kscience.kmath.functions.testUtils.iota
import kotlin.test.*
// TODO: Тесты на конвертацию.

View File

@ -6,16 +6,15 @@
package space.kscience.kmath.functions
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.functions.testUtils.Rational
import space.kscience.kmath.functions.testUtils.RationalField
import space.kscience.kmath.functions.testUtils.iota
import space.kscience.kmath.functions.testUtils.x
import space.kscience.kmath.functions.testUtils.y
import space.kscience.kmath.misc.UnstableKMathAPI
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertEquals
import space.kscience.kmath.functions.testUtils.x
import space.kscience.kmath.functions.testUtils.y
import space.kscience.kmath.functions.testUtils.iota
import space.kscience.kmath.functions.testUtils.assertEquals
class LabeledPolynomialUtilTest {
@Test

View File

@ -27,8 +27,8 @@ include(
":kmath-coroutines",
":kmath-functions",
":test-utils-functions",
":kmath-polynomialX",
":test-utils-polynomialX",
":kmath-polynomial",
":test-utils-polynomial",
":kmath-histograms",
":kmath-commons",
":kmath-viktor",

View File

@ -7,9 +7,7 @@ kotlin.sourceSets {
commonMain {
dependencies {
api(projects.kmathCore)
api(projects.kmathFunctions)
api(projects.testUtilsFunctions)
api(projects.kmathPolynomialX)
api(projects.kmathPolynomial)
api(kotlin("test"))
}
}