From 25ec59b9851e71e3a652e043002a50902e8479b4 Mon Sep 17 00:00:00 2001 From: Gleb Minaev <43728100+lounres@users.noreply.github.com> Date: Sun, 20 Mar 2022 23:22:39 +0300 Subject: [PATCH] Finished with tests for Polynomial. --- .../kscience/kmath/functions/Polynomial.kt | 17 +- .../kmath/functions/polynomialUtil.kt | 50 ++-- .../kmath/functions/PolynomialTest.kt | 154 ++++++++++++ .../kmath/functions/PolynomialUtilTest.kt | 223 +++++++++++++++++- 4 files changed, 407 insertions(+), 37 deletions(-) diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt index 7e0f6f62d..d6c959fd1 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt @@ -386,7 +386,7 @@ public open class PolynomialSpace>( /** * Instance of unit constant (unit of the underlying ring). */ - override val one: Polynomial = Polynomial(listOf(constantZero)) + override val one: Polynomial = Polynomial(listOf(constantOne)) /** * Checks equality of the polynomials. @@ -394,11 +394,8 @@ public open class PolynomialSpace>( public override infix fun Polynomial.equalsTo(other: Polynomial): Boolean = when { this === other -> true - else -> { - if (this.degree == other.degree) - (0..degree).all { coefficients[it] == other.coefficients[it] } - else false - } + this.degree == other.degree -> (0..degree).all { coefficients[it] == other.coefficients[it] } + else -> false } /** @@ -415,8 +412,8 @@ public open class PolynomialSpace>( with(coefficients) { when { isEmpty() -> constantZero - withIndex().any { (index, c) -> index == 0 || c.isZero() } -> null - else -> first() + withIndex().all { (index, c) -> index == 0 || c.isZero() } -> first() + else -> null } } @@ -489,8 +486,6 @@ public open class PolynomialSpace>( public class ScalablePolynomialSpace( ring: A, ) : PolynomialSpace(ring), ScaleOperations> where A : Ring, A : ScaleOperations { - override fun scale(a: Polynomial, value: Double): Polynomial = - ring { Polynomial(List(a.coefficients.size) { index -> a.coefficients[index] * value }) } - + ring { Polynomial(a.coefficients.map { scale(it, value) }) } } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/polynomialUtil.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/polynomialUtil.kt index 84c8d2e88..afaf27a52 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/polynomialUtil.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/polynomialUtil.kt @@ -127,9 +127,10 @@ public fun Polynomial.substitute(ring: Ring, arg: Polynomial) : Pol if (argDegree == -1) return coefficients[0].asPolynomial() val constantZero = zero val resultCoefs: MutableList = MutableList(thisDegree * argDegree + 1) { constantZero } + resultCoefs[0] = coefficients[thisDegree] val resultCoefsUpdate: MutableList = MutableList(thisDegree * argDegree + 1) { constantZero } var resultDegree = 0 - for (deg in thisDegree downTo 0) { + for (deg in thisDegree - 1 downTo 0) { resultCoefsUpdate[0] = coefficients[deg] multiplyAddingToUpdater( ring = ring, @@ -142,6 +143,8 @@ public fun Polynomial.substitute(ring: Ring, arg: Polynomial) : Pol ) resultDegree += argDegree } + + with(resultCoefs) { while (isNotEmpty() && elementAt(lastIndex) == constantZero) removeAt(lastIndex) } return Polynomial(resultCoefs) } @@ -162,7 +165,12 @@ public fun > Polynomial.asPolynomialFunctionOver(ring: A): (Po public fun Polynomial.derivative( algebra: A, ): Polynomial where A : Ring, A : NumericAlgebra = algebra { - Polynomial(coefficients.drop(1).mapIndexed { index, c -> number(index + 1) * c }) + Polynomial( + buildList(max(0, coefficients.size - 1)) { + for (deg in 1 .. coefficients.lastIndex) add(number(deg) * coefficients[deg]) + while (isNotEmpty() && elementAt(lastIndex) == algebra.zero) removeAt(lastIndex) + } + ) } /** @@ -171,9 +179,16 @@ public fun Polynomial.derivative( @UnstableKMathAPI public fun Polynomial.nthDerivative( algebra: A, - order: UInt, + order: Int, ): Polynomial where A : Ring, A : NumericAlgebra = algebra { - Polynomial(coefficients.drop(order.toInt()).mapIndexed { index, c -> (index + 1..index + order.toInt()).fold(c) { acc, i -> acc * number(i) } }) + require(order >= 0) { "Order of derivative must be non-negative" } + Polynomial( + 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) }) + while (isNotEmpty() && elementAt(lastIndex) == algebra.zero) removeAt(lastIndex) + } + ) } /** @@ -183,11 +198,13 @@ public fun Polynomial.nthDerivative( public fun Polynomial.antiderivative( algebra: A, ): Polynomial where A : Field, A : NumericAlgebra = algebra { - val integratedCoefficients = buildList(coefficients.size + 1) { - add(zero) - coefficients.mapIndexedTo(this) { index, t -> t / number(index + 1) } - } - Polynomial(integratedCoefficients) + Polynomial( + buildList(coefficients.size + 1) { + add(zero) + coefficients.mapIndexedTo(this) { index, t -> t / number(index + 1) } + while (isNotEmpty() && elementAt(lastIndex) == algebra.zero) removeAt(lastIndex) + } + ) } /** @@ -196,13 +213,16 @@ public fun Polynomial.antiderivative( @UnstableKMathAPI public fun Polynomial.nthAntiderivative( algebra: A, - order: UInt, + order: Int, ): Polynomial where A : Field, A : NumericAlgebra = algebra { - val newCoefficients = buildList(coefficients.size + order.toInt()) { - repeat(order.toInt()) { add(zero) } - coefficients.mapIndexedTo(this) { index, c -> (1..order.toInt()).fold(c) { acc, i -> acc / number(index + i) } } - } - return Polynomial(newCoefficients) + require(order >= 0) { "Order of antiderivative must be non-negative" } + Polynomial( + buildList(coefficients.size + order) { + repeat(order) { add(zero) } + coefficients.mapIndexedTo(this) { index, c -> (1..order).fold(c) { acc, i -> acc / number(index + i) } } + while (isNotEmpty() && elementAt(lastIndex) == algebra.zero) removeAt(lastIndex) + } + ) } /** diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/PolynomialTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/PolynomialTest.kt index 85b309467..141bdd436 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/PolynomialTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/PolynomialTest.kt @@ -534,4 +534,158 @@ class PolynomialTest { assertFalse("test 12") { Polynomial(Rational(-1), Rational(5, -5), Rational(0)).isMinusOne() } } } + @Test + fun test_Polynomial_equalsTo() { + RationalField.polynomial { + assertTrue("test 1") { + Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) equalsTo + Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) + } + assertTrue("test 2") { + Polynomial(Rational(5, 9), Rational(0), Rational(-8, 7)) equalsTo + Polynomial(Rational(5, 9), Rational(0), Rational(-8, 7)) + } + assertTrue("test 3") { + Polynomial(Rational(0), Rational(0), Rational(-8, 7), Rational(0), Rational(0)) equalsTo + Polynomial(Rational(0), Rational(0), Rational(-8, 7)) + } + assertFalse("test 4") { + Polynomial(Rational(5, 9), Rational(5, 7), Rational(-8, 7)) equalsTo + Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) + } + assertFalse("test 5") { + Polynomial(Rational(8, 3), Rational(0), Rational(-8, 7)) equalsTo + Polynomial(Rational(5, 9), Rational(0), Rational(-8, 7)) + } + assertFalse("test 6") { + Polynomial(Rational(0), Rational(0), Rational(-8, 7), Rational(0), Rational(0)) equalsTo + Polynomial(Rational(0), Rational(0), Rational(8, 7)) + } + } + } + @Test + fun test_Polynomial_degree() { + RationalField.polynomial { + assertEquals( + 2, + Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)).degree, + "test 1" + ) + assertEquals( + -1, + Polynomial().degree, + "test 2" + ) + assertEquals( + -1, + Polynomial(Rational(0)).degree, + "test 3" + ) + assertEquals( + -1, + Polynomial(Rational(0), Rational(0)).degree, + "test 4" + ) + assertEquals( + -1, + Polynomial(Rational(0), Rational(0), Rational(0)).degree, + "test 5" + ) + assertEquals( + 0, + Polynomial(Rational(5, 9)).degree, + "test 6" + ) + assertEquals( + 0, + Polynomial(Rational(5, 9), Rational(0)).degree, + "test 7" + ) + assertEquals( + 0, + Polynomial(Rational(5, 9), Rational(0), Rational(0)).degree, + "test 8" + ) + assertEquals( + 2, + Polynomial(Rational(0), Rational(0), Rational(-8, 7)).degree, + "test 9" + ) + assertEquals( + 2, + Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7), Rational(0), Rational(0)).degree, + "test 10" + ) + assertEquals( + 2, + Polynomial(Rational(0), Rational(0), Rational(-8, 7), Rational(0), Rational(0)).degree, + "test 11" + ) + } + } + @Test + fun test_Polynomial_asConstantOrNull() { + RationalField.polynomial { + assertEquals( + Rational(0), + Polynomial().asConstantOrNull(), + "test 1" + ) + assertEquals( + Rational(0), + Polynomial(Rational(0)).asConstantOrNull(), + "test 2" + ) + assertEquals( + Rational(0), + Polynomial(Rational(0), Rational(0)).asConstantOrNull(), + "test 3" + ) + assertEquals( + Rational(0), + Polynomial(Rational(0), Rational(0), Rational(0)).asConstantOrNull(), + "test 4" + ) + assertEquals( + Rational(-7, 9), + Polynomial(Rational(-7, 9)).asConstantOrNull(), + "test 5" + ) + assertEquals( + Rational(-7, 9), + Polynomial(Rational(-7, 9), Rational(0)).asConstantOrNull(), + "test 6" + ) + assertEquals( + Rational(-7, 9), + Polynomial(Rational(-7, 9), Rational(0), Rational(0)).asConstantOrNull(), + "test 7" + ) + assertEquals( + null, + Polynomial(Rational(0), Rational(-7, 9)).asConstantOrNull(), + "test 8" + ) + assertEquals( + null, + Polynomial(Rational(0), Rational(-7, 9), Rational(0)).asConstantOrNull(), + "test 9" + ) + assertEquals( + null, + Polynomial(Rational(0), Rational(0), Rational(-7, 9)).asConstantOrNull(), + "test 10" + ) + assertEquals( + null, + Polynomial(Rational(4, 15), Rational(0), Rational(-7, 9)).asConstantOrNull(), + "test 11" + ) + assertEquals( + null, + Polynomial(Rational(4, 15), Rational(0), Rational(-7, 9), Rational(0)).asConstantOrNull(), + "test 12" + ) + } + } } \ No newline at end of file diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/PolynomialUtilTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/PolynomialUtilTest.kt index b9dd74800..fbae61ed1 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/PolynomialUtilTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/PolynomialUtilTest.kt @@ -5,23 +5,224 @@ package space.kscience.kmath.functions -import space.kscience.kmath.operations.algebra +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 PolynomialUtilTest { @Test - fun simple_polynomial_test() { - val polynomial : Polynomial - Double.algebra.scalablePolynomial { - val x = Polynomial(listOf(0.0, 1.0)) - polynomial = x * x - 2 * x + 1 - } - assertEquals(0.0, polynomial.substitute(1.0), 0.001) - } - @Test - fun testIntegration() { + fun test_substitute_Double() { val polynomial = Polynomial(1.0, -2.0, 1.0) assertEquals(0.0, polynomial.substitute(1.0), 0.001) } + @Test + fun test_substitute_Constant() { + assertEquals( + Rational(0), + Polynomial(Rational(1), Rational(-2), Rational(1)).substitute(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)), + "test 2" + ) + assertEquals( + Rational(2983, 5250), + Polynomial(Rational(0), Rational(8, 3), Rational(4, 7), Rational(3, 2)) + .substitute(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)), + "test 4" + ) + assertEquals( + Rational(3511, 3000), + Polynomial(Rational(5,8), Rational(8, 3), Rational(0), Rational(3, 2)) + .substitute(RationalField, Rational(1, 5)), + "test 5" + ) + } + @Test + fun test_substitute_Polynomial() { + assertEquals( + Polynomial(), + Polynomial(Rational(1), Rational(-2), Rational(1)).substitute(RationalField, Polynomial(Rational(1))), + "test 1" + ) + assertEquals( + Polynomial(Rational(709, 378), Rational(155, 252), Rational(19, 525), Rational(2, 875)), + Polynomial(Rational(1, 7), Rational(9, 4), Rational(1, 3), Rational(2, 7)) + .substitute(RationalField, Polynomial(Rational(6, 9), Rational(1, 5))), + "test 2" + ) + assertEquals( + Polynomial(Rational(655, 378), Rational(155, 252), Rational(19, 525), Rational(2, 875)), + Polynomial(Rational(0), Rational(9, 4), Rational(1, 3), Rational(2, 7)) + .substitute(RationalField, Polynomial(Rational(6, 9), Rational(1, 5))), + "test 3" + ) + assertEquals( + Polynomial(Rational(677, 378), Rational(97, 180), Rational(1, 75)), + Polynomial(Rational(1, 7), Rational(9, 4), Rational(1, 3), Rational(0)) + .substitute(RationalField, Polynomial(Rational(6, 9), Rational(1, 5))), + "test 4" + ) + assertEquals( + Polynomial(Rational(653, 378), Rational(221, 420), Rational(4, 175), Rational(2, 875)), + Polynomial(Rational(1, 7), Rational(9, 4), Rational(0), Rational(2, 7)) + .substitute(RationalField, Polynomial(Rational(6, 9), Rational(1, 5))), + "test 5" + ) + assertEquals( + Polynomial(Rational(89, 54)), + Polynomial(Rational(0), Rational(9, 4), Rational(1, 3), Rational(0)) + .substitute(RationalField, Polynomial(Rational(6, 9), Rational(0))), + "test 6" + ) + } + @Test + fun test_derivative() { + assertEquals( + Polynomial(Rational(-2), Rational(2)), + Polynomial(Rational(1), Rational(-2), Rational(1)).derivative(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), + "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), + "test 3" + ) + assertEquals( + Polynomial(Rational(-8, 3), Rational(8, 9), Rational(15, 7)), + Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).derivative(RationalField), + "test 4" + ) + } + @Test + fun test_nthDerivative() { + assertEquals( + Polynomial(Rational(-2), Rational(2)), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 1), + "test 1" + ) + assertFailsWith("test2") { + Polynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, -1) + } + assertEquals( + Polynomial(Rational(1), Rational(-2), Rational(1)), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 0), + "test 3" + ) + assertEquals( + Polynomial(Rational(2)), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 2), + "test 4" + ) + assertEquals( + Polynomial(), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 3), + "test 5" + ) + assertEquals( + Polynomial(), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 4), + "test 6" + ) + assertEquals( + Polynomial(Rational(8, 9), Rational(30, 7), Rational(-20, 3)), + Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthDerivative(RationalField, 2), + "test 7" + ) + assertEquals( + Polynomial(Rational(8, 9), Rational(30, 7), Rational(-20, 3)), + Polynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthDerivative(RationalField, 2), + "test 8" + ) + assertEquals( + Polynomial(Rational(8, 9), Rational(30, 7)), + Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).nthDerivative(RationalField, 2), + "test 9" + ) + } + @Test + fun test_antiderivative() { + assertEquals( + Polynomial(Rational(0), Rational(1), Rational(-1), Rational(1, 3)), + Polynomial(Rational(1), Rational(-2), Rational(1)).antiderivative(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), + "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), + "test 3" + ) + assertEquals( + Polynomial(Rational(0), Rational(1, 5), Rational(-4, 3), Rational(4, 27), Rational(5, 28)), + Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).antiderivative(RationalField), + "test 4" + ) + } + @Test + fun test_nthAntiderivative() { + assertEquals( + Polynomial(Rational(0), Rational(1), Rational(-1), Rational(1, 3)), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 1), + "test 1" + ) + assertFailsWith("test2") { + Polynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, -1) + } + assertEquals( + Polynomial(Rational(1), Rational(-2), Rational(1)), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 0), + "test 3" + ) + assertEquals( + Polynomial(Rational(0), Rational(0), Rational(1, 2), Rational(-1, 3), Rational(1, 12)), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 2), + "test 4" + ) + assertEquals( + Polynomial(Rational(0), Rational(0), Rational(0), Rational(1, 6), Rational(-1, 12), Rational(1, 60)), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 3), + "test 5" + ) + assertEquals( + Polynomial(Rational(0), Rational(0), Rational(0), Rational(0), Rational(1, 24), Rational(-1, 60), Rational(1, 360)), + Polynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 4), + "test 6" + ) + assertEquals( + Polynomial(Rational(0), Rational(0), Rational(1, 10), Rational(-4, 9), Rational(1, 27), Rational(1, 28), Rational(-1, 54)), + Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthAntiderivative(RationalField, 2), + "test 7" + ) + assertEquals( + Polynomial(Rational(0), Rational(0), Rational(0), Rational(0), Rational(1, 27), Rational(1, 28), Rational(-1, 54)), + Polynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthAntiderivative(RationalField, 2), + "test 8" + ) + assertEquals( + Polynomial(Rational(0), Rational(0), Rational(1, 10), Rational(-4, 9), Rational(1, 27), Rational(1, 28)), + Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).nthAntiderivative(RationalField, 2), + "test 9" + ) + } } \ No newline at end of file