Feature: Polynomials and rational functions #469

Merged
lounres merged 132 commits from feature/polynomials into dev 2022-07-28 18:04:06 +03:00
2 changed files with 210 additions and 27 deletions
Showing only changes of commit ed2f14b68e - Show all commits

View File

@ -50,24 +50,36 @@ public inline fun <C, A, R> A.scalablePolynomial(block: ScalablePolynomialSpace<
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
internal inline fun <C> iadd( internal inline fun <C> copyTo(
ring: Ring<C>, origin: List<C>,
augend: MutableList<C>, originDegree: Int,
addend: List<C>, target: MutableList<C>,
degree: Int ) {
) = ring { for (deg in 0 .. originDegree) target[deg] = origin[deg]
for (deg in 0 .. degree) augend[deg] += addend[deg]
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
internal inline fun <C> addTo( internal inline fun <C> multiplyAddingToUpdater(
ring: Ring<C>, ring: Ring<C>,
augend: List<C>, multiplicand: MutableList<C>,
addend: List<C>, multiplicandDegree: Int,
degree: Int, multiplier: List<C>,
target: MutableList<C> multiplierDegree: Int,
) = ring { updater: MutableList<C>,
for (deg in 0 .. degree) target[deg] = augend[deg] + addend[deg] zero: C,
) {
multiplyAddingTo(
ring = ring,
multiplicand = multiplicand,
multiplicandDegree = multiplicandDegree,
multiplier = multiplier,
multiplierDegree = multiplierDegree,
target = updater
)
for (updateDeg in 0 .. multiplicandDegree + multiplierDegree) {
multiplicand[updateDeg] = updater[updateDeg]
updater[updateDeg] = zero
}
} }
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
@ -107,32 +119,29 @@ public fun <C> Polynomial<C>.substitute(ring: Ring<C>, arg: C): C = ring {
return result return result
} }
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 {
if (coefficients.isEmpty()) return zero if (coefficients.isEmpty()) return Polynomial(emptyList())
val thisDegree = degree val thisDegree = coefficients.indexOfLast { it != zero }
if (thisDegree == -1) return zero if (thisDegree == -1) return Polynomial(emptyList())
val argDegree = arg.degree val argDegree = arg.coefficients.indexOfLast { it != zero }
if (argDegree == -1) return coefficients[0].asPolynomial() if (argDegree == -1) return coefficients[0].asPolynomial()
val constantZero = constantZero val constantZero = zero
val resultCoefs: MutableList<C> = MutableList(thisDegree * argDegree + 1) { constantZero } val resultCoefs: MutableList<C> = MutableList(thisDegree * argDegree + 1) { constantZero }
val resultCoefsUpdate: MutableList<C> = MutableList(thisDegree * argDegree + 1) { constantZero } val resultCoefsUpdate: MutableList<C> = MutableList(thisDegree * argDegree + 1) { constantZero }
var resultDegree = 0 var resultDegree = 0
for (deg in thisDegree downTo 0) { for (deg in thisDegree downTo 0) {
resultCoefsUpdate[0] = coefficients[deg] resultCoefsUpdate[0] = coefficients[deg]
multiplyAddingTo( multiplyAddingToUpdater(
ring=ring, ring = ring,
multiplicand = resultCoefs, multiplicand = resultCoefs,
multiplicandDegree = resultDegree, multiplicandDegree = resultDegree,
multiplier = arg.coefficients, multiplier = arg.coefficients,
multiplierDegree = argDegree, multiplierDegree = argDegree,
target = resultCoefsUpdate updater = resultCoefsUpdate,
zero = constantZero
) )
resultDegree += argDegree resultDegree += argDegree
for (updateDeg in 0 .. resultDegree) {
resultCoefs[updateDeg] = resultCoefsUpdate[updateDeg]
resultCoefsUpdate[updateDeg] = constantZero
}
} }
return Polynomial<C>(resultCoefs) return Polynomial<C>(resultCoefs)
} }

View File

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