Feature: Polynomials and rational functions #469
@ -117,16 +117,16 @@ public fun <T : Comparable<T>> PiecewisePolynomial(
|
||||
* 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)
|
||||
public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.value(ring: C, arg: T): T? =
|
||||
findPiece(arg)?.value(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) }
|
||||
public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.asFunction(ring: C): (T) -> T? = { value(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 }
|
||||
{ value(ring, it) ?: defaultValue }
|
||||
|
@ -59,12 +59,12 @@ public value class Polynomial<C>(
|
||||
* @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 PolynomialSpace<C, A : Ring<C>>(
|
||||
public open class PolynomialSpace<C, A>(
|
||||
/**
|
||||
* Underlying ring of constants. Its operations on constants are inherited by local operations on constants.
|
||||
*/
|
||||
public val ring: A,
|
||||
) : Ring<Polynomial<C>> {
|
||||
) : Ring<Polynomial<C>>, ScaleOperations<Polynomial<C>> where A : Ring<C>, A : ScaleOperations<C> {
|
||||
|
||||
/**
|
||||
* Instance of zero constant (zero of the underlying ring).
|
||||
@ -267,13 +267,15 @@ public open class PolynomialSpace<C, A : Ring<C>>(
|
||||
|
||||
override fun add(left: Polynomial<C>, right: Polynomial<C>): Polynomial<C> = left + right
|
||||
override fun multiply(left: Polynomial<C>, right: Polynomial<C>): Polynomial<C> = left * right
|
||||
override fun scale(a: Polynomial<C>, value: Double): Polynomial<C> =
|
||||
ring { Polynomial(a.coefficients.map { scale(it, value) }) }
|
||||
|
||||
// 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 Polynomial<C>.substitute(argument: C): C = substitute(ring, argument)
|
||||
public inline fun Polynomial<C>.substitute(argument: C): C = value(ring, argument)
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
@ -283,19 +285,5 @@ public open class PolynomialSpace<C, A : Ring<C>>(
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided [argument].
|
||||
*/
|
||||
public inline operator fun Polynomial<C>.invoke(argument: C): 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 ScalablePolynomialSpace<C, A>(
|
||||
ring: A,
|
||||
) : PolynomialSpace<C, A>(ring), ScaleOperations<Polynomial<C>> where A : Ring<C>, A : ScaleOperations<C> {
|
||||
override fun scale(a: Polynomial<C>, value: Double): Polynomial<C> =
|
||||
ring { Polynomial(a.coefficients.map { scale(it, value) }) }
|
||||
public inline operator fun Polynomial<C>.invoke(argument: C): C = value(ring, argument)
|
||||
}
|
||||
|
@ -16,36 +16,22 @@ import kotlin.math.pow
|
||||
/**
|
||||
* Creates a [PolynomialSpace] over a received ring.
|
||||
*/
|
||||
public inline val <C, A : Ring<C>> A.polynomialSpace: PolynomialSpace<C, A>
|
||||
public inline val <C, A> A.polynomialSpace: PolynomialSpace<C, A> where A : Ring<C>, A : ScaleOperations<C>
|
||||
get() = PolynomialSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [PolynomialSpace]'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.polynomialSpace(block: PolynomialSpace<C, A>.() -> R): R {
|
||||
public inline fun <C, A, R> A.polynomialSpace(block: PolynomialSpace<C, A>.() -> R): R where A : Ring<C>, A : ScaleOperations<C> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return PolynomialSpace(this).block()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [ScalablePolynomialSpace] over a received scalable ring.
|
||||
*/
|
||||
public inline val <C, A> A.scalablePolynomialSpace: ScalablePolynomialSpace<C, A> where A : Ring<C>, A : ScaleOperations<C>
|
||||
get() = ScalablePolynomialSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [ScalablePolynomialSpace]'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.scalablePolynomialSpace(block: ScalablePolynomialSpace<C, A>.() -> R): R where A : Ring<C>, A : ScaleOperations<C> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return ScalablePolynomialSpace(this).block()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] Double polynomial on provided Double argument.
|
||||
*/
|
||||
public fun Polynomial<Double>.substitute(arg: Double): Double =
|
||||
public fun Polynomial<Double>.value(arg: Double): Double =
|
||||
coefficients.reduceIndexedOrNull { index, acc, c ->
|
||||
acc + c * arg.pow(index)
|
||||
} ?: .0
|
||||
@ -55,7 +41,7 @@ public fun Polynomial<Double>.substitute(arg: Double): Double =
|
||||
*
|
||||
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
|
||||
*/
|
||||
public fun <C> Polynomial<C>.substitute(ring: Ring<C>, arg: C): C = ring {
|
||||
public fun <C> Polynomial<C>.value(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) {
|
||||
@ -67,13 +53,13 @@ public fun <C> Polynomial<C>.substitute(ring: Ring<C>, arg: C): C = ring {
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> Polynomial<C>.asFunctionOver(ring: A): (C) -> C = { substitute(ring, it) }
|
||||
public fun <C, A : Ring<C>> Polynomial<C>.asFunctionOver(ring: A): (C) -> C = { value(ring, it) }
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A> Polynomial<C>.derivative(
|
||||
public fun <C, A> Polynomial<C>.differentiate(
|
||||
ring: A,
|
||||
): Polynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = ring {
|
||||
Polynomial(
|
||||
@ -87,7 +73,7 @@ public fun <C, A> Polynomial<C>.derivative(
|
||||
* Returns algebraic antiderivative of received polynomial.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A> Polynomial<C>.antiderivative(
|
||||
public fun <C, A> Polynomial<C>.integrate(
|
||||
ring: A,
|
||||
): Polynomial<C> where A : Field<C>, A : NumericAlgebra<C> = ring {
|
||||
Polynomial(
|
||||
@ -106,6 +92,6 @@ public fun <C : Comparable<C>> Polynomial<C>.integrate(
|
||||
ring: Field<C>,
|
||||
range: ClosedRange<C>,
|
||||
): C {
|
||||
val antiderivative = antiderivative(ring)
|
||||
return ring { antiderivative.substitute(ring, range.endInclusive) - antiderivative.substitute(ring, range.start) }
|
||||
val antiderivative = integrate(ring)
|
||||
return ring { antiderivative.value(ring, range.endInclusive) - antiderivative.value(ring, range.start) }
|
||||
}
|
@ -7,7 +7,6 @@ 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
|
||||
@ -24,7 +23,7 @@ import space.kscience.kmath.structures.MutableBufferFactory
|
||||
@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) })
|
||||
PiecewisePolynomial(pieces.map { it.first to it.second.integrate(algebra) })
|
||||
|
||||
/**
|
||||
* Compute definite integral of given [PiecewisePolynomial] piece by piece in a given [range]
|
||||
|
@ -10,7 +10,7 @@ package space.kscience.kmath.interpolation
|
||||
import space.kscience.kmath.data.XYColumnarData
|
||||
import space.kscience.kmath.functions.PiecewisePolynomial
|
||||
import space.kscience.kmath.functions.asFunction
|
||||
import space.kscience.kmath.functions.substitute
|
||||
import space.kscience.kmath.functions.value
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
@ -34,7 +34,7 @@ public interface PolynomialInterpolator<T : Comparable<T>> : Interpolator<T, T,
|
||||
public fun interpolatePolynomials(points: XYColumnarData<T, T, T>): PiecewisePolynomial<T>
|
||||
|
||||
override fun interpolate(points: XYColumnarData<T, T, T>): (T) -> T = { x ->
|
||||
interpolatePolynomials(points).substitute(algebra, x) ?: getDefaultValue()
|
||||
interpolatePolynomials(points).value(algebra, x) ?: getDefaultValue()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,119 +15,119 @@ import kotlin.test.assertEquals
|
||||
@OptIn(UnstableKMathAPI::class)
|
||||
class PolynomialUtilTest {
|
||||
@Test
|
||||
fun test_Polynomial_substitute_Double() {
|
||||
fun test_Polynomial_value_Double() {
|
||||
assertEquals(
|
||||
0.0,
|
||||
Polynomial(1.0, -2.0, 1.0).substitute(1.0),
|
||||
Polynomial(1.0, -2.0, 1.0).value(1.0),
|
||||
0.001,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
0.0,
|
||||
Polynomial(1.0, -2.0, 1.0).substitute(1.0),
|
||||
Polynomial(1.0, -2.0, 1.0).value(1.0),
|
||||
0.001,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
1.1931904761904761,
|
||||
Polynomial(0.625, 2.6666666666666665, 0.5714285714285714, 1.5).substitute(0.2),
|
||||
Polynomial(0.625, 2.6666666666666665, 0.5714285714285714, 1.5).value(0.2),
|
||||
0.001,
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
0.5681904761904762,
|
||||
Polynomial(0.0, 2.6666666666666665, 0.5714285714285714, 1.5).substitute(0.2),
|
||||
Polynomial(0.0, 2.6666666666666665, 0.5714285714285714, 1.5).value(0.2),
|
||||
0.001,
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
1.1811904761904761,
|
||||
Polynomial(0.625, 2.6666666666666665, 0.5714285714285714, 0.0).substitute(0.2),
|
||||
Polynomial(0.625, 2.6666666666666665, 0.5714285714285714, 0.0).value(0.2),
|
||||
0.001,
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
1.1703333333333332,
|
||||
Polynomial(0.625, 2.6666666666666665, 0.0, 1.5).substitute(0.2),
|
||||
Polynomial(0.625, 2.6666666666666665, 0.0, 1.5).value(0.2),
|
||||
0.001,
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_substitute_Constant() {
|
||||
fun test_Polynomial_value_Constant() {
|
||||
assertEquals(
|
||||
Rational(0),
|
||||
Polynomial(Rational(1), Rational(-2), Rational(1)).substitute(RationalField, Rational(1)),
|
||||
Polynomial(Rational(1), Rational(-2), Rational(1)).value(RationalField, Rational(1)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(25057, 21000),
|
||||
Polynomial(Rational(5, 8), Rational(8, 3), Rational(4, 7), Rational(3, 2))
|
||||
.substitute(RationalField, Rational(1, 5)),
|
||||
.value(RationalField, Rational(1, 5)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(2983, 5250),
|
||||
Polynomial(Rational(0), Rational(8, 3), Rational(4, 7), Rational(3, 2))
|
||||
.substitute(RationalField, Rational(1, 5)),
|
||||
.value(RationalField, Rational(1, 5)),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(4961, 4200),
|
||||
Polynomial(Rational(5, 8), Rational(8, 3), Rational(4, 7), Rational(0))
|
||||
.substitute(RationalField, Rational(1, 5)),
|
||||
.value(RationalField, Rational(1, 5)),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(3511, 3000),
|
||||
Polynomial(Rational(5, 8), Rational(8, 3), Rational(0), Rational(3, 2))
|
||||
.substitute(RationalField, Rational(1, 5)),
|
||||
.value(RationalField, Rational(1, 5)),
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_derivative() {
|
||||
fun test_Polynomial_differentiate() {
|
||||
assertEquals(
|
||||
Polynomial(Rational(-2), Rational(2)),
|
||||
Polynomial(Rational(1), Rational(-2), Rational(1)).derivative(RationalField),
|
||||
Polynomial(Rational(1), Rational(-2), Rational(1)).differentiate(RationalField),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-8, 3), Rational(8, 9), Rational(15, 7), Rational(-20, 9)),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).derivative(RationalField),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).differentiate(RationalField),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(8, 9), Rational(15, 7), Rational(-20, 9)),
|
||||
Polynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).derivative(RationalField),
|
||||
Polynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).differentiate(RationalField),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-8, 3), Rational(8, 9), Rational(15, 7), Rational(0)),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).derivative(RationalField),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).differentiate(RationalField),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_antiderivative() {
|
||||
fun test_Polynomial_integrate() {
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(1), Rational(-1), Rational(1, 3)),
|
||||
Polynomial(Rational(1), Rational(-2), Rational(1)).antiderivative(RationalField),
|
||||
Polynomial(Rational(1), Rational(-2), Rational(1)).integrate(RationalField),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(1, 5), Rational(-4, 3), Rational(4, 27), Rational(5, 28), Rational(-1, 9)),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).antiderivative(RationalField),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).integrate(RationalField),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(0), Rational(0), Rational(4, 27), Rational(5, 28), Rational(-1, 9)),
|
||||
Polynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).antiderivative(RationalField),
|
||||
Polynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).integrate(RationalField),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(1, 5), Rational(-4, 3), Rational(4, 27), Rational(5, 28), Rational(0)),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).antiderivative(RationalField),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).integrate(RationalField),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user