Feature: Polynomials and rational functions #469
@ -14,7 +14,7 @@ import kotlin.math.min
|
|||||||
*
|
*
|
||||||
* @param coefficients constant is the leftmost coefficient.
|
* @param coefficients constant is the leftmost coefficient.
|
||||||
*/
|
*/
|
||||||
public data class Polynomial<T>(public val coefficients: List<T>) : AbstractPolynomial<T> {
|
public data class Polynomial<C>(public val coefficients: List<C>) : AbstractPolynomial<C> {
|
||||||
override fun toString(): String = "Polynomial$coefficients"
|
override fun toString(): String = "Polynomial$coefficients"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ internal fun polynomialError(message: Any): Nothing = throw PolynomialError(mess
|
|||||||
* [reverse] parameter is true.
|
* [reverse] parameter is true.
|
||||||
*/
|
*/
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
public fun <T> Polynomial(coefficients: List<T>, reverse: Boolean = false): Polynomial<T> =
|
public fun <C> Polynomial(coefficients: List<C>, reverse: Boolean = false): Polynomial<C> =
|
||||||
Polynomial(with(coefficients) { if (reverse) reversed() else this })
|
Polynomial(with(coefficients) { if (reverse) reversed() else this })
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,10 +52,10 @@ public fun <T> Polynomial(coefficients: List<T>, reverse: Boolean = false): Poly
|
|||||||
* [reverse] parameter is true.
|
* [reverse] parameter is true.
|
||||||
*/
|
*/
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
public fun <T> Polynomial(vararg coefficients: T, reverse: Boolean = false): Polynomial<T> =
|
public fun <C> Polynomial(vararg coefficients: C, reverse: Boolean = false): Polynomial<C> =
|
||||||
Polynomial(with(coefficients) { if (reverse) reversed() else toList() })
|
Polynomial(with(coefficients) { if (reverse) reversed() else toList() })
|
||||||
|
|
||||||
public fun <T> T.asPolynomial() : Polynomial<T> = Polynomial(listOf(this))
|
public fun <C> C.asPolynomial() : Polynomial<C> = Polynomial(listOf(this))
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
@ -352,7 +352,7 @@ public open class PolynomialSpace<C, A : Ring<C>>(
|
|||||||
Polynomial(
|
Polynomial(
|
||||||
(0..(thisDegree + otherDegree))
|
(0..(thisDegree + otherDegree))
|
||||||
.map { d ->
|
.map { d ->
|
||||||
(max(0, d - otherDegree)..(min(thisDegree, d)))
|
(max(0, d - otherDegree)..min(thisDegree, d))
|
||||||
.map { coefficients[it] * other.coefficients[d - it] }
|
.map { coefficients[it] * other.coefficients[d - it] }
|
||||||
.reduce { acc, rational -> acc + rational }
|
.reduce { acc, rational -> acc + rational }
|
||||||
}
|
}
|
||||||
@ -370,16 +370,14 @@ public open class PolynomialSpace<C, A : Ring<C>>(
|
|||||||
*/
|
*/
|
||||||
public override fun Polynomial<C>.isOne(): Boolean =
|
public override fun Polynomial<C>.isOne(): Boolean =
|
||||||
with(coefficients) {
|
with(coefficients) {
|
||||||
isNotEmpty() &&
|
isNotEmpty() && withIndex().any { (index, c) -> if (index == 0) c.isOne() else c.isZero() }
|
||||||
asSequence().withIndex().any { (index, c) -> if (index == 0) c.isOne() else c.isZero() } // TODO: It's better to write new methods like `anyIndexed`. But what's better way to do it?
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Check if the instant is minus unit polynomial.
|
* Check if the instant is minus unit polynomial.
|
||||||
*/
|
*/
|
||||||
public override fun Polynomial<C>.isMinusOne(): Boolean =
|
public override fun Polynomial<C>.isMinusOne(): Boolean =
|
||||||
with(coefficients) {
|
with(coefficients) {
|
||||||
isNotEmpty() &&
|
isNotEmpty() && withIndex().any { (index, c) -> if (index == 0) c.isMinusOne() else c.isZero() }
|
||||||
asSequence().withIndex().any { (index, c) -> if (index == 0) c.isMinusOne() else c.isZero() } // TODO: It's better to write new methods like `anyIndexed`. But what's better way to do it?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +9,8 @@ import space.kscience.kmath.misc.UnstableKMathAPI
|
|||||||
import space.kscience.kmath.operations.*
|
import space.kscience.kmath.operations.*
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
import kotlin.jvm.JvmName
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
@ -77,15 +78,32 @@ public fun <C> Polynomial<C>.substitute(ring: Ring<C>, arg: C): C = ring {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: (Waiting for hero) Replace with optimisation: the [result] may be unboxed, and all operations may be performed
|
|
||||||
// as soon as possible on it
|
|
||||||
public fun <C> Polynomial<C>.substitute(ring: Ring<C>, arg: Polynomial<C>) : Polynomial<C> = ring.polynomial {
|
public fun <C> Polynomial<C>.substitute(ring: Ring<C>, arg: Polynomial<C>) : Polynomial<C> = ring.polynomial {
|
||||||
if (coefficients.isEmpty()) return zero
|
if (coefficients.isEmpty()) return zero
|
||||||
var result: Polynomial<C> = coefficients.last().asPolynomial()
|
|
||||||
for (j in coefficients.size - 2 downTo 0) {
|
val thisDegree = degree
|
||||||
result = (arg * result) + coefficients[j]
|
if (thisDegree == -1) return zero
|
||||||
|
val argDegree = arg.degree
|
||||||
|
if (argDegree == -1) return coefficients[0].asPolynomial()
|
||||||
|
val constantZero = constantZero
|
||||||
|
val resultCoefs: MutableList<C> = MutableList(thisDegree + argDegree + 1) { constantZero }
|
||||||
|
val resultCoefsUpdate: MutableList<C> = MutableList(thisDegree + argDegree + 1) { constantZero }
|
||||||
|
var resultDegree = 0
|
||||||
|
for (deg in thisDegree downTo 0) {
|
||||||
|
resultCoefsUpdate[0] = coefficients[deg]
|
||||||
|
for (updateDeg in 0 .. resultDegree + argDegree) {
|
||||||
|
var newC = resultCoefsUpdate[updateDeg]
|
||||||
|
for (deg1 in max(0, updateDeg - argDegree)..min(resultDegree, updateDeg))
|
||||||
|
newC += resultCoefs[deg1] * arg.coefficients[updateDeg - deg1]
|
||||||
|
resultCoefsUpdate[updateDeg] = newC
|
||||||
}
|
}
|
||||||
return result
|
resultDegree += argDegree
|
||||||
|
for (updateDeg in 0 .. resultDegree + argDegree) {
|
||||||
|
resultCoefs[updateDeg] = resultCoefsUpdate[updateDeg]
|
||||||
|
resultCoefsUpdate[updateDeg] = constantZero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Polynomial<C>(resultCoefs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,7 +127,7 @@ public fun <C, A : Ring<C>> Polynomial<C>.asPolynomialFunctionOver(ring: A): (Po
|
|||||||
public fun <C, A> Polynomial<C>.derivative(
|
public fun <C, A> Polynomial<C>.derivative(
|
||||||
algebra: A,
|
algebra: A,
|
||||||
): Polynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = algebra {
|
): Polynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = algebra {
|
||||||
Polynomial(coefficients.drop(1).mapIndexed { index, c -> number(index) * c })
|
Polynomial(coefficients.drop(1).mapIndexed { index, c -> number(index + 1) * c })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,8 +138,7 @@ public fun <C, A> Polynomial<C>.nthDerivative(
|
|||||||
algebra: A,
|
algebra: A,
|
||||||
order: UInt,
|
order: UInt,
|
||||||
): Polynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = algebra {
|
): Polynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = algebra {
|
||||||
TODO()
|
Polynomial(coefficients.drop(order.toInt()).mapIndexed { index, c -> (1..order.toInt()).fold(c) { acc, i -> acc * number(index + i) } })
|
||||||
Polynomial(coefficients.drop(order.toInt()).mapIndexed { index, c -> number(index) * c })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,7 +150,7 @@ public fun <C, A> Polynomial<C>.antiderivative(
|
|||||||
): Polynomial<C> where A : Field<C>, A : NumericAlgebra<C> = algebra {
|
): Polynomial<C> where A : Field<C>, A : NumericAlgebra<C> = algebra {
|
||||||
val integratedCoefficients = buildList(coefficients.size + 1) {
|
val integratedCoefficients = buildList(coefficients.size + 1) {
|
||||||
add(zero)
|
add(zero)
|
||||||
coefficients.forEachIndexed{ index, t -> add(t / number(index + 1)) }
|
coefficients.mapIndexedTo(this) { index, t -> t / number(index + 1) }
|
||||||
}
|
}
|
||||||
Polynomial(integratedCoefficients)
|
Polynomial(integratedCoefficients)
|
||||||
}
|
}
|
||||||
@ -146,12 +163,11 @@ public fun <C, A> Polynomial<C>.nthAntiderivative(
|
|||||||
algebra: A,
|
algebra: A,
|
||||||
order: UInt,
|
order: UInt,
|
||||||
): Polynomial<C> where A : Field<C>, A : NumericAlgebra<C> = algebra {
|
): Polynomial<C> where A : Field<C>, A : NumericAlgebra<C> = algebra {
|
||||||
TODO()
|
val newCoefficients = buildList(coefficients.size + order.toInt()) {
|
||||||
val integratedCoefficients = buildList(coefficients.size + 1) {
|
repeat(order.toInt()) { add(zero) }
|
||||||
add(zero)
|
coefficients.mapIndexedTo(this) { index, c -> (1..order.toInt()).fold(c) { acc, i -> acc / number(index + i) } }
|
||||||
coefficients.forEachIndexed{ index, t -> add(t / number(index + 1)) }
|
|
||||||
}
|
}
|
||||||
Polynomial(integratedCoefficients)
|
return Polynomial(newCoefficients)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user