Sifted ListPolynomial's basics.
This commit is contained in:
parent
89cdbf4d71
commit
03b92de6e0
@ -1,539 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.expressions.Symbol
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
import space.kscience.kmath.operations.ScaleOperations
|
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents multivariate polynomials with labeled variables.
|
|
||||||
*
|
|
||||||
* @param C Ring in which the polynomial is considered.
|
|
||||||
*/
|
|
||||||
public data class LabeledPolynomial<C>
|
|
||||||
internal constructor(
|
|
||||||
/**
|
|
||||||
* Map that collects coefficients of the polynomial. Every non-zero monomial
|
|
||||||
* `a x_1^{d_1} ... x_n^{d_n}` is represented as pair "key-value" in the map, where value is coefficients `a` and
|
|
||||||
* key is map that associates variables in the monomial with multiplicity of them occurring in the monomial.
|
|
||||||
* For example polynomial
|
|
||||||
* ```
|
|
||||||
* 5 a^2 c^3 - 6 b + 0 b c
|
|
||||||
* ```
|
|
||||||
* has coefficients represented as
|
|
||||||
* ```
|
|
||||||
* mapOf(
|
|
||||||
* mapOf(
|
|
||||||
* a to 2,
|
|
||||||
* c to 3
|
|
||||||
* ) to 5,
|
|
||||||
* mapOf(
|
|
||||||
* b to 1
|
|
||||||
* ) to (-6)
|
|
||||||
* )
|
|
||||||
* ```
|
|
||||||
* where `a`, `b` and `c` are corresponding [Symbol] objects.
|
|
||||||
*/
|
|
||||||
public val coefficients: Map<Map<Symbol, UInt>, C>
|
|
||||||
) : Polynomial<C> {
|
|
||||||
override fun toString(): String = "LabeledPolynomial$coefficients"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Space of polynomials.
|
|
||||||
*
|
|
||||||
* @param C the type of operated polynomials.
|
|
||||||
* @param A the intersection of [Ring] of [C] and [ScaleOperations] of [C].
|
|
||||||
* @param ring the [A] instance.
|
|
||||||
*/
|
|
||||||
public class LabeledPolynomialSpace<C, A : Ring<C>>(
|
|
||||||
public override val ring: A,
|
|
||||||
) : MultivariatePolynomialSpace<C, Symbol, LabeledPolynomial<C>>, PolynomialSpaceOverRing<C, LabeledPolynomial<C>, A> {
|
|
||||||
public override operator fun Symbol.plus(other: Int): LabeledPolynomial<C> =
|
|
||||||
if (other == 0) LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this@plus to 1U) to constantOne,
|
|
||||||
))
|
|
||||||
else LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this@plus to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to constantOne * other,
|
|
||||||
))
|
|
||||||
public override operator fun Symbol.minus(other: Int): LabeledPolynomial<C> =
|
|
||||||
if (other == 0) LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this@minus to 1U) to -constantOne,
|
|
||||||
))
|
|
||||||
else LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this@minus to 1U) to -constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to constantOne * other,
|
|
||||||
))
|
|
||||||
public override operator fun Symbol.times(other: Int): LabeledPolynomial<C> =
|
|
||||||
if (other == 0) zero
|
|
||||||
else LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this to 1U) to constantOne * other,
|
|
||||||
))
|
|
||||||
|
|
||||||
public override operator fun Int.plus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == 0) LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(other to 1U) to constantOne,
|
|
||||||
))
|
|
||||||
else LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(other to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to constantOne * this@plus,
|
|
||||||
))
|
|
||||||
public override operator fun Int.minus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == 0) LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(other to 1U) to -constantOne,
|
|
||||||
))
|
|
||||||
else LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(other to 1U) to -constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to constantOne * this@minus,
|
|
||||||
))
|
|
||||||
public override operator fun Int.times(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == 0) zero
|
|
||||||
else LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(other to 1U) to constantOne * this@times,
|
|
||||||
))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the polynomial and the integer represented as polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
|
|
||||||
*/
|
|
||||||
public override operator fun LabeledPolynomial<C>.plus(other: Int): LabeledPolynomial<C> =
|
|
||||||
if (other == 0) this
|
|
||||||
else with(coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to other.asConstant()))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyMap<Symbol, UInt>()
|
|
||||||
|
|
||||||
this[degs] = getOrElse(degs) { constantZero } + other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns difference between the polynomial and the integer represented as polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
|
|
||||||
*/
|
|
||||||
public override operator fun LabeledPolynomial<C>.minus(other: Int): LabeledPolynomial<C> =
|
|
||||||
if (other == 0) this
|
|
||||||
else with(coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to (-other).asConstant()))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyMap<Symbol, UInt>()
|
|
||||||
|
|
||||||
this[degs] = getOrElse(degs) { constantZero } - other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns product of the polynomial and the integer represented as polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to sum of [other] copies of [this].
|
|
||||||
*/
|
|
||||||
public override operator fun LabeledPolynomial<C>.times(other: Int): LabeledPolynomial<C> =
|
|
||||||
if (other == 0) zero
|
|
||||||
else LabeledPolynomial(
|
|
||||||
coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
for (degs in keys) this[degs] = this[degs]!! * other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the integer represented as polynomial and the polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
|
|
||||||
*/
|
|
||||||
public override operator fun Int.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
if (this == 0) other
|
|
||||||
else with(other.coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this@plus.asConstant()))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyMap<Symbol, UInt>()
|
|
||||||
|
|
||||||
this[degs] = this@plus + getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns difference between the integer represented as polynomial and the polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
|
|
||||||
*/
|
|
||||||
public override operator fun Int.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
if (this == 0) other
|
|
||||||
else with(other.coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this@minus.asConstant()))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyMap<Symbol, UInt>()
|
|
||||||
|
|
||||||
this[degs] = this@minus - getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns product of the integer represented as polynomial and the polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to sum of [this] copies of [other].
|
|
||||||
*/
|
|
||||||
public override operator fun Int.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
if (this == 0) zero
|
|
||||||
else LabeledPolynomial(
|
|
||||||
other.coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
for (degs in keys) this[degs] = this@times * this[degs]!!
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the integer [value] to polynomial.
|
|
||||||
*/
|
|
||||||
public override fun number(value: Int): LabeledPolynomial<C> = number(constantNumber(value))
|
|
||||||
|
|
||||||
public override operator fun C.plus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(other to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to this@plus,
|
|
||||||
))
|
|
||||||
public override operator fun C.minus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(other to 1U) to -constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to this@minus,
|
|
||||||
))
|
|
||||||
public override operator fun C.times(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(other to 1U) to this@times,
|
|
||||||
))
|
|
||||||
|
|
||||||
public override operator fun Symbol.plus(other: C): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this@plus to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to other,
|
|
||||||
))
|
|
||||||
public override operator fun Symbol.minus(other: C): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this@minus to 1U) to -constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to other,
|
|
||||||
))
|
|
||||||
public override operator fun Symbol.times(other: C): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this@times to 1U) to other,
|
|
||||||
))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun C.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
with(other.coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this@plus))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyMap<Symbol, UInt>()
|
|
||||||
|
|
||||||
this[degs] = this@plus + getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns difference between the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun C.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
with(other.coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this@minus))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c }
|
|
||||||
|
|
||||||
val degs = emptyMap<Symbol, UInt>()
|
|
||||||
|
|
||||||
this[degs] = this@minus - getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns product of the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun C.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
other.coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
for (degs in keys) this[degs] = this@times * this[degs]!!
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.plus(other: C): LabeledPolynomial<C> =
|
|
||||||
with(coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to other))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyMap<Symbol, UInt>()
|
|
||||||
|
|
||||||
this[degs] = getOrElse(degs) { constantZero } + other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns difference between the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.minus(other: C): LabeledPolynomial<C> =
|
|
||||||
with(coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to other))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c }
|
|
||||||
|
|
||||||
val degs = emptyMap<Symbol, UInt>()
|
|
||||||
|
|
||||||
this[degs] = getOrElse(degs) { constantZero } - other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns product of the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.times(other: C): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
for (degs in keys) this[degs] = this[degs]!! * other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the constant [value] to polynomial.
|
|
||||||
*/
|
|
||||||
public override fun number(value: C): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial(mapOf(emptyMap<Symbol, UInt>() to value))
|
|
||||||
|
|
||||||
public override operator fun Symbol.unaryPlus(): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this to 1U) to constantOne,
|
|
||||||
))
|
|
||||||
public override operator fun Symbol.unaryMinus(): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this to 1U) to -constantOne,
|
|
||||||
))
|
|
||||||
public override operator fun Symbol.plus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == other) LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this to 1U) to constantOne * 2
|
|
||||||
))
|
|
||||||
else LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this to 1U) to constantOne,
|
|
||||||
mapOf(other to 1U) to constantOne,
|
|
||||||
))
|
|
||||||
public override operator fun Symbol.minus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == other) zero
|
|
||||||
else LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this to 1U) to constantOne,
|
|
||||||
mapOf(other to 1U) to -constantOne,
|
|
||||||
))
|
|
||||||
public override operator fun Symbol.times(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == other) LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this to 2U) to constantOne
|
|
||||||
))
|
|
||||||
else LabeledPolynomial<C>(mapOf(
|
|
||||||
mapOf(this to 1U, other to 1U) to constantOne,
|
|
||||||
))
|
|
||||||
|
|
||||||
public override operator fun Symbol.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
with(other.coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(mapOf(this@plus to 1u) to constantOne))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = mapOf(this@plus to 1U)
|
|
||||||
|
|
||||||
this[degs] = constantOne + getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
public override operator fun Symbol.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
with(other.coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(mapOf(this@minus to 1u) to constantOne))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c }
|
|
||||||
|
|
||||||
val degs = mapOf(this@minus to 1U)
|
|
||||||
|
|
||||||
this[degs] = constantOne - getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
public override operator fun Symbol.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
other.coefficients
|
|
||||||
.mapKeys { (degs, _) -> degs.toMutableMap().also{ it[this] = if (this in it) it[this]!! + 1U else 1U } }
|
|
||||||
)
|
|
||||||
|
|
||||||
public override operator fun LabeledPolynomial<C>.plus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
with(coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(mapOf(other to 1u) to constantOne))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = mapOf(other to 1U)
|
|
||||||
|
|
||||||
this[degs] = constantOne + getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
public override operator fun LabeledPolynomial<C>.minus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
with(coefficients) {
|
|
||||||
if (isEmpty()) LabeledPolynomial<C>(mapOf(mapOf(other to 1u) to constantOne))
|
|
||||||
else LabeledPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = mapOf(other to 1U)
|
|
||||||
|
|
||||||
this[degs] = constantOne - getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
public override operator fun LabeledPolynomial<C>.times(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
coefficients
|
|
||||||
.mapKeys { (degs, _) -> degs.toMutableMap().also{ it[other] = if (other in it) it[other]!! + 1U else 1U } }
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns negation of the polynomial.
|
|
||||||
*/
|
|
||||||
override fun LabeledPolynomial<C>.unaryMinus(): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
coefficients.mapValues { -it.value }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns sum of the polynomials.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size + other.coefficients.size) {
|
|
||||||
other.coefficients.mapValuesTo(this) { it.value }
|
|
||||||
other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! + value else value }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference of the polynomials.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size + other.coefficients.size) {
|
|
||||||
other.coefficients.mapValuesTo(this) { it.value }
|
|
||||||
other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! - value else -value }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the polynomials.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size * other.coefficients.size) {
|
|
||||||
for ((degs1, c1) in coefficients) for ((degs2, c2) in other.coefficients) {
|
|
||||||
val degs = degs1.toMutableMap()
|
|
||||||
degs2.mapValuesTo(degs) { (variable, deg) -> degs.getOrElse(variable) { 0u } + deg }
|
|
||||||
val c = c1 * c2
|
|
||||||
this[degs] = if (degs in this) this[degs]!! + c else c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of zero polynomial (zero of the polynomial ring).
|
|
||||||
*/
|
|
||||||
override val zero: LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to constantZero))
|
|
||||||
/**
|
|
||||||
* Instance of unit polynomial (unit of the polynomial ring).
|
|
||||||
*/
|
|
||||||
override val one: LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to constantOne))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
|
|
||||||
* zero, degree is -1.
|
|
||||||
*/
|
|
||||||
override val LabeledPolynomial<C>.degree: Int
|
|
||||||
get() = coefficients.entries.maxOfOrNull { (degs, c) -> degs.values.sum().toInt() } ?: -1
|
|
||||||
/**
|
|
||||||
* 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 override val LabeledPolynomial<C>.degrees: Map<Symbol, UInt>
|
|
||||||
get() =
|
|
||||||
buildMap {
|
|
||||||
coefficients.entries.forEach { (degs, _) ->
|
|
||||||
degs.mapValuesTo(this) { (variable, deg) ->
|
|
||||||
max(getOrElse(variable) { 0u }, deg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Counts degree of the polynomial by the specified [variable].
|
|
||||||
*/
|
|
||||||
public override fun LabeledPolynomial<C>.degreeBy(variable: Symbol): UInt =
|
|
||||||
coefficients.entries.maxOfOrNull { (degs, _) -> degs.getOrElse(variable) { 0u } } ?: 0u
|
|
||||||
/**
|
|
||||||
* Counts degree of the polynomial by the specified [variables].
|
|
||||||
*/
|
|
||||||
public override fun LabeledPolynomial<C>.degreeBy(variables: Collection<Symbol>): UInt =
|
|
||||||
coefficients.entries.maxOfOrNull { (degs, _) -> degs.filterKeys { it in variables }.values.sum() } ?: 0u
|
|
||||||
/**
|
|
||||||
* Set of all variables that appear in the polynomial in positive exponents.
|
|
||||||
*/
|
|
||||||
public override val LabeledPolynomial<C>.variables: Set<Symbol>
|
|
||||||
get() =
|
|
||||||
buildSet {
|
|
||||||
coefficients.entries.forEach { (degs, _) -> addAll(degs.keys) }
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Count of all variables that appear in the polynomial in positive exponents.
|
|
||||||
*/
|
|
||||||
public override val LabeledPolynomial<C>.countOfVariables: Int get() = variables.size
|
|
||||||
|
|
||||||
// @Suppress("NOTHING_TO_INLINE")
|
|
||||||
// public inline fun LabeledPolynomial<C>.substitute(argument: Map<Symbol, C>): LabeledPolynomial<C> = this.substitute(ring, argument)
|
|
||||||
// @Suppress("NOTHING_TO_INLINE")
|
|
||||||
// @JvmName("substitutePolynomial")
|
|
||||||
// public inline fun LabeledPolynomial<C>.substitute(argument: Map<Symbol, LabeledPolynomial<C>>): LabeledPolynomial<C> = this.substitute(ring, argument)
|
|
||||||
//
|
|
||||||
// @Suppress("NOTHING_TO_INLINE")
|
|
||||||
// public inline fun LabeledPolynomial<C>.asFunction(): (Map<Symbol, C>) -> LabeledPolynomial<C> = { this.substitute(ring, it) }
|
|
||||||
// @Suppress("NOTHING_TO_INLINE")
|
|
||||||
// public inline fun LabeledPolynomial<C>.asFunctionOnConstants(): (Map<Symbol, C>) -> LabeledPolynomial<C> = { this.substitute(ring, it) }
|
|
||||||
// @Suppress("NOTHING_TO_INLINE")
|
|
||||||
// public inline fun LabeledPolynomial<C>.asFunctionOnPolynomials(): (Map<Symbol, LabeledPolynomial<C>>) -> LabeledPolynomial<C> = { this.substitute(ring, it) }
|
|
||||||
//
|
|
||||||
// @Suppress("NOTHING_TO_INLINE")
|
|
||||||
// public inline operator fun LabeledPolynomial<C>.invoke(argument: Map<Symbol, C>): LabeledPolynomial<C> = this.substitute(ring, argument)
|
|
||||||
// @Suppress("NOTHING_TO_INLINE")
|
|
||||||
// @JvmName("invokePolynomial")
|
|
||||||
// public inline operator fun LabeledPolynomial<C>.invoke(argument: Map<Symbol, LabeledPolynomial<C>>): LabeledPolynomial<C> = this.substitute(ring, argument)
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.expressions.Symbol
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
import space.kscience.kmath.operations.invoke
|
|
||||||
|
|
||||||
|
|
||||||
public class LabeledRationalFunction<C>(
|
|
||||||
public override val numerator: LabeledPolynomial<C>,
|
|
||||||
public override val denominator: LabeledPolynomial<C>
|
|
||||||
) : RationalFunction<C, LabeledPolynomial<C>> {
|
|
||||||
override fun toString(): String = "LabeledRationalFunction${numerator.coefficients}/${denominator.coefficients}"
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LabeledRationalFunctionSpace<C, A: Ring<C>>(
|
|
||||||
public val ring: A,
|
|
||||||
) :
|
|
||||||
MultivariateRationalFunctionalSpaceOverMultivariatePolynomialSpace<
|
|
||||||
C,
|
|
||||||
Symbol,
|
|
||||||
LabeledPolynomial<C>,
|
|
||||||
LabeledRationalFunction<C>,
|
|
||||||
LabeledPolynomialSpace<C, A>,
|
|
||||||
>,
|
|
||||||
MultivariatePolynomialSpaceOfFractions<
|
|
||||||
C,
|
|
||||||
Symbol,
|
|
||||||
LabeledPolynomial<C>,
|
|
||||||
LabeledRationalFunction<C>,
|
|
||||||
>() {
|
|
||||||
|
|
||||||
override val polynomialRing : LabeledPolynomialSpace<C, A> = LabeledPolynomialSpace(ring)
|
|
||||||
override fun constructRationalFunction(
|
|
||||||
numerator: LabeledPolynomial<C>,
|
|
||||||
denominator: LabeledPolynomial<C>
|
|
||||||
): LabeledRationalFunction<C> =
|
|
||||||
LabeledRationalFunction<C>(numerator, denominator)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of zero rational function (zero of the rational functions ring).
|
|
||||||
*/
|
|
||||||
public override val zero: LabeledRationalFunction<C> = LabeledRationalFunction<C>(polynomialZero, polynomialOne)
|
|
||||||
/**
|
|
||||||
* Instance of unit polynomial (unit of the rational functions ring).
|
|
||||||
*/
|
|
||||||
public override val one: LabeledRationalFunction<C> = LabeledRationalFunction<C>(polynomialOne, polynomialOne)
|
|
||||||
|
|
||||||
// TODO: Разобрать
|
|
||||||
|
|
||||||
// operator fun invoke(arg: Map<Symbol, C>): LabeledRationalFunction<C> =
|
|
||||||
// LabeledRationalFunction(
|
|
||||||
// numerator(arg),
|
|
||||||
// denominator(arg)
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// @JvmName("invokeLabeledPolynomial")
|
|
||||||
// operator fun invoke(arg: Map<Symbol, LabeledPolynomial<C>>): LabeledRationalFunction<C> =
|
|
||||||
// LabeledRationalFunction(
|
|
||||||
// numerator(arg),
|
|
||||||
// denominator(arg)
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// @JvmName("invokeLabeledRationalFunction")
|
|
||||||
// operator fun invoke(arg: Map<Symbol, LabeledRationalFunction<C>>): LabeledRationalFunction<C> {
|
|
||||||
// var num = numerator invokeRFTakeNumerator arg
|
|
||||||
// var den = denominator invokeRFTakeNumerator arg
|
|
||||||
// for (variable in variables) if (variable in arg) {
|
|
||||||
// val degreeDif = degrees[variable]!!
|
|
||||||
// if (degreeDif > 0)
|
|
||||||
// den = multiplyByPower(den, arg[variable]!!.denominator, degreeDif)
|
|
||||||
// else
|
|
||||||
// num = multiplyByPower(num, arg[variable]!!.denominator, -degreeDif)
|
|
||||||
// }
|
|
||||||
// return LabeledRationalFunction(num, den)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun toString(): String = toString(emptyMap())
|
|
||||||
//
|
|
||||||
// fun toString(names: Map<Symbol, String> = emptyMap()): String =
|
|
||||||
// when (true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toString(names)
|
|
||||||
// else -> "${numerator.toStringWithBrackets(names)}/${denominator.toStringWithBrackets(names)}"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toString(namer: (Symbol) -> String): String =
|
|
||||||
// when (true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toString(namer)
|
|
||||||
// else -> "${numerator.toStringWithBrackets(namer)}/${denominator.toStringWithBrackets(namer)}"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toStringWithBrackets(names: Map<Symbol, String> = emptyMap()): String =
|
|
||||||
// when (true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toStringWithBrackets(names)
|
|
||||||
// else -> "(${numerator.toStringWithBrackets(names)}/${denominator.toStringWithBrackets(names)})"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toStringWithBrackets(namer: (Symbol) -> String): String =
|
|
||||||
// when (true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toStringWithBrackets(namer)
|
|
||||||
// else -> "(${numerator.toStringWithBrackets(namer)}/${denominator.toStringWithBrackets(namer)})"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toReversedString(names: Map<Symbol, String> = emptyMap()): String =
|
|
||||||
// when (true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toReversedString(names)
|
|
||||||
// else -> "${numerator.toReversedStringWithBrackets(names)}/${denominator.toReversedStringWithBrackets(names)}"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toReversedString(namer: (Symbol) -> String): String =
|
|
||||||
// when (true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toReversedString(namer)
|
|
||||||
// else -> "${numerator.toReversedStringWithBrackets(namer)}/${denominator.toReversedStringWithBrackets(namer)}"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toReversedStringWithBrackets(names: Map<Symbol, String> = emptyMap()): String =
|
|
||||||
// when (true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toReversedStringWithBrackets(names)
|
|
||||||
// else -> "(${numerator.toReversedStringWithBrackets(names)}/${denominator.toReversedStringWithBrackets(names)})"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toReversedStringWithBrackets(namer: (Symbol) -> String): String =
|
|
||||||
// when (true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toReversedStringWithBrackets(namer)
|
|
||||||
// else -> "(${numerator.toReversedStringWithBrackets(namer)}/${denominator.toReversedStringWithBrackets(namer)})"
|
|
||||||
// }
|
|
||||||
}
|
|
@ -1,389 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.invoke
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
import space.kscience.kmath.operations.ScaleOperations
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.experimental.ExperimentalTypeInference
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Polynomial model without fixation on specific context they are applied to.
|
|
||||||
*
|
|
||||||
* @param C the type of constants.
|
|
||||||
*/
|
|
||||||
public data class NumberedPolynomial<C>
|
|
||||||
internal constructor(
|
|
||||||
/**
|
|
||||||
* Map that collects coefficients of the polynomial. Every monomial `a x_1^{d_1} ... x_n^{d_n}` is represented as
|
|
||||||
* pair "key-value" in the map, where value is coefficients `a` and
|
|
||||||
* key is list that associates index of every variable in the monomial with multiplicity of the variable occurring
|
|
||||||
* in the monomial. For example coefficients of polynomial `5 x_1^2 x_3^3 - 6 x_2` can be represented as
|
|
||||||
* ```
|
|
||||||
* mapOf(
|
|
||||||
* listOf(2, 0, 3) to 5,
|
|
||||||
* listOf(0, 1) to (-6),
|
|
||||||
* )
|
|
||||||
* ```
|
|
||||||
* and also as
|
|
||||||
* ```
|
|
||||||
* mapOf(
|
|
||||||
* listOf(2, 0, 3) to 5,
|
|
||||||
* listOf(0, 1) to (-6),
|
|
||||||
* listOf(0, 1, 1) to 0,
|
|
||||||
* )
|
|
||||||
* ```
|
|
||||||
* It is recommended not to put zero monomials into the map, but is not prohibited. Lists of degrees always do not
|
|
||||||
* contain any zeros on end, but can contain zeros on start or anywhere in middle.
|
|
||||||
*/
|
|
||||||
public val coefficients: Map<List<UInt>, C>
|
|
||||||
) : Polynomial<C> {
|
|
||||||
override fun toString(): String = "NumberedPolynomial$coefficients"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Space of polynomials.
|
|
||||||
*
|
|
||||||
* @param C the type of operated polynomials.
|
|
||||||
* @param A the intersection of [Ring] of [C] and [ScaleOperations] of [C].
|
|
||||||
* @param ring the [A] instance.
|
|
||||||
*/
|
|
||||||
public open class NumberedPolynomialSpace<C, A : Ring<C>>(
|
|
||||||
public final override val ring: A,
|
|
||||||
) : PolynomialSpaceOverRing<C, NumberedPolynomial<C>, A> {
|
|
||||||
/**
|
|
||||||
* Returns sum of the polynomial and the integer represented as polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
|
|
||||||
*/
|
|
||||||
public override operator fun NumberedPolynomial<C>.plus(other: Int): NumberedPolynomial<C> =
|
|
||||||
if (other == 0) this
|
|
||||||
else
|
|
||||||
NumberedPolynomial(
|
|
||||||
coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyList<UInt>()
|
|
||||||
|
|
||||||
this[degs] = getOrElse(degs) { constantZero } + other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the polynomial and the integer represented as polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
|
|
||||||
*/
|
|
||||||
public override operator fun NumberedPolynomial<C>.minus(other: Int): NumberedPolynomial<C> =
|
|
||||||
if (other == 0) this
|
|
||||||
else
|
|
||||||
NumberedPolynomial(
|
|
||||||
coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyList<UInt>()
|
|
||||||
|
|
||||||
this[degs] = getOrElse(degs) { constantZero } - other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the polynomial and the integer represented as polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to sum of [other] copies of [this].
|
|
||||||
*/
|
|
||||||
public override operator fun NumberedPolynomial<C>.times(other: Int): NumberedPolynomial<C> =
|
|
||||||
if (other == 0) zero
|
|
||||||
else NumberedPolynomial<C>(
|
|
||||||
coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
for (degs in keys) this[degs] = this[degs]!! * other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the integer represented as polynomial and the polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
|
|
||||||
*/
|
|
||||||
public override operator fun Int.plus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
|
||||||
if (this == 0) other
|
|
||||||
else
|
|
||||||
NumberedPolynomial(
|
|
||||||
other.coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyList<UInt>()
|
|
||||||
|
|
||||||
this[degs] = this@plus + getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the integer represented as polynomial and the polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
|
|
||||||
*/
|
|
||||||
public override operator fun Int.minus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
|
||||||
if (this == 0) other
|
|
||||||
else
|
|
||||||
NumberedPolynomial(
|
|
||||||
other.coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyList<UInt>()
|
|
||||||
|
|
||||||
this[degs] = this@minus - getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the integer represented as polynomial and the polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to sum of [this] copies of [other].
|
|
||||||
*/
|
|
||||||
public override operator fun Int.times(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
|
||||||
if (this == 0) zero
|
|
||||||
else NumberedPolynomial(
|
|
||||||
other.coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
for (degs in keys) this[degs] = this@times * this[degs]!!
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the integer [value] to polynomial.
|
|
||||||
*/
|
|
||||||
public override fun number(value: Int): NumberedPolynomial<C> = number(constantNumber(value))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun C.plus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
|
||||||
with(other.coefficients) {
|
|
||||||
if (isEmpty()) NumberedPolynomial<C>(mapOf(emptyList<UInt>() to this@plus))
|
|
||||||
else NumberedPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyList<UInt>()
|
|
||||||
|
|
||||||
this[degs] = this@plus + getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns difference between the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun C.minus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
|
||||||
with(other.coefficients) {
|
|
||||||
if (isEmpty()) NumberedPolynomial<C>(mapOf(emptyList<UInt>() to this@minus))
|
|
||||||
else NumberedPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c }
|
|
||||||
|
|
||||||
val degs = emptyList<UInt>()
|
|
||||||
|
|
||||||
this[degs] = this@minus - getOrElse(degs) { constantZero }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns product of the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun C.times(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
other.coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
for (degs in keys) this[degs] = this@times * this[degs]!!
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun NumberedPolynomial<C>.plus(other: C): NumberedPolynomial<C> =
|
|
||||||
with(coefficients) {
|
|
||||||
if (isEmpty()) NumberedPolynomial<C>(mapOf(emptyList<UInt>() to other))
|
|
||||||
else NumberedPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyList<UInt>()
|
|
||||||
|
|
||||||
this[degs] = getOrElse(degs) { constantZero } + other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns difference between the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun NumberedPolynomial<C>.minus(other: C): NumberedPolynomial<C> =
|
|
||||||
with(coefficients) {
|
|
||||||
if (isEmpty()) NumberedPolynomial<C>(mapOf(emptyList<UInt>() to other))
|
|
||||||
else NumberedPolynomial<C>(
|
|
||||||
toMutableMap()
|
|
||||||
.apply {
|
|
||||||
val degs = emptyList<UInt>()
|
|
||||||
|
|
||||||
this[degs] = getOrElse(degs) { constantZero } - other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns product of the constant represented as polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun NumberedPolynomial<C>.times(other: C): NumberedPolynomial<C> =
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
coefficients
|
|
||||||
.toMutableMap()
|
|
||||||
.apply {
|
|
||||||
for (degs in keys) this[degs] = this[degs]!! * other
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the constant [value] to polynomial.
|
|
||||||
*/
|
|
||||||
public override fun number(value: C): NumberedPolynomial<C> =
|
|
||||||
NumberedPolynomial(mapOf(emptyList<UInt>() to value))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns negation of the polynomial.
|
|
||||||
*/
|
|
||||||
override fun NumberedPolynomial<C>.unaryMinus(): NumberedPolynomial<C> =
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
coefficients.mapValues { -it.value }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns sum of the polynomials.
|
|
||||||
*/
|
|
||||||
override operator fun NumberedPolynomial<C>.plus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size + other.coefficients.size) {
|
|
||||||
other.coefficients.mapValuesTo(this) { it.value }
|
|
||||||
other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! + value else value }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference of the polynomials.
|
|
||||||
*/
|
|
||||||
override operator fun NumberedPolynomial<C>.minus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size + other.coefficients.size) {
|
|
||||||
other.coefficients.mapValuesTo(this) { it.value }
|
|
||||||
other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! - value else -value }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the polynomials.
|
|
||||||
*/
|
|
||||||
override operator fun NumberedPolynomial<C>.times(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size * other.coefficients.size) {
|
|
||||||
for ((degs1, c1) in coefficients) for ((degs2, c2) in other.coefficients) {
|
|
||||||
val degs =
|
|
||||||
(0..max(degs1.lastIndex, degs2.lastIndex))
|
|
||||||
.map { degs1.getOrElse(it) { 0U } + degs2.getOrElse(it) { 0U } }
|
|
||||||
val c = c1 * c2
|
|
||||||
this[degs] = if (degs in this) this[degs]!! + c else c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of zero polynomial (zero of the polynomial ring).
|
|
||||||
*/
|
|
||||||
override val zero: NumberedPolynomial<C> = NumberedPolynomial<C>(emptyMap())
|
|
||||||
/**
|
|
||||||
* Instance of unit polynomial (unit of the polynomial ring).
|
|
||||||
*/
|
|
||||||
override val one: NumberedPolynomial<C> =
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
mapOf(
|
|
||||||
emptyList<UInt>() to constantOne // 1 * x_1^0 * x_2^0 * ...
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximal index (ID) of variable occurring in the polynomial with positive power. If there is no such variable,
|
|
||||||
* the result is `-1`.
|
|
||||||
*/
|
|
||||||
public val NumberedPolynomial<C>.lastVariable: Int
|
|
||||||
get() = coefficients.entries.maxOfOrNull { (degs, _) -> degs.lastIndex } ?: -1
|
|
||||||
/**
|
|
||||||
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
|
|
||||||
* zero, degree is -1.
|
|
||||||
*/
|
|
||||||
override val NumberedPolynomial<C>.degree: Int
|
|
||||||
get() = coefficients.entries.maxOfOrNull { (degs, _) -> degs.sum().toInt() } ?: -1
|
|
||||||
/**
|
|
||||||
* List that associates indices of variables (that appear in the polynomial in positive exponents) with their most
|
|
||||||
* exponents in which the variables are appeared in the polynomial.
|
|
||||||
*
|
|
||||||
* As consequence all values in the list are non-negative integers. Also, if the polynomial is constant, the list is empty.
|
|
||||||
* And last index of the list is [lastVariable].
|
|
||||||
*/
|
|
||||||
public val NumberedPolynomial<C>.degrees: List<UInt>
|
|
||||||
get() =
|
|
||||||
MutableList(lastVariable + 1) { 0u }.apply {
|
|
||||||
coefficients.entries.forEach { (degs, _) ->
|
|
||||||
degs.forEachIndexed { index, deg ->
|
|
||||||
this[index] = max(this[index], deg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Counts degree of the polynomial by the specified [variable].
|
|
||||||
*/
|
|
||||||
public fun NumberedPolynomial<C>.degreeBy(variable: Int): UInt =
|
|
||||||
coefficients.entries.maxOfOrNull { (degs, _) -> degs.getOrElse(variable) { 0u } } ?: 0u
|
|
||||||
/**
|
|
||||||
* Counts degree of the polynomial by the specified [variables].
|
|
||||||
*/
|
|
||||||
public fun NumberedPolynomial<C>.degreeBy(variables: Collection<Int>): UInt =
|
|
||||||
coefficients.entries.maxOfOrNull { (degs, _) ->
|
|
||||||
degs.withIndex().filter { (index, _) -> index in variables }.sumOf { it.value }
|
|
||||||
} ?: 0u
|
|
||||||
/**
|
|
||||||
* Count of variables occurring in the polynomial with positive power. If there is no such variable,
|
|
||||||
* the result is `0`.
|
|
||||||
*/
|
|
||||||
public val NumberedPolynomial<C>.countOfVariables: Int
|
|
||||||
get() =
|
|
||||||
MutableList(lastVariable + 1) { false }.apply {
|
|
||||||
coefficients.entries.forEach { (degs, _) ->
|
|
||||||
degs.forEachIndexed { index, deg ->
|
|
||||||
if (deg != 0u) this[index] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.count { it }
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline fun NumberedPolynomial<C>.substitute(argument: Map<Int, C>): NumberedPolynomial<C> = this.substitute(ring, argument)
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
@JvmName("substitutePolynomial")
|
|
||||||
public inline fun NumberedPolynomial<C>.substitute(argument: Map<Int, NumberedPolynomial<C>>): NumberedPolynomial<C> = this.substitute(ring, argument)
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline fun NumberedPolynomial<C>.asFunction(): (Map<Int, C>) -> NumberedPolynomial<C> = { this.substitute(ring, it) }
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline fun NumberedPolynomial<C>.asFunctionOnConstants(): (Map<Int, C>) -> NumberedPolynomial<C> = { this.substitute(ring, it) }
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline fun NumberedPolynomial<C>.asFunctionOnPolynomials(): (Map<Int, NumberedPolynomial<C>>) -> NumberedPolynomial<C> = { this.substitute(ring, it) }
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline operator fun NumberedPolynomial<C>.invoke(argument: Map<Int, C>): NumberedPolynomial<C> = this.substitute(ring, argument)
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
@JvmName("invokePolynomial")
|
|
||||||
public inline operator fun NumberedPolynomial<C>.invoke(argument: Map<Int, NumberedPolynomial<C>>): NumberedPolynomial<C> = this.substitute(ring, argument)
|
|
||||||
|
|
||||||
// FIXME: Move to other constructors with context receiver
|
|
||||||
public fun C.asNumberedPolynomial() : NumberedPolynomial<C> = NumberedPolynomial<C>(mapOf(emptyList<UInt>() to this))
|
|
||||||
}
|
|
@ -1,188 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.math.max
|
|
||||||
|
|
||||||
|
|
||||||
public class NumberedRationalFunction<C> internal constructor(
|
|
||||||
public override val numerator: NumberedPolynomial<C>,
|
|
||||||
public override val denominator: NumberedPolynomial<C>
|
|
||||||
) : RationalFunction<C, NumberedPolynomial<C>> {
|
|
||||||
override fun toString(): String = "NumberedRationalFunction${numerator.coefficients}/${denominator.coefficients}"
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NumberedRationalFunctionSpace<C, A: Ring<C>> (
|
|
||||||
public val ring: A,
|
|
||||||
) :
|
|
||||||
RationalFunctionalSpaceOverPolynomialSpace<
|
|
||||||
C,
|
|
||||||
NumberedPolynomial<C>,
|
|
||||||
NumberedRationalFunction<C>,
|
|
||||||
NumberedPolynomialSpace<C, A>,
|
|
||||||
>,
|
|
||||||
PolynomialSpaceOfFractions<
|
|
||||||
C,
|
|
||||||
NumberedPolynomial<C>,
|
|
||||||
NumberedRationalFunction<C>,
|
|
||||||
>() {
|
|
||||||
|
|
||||||
override val polynomialRing : NumberedPolynomialSpace<C, A> = NumberedPolynomialSpace(ring)
|
|
||||||
override fun constructRationalFunction(
|
|
||||||
numerator: NumberedPolynomial<C>,
|
|
||||||
denominator: NumberedPolynomial<C>
|
|
||||||
): NumberedRationalFunction<C> =
|
|
||||||
NumberedRationalFunction(numerator, denominator)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of zero rational function (zero of the rational functions ring).
|
|
||||||
*/
|
|
||||||
public override val zero: NumberedRationalFunction<C> = NumberedRationalFunction(polynomialZero, polynomialOne)
|
|
||||||
/**
|
|
||||||
* Instance of unit polynomial (unit of the rational functions ring).
|
|
||||||
*/
|
|
||||||
public override val one: NumberedRationalFunction<C> = NumberedRationalFunction(polynomialOne, polynomialOne)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximal index (ID) of variable occurring in the polynomial with positive power. If there is no such variable,
|
|
||||||
* the result is `-1`.
|
|
||||||
*/
|
|
||||||
public val NumberedPolynomial<C>.lastVariable: Int get() = polynomialRing { lastVariable }
|
|
||||||
/**
|
|
||||||
* List that associates indices of variables (that appear in the polynomial in positive exponents) with their most
|
|
||||||
* exponents in which the variables are appeared in the polynomial.
|
|
||||||
*
|
|
||||||
* As consequence all values in the list are non-negative integers. Also, if the polynomial is constant, the list is empty.
|
|
||||||
* And last index of the list is [lastVariable].
|
|
||||||
*/
|
|
||||||
public val NumberedPolynomial<C>.degrees: List<UInt> get() = polynomialRing { degrees }
|
|
||||||
/**
|
|
||||||
* Counts degree of the polynomial by the specified [variable].
|
|
||||||
*/
|
|
||||||
public fun NumberedPolynomial<C>.degreeBy(variable: Int): UInt = polynomialRing { degreeBy(variable) }
|
|
||||||
/**
|
|
||||||
* Counts degree of the polynomial by the specified [variables].
|
|
||||||
*/
|
|
||||||
public fun NumberedPolynomial<C>.degreeBy(variables: Collection<Int>): UInt = polynomialRing { degreeBy(variables) }
|
|
||||||
/**
|
|
||||||
* Count of variables occurring in the polynomial with positive power. If there is no such variable,
|
|
||||||
* the result is `0`.
|
|
||||||
*/
|
|
||||||
public val NumberedPolynomial<C>.countOfVariables: Int get() = polynomialRing { countOfVariables }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Count of all variables that appear in the polynomial in positive exponents.
|
|
||||||
*/
|
|
||||||
public val NumberedRationalFunction<C>.lastVariable: Int
|
|
||||||
get() = polynomialRing { max(numerator.lastVariable, denominator.lastVariable) }
|
|
||||||
/**
|
|
||||||
* Count of variables occurring in the rational function with positive power. If there is no such variable,
|
|
||||||
* the result is `0`.
|
|
||||||
*/
|
|
||||||
public val NumberedRationalFunction<C>.countOfVariables: Int
|
|
||||||
get() =
|
|
||||||
MutableList(lastVariable + 1) { false }.apply {
|
|
||||||
numerator.coefficients.entries.forEach { (degs, _) ->
|
|
||||||
degs.forEachIndexed { index, deg ->
|
|
||||||
if (deg != 0u) this[index] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
denominator.coefficients.entries.forEach { (degs, _) ->
|
|
||||||
degs.forEachIndexed { index, deg ->
|
|
||||||
if (deg != 0u) this[index] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.count { it }
|
|
||||||
|
|
||||||
// TODO: Разобрать
|
|
||||||
|
|
||||||
// operator fun invoke(arg: Map<Int, C>): NumberedRationalFunction<C> =
|
|
||||||
// NumberedRationalFunction(
|
|
||||||
// numerator(arg),
|
|
||||||
// denominator(arg)
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// @JvmName("invokePolynomial")
|
|
||||||
// operator fun invoke(arg: Map<Int, Polynomial<C>>): NumberedRationalFunction<C> =
|
|
||||||
// NumberedRationalFunction(
|
|
||||||
// numerator(arg),
|
|
||||||
// denominator(arg)
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// @JvmName("invokeRationalFunction")
|
|
||||||
// operator fun invoke(arg: Map<Int, NumberedRationalFunction<C>>): NumberedRationalFunction<C> {
|
|
||||||
// var num = numerator invokeRFTakeNumerator arg
|
|
||||||
// var den = denominator invokeRFTakeNumerator arg
|
|
||||||
// for (variable in 0 until max(numerator.countOfVariables, denominator.countOfVariables)) if (variable in arg) {
|
|
||||||
// val degreeDif = numerator.degrees.getOrElse(variable) { 0 } - denominator.degrees.getOrElse(variable) { 0 }
|
|
||||||
// if (degreeDif > 0)
|
|
||||||
// den = multiplyByPower(den, arg[variable]!!.denominator, degreeDif)
|
|
||||||
// else
|
|
||||||
// num = multiplyByPower(num, arg[variable]!!.denominator, -degreeDif)
|
|
||||||
// }
|
|
||||||
// return NumberedRationalFunction(num, den)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun toString(): String = toString(Polynomial.variableName)
|
|
||||||
//
|
|
||||||
// fun toString(withVariableName: String = Polynomial.variableName): String =
|
|
||||||
// when(true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toString(withVariableName)
|
|
||||||
// else -> "${numerator.toStringWithBrackets(withVariableName)}/${denominator.toStringWithBrackets(withVariableName)}"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toString(namer: (Int) -> String): String =
|
|
||||||
// when(true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toString(namer)
|
|
||||||
// else -> "${numerator.toStringWithBrackets(namer)}/${denominator.toStringWithBrackets(namer)}"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toStringWithBrackets(withVariableName: String = Polynomial.variableName): String =
|
|
||||||
// when(true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toStringWithBrackets(withVariableName)
|
|
||||||
// else -> "(${numerator.toStringWithBrackets(withVariableName)}/${denominator.toStringWithBrackets(withVariableName)})"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toStringWithBrackets(namer: (Int) -> String): String =
|
|
||||||
// when(true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toStringWithBrackets(namer)
|
|
||||||
// else -> "(${numerator.toStringWithBrackets(namer)}/${denominator.toStringWithBrackets(namer)})"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toReversedString(withVariableName: String = Polynomial.variableName): String =
|
|
||||||
// when(true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toReversedString(withVariableName)
|
|
||||||
// else -> "${numerator.toReversedStringWithBrackets(withVariableName)}/${denominator.toReversedStringWithBrackets(withVariableName)}"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toReversedString(namer: (Int) -> String): String =
|
|
||||||
// when(true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toReversedString(namer)
|
|
||||||
// else -> "${numerator.toReversedStringWithBrackets(namer)}/${denominator.toReversedStringWithBrackets(namer)}"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toReversedStringWithBrackets(withVariableName: String = Polynomial.variableName): String =
|
|
||||||
// when(true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toReversedStringWithBrackets(withVariableName)
|
|
||||||
// else -> "(${numerator.toReversedStringWithBrackets(withVariableName)}/${denominator.toReversedStringWithBrackets(withVariableName)})"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun toReversedStringWithBrackets(namer: (Int) -> String): String =
|
|
||||||
// when(true) {
|
|
||||||
// numerator.isZero() -> "0"
|
|
||||||
// denominator.isOne() -> numerator.toReversedStringWithBrackets(namer)
|
|
||||||
// else -> "(${numerator.toReversedStringWithBrackets(namer)}/${denominator.toReversedStringWithBrackets(namer)})"
|
|
||||||
// }
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.PerformancePitfall
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents piecewise-defined function.
|
|
||||||
*
|
|
||||||
* @param T the piece key type.
|
|
||||||
* @param R the sub-function type.
|
|
||||||
*/
|
|
||||||
public fun interface Piecewise<in T, out R> {
|
|
||||||
/**
|
|
||||||
* Returns the appropriate sub-function for given piece key.
|
|
||||||
*/
|
|
||||||
public fun findPiece(arg: T): R?
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents piecewise-defined function where all the sub-functions are polynomials.
|
|
||||||
*
|
|
||||||
* @property pieces An ordered list of range-polynomial pairs. The list does not in general guarantee that there are no
|
|
||||||
* "holes" in it.
|
|
||||||
*/
|
|
||||||
public interface PiecewisePolynomial<T : Comparable<T>> : Piecewise<T, ListPolynomial<T>> {
|
|
||||||
public val pieces: Collection<Pair<ClosedRange<T>, ListPolynomial<T>>>
|
|
||||||
|
|
||||||
override fun findPiece(arg: T): ListPolynomial<T>?
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A generic piecewise without constraints on how pieces are placed
|
|
||||||
*/
|
|
||||||
@PerformancePitfall("findPiece method of resulting piecewise is slow")
|
|
||||||
public fun <T : Comparable<T>> PiecewisePolynomial(
|
|
||||||
pieces: Collection<Pair<ClosedRange<T>, ListPolynomial<T>>>,
|
|
||||||
): PiecewisePolynomial<T> = object : PiecewisePolynomial<T> {
|
|
||||||
override val pieces: Collection<Pair<ClosedRange<T>, ListPolynomial<T>>> = pieces
|
|
||||||
|
|
||||||
override fun findPiece(arg: T): ListPolynomial<T>? = pieces.firstOrNull { arg in it.first }?.second
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An optimized piecewise that uses not separate pieces, but a range separated by delimiters.
|
|
||||||
* The pieces search is logarithmic.
|
|
||||||
*/
|
|
||||||
private class OrderedPiecewisePolynomial<T : Comparable<T>>(
|
|
||||||
override val pieces: List<Pair<ClosedRange<T>, ListPolynomial<T>>>,
|
|
||||||
) : PiecewisePolynomial<T> {
|
|
||||||
|
|
||||||
override fun findPiece(arg: T): ListPolynomial<T>? {
|
|
||||||
val index = pieces.binarySearch { (range, _) ->
|
|
||||||
when {
|
|
||||||
arg >= range.endInclusive -> -1
|
|
||||||
arg < range.start -> +1
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return if (index < 0) null else pieces[index].second
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [Piecewise] builder where all the pieces are ordered by the [Comparable] type instances.
|
|
||||||
*
|
|
||||||
* @param T the comparable piece key type.
|
|
||||||
* @param delimiter the initial piecewise separator
|
|
||||||
*/
|
|
||||||
public class PiecewiseBuilder<T : Comparable<T>>(delimiter: T) {
|
|
||||||
private val delimiters: MutableList<T> = arrayListOf(delimiter)
|
|
||||||
private val pieces: MutableList<ListPolynomial<T>> = arrayListOf()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dynamically adds a piece to the right side (beyond maximum argument value of previous piece)
|
|
||||||
*
|
|
||||||
* @param right new rightmost position. If is less than current rightmost position, an error is thrown.
|
|
||||||
* @param piece the sub-function.
|
|
||||||
*/
|
|
||||||
public fun putRight(right: T, piece: ListPolynomial<T>) {
|
|
||||||
require(right > delimiters.last()) { "New delimiter should be to the right of old one" }
|
|
||||||
delimiters += right
|
|
||||||
pieces += piece
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dynamically adds a piece to the left side (beyond maximum argument value of previous piece)
|
|
||||||
*
|
|
||||||
* @param left the new leftmost position. If is less than current rightmost position, an error is thrown.
|
|
||||||
* @param piece the sub-function.
|
|
||||||
*/
|
|
||||||
public fun putLeft(left: T, piece: ListPolynomial<T>) {
|
|
||||||
require(left < delimiters.first()) { "New delimiter should be to the left of old one" }
|
|
||||||
delimiters.add(0, left)
|
|
||||||
pieces.add(0, piece)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun build(): PiecewisePolynomial<T> = OrderedPiecewisePolynomial(delimiters.zipWithNext { l, r ->
|
|
||||||
l..r
|
|
||||||
}.zip(pieces))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A builder for [PiecewisePolynomial]
|
|
||||||
*/
|
|
||||||
public fun <T : Comparable<T>> PiecewisePolynomial(
|
|
||||||
startingPoint: T,
|
|
||||||
builder: PiecewiseBuilder<T>.() -> Unit,
|
|
||||||
): PiecewisePolynomial<T> = PiecewiseBuilder(startingPoint).apply(builder).build()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 }
|
|
@ -1,203 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.expressions.Symbol
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the same degrees' description of the monomial, but without zero degrees.
|
|
||||||
*/
|
|
||||||
internal fun Map<Symbol, UInt>.cleanUp() = filterValues { it > 0U }
|
|
||||||
|
|
||||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
|
||||||
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(coefs, toCheckInput)
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(coefs, toCheckInput)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
internal fun <C, A: Ring<C>> A.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> {
|
|
||||||
if (!toCheckInput) return LabeledPolynomial<C>(coefs)
|
|
||||||
|
|
||||||
val fixedCoefs = LinkedHashMap<Map<Symbol, UInt>, C>(coefs.size)
|
|
||||||
|
|
||||||
for (entry in coefs) {
|
|
||||||
val key = entry.key.cleanUp()
|
|
||||||
val value = entry.value
|
|
||||||
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
|
|
||||||
}
|
|
||||||
|
|
||||||
return LabeledPolynomial<C>(fixedCoefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(pairs, toCheckInput)
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(pairs, toCheckInput)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
internal fun <C, A: Ring<C>> A.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>, toCheckInput: Boolean = true) : LabeledPolynomial<C> {
|
|
||||||
if (!toCheckInput) return LabeledPolynomial<C>(pairs.toMap())
|
|
||||||
|
|
||||||
val fixedCoefs = LinkedHashMap<Map<Symbol, UInt>, C>(pairs.size)
|
|
||||||
|
|
||||||
for (entry in pairs) {
|
|
||||||
val key = entry.first.cleanUp()
|
|
||||||
val value = entry.second
|
|
||||||
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
|
|
||||||
}
|
|
||||||
|
|
||||||
return LabeledPolynomial<C>(fixedCoefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(pairs = pairs, toCheckInput = toCheckInput)
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> = ring.LabeledPolynomial(pairs = pairs, toCheckInput = toCheckInput)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
internal fun <C, A: Ring<C>> A.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>, toCheckInput: Boolean = true) : LabeledPolynomial<C> {
|
|
||||||
if (!toCheckInput) return LabeledPolynomial<C>(pairs.toMap())
|
|
||||||
|
|
||||||
val fixedCoefs = LinkedHashMap<Map<Symbol, UInt>, C>(pairs.size)
|
|
||||||
|
|
||||||
for (entry in pairs) {
|
|
||||||
val key = entry.first.cleanUp()
|
|
||||||
val value = entry.second
|
|
||||||
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
|
|
||||||
}
|
|
||||||
|
|
||||||
return LabeledPolynomial<C>(fixedCoefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs, toCheckInput = true)
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs, toCheckInput = true)
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs, toCheckInput = true)
|
|
||||||
|
|
||||||
//context(A)
|
|
||||||
//public fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to one))
|
|
||||||
//context(LabeledPolynomialSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to constantOne))
|
|
||||||
//context(LabeledRationalFunctionSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to constantOne))
|
|
||||||
|
|
||||||
public fun <C> C.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(emptyMap<Symbol, UInt>() to this))
|
|
||||||
|
|
||||||
@DslMarker
|
|
||||||
@UnstableKMathAPI
|
|
||||||
internal annotation class LabeledPolynomialConstructorDSL
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@LabeledPolynomialConstructorDSL
|
|
||||||
public class LabeledPolynomialTermSignatureBuilder {
|
|
||||||
private val signature: MutableMap<Symbol, UInt> = LinkedHashMap()
|
|
||||||
public fun build(): Map<Symbol, UInt> = signature
|
|
||||||
public infix fun Symbol.inPowerOf(deg: UInt) {
|
|
||||||
signature[this] = deg
|
|
||||||
}
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun Symbol.pow(deg: UInt): Unit = this inPowerOf deg
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun Symbol.`in`(deg: UInt): Unit = this inPowerOf deg
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun Symbol.of(deg: UInt): Unit = this inPowerOf deg
|
|
||||||
}
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public class LabeledPolynomialBuilder<C>(private val zero: C, private val add: (C, C) -> C, capacity: Int = 0) {
|
|
||||||
private val coefficients: MutableMap<Map<Symbol, UInt>, C> = LinkedHashMap(capacity)
|
|
||||||
public fun build(): LabeledPolynomial<C> = LabeledPolynomial<C>(coefficients)
|
|
||||||
public operator fun C.invoke(block: LabeledPolynomialTermSignatureBuilder.() -> Unit) {
|
|
||||||
val signature = LabeledPolynomialTermSignatureBuilder().apply(block).build()
|
|
||||||
coefficients[signature] = add(coefficients.getOrElse(signature) { zero }, this@invoke)
|
|
||||||
}
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun C.with(noinline block: LabeledPolynomialTermSignatureBuilder.() -> Unit): Unit = this.invoke(block)
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun (LabeledPolynomialTermSignatureBuilder.() -> Unit).with(coef: C): Unit = coef.invoke(this)
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public infix fun sig(block: LabeledPolynomialTermSignatureBuilder.() -> Unit): LabeledPolynomialTermSignatureBuilder.() -> Unit = block
|
|
||||||
}
|
|
||||||
|
|
||||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@LabeledPolynomialConstructorDSL
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public inline fun <C, A: Ring<C>> A.LabeledPolynomial(block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = LabeledPolynomialBuilder(zero, ::add).apply(block).build()
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@LabeledPolynomialConstructorDSL
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public inline fun <C, A: Ring<C>> A.LabeledPolynomial(capacity: Int, block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = LabeledPolynomialBuilder(zero, ::add, capacity).apply(block).build()
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@LabeledPolynomialConstructorDSL
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = LabeledPolynomialBuilder(constantZero, { left: C, right: C -> left + right}).apply(block).build()
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@LabeledPolynomialConstructorDSL
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(capacity: Int, block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = LabeledPolynomialBuilder(constantZero, { left: C, right: C -> left + right}, capacity).apply(block).build()
|
|
||||||
|
|
||||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>, denominatorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
|
|
||||||
LabeledRationalFunction<C>(
|
|
||||||
LabeledPolynomial(numeratorCoefficients, toCheckInput = true),
|
|
||||||
LabeledPolynomial(denominatorCoefficients, toCheckInput = true)
|
|
||||||
)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>, denominatorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
|
|
||||||
LabeledRationalFunction<C>(
|
|
||||||
LabeledPolynomial(numeratorCoefficients, toCheckInput = true),
|
|
||||||
LabeledPolynomial(denominatorCoefficients, toCheckInput = true)
|
|
||||||
)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numerator: LabeledPolynomial<C>): LabeledRationalFunction<C> =
|
|
||||||
LabeledRationalFunction<C>(numerator, polynomialOne)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.LabeledRationalFunction(numerator: LabeledPolynomial<C>): LabeledRationalFunction<C> =
|
|
||||||
LabeledRationalFunction<C>(numerator, LabeledPolynomial(mapOf(emptyMap<Symbol, UInt>() to one), toCheckInput = false))
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
|
|
||||||
LabeledRationalFunction<C>(
|
|
||||||
LabeledPolynomial(numeratorCoefficients, toCheckInput = true),
|
|
||||||
polynomialOne
|
|
||||||
)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
|
|
||||||
LabeledRationalFunction<C>(
|
|
||||||
LabeledPolynomial(numeratorCoefficients, toCheckInput = true),
|
|
||||||
LabeledPolynomial(mapOf(emptyMap<Symbol, UInt>() to one), toCheckInput = false)
|
|
||||||
)
|
|
||||||
|
|
||||||
//context(A)
|
|
||||||
//public fun <C, A: Ring<C>> Symbol.asLabeledRationalFunction() : LabeledRationalFunction<C> = LabeledRationalFunction(asLabeledPolynomial())
|
|
||||||
//context(LabeledRationalFunctionSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> Symbol.asLabeledRationalFunction() : LabeledRationalFunction<C> = LabeledRationalFunction(asLabeledPolynomial())
|
|
||||||
|
|
||||||
//context(A)
|
|
||||||
//public fun <C, A: Ring<C>> C.asLabeledRationalFunction() : LabeledRationalFunction<C> = LabeledRationalFunction(asLabeledPolynomial())
|
|
||||||
//context(LabeledRationalFunctionSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> C.asLabeledRationalFunction() : LabeledRationalFunction<C> = LabeledRationalFunction(asLabeledPolynomial())
|
|
@ -1,495 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.expressions.Symbol
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.Field
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
import space.kscience.kmath.operations.invoke
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Docs
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a [LabeledPolynomialSpace] over a received ring.
|
|
||||||
*/
|
|
||||||
public fun <C, A : Ring<C>> A.labeledPolynomial(): LabeledPolynomialSpace<C, A> =
|
|
||||||
LabeledPolynomialSpace(this)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a [LabeledPolynomialSpace]'s scope over a received ring.
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalContracts::class)
|
|
||||||
public inline fun <C, A : Ring<C>, R> A.labeledPolynomial(block: LabeledPolynomialSpace<C, A>.() -> R): R {
|
|
||||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
|
||||||
return LabeledPolynomialSpace(this).block()
|
|
||||||
}
|
|
||||||
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] with names of variables substituted with names from [names].
|
|
||||||
// * Consider that monomials are sorted in lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(LabeledPolynomialSpace<C, A>)
|
|
||||||
//fun <C, A: Ring<C>> LabeledPolynomial<C>.represent(names: Map<Symbol, String> = emptyMap()): String =
|
|
||||||
// coefficients.entries
|
|
||||||
// .sortedWith { o1, o2 -> LabeledPolynomial.monomialComparator.compare(o1.key, o2.key) }
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, t) ->
|
|
||||||
// if (degs.isEmpty()) "$t"
|
|
||||||
// else {
|
|
||||||
// when {
|
|
||||||
// t.isOne() -> ""
|
|
||||||
// t.isMinusOne() -> "-"
|
|
||||||
// else -> "$t "
|
|
||||||
// } +
|
|
||||||
// degs
|
|
||||||
// .toSortedMap()
|
|
||||||
// .filter { it.value > 0U }
|
|
||||||
// .map { (variable, deg) ->
|
|
||||||
// val variableName = names.getOrDefault(variable, variable.toString())
|
|
||||||
// when (deg) {
|
|
||||||
// 1U -> variableName
|
|
||||||
// else -> "$variableName^$deg"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " ") { it }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " + ") { it }
|
|
||||||
// .ifEmpty { "0" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] naming variables by [namer].
|
|
||||||
// * Consider that monomials are sorted in lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(LabeledPolynomialSpace<C, A>)
|
|
||||||
//fun <C, A: Ring<C>> LabeledPolynomial<C>.represent(namer: (Symbol) -> String): String =
|
|
||||||
// coefficients.entries
|
|
||||||
// .sortedWith { o1, o2 -> LabeledPolynomial.monomialComparator.compare(o1.key, o2.key) }
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, t) ->
|
|
||||||
// if (degs.isEmpty()) "$t"
|
|
||||||
// else {
|
|
||||||
// when {
|
|
||||||
// t.isOne() -> ""
|
|
||||||
// t.isMinusOne() -> "-"
|
|
||||||
// else -> "$t "
|
|
||||||
// } +
|
|
||||||
// degs
|
|
||||||
// .toSortedMap()
|
|
||||||
// .filter { it.value > 0U }
|
|
||||||
// .map { (variable, deg) ->
|
|
||||||
// when (deg) {
|
|
||||||
// 1U -> namer(variable)
|
|
||||||
// else -> "${namer(variable)}^$deg"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " ") { it }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " + ") { it }
|
|
||||||
// .ifEmpty { "0" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] with names of variables substituted with names from [names] and with
|
|
||||||
// * brackets around the string if needed (i.e. when there are at least two addends in the representation).
|
|
||||||
// * Consider that monomials are sorted in lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(LabeledPolynomialSpace<C, A>)
|
|
||||||
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representWithBrackets(names: Map<Symbol, String> = emptyMap()): String =
|
|
||||||
// with(represent(names)) { if (coefficients.count() == 1) this else "($this)" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] naming variables by [namer] and with brackets around the string if needed
|
|
||||||
// * (i.e. when there are at least two addends in the representation).
|
|
||||||
// * Consider that monomials are sorted in lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(LabeledPolynomialSpace<C, A>)
|
|
||||||
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representWithBrackets(namer: (Symbol) -> String): String =
|
|
||||||
// with(represent(namer)) { if (coefficients.count() == 1) this else "($this)" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] with names of variables substituted with names from [names].
|
|
||||||
// * Consider that monomials are sorted in **reversed** lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(LabeledPolynomialSpace<C, A>)
|
|
||||||
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representReversed(names: Map<Symbol, String> = emptyMap()): String =
|
|
||||||
// coefficients.entries
|
|
||||||
// .sortedWith { o1, o2 -> -LabeledPolynomial.monomialComparator.compare(o1.key, o2.key) }
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, t) ->
|
|
||||||
// if (degs.isEmpty()) "$t"
|
|
||||||
// else {
|
|
||||||
// when {
|
|
||||||
// t.isOne() -> ""
|
|
||||||
// t.isMinusOne() -> "-"
|
|
||||||
// else -> "$t "
|
|
||||||
// } +
|
|
||||||
// degs
|
|
||||||
// .toSortedMap()
|
|
||||||
// .filter { it.value > 0U }
|
|
||||||
// .map { (variable, deg) ->
|
|
||||||
// val variableName = names.getOrDefault(variable, variable.toString())
|
|
||||||
// when (deg) {
|
|
||||||
// 1U -> variableName
|
|
||||||
// else -> "$variableName^$deg"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " ") { it }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " + ") { it }
|
|
||||||
// .ifEmpty { "0" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] naming variables by [namer].
|
|
||||||
// * Consider that monomials are sorted in **reversed** lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(LabeledPolynomialSpace<C, A>)
|
|
||||||
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representReversed(namer: (Symbol) -> String): String =
|
|
||||||
// coefficients.entries
|
|
||||||
// .sortedWith { o1, o2 -> -LabeledPolynomial.monomialComparator.compare(o1.key, o2.key) }
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, t) ->
|
|
||||||
// if (degs.isEmpty()) "$t"
|
|
||||||
// else {
|
|
||||||
// when {
|
|
||||||
// t.isOne() -> ""
|
|
||||||
// t.isMinusOne() -> "-"
|
|
||||||
// else -> "$t "
|
|
||||||
// } +
|
|
||||||
// degs
|
|
||||||
// .toSortedMap()
|
|
||||||
// .filter { it.value > 0U }
|
|
||||||
// .map { (variable, deg) ->
|
|
||||||
// when (deg) {
|
|
||||||
// 1U -> namer(variable)
|
|
||||||
// else -> "${namer(variable)}^$deg"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " ") { it }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " + ") { it }
|
|
||||||
// .ifEmpty { "0" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] with names of variables substituted with names from [names] and with
|
|
||||||
// * brackets around the string if needed (i.e. when there are at least two addends in the representation).
|
|
||||||
// * Consider that monomials are sorted in **reversed** lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(LabeledPolynomialSpace<C, A>)
|
|
||||||
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representReversedWithBrackets(names: Map<Symbol, String> = emptyMap()): String =
|
|
||||||
// with(representReversed(names)) { if (coefficients.count() == 1) this else "($this)" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] naming variables by [namer] and with brackets around the string if needed
|
|
||||||
// * (i.e. when there are at least two addends in the representation).
|
|
||||||
// * Consider that monomials are sorted in **reversed** lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(LabeledPolynomialSpace<C, A>)
|
|
||||||
//fun <C, A: Ring<C>> LabeledPolynomial<C>.representReversedWithBrackets(namer: (Symbol) -> String): String =
|
|
||||||
// with(representReversed(namer)) { if (coefficients.count() == 1) this else "($this)" }
|
|
||||||
|
|
||||||
//operator fun <T: Field<T>> Polynomial<T>.div(other: T): Polynomial<T> =
|
|
||||||
// if (other.isZero()) throw ArithmeticException("/ by zero")
|
|
||||||
// else
|
|
||||||
// Polynomial(
|
|
||||||
// coefficients
|
|
||||||
// .mapValues { it.value / other },
|
|
||||||
// toCheckInput = false
|
|
||||||
// )
|
|
||||||
|
|
||||||
//public fun <C> LabeledPolynomial<C>.substitute(ring: Ring<C>, args: Map<Symbol, C>): LabeledPolynomial<C> = ring {
|
|
||||||
// if (coefficients.isEmpty()) return this@substitute
|
|
||||||
// LabeledPolynomial<C>(
|
|
||||||
// buildMap {
|
|
||||||
// coefficients.forEach { (degs, c) ->
|
|
||||||
// val newDegs = degs.filterKeys { it !in args }
|
|
||||||
// val newC = degs.entries.asSequence().filter { it.key in args }.fold(c) { acc, (variable, deg) ->
|
|
||||||
// multiplyWithPower(acc, args[variable]!!, deg)
|
|
||||||
// }
|
|
||||||
// this[newDegs] = if (newDegs in this) this[newDegs]!! + newC else newC
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// TODO: Replace with optimisation: the [result] may be unboxed, and all operations may be performed as soon as
|
|
||||||
//// possible on it
|
|
||||||
//@JvmName("substitutePolynomial")
|
|
||||||
//fun <C> LabeledPolynomial<C>.substitute(ring: Ring<C>, arg: Map<Symbol, LabeledPolynomial<C>>) : LabeledPolynomial<C> =
|
|
||||||
// ring.labeledPolynomial {
|
|
||||||
// if (coefficients.isEmpty()) return zero
|
|
||||||
// coefficients
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, c) ->
|
|
||||||
// degs.entries
|
|
||||||
// .asSequence()
|
|
||||||
// .filter { it.key in arg }
|
|
||||||
// .fold(LabeledPolynomial(mapOf(degs.filterKeys { it !in arg } to c))) { acc, (index, deg) ->
|
|
||||||
// multiplyWithPower(acc, arg[index]!!, deg)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .reduce { acc, polynomial -> acc + polynomial } // TODO: Rewrite. Might be slow.
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//// TODO: Substitute rational function
|
|
||||||
//
|
|
||||||
//fun <C, A : Ring<C>> LabeledPolynomial<C>.asFunctionOver(ring: A): (Map<Symbol, C>) -> LabeledPolynomial<C> =
|
|
||||||
// { substitute(ring, it) }
|
|
||||||
//
|
|
||||||
//fun <C, A : Ring<C>> LabeledPolynomial<C>.asPolynomialFunctionOver(ring: A): (Map<Symbol, LabeledPolynomial<C>>) -> LabeledPolynomial<C> =
|
|
||||||
// { substitute(ring, it) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Ring<C>> LabeledPolynomial<C>.derivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variable: Symbol,
|
|
||||||
): LabeledPolynomial<C> = algebra {
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
if (variable !in degs) return@forEach
|
|
||||||
put(
|
|
||||||
buildMap {
|
|
||||||
degs.forEach { (vari, deg) ->
|
|
||||||
when {
|
|
||||||
vari != variable -> put(vari, deg)
|
|
||||||
deg > 1u -> put(vari, deg - 1u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
multiplyByDoubling(c, degs[variable]!!)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Ring<C>> LabeledPolynomial<C>.derivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variables: Collection<Symbol>,
|
|
||||||
): LabeledPolynomial<C> = algebra {
|
|
||||||
val cleanedVariables = variables.toSet()
|
|
||||||
if (cleanedVariables.isEmpty()) return this@derivativeWithRespectTo
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
if (!degs.keys.containsAll(cleanedVariables)) return@forEach
|
|
||||||
put(
|
|
||||||
buildMap {
|
|
||||||
degs.forEach { (vari, deg) ->
|
|
||||||
when {
|
|
||||||
vari !in cleanedVariables -> put(vari, deg)
|
|
||||||
deg > 1u -> put(vari, deg - 1u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cleanedVariables.fold(c) { acc, variable -> multiplyByDoubling(acc, degs[variable]!!) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Ring<C>> LabeledPolynomial<C>.nthDerivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variable: Symbol,
|
|
||||||
order: UInt
|
|
||||||
): LabeledPolynomial<C> = algebra {
|
|
||||||
if (order == 0u) return this@nthDerivativeWithRespectTo
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
if (degs.getOrElse(variable) { 0u } < order) return@forEach
|
|
||||||
put(
|
|
||||||
buildMap {
|
|
||||||
degs.forEach { (vari, deg) ->
|
|
||||||
when {
|
|
||||||
vari != variable -> put(vari, deg)
|
|
||||||
deg > order -> put(vari, deg - order)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
degs[variable]!!.let { deg ->
|
|
||||||
(deg downTo deg - order + 1u)
|
|
||||||
.fold(c) { acc, ord -> multiplyByDoubling(acc, ord) }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Ring<C>> LabeledPolynomial<C>.nthDerivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variablesAndOrders: Map<Symbol, UInt>,
|
|
||||||
): LabeledPolynomial<C> = algebra {
|
|
||||||
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
|
|
||||||
if (filteredVariablesAndOrders.isEmpty()) return this@nthDerivativeWithRespectTo
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
if (filteredVariablesAndOrders.any { (variable, order) -> degs.getOrElse(variable) { 0u } < order }) return@forEach
|
|
||||||
put(
|
|
||||||
buildMap {
|
|
||||||
degs.forEach { (vari, deg) ->
|
|
||||||
if (vari !in filteredVariablesAndOrders) put(vari, deg)
|
|
||||||
else {
|
|
||||||
val order = filteredVariablesAndOrders[vari]!!
|
|
||||||
if (deg > order) put(vari, deg - order)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) ->
|
|
||||||
degs[index]!!.let { deg ->
|
|
||||||
(deg downTo deg - order + 1u)
|
|
||||||
.fold(acc1) { acc2, ord -> multiplyByDoubling(acc2, ord) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic antiderivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Field<C>> LabeledPolynomial<C>.antiderivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variable: Symbol,
|
|
||||||
): LabeledPolynomial<C> = algebra {
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
val newDegs = buildMap<Symbol, UInt>(degs.size + 1) {
|
|
||||||
put(variable, 1u)
|
|
||||||
for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u })
|
|
||||||
}
|
|
||||||
put(
|
|
||||||
newDegs,
|
|
||||||
c / multiplyByDoubling(one, newDegs[variable]!!)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic antiderivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Field<C>> LabeledPolynomial<C>.antiderivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variables: Collection<Symbol>,
|
|
||||||
): LabeledPolynomial<C> = algebra {
|
|
||||||
val cleanedVariables = variables.toSet()
|
|
||||||
if (cleanedVariables.isEmpty()) return this@antiderivativeWithRespectTo
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
val newDegs = buildMap<Symbol, UInt>(degs.size + 1) {
|
|
||||||
for (variable in cleanedVariables) put(variable, 1u)
|
|
||||||
for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u })
|
|
||||||
}
|
|
||||||
put(
|
|
||||||
newDegs,
|
|
||||||
cleanedVariables.fold(c) { acc, variable -> acc / multiplyByDoubling(one, newDegs[variable]!!) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Field<C>> LabeledPolynomial<C>.nthAntiderivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variable: Symbol,
|
|
||||||
order: UInt
|
|
||||||
): LabeledPolynomial<C> = algebra {
|
|
||||||
if (order == 0u) return this@nthAntiderivativeWithRespectTo
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
val newDegs = buildMap<Symbol, UInt>(degs.size + 1) {
|
|
||||||
put(variable, order)
|
|
||||||
for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u })
|
|
||||||
}
|
|
||||||
put(
|
|
||||||
newDegs,
|
|
||||||
newDegs[variable]!!.let { deg ->
|
|
||||||
(deg downTo deg - order + 1u)
|
|
||||||
.fold(c) { acc, ord -> acc / multiplyByDoubling(one, ord) }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Field<C>> LabeledPolynomial<C>.nthAntiderivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variablesAndOrders: Map<Symbol, UInt>,
|
|
||||||
): LabeledPolynomial<C> = algebra {
|
|
||||||
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
|
|
||||||
if (filteredVariablesAndOrders.isEmpty()) return this@nthAntiderivativeWithRespectTo
|
|
||||||
LabeledPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
val newDegs = buildMap<Symbol, UInt>(degs.size + 1) {
|
|
||||||
for ((variable, order) in filteredVariablesAndOrders) put(variable, order)
|
|
||||||
for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u })
|
|
||||||
}
|
|
||||||
put(
|
|
||||||
newDegs,
|
|
||||||
filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) ->
|
|
||||||
newDegs[index]!!.let { deg ->
|
|
||||||
(deg downTo deg - order + 1u)
|
|
||||||
.fold(acc1) { acc2, ord -> acc2 / multiplyByDoubling(one, ord) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a [LabeledRationalFunctionSpace] over a received ring.
|
|
||||||
*/
|
|
||||||
public fun <C, A : Ring<C>> A.labeledRationalFunction(): LabeledRationalFunctionSpace<C, A> =
|
|
||||||
LabeledRationalFunctionSpace(this)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a [LabeledRationalFunctionSpace]'s scope over a received ring.
|
|
||||||
*/
|
|
||||||
public inline fun <C, A : Ring<C>, R> A.labeledRationalFunction(block: LabeledRationalFunctionSpace<C, A>.() -> R): R {
|
|
||||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
|
||||||
return LabeledRationalFunctionSpace(this).block()
|
|
||||||
}
|
|
||||||
|
|
||||||
//fun <T: Field<T>> LabeledRationalFunction<T>.reduced(): LabeledRationalFunction<T> {
|
|
||||||
// val greatestCommonDivider = polynomialGCD(numerator, denominator)
|
|
||||||
// return LabeledRationalFunction(
|
|
||||||
// numerator / greatestCommonDivider,
|
|
||||||
// denominator / greatestCommonDivider
|
|
||||||
// )
|
|
||||||
//}
|
|
@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.Ring
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the same degrees' description of the monomial, but without extra zero degrees on the end.
|
|
||||||
*/
|
|
||||||
internal fun List<UInt>.cleanUp() = subList(0, indexOfLast { it != 0U } + 1)
|
|
||||||
|
|
||||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
|
||||||
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(coefs, toCheckInput)
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(coefs, toCheckInput)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
internal fun <C, A: Ring<C>> A.NumberedPolynomial(coefs: Map<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> {
|
|
||||||
if (!toCheckInput) return NumberedPolynomial<C>(coefs)
|
|
||||||
|
|
||||||
val fixedCoefs = mutableMapOf<List<UInt>, C>()
|
|
||||||
|
|
||||||
for (entry in coefs) {
|
|
||||||
val key = entry.key.cleanUp()
|
|
||||||
val value = entry.value
|
|
||||||
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
|
|
||||||
}
|
|
||||||
|
|
||||||
return NumberedPolynomial<C>(fixedCoefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(pairs, toCheckInput)
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(pairs, toCheckInput)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
internal fun <C, A: Ring<C>> A.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>, toCheckInput: Boolean = true) : NumberedPolynomial<C> {
|
|
||||||
if (!toCheckInput) return NumberedPolynomial<C>(pairs.toMap())
|
|
||||||
|
|
||||||
val fixedCoefs = mutableMapOf<List<UInt>, C>()
|
|
||||||
|
|
||||||
for (entry in pairs) {
|
|
||||||
val key = entry.first.cleanUp()
|
|
||||||
val value = entry.second
|
|
||||||
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
|
|
||||||
}
|
|
||||||
|
|
||||||
return NumberedPolynomial<C>(fixedCoefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(pairs = pairs, toCheckInput = toCheckInput)
|
|
||||||
@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> = ring.NumberedPolynomial(pairs = pairs, toCheckInput = toCheckInput)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
internal fun <C, A: Ring<C>> A.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>, toCheckInput: Boolean = true) : NumberedPolynomial<C> {
|
|
||||||
if (!toCheckInput) return NumberedPolynomial<C>(pairs.toMap())
|
|
||||||
|
|
||||||
val fixedCoefs = mutableMapOf<List<UInt>, C>()
|
|
||||||
|
|
||||||
for (entry in pairs) {
|
|
||||||
val key = entry.first.cleanUp()
|
|
||||||
val value = entry.second
|
|
||||||
fixedCoefs[key] = if (key in fixedCoefs) fixedCoefs[key]!! + value else value
|
|
||||||
}
|
|
||||||
|
|
||||||
return NumberedPolynomial<C>(fixedCoefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs, toCheckInput = true)
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs, toCheckInput = true)
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs, toCheckInput = true)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs, toCheckInput = true)
|
|
||||||
|
|
||||||
public fun <C> C.asNumberedPolynomial() : NumberedPolynomial<C> = NumberedPolynomial<C>(mapOf(emptyList<UInt>() to this))
|
|
||||||
|
|
||||||
@DslMarker
|
|
||||||
@UnstableKMathAPI
|
|
||||||
internal annotation class NumberedPolynomialConstructorDSL
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@NumberedPolynomialConstructorDSL
|
|
||||||
public class NumberedPolynomialTermSignatureBuilder {
|
|
||||||
private val signature: MutableList<UInt> = ArrayList()
|
|
||||||
public fun build(): List<UInt> = signature
|
|
||||||
public infix fun Int.inPowerOf(deg: UInt) {
|
|
||||||
if (this > signature.lastIndex) {
|
|
||||||
signature.addAll(List(this - signature.lastIndex - 1) { 0u })
|
|
||||||
signature.add(deg)
|
|
||||||
} else {
|
|
||||||
signature[this] = deg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun Int.pow(deg: UInt): Unit = this inPowerOf deg
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun Int.`in`(deg: UInt): Unit = this inPowerOf deg
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun Int.of(deg: UInt): Unit = this inPowerOf deg
|
|
||||||
}
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public class NumberedPolynomialBuilder<C>(private val zero: C, private val add: (C, C) -> C, capacity: Int = 0) {
|
|
||||||
private val coefficients: MutableMap<List<UInt>, C> = LinkedHashMap(capacity)
|
|
||||||
public fun build(): NumberedPolynomial<C> = NumberedPolynomial<C>(coefficients)
|
|
||||||
public operator fun C.invoke(block: NumberedPolynomialTermSignatureBuilder.() -> Unit) {
|
|
||||||
val signature = NumberedPolynomialTermSignatureBuilder().apply(block).build()
|
|
||||||
coefficients[signature] = add(coefficients.getOrElse(signature) { zero }, this@invoke)
|
|
||||||
}
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun C.with(noinline block: NumberedPolynomialTermSignatureBuilder.() -> Unit): Unit = this.invoke(block)
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public inline infix fun (NumberedPolynomialTermSignatureBuilder.() -> Unit).with(coef: C): Unit = coef.invoke(this)
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
public infix fun sig(block: NumberedPolynomialTermSignatureBuilder.() -> Unit): NumberedPolynomialTermSignatureBuilder.() -> Unit = block
|
|
||||||
}
|
|
||||||
|
|
||||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@NumberedPolynomialConstructorDSL
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public inline fun <C, A: Ring<C>> A.NumberedPolynomial(block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = NumberedPolynomialBuilder(zero, ::add).apply(block).build()
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@NumberedPolynomialConstructorDSL
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public inline fun <C, A: Ring<C>> A.NumberedPolynomial(capacity: Int, block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = NumberedPolynomialBuilder(zero, ::add, capacity).apply(block).build()
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@NumberedPolynomialConstructorDSL
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = NumberedPolynomialBuilder(constantZero, { left: C, right: C -> left + right}).apply(block).build()
|
|
||||||
@UnstableKMathAPI
|
|
||||||
@NumberedPolynomialConstructorDSL
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(capacity: Int, block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = NumberedPolynomialBuilder(constantZero, { left: C, right: C -> left + right}, capacity).apply(block).build()
|
|
||||||
|
|
||||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>, denominatorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
|
|
||||||
NumberedRationalFunction<C>(
|
|
||||||
NumberedPolynomial(numeratorCoefficients, toCheckInput = true),
|
|
||||||
NumberedPolynomial(denominatorCoefficients, toCheckInput = true)
|
|
||||||
)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>, denominatorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
|
|
||||||
NumberedRationalFunction<C>(
|
|
||||||
NumberedPolynomial(numeratorCoefficients, toCheckInput = true),
|
|
||||||
NumberedPolynomial(denominatorCoefficients, toCheckInput = true)
|
|
||||||
)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numerator: NumberedPolynomial<C>): NumberedRationalFunction<C> =
|
|
||||||
NumberedRationalFunction<C>(numerator, polynomialOne)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.NumberedRationalFunction(numerator: NumberedPolynomial<C>): NumberedRationalFunction<C> =
|
|
||||||
NumberedRationalFunction<C>(numerator, NumberedPolynomial(mapOf(emptyList<UInt>() to one), toCheckInput = false))
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
|
|
||||||
NumberedRationalFunction<C>(
|
|
||||||
NumberedPolynomial(numeratorCoefficients, toCheckInput = true),
|
|
||||||
polynomialOne
|
|
||||||
)
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
public fun <C, A: Ring<C>> A.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
|
|
||||||
NumberedRationalFunction<C>(
|
|
||||||
NumberedPolynomial(numeratorCoefficients, toCheckInput = true),
|
|
||||||
NumberedPolynomial(mapOf(emptyList<UInt>() to one), toCheckInput = false)
|
|
||||||
)
|
|
||||||
|
|
||||||
//context(A)
|
|
||||||
//public fun <C, A: Ring<C>> C.asNumberedRationalFunction() : NumberedRationalFunction<C> = NumberedRationalFunction(asLabeledPolynomial())
|
|
||||||
//context(NumberedRationalFunctionSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> C.asNumberedRationalFunction() : NumberedRationalFunction<C> = NumberedRationalFunction(asLabeledPolynomial())
|
|
@ -1,528 +0,0 @@
|
|||||||
package space.kscience.kmath.functions
|
|
||||||
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.*
|
|
||||||
import kotlin.contracts.*
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Docs
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a [NumberedPolynomialSpace] over a received ring.
|
|
||||||
*/
|
|
||||||
public fun <C, A : Ring<C>> A.numberedPolynomial(): NumberedPolynomialSpace<C, A> =
|
|
||||||
NumberedPolynomialSpace(this)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a [NumberedPolynomialSpace]'s scope over a received ring.
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalContracts::class)
|
|
||||||
public inline fun <C, A : Ring<C>, R> A.numberedPolynomial(block: NumberedPolynomialSpace<C, A>.() -> R): R {
|
|
||||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
|
||||||
return NumberedPolynomialSpace(this).block()
|
|
||||||
}
|
|
||||||
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] where name of variable with index `i` is [withVariableName] + `"_${i+1}"`.
|
|
||||||
// * Consider that monomials are sorted in lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(NumberedPolynomialSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.represent(withVariableName: String = NumberedPolynomial.defaultVariableName): String =
|
|
||||||
// coefficients.entries
|
|
||||||
// .sortedWith { o1, o2 -> NumberedPolynomial.monomialComparator.compare(o1.key, o2.key) }
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, t) ->
|
|
||||||
// if (degs.isEmpty()) "$t"
|
|
||||||
// else {
|
|
||||||
// when {
|
|
||||||
// t.isOne() -> ""
|
|
||||||
// t.isMinusOne() -> "-"
|
|
||||||
// else -> "$t "
|
|
||||||
// } +
|
|
||||||
// degs
|
|
||||||
// .mapIndexed { index, deg ->
|
|
||||||
// when (deg) {
|
|
||||||
// 0U -> ""
|
|
||||||
// 1U -> "${withVariableName}_${index+1}"
|
|
||||||
// else -> "${withVariableName}_${index+1}^$deg"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .filter { it.isNotEmpty() }
|
|
||||||
// .joinToString(separator = " ") { it }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " + ") { it }
|
|
||||||
// .ifEmpty { "0" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] naming variables by [namer].
|
|
||||||
// * Consider that monomials are sorted in lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(NumberedPolynomialSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.represent(namer: (Int) -> String): String =
|
|
||||||
// coefficients.entries
|
|
||||||
// .sortedWith { o1, o2 -> NumberedPolynomial.monomialComparator.compare(o1.key, o2.key) }
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, t) ->
|
|
||||||
// if (degs.isEmpty()) "$t"
|
|
||||||
// else {
|
|
||||||
// when {
|
|
||||||
// t.isOne() -> ""
|
|
||||||
// t.isMinusOne() -> "-"
|
|
||||||
// else -> "$t "
|
|
||||||
// } +
|
|
||||||
// degs
|
|
||||||
// .mapIndexed { index, deg ->
|
|
||||||
// when (deg) {
|
|
||||||
// 0U -> ""
|
|
||||||
// 1U -> namer(index)
|
|
||||||
// else -> "${namer(index)}^$deg"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .filter { it.isNotEmpty() }
|
|
||||||
// .joinToString(separator = " ") { it }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " + ") { it }
|
|
||||||
// .ifEmpty { "0" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] where name of variable with index `i` is [withVariableName] + `"_${i+1}"`
|
|
||||||
// * and with brackets around the string if needed (i.e. when there are at least two addends in the representation).
|
|
||||||
// * Consider that monomials are sorted in lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(NumberedPolynomialSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representWithBrackets(withVariableName: String = NumberedPolynomial.defaultVariableName): String =
|
|
||||||
// with(represent(withVariableName)) { if (coefficients.count() == 1) this else "($this)" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] naming variables by [namer] and with brackets around the string if needed
|
|
||||||
// * (i.e. when there are at least two addends in the representation).
|
|
||||||
// * Consider that monomials are sorted in lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(NumberedPolynomialSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representWithBrackets(namer: (Int) -> String): String =
|
|
||||||
// with(represent(namer)) { if (coefficients.count() == 1) this else "($this)" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] where name of variable with index `i` is [withVariableName] + `"_${i+1}"`.
|
|
||||||
// * Consider that monomials are sorted in **reversed** lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(NumberedPolynomialSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representReversed(withVariableName: String = NumberedPolynomial.defaultVariableName): String =
|
|
||||||
// coefficients.entries
|
|
||||||
// .sortedWith { o1, o2 -> -NumberedPolynomial.monomialComparator.compare(o1.key, o2.key) }
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, t) ->
|
|
||||||
// if (degs.isEmpty()) "$t"
|
|
||||||
// else {
|
|
||||||
// when {
|
|
||||||
// t.isOne() -> ""
|
|
||||||
// t.isMinusOne() -> "-"
|
|
||||||
// else -> "$t "
|
|
||||||
// } +
|
|
||||||
// degs
|
|
||||||
// .mapIndexed { index, deg ->
|
|
||||||
// when (deg) {
|
|
||||||
// 0U -> ""
|
|
||||||
// 1U -> "${withVariableName}_${index+1}"
|
|
||||||
// else -> "${withVariableName}_${index+1}^$deg"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .filter { it.isNotEmpty() }
|
|
||||||
// .joinToString(separator = " ") { it }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " + ") { it }
|
|
||||||
// .ifEmpty { "0" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] naming variables by [namer].
|
|
||||||
// * Consider that monomials are sorted in **reversed** lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(NumberedPolynomialSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representReversed(namer: (Int) -> String): String =
|
|
||||||
// coefficients.entries
|
|
||||||
// .sortedWith { o1, o2 -> -NumberedPolynomial.monomialComparator.compare(o1.key, o2.key) }
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, t) ->
|
|
||||||
// if (degs.isEmpty()) "$t"
|
|
||||||
// else {
|
|
||||||
// when {
|
|
||||||
// t.isOne() -> ""
|
|
||||||
// t.isMinusOne() -> "-"
|
|
||||||
// else -> "$t "
|
|
||||||
// } +
|
|
||||||
// degs
|
|
||||||
// .mapIndexed { index, deg ->
|
|
||||||
// when (deg) {
|
|
||||||
// 0U -> ""
|
|
||||||
// 1U -> namer(index)
|
|
||||||
// else -> "${namer(index)}^$deg"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .filter { it.isNotEmpty() }
|
|
||||||
// .joinToString(separator = " ") { it }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .joinToString(separator = " + ") { it }
|
|
||||||
// .ifEmpty { "0" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] where name of variable with index `i` is [withVariableName] + `"_${i+1}"`
|
|
||||||
// * and with brackets around the string if needed (i.e. when there are at least two addends in the representation).
|
|
||||||
// * Consider that monomials are sorted in **reversed** lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(NumberedPolynomialSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representReversedWithBrackets(withVariableName: String = NumberedPolynomial.defaultVariableName): String =
|
|
||||||
// with(representReversed(withVariableName)) { if (coefficients.count() == 1) this else "($this)" }
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Represents the polynomial as a [String] naming variables by [namer] and with brackets around the string if needed
|
|
||||||
// * (i.e. when there are at least two addends in the representation).
|
|
||||||
// * Consider that monomials are sorted in **reversed** lexicographic order.
|
|
||||||
// */
|
|
||||||
//context(NumberedPolynomialSpace<C, A>)
|
|
||||||
//public fun <C, A: Ring<C>> NumberedPolynomial<C>.representReversedWithBrackets(namer: (Int) -> String): String =
|
|
||||||
// with(representReversed(namer)) { if (coefficients.count() == 1) this else "($this)" }
|
|
||||||
|
|
||||||
//public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, C>): NumberedPolynomial<C> = ring {
|
|
||||||
// if (coefficients.isEmpty()) return this@substitute
|
|
||||||
// NumberedPolynomial<C>(
|
|
||||||
// buildMap {
|
|
||||||
// coefficients.forEach { (degs, c) ->
|
|
||||||
// val newDegs = degs.mapIndexed { index, deg -> if (index in args) 0U else deg }.cleanUp()
|
|
||||||
// val newC = degs.foldIndexed(c) { index, acc, deg ->
|
|
||||||
// if (index in args) multiplyWithPower(acc, args[index]!!, deg)
|
|
||||||
// else acc
|
|
||||||
// }
|
|
||||||
// this[newDegs] = if (newDegs in this) this[newDegs]!! + newC else newC
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// TODO: Replace with optimisation: the [result] may be unboxed, and all operations may be performed as soon as
|
|
||||||
//// possible on it
|
|
||||||
//@JvmName("substitutePolynomial")
|
|
||||||
//public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, arg: Map<Int, NumberedPolynomial<C>>) : NumberedPolynomial<C> =
|
|
||||||
// ring.numberedPolynomialSpace {
|
|
||||||
// if (coefficients.isEmpty()) return zero
|
|
||||||
// coefficients
|
|
||||||
// .asSequence()
|
|
||||||
// .map { (degs, c) ->
|
|
||||||
// degs.foldIndexed(
|
|
||||||
// NumberedPolynomial(
|
|
||||||
// degs.mapIndexed { index, deg -> if (index in arg) 0U else deg } to c
|
|
||||||
// )
|
|
||||||
// ) { index, acc, deg -> if (index in arg) multiplyWithPower(acc, arg[index]!!, deg) else acc }
|
|
||||||
// }
|
|
||||||
// .reduce { acc, polynomial -> acc + polynomial } // TODO: Rewrite. Might be slow.
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//// TODO: Substitute rational function
|
|
||||||
//
|
|
||||||
//public fun <C, A : Ring<C>> NumberedPolynomial<C>.asFunctionOver(ring: A): (Map<Int, C>) -> NumberedPolynomial<C> =
|
|
||||||
// { substitute(ring, it) }
|
|
||||||
//
|
|
||||||
//public fun <C, A : Ring<C>> NumberedPolynomial<C>.asPolynomialFunctionOver(ring: A): (Map<Int, NumberedPolynomial<C>>) -> NumberedPolynomial<C> =
|
|
||||||
// { substitute(ring, it) }
|
|
||||||
|
|
||||||
//operator fun <T: Field<T>> Polynomial<T>.div(other: T): Polynomial<T> =
|
|
||||||
// if (other.isZero()) throw ArithmeticException("/ by zero")
|
|
||||||
// else
|
|
||||||
// Polynomial(
|
|
||||||
// coefficients
|
|
||||||
// .mapValues { it.value / other },
|
|
||||||
// toCheckInput = false
|
|
||||||
// )
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Evaluates the value of the given double polynomial for given double argument.
|
|
||||||
*/
|
|
||||||
public fun NumberedPolynomial<Double>.substitute(args: Map<Int, Double>): NumberedPolynomial<Double> = Double.algebra {
|
|
||||||
val acc = LinkedHashMap<List<UInt>, Double>(coefficients.size)
|
|
||||||
for ((degs, c) in coefficients) {
|
|
||||||
val newDegs = degs.mapIndexed { index, deg -> if (index !in args) deg else 0u }.cleanUp()
|
|
||||||
val newC = args.entries.fold(c) { product, (variable, substitution) ->
|
|
||||||
val deg = degs.getOrElse(variable) { 0u }
|
|
||||||
if (deg == 0u) product else product * substitution.pow(deg.toInt())
|
|
||||||
}
|
|
||||||
if (newDegs !in acc) acc[newDegs] = newC
|
|
||||||
else acc[newDegs] = acc[newDegs]!! + newC
|
|
||||||
}
|
|
||||||
return NumberedPolynomial<Double>(acc)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, C>): NumberedPolynomial<C> = ring {
|
|
||||||
val acc = LinkedHashMap<List<UInt>, C>(coefficients.size)
|
|
||||||
for ((degs, c) in coefficients) {
|
|
||||||
val newDegs = degs.mapIndexed { index, deg -> if (index !in args) deg else 0u }.cleanUp()
|
|
||||||
val newC = args.entries.fold(c) { product, (variable, substitution) ->
|
|
||||||
val deg = degs.getOrElse(variable) { 0u }
|
|
||||||
if (deg == 0u) product else product * power(substitution, deg)
|
|
||||||
}
|
|
||||||
if (newDegs !in acc) acc[newDegs] = newC
|
|
||||||
else acc[newDegs] = acc[newDegs]!! + newC
|
|
||||||
}
|
|
||||||
return NumberedPolynomial<C>(acc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: (Waiting for hero) Replace with optimisation: the [result] may be unboxed, and all operations may be performed
|
|
||||||
// as soon as possible on it
|
|
||||||
@JvmName("substitutePolynomial")
|
|
||||||
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, NumberedPolynomial<C>>) : NumberedPolynomial<C> = TODO() /*ring.numberedPolynomial {
|
|
||||||
val acc = LinkedHashMap<List<UInt>, NumberedPolynomial<C>>(coefficients.size)
|
|
||||||
for ((degs, c) in coefficients) {
|
|
||||||
val newDegs = degs.mapIndexed { index, deg -> if (index !in args) deg else 0u }.cleanUp()
|
|
||||||
val newC = args.entries.fold(c.asNumberedPolynomial()) { product, (variable, substitution) ->
|
|
||||||
val deg = degs.getOrElse(variable) { 0u }
|
|
||||||
if (deg == 0u) product else product * power(substitution, deg)
|
|
||||||
}
|
|
||||||
if (newDegs !in acc) acc[newDegs] = c.asNumberedPolynomial()
|
|
||||||
else acc[newDegs] = acc[newDegs]!! + c
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represent the polynomial as a regular context-less function.
|
|
||||||
*/
|
|
||||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.asFunction(ring: A): (Map<Int, C>) -> NumberedPolynomial<C> = { substitute(ring, it) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represent the polynomial as a regular context-less function.
|
|
||||||
*/
|
|
||||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.asPolynomialFunctionOver(ring: A): (Map<Int, NumberedPolynomial<C>>) -> NumberedPolynomial<C> = { substitute(ring, it) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.derivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variable: Int,
|
|
||||||
): NumberedPolynomial<C> = algebra {
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
if (degs.size > variable) return@forEach
|
|
||||||
put(
|
|
||||||
degs.mapIndexed { index, deg ->
|
|
||||||
when {
|
|
||||||
index != variable -> deg
|
|
||||||
deg > 0u -> deg - 1u
|
|
||||||
else -> return@forEach
|
|
||||||
}
|
|
||||||
}.cleanUp(),
|
|
||||||
multiplyByDoubling(c, degs[variable])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.derivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variables: Collection<Int>,
|
|
||||||
): NumberedPolynomial<C> = algebra {
|
|
||||||
val cleanedVariables = variables.toSet()
|
|
||||||
if (cleanedVariables.isEmpty()) return this@derivativeWithRespectTo
|
|
||||||
val maxRespectedVariable = cleanedVariables.maxOrNull()!!
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
if (degs.size > maxRespectedVariable) return@forEach
|
|
||||||
put(
|
|
||||||
degs.mapIndexed { index, deg ->
|
|
||||||
when {
|
|
||||||
index !in cleanedVariables -> deg
|
|
||||||
deg > 0u -> deg - 1u
|
|
||||||
else -> return@forEach
|
|
||||||
}
|
|
||||||
}.cleanUp(),
|
|
||||||
cleanedVariables.fold(c) { acc, variable -> multiplyByDoubling(acc, degs[variable]) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.nthDerivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variable: Int,
|
|
||||||
order: UInt
|
|
||||||
): NumberedPolynomial<C> = algebra {
|
|
||||||
if (order == 0u) return this@nthDerivativeWithRespectTo
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
if (degs.size > variable) return@forEach
|
|
||||||
put(
|
|
||||||
degs.mapIndexed { index, deg ->
|
|
||||||
when {
|
|
||||||
index != variable -> deg
|
|
||||||
deg >= order -> deg - order
|
|
||||||
else -> return@forEach
|
|
||||||
}
|
|
||||||
}.cleanUp(),
|
|
||||||
degs[variable].let { deg ->
|
|
||||||
(deg downTo deg - order + 1u)
|
|
||||||
.fold(c) { acc, ord -> multiplyByDoubling(acc, ord) }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.nthDerivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variablesAndOrders: Map<Int, UInt>,
|
|
||||||
): NumberedPolynomial<C> = algebra {
|
|
||||||
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
|
|
||||||
if (filteredVariablesAndOrders.isEmpty()) return this@nthDerivativeWithRespectTo
|
|
||||||
val maxRespectedVariable = filteredVariablesAndOrders.keys.maxOrNull()!!
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
if (degs.size > maxRespectedVariable) return@forEach
|
|
||||||
put(
|
|
||||||
degs.mapIndexed { index, deg ->
|
|
||||||
if (index !in filteredVariablesAndOrders) return@mapIndexed deg
|
|
||||||
val order = filteredVariablesAndOrders[index]!!
|
|
||||||
if (deg >= order) deg - order else return@forEach
|
|
||||||
}.cleanUp(),
|
|
||||||
filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) ->
|
|
||||||
degs[index].let { deg ->
|
|
||||||
(deg downTo deg - order + 1u)
|
|
||||||
.fold(acc1) { acc2, ord -> multiplyByDoubling(acc2, ord) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic antiderivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Field<C>> NumberedPolynomial<C>.antiderivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variable: Int,
|
|
||||||
): NumberedPolynomial<C> = algebra {
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
put(
|
|
||||||
List(max(variable + 1, degs.size)) { if (it != variable) degs[it] else degs[it] + 1u },
|
|
||||||
c / multiplyByDoubling(one, degs[variable])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic antiderivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Field<C>> NumberedPolynomial<C>.antiderivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variables: Collection<Int>,
|
|
||||||
): NumberedPolynomial<C> = algebra {
|
|
||||||
val cleanedVariables = variables.toSet()
|
|
||||||
if (cleanedVariables.isEmpty()) return this@antiderivativeWithRespectTo
|
|
||||||
val maxRespectedVariable = cleanedVariables.maxOrNull()!!
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
put(
|
|
||||||
List(max(maxRespectedVariable + 1, degs.size)) { if (it !in variables) degs[it] else degs[it] + 1u },
|
|
||||||
cleanedVariables.fold(c) { acc, variable -> acc / multiplyByDoubling(one, degs[variable]) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Field<C>> NumberedPolynomial<C>.nthAntiderivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variable: Int,
|
|
||||||
order: UInt
|
|
||||||
): NumberedPolynomial<C> = algebra {
|
|
||||||
if (order == 0u) return this@nthAntiderivativeWithRespectTo
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
put(
|
|
||||||
List(max(variable + 1, degs.size)) { if (it != variable) degs[it] else degs[it] + order },
|
|
||||||
degs[variable].let { deg ->
|
|
||||||
(deg downTo deg - order + 1u)
|
|
||||||
.fold(c) { acc, ord -> acc / multiplyByDoubling(one, ord) }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns algebraic derivative of received polynomial.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <C, A : Field<C>> NumberedPolynomial<C>.nthAntiderivativeWithRespectTo(
|
|
||||||
algebra: A,
|
|
||||||
variablesAndOrders: Map<Int, UInt>,
|
|
||||||
): NumberedPolynomial<C> = algebra {
|
|
||||||
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
|
|
||||||
if (filteredVariablesAndOrders.isEmpty()) return this@nthAntiderivativeWithRespectTo
|
|
||||||
val maxRespectedVariable = filteredVariablesAndOrders.keys.maxOrNull()!!
|
|
||||||
NumberedPolynomial<C>(
|
|
||||||
buildMap(coefficients.size) {
|
|
||||||
coefficients
|
|
||||||
.forEach { (degs, c) ->
|
|
||||||
put(
|
|
||||||
List(max(maxRespectedVariable + 1, degs.size)) { degs[it] + filteredVariablesAndOrders.getOrElse(it) { 0u } },
|
|
||||||
filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) ->
|
|
||||||
degs[index].let { deg ->
|
|
||||||
(deg downTo deg - order + 1u)
|
|
||||||
.fold(acc1) { acc2, ord -> acc2 / multiplyByDoubling(one, ord) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a [NumberedRationalFunctionSpace] over a received ring.
|
|
||||||
*/
|
|
||||||
public fun <C, A : Ring<C>> A.numberedRationalFunction(): NumberedRationalFunctionSpace<C, A> =
|
|
||||||
NumberedRationalFunctionSpace(this)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a [NumberedRationalFunctionSpace]'s scope over a received ring.
|
|
||||||
*/
|
|
||||||
public inline fun <C, A : Ring<C>, R> A.numberedRationalFunction(block: NumberedRationalFunctionSpace<C, A>.() -> R): R {
|
|
||||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
|
||||||
return NumberedRationalFunctionSpace(this).block()
|
|
||||||
}
|
|
||||||
|
|
||||||
//fun <T: Field<T>> NumberedRationalFunction<T>.reduced(): NumberedRationalFunction<T> {
|
|
||||||
// val greatestCommonDivider = polynomialGCD(numerator, denominator)
|
|
||||||
// return NumberedRationalFunction(
|
|
||||||
// numerator / greatestCommonDivider,
|
|
||||||
// denominator / greatestCommonDivider
|
|
||||||
// )
|
|
||||||
//}
|
|
@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.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
|
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.*
|
|
||||||
import space.kscience.kmath.structures.Buffer
|
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
|
||||||
import space.kscience.kmath.structures.MutableBufferFactory
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute analytical indefinite integral of this [PiecewisePolynomial], keeping all intervals intact
|
|
||||||
*/
|
|
||||||
@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) })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute definite integral of given [PiecewisePolynomial] piece by piece in a given [range]
|
|
||||||
* Requires [UnivariateIntegrationNodes] or [IntegrationRange] and [IntegrandMaxCalls]
|
|
||||||
*
|
|
||||||
* TODO use context receiver for algebra
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <T : Comparable<T>> PiecewisePolynomial<T>.integrate(
|
|
||||||
algebra: Field<T>, range: ClosedRange<T>,
|
|
||||||
): T = algebra.sum(
|
|
||||||
pieces.map { (region, poly) ->
|
|
||||||
val intersectedRange = maxOf(range.start, region.start)..minOf(range.endInclusive, region.endInclusive)
|
|
||||||
//Check if polynomial range is not used
|
|
||||||
if (intersectedRange.start == intersectedRange.endInclusive) algebra.zero
|
|
||||||
else poly.integrate(algebra, intersectedRange)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A generic spline-interpolation-based analytic integration
|
|
||||||
* * [IntegrationRange]—the univariate range of integration. By default, uses `0..1` interval.
|
|
||||||
* * [IntegrandMaxCalls]—the maximum number of function calls during integration. For non-iterative rules, always uses
|
|
||||||
* the maximum number of points. By default, uses 10 points.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public class SplineIntegrator<T : Comparable<T>>(
|
|
||||||
public val algebra: Field<T>,
|
|
||||||
public val bufferFactory: MutableBufferFactory<T>,
|
|
||||||
) : UnivariateIntegrator<T> {
|
|
||||||
override fun process(integrand: UnivariateIntegrand<T>): UnivariateIntegrand<T> = algebra {
|
|
||||||
val range = integrand.getFeature<IntegrationRange>()?.range ?: 0.0..1.0
|
|
||||||
|
|
||||||
val interpolator: PolynomialInterpolator<T> = SplineInterpolator(algebra, bufferFactory)
|
|
||||||
|
|
||||||
val nodes: Buffer<Double> = integrand.getFeature<UnivariateIntegrationNodes>()?.nodes ?: run {
|
|
||||||
val numPoints = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: 100
|
|
||||||
val step = (range.endInclusive - range.start) / (numPoints - 1)
|
|
||||||
DoubleBuffer(numPoints) { i -> range.start + i * step }
|
|
||||||
}
|
|
||||||
|
|
||||||
val values = nodes.map(bufferFactory) { integrand.function(it) }
|
|
||||||
val polynomials = interpolator.interpolatePolynomials(
|
|
||||||
nodes.map(bufferFactory) { number(it) },
|
|
||||||
values
|
|
||||||
)
|
|
||||||
val res = polynomials.integrate(algebra, number(range.start)..number(range.endInclusive))
|
|
||||||
integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + nodes.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simplified double-based spline-interpolation-based analytic integration
|
|
||||||
* * [IntegrationRange]—the univariate range of integration. By default, uses `0.0..1.0` interval.
|
|
||||||
* * [IntegrandMaxCalls]—the maximum number of function calls during integration. For non-iterative rules, always
|
|
||||||
* uses the maximum number of points. By default, uses 10 points.
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public object DoubleSplineIntegrator : UnivariateIntegrator<Double> {
|
|
||||||
override fun process(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
|
|
||||||
val range = integrand.getFeature<IntegrationRange>()?.range ?: 0.0..1.0
|
|
||||||
val interpolator: PolynomialInterpolator<Double> = SplineInterpolator(DoubleField, ::DoubleBuffer)
|
|
||||||
|
|
||||||
val nodes: Buffer<Double> = integrand.getFeature<UnivariateIntegrationNodes>()?.nodes ?: run {
|
|
||||||
val numPoints = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: 100
|
|
||||||
val step = (range.endInclusive - range.start) / (numPoints - 1)
|
|
||||||
DoubleBuffer(numPoints) { i -> range.start + i * step }
|
|
||||||
}
|
|
||||||
|
|
||||||
val values = nodes.map { integrand.function(it) }
|
|
||||||
val polynomials = interpolator.interpolatePolynomials(nodes, values)
|
|
||||||
val res = polynomials.integrate(DoubleField, range)
|
|
||||||
return integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + nodes.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public inline val DoubleField.splineIntegrator: UnivariateIntegrator<Double>
|
|
||||||
get() = DoubleSplineIntegrator
|
|
@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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:OptIn(UnstableKMathAPI::class)
|
|
||||||
|
|
||||||
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.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
import space.kscience.kmath.structures.Buffer
|
|
||||||
import space.kscience.kmath.structures.asBuffer
|
|
||||||
|
|
||||||
/**
|
|
||||||
* And interpolator for data with x column type [X], y column type [Y].
|
|
||||||
*/
|
|
||||||
public fun interface Interpolator<T, in X : T, Y : T> {
|
|
||||||
public fun interpolate(points: XYColumnarData<T, X, Y>): (X) -> Y
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* And interpolator returning [PiecewisePolynomial] function
|
|
||||||
*/
|
|
||||||
public interface PolynomialInterpolator<T : Comparable<T>> : Interpolator<T, T, T> {
|
|
||||||
public val algebra: Ring<T>
|
|
||||||
|
|
||||||
public fun getDefaultValue(): T = error("Out of bounds")
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
|
|
||||||
x: Buffer<T>,
|
|
||||||
y: Buffer<T>,
|
|
||||||
): PiecewisePolynomial<T> {
|
|
||||||
val pointSet = XYColumnarData.of(x, y)
|
|
||||||
return interpolatePolynomials(pointSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
|
|
||||||
data: Map<T, T>,
|
|
||||||
): PiecewisePolynomial<T> {
|
|
||||||
val pointSet = XYColumnarData.of(data.keys.toList().asBuffer(), data.values.toList().asBuffer())
|
|
||||||
return interpolatePolynomials(pointSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
|
|
||||||
data: List<Pair<T, T>>,
|
|
||||||
): PiecewisePolynomial<T> {
|
|
||||||
val pointSet = XYColumnarData.of(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer())
|
|
||||||
return interpolatePolynomials(pointSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
x: Buffer<T>,
|
|
||||||
y: Buffer<T>,
|
|
||||||
): (T) -> T? = interpolatePolynomials(x, y).asFunction(algebra)
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
data: Map<T, T>,
|
|
||||||
): (T) -> T? = interpolatePolynomials(data).asFunction(algebra)
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
data: List<Pair<T, T>>,
|
|
||||||
): (T) -> T? = interpolatePolynomials(data).asFunction(algebra)
|
|
||||||
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
x: Buffer<T>,
|
|
||||||
y: Buffer<T>,
|
|
||||||
defaultValue: T,
|
|
||||||
): (T) -> T = interpolatePolynomials(x, y).asFunction(algebra, defaultValue)
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
data: Map<T, T>,
|
|
||||||
defaultValue: T,
|
|
||||||
): (T) -> T = interpolatePolynomials(data).asFunction(algebra, defaultValue)
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
data: List<Pair<T, T>>,
|
|
||||||
defaultValue: T,
|
|
||||||
): (T) -> T = interpolatePolynomials(data).asFunction(algebra, defaultValue)
|
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.interpolation
|
|
||||||
|
|
||||||
import space.kscience.kmath.data.XYColumnarData
|
|
||||||
import space.kscience.kmath.functions.PiecewisePolynomial
|
|
||||||
import space.kscience.kmath.functions.ListPolynomial
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.Field
|
|
||||||
import space.kscience.kmath.operations.invoke
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
internal fun <T : Comparable<T>> insureSorted(points: XYColumnarData<*, T, *>) {
|
|
||||||
for (i in 0 until points.size - 1)
|
|
||||||
require(points.x[i + 1] > points.x[i]) { "Input data is not sorted at index $i" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference JVM implementation: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/analysis/interpolation/LinearInterpolator.java
|
|
||||||
*/
|
|
||||||
public class LinearInterpolator<T : Comparable<T>>(override val algebra: Field<T>) : PolynomialInterpolator<T> {
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
override fun interpolatePolynomials(points: XYColumnarData<T, T, T>): PiecewisePolynomial<T> = algebra {
|
|
||||||
require(points.size > 0) { "Point array should not be empty" }
|
|
||||||
insureSorted(points)
|
|
||||||
|
|
||||||
PiecewisePolynomial(points.x[0]) {
|
|
||||||
for (i in 0 until points.size - 1) {
|
|
||||||
val slope = (points.y[i + 1] - points.y[i]) / (points.x[i + 1] - points.x[i])
|
|
||||||
val const = points.y[i] - slope * points.x[i]
|
|
||||||
val polynomial = ListPolynomial(const, slope)
|
|
||||||
putRight(points.x[i + 1], polynomial)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public val <T : Comparable<T>> Field<T>.linearInterpolator: LinearInterpolator<T>
|
|
||||||
get() = LinearInterpolator(this)
|
|
@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.interpolation
|
|
||||||
|
|
||||||
import space.kscience.kmath.data.XYColumnarData
|
|
||||||
import space.kscience.kmath.functions.PiecewisePolynomial
|
|
||||||
import space.kscience.kmath.functions.ListPolynomial
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.DoubleField
|
|
||||||
import space.kscience.kmath.operations.Field
|
|
||||||
import space.kscience.kmath.operations.invoke
|
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
|
||||||
import space.kscience.kmath.structures.MutableBufferFactory
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic spline interpolator. Not recommended for performance critical places, use platform-specific and type
|
|
||||||
* specific ones.
|
|
||||||
*
|
|
||||||
* Based on
|
|
||||||
* https://github.com/apache/commons-math/blob/eb57d6d457002a0bb5336d789a3381a24599affe/src/main/java/org/apache/commons/math4/analysis/interpolation/SplineInterpolator.java
|
|
||||||
*/
|
|
||||||
public class SplineInterpolator<T : Comparable<T>>(
|
|
||||||
override val algebra: Field<T>,
|
|
||||||
public val bufferFactory: MutableBufferFactory<T>,
|
|
||||||
) : PolynomialInterpolator<T> {
|
|
||||||
//TODO possibly optimize zeroed buffers
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
override fun interpolatePolynomials(points: XYColumnarData<T, T, T>): PiecewisePolynomial<T> = algebra {
|
|
||||||
require(points.size >= 3) { "Can't use spline interpolator with less than 3 points" }
|
|
||||||
insureSorted(points)
|
|
||||||
// Number of intervals. The number of data points is n + 1.
|
|
||||||
val n = points.size - 1
|
|
||||||
// Differences between knot points
|
|
||||||
val h = bufferFactory(n) { i -> points.x[i + 1] - points.x[i] }
|
|
||||||
val mu = bufferFactory(n) { zero }
|
|
||||||
val z = bufferFactory(n + 1) { zero }
|
|
||||||
|
|
||||||
for (i in 1 until n) {
|
|
||||||
val g = 2.0 * (points.x[i + 1] - points.x[i - 1]) - h[i - 1] * mu[i - 1]
|
|
||||||
mu[i] = h[i] / g
|
|
||||||
z[i] =
|
|
||||||
((points.y[i + 1] * h[i - 1] - points.y[i] * (points.x[i + 1] - points.x[i - 1]) + points.y[i - 1] * h[i]) * 3.0 /
|
|
||||||
(h[i - 1] * h[i]) - h[i - 1] * z[i - 1]) / g
|
|
||||||
}
|
|
||||||
|
|
||||||
// cubic spline coefficients -- b is linear, c quadratic, d is cubic (original y's are constants)
|
|
||||||
|
|
||||||
PiecewisePolynomial(points.x[points.size - 1]) {
|
|
||||||
var cOld = zero
|
|
||||||
|
|
||||||
for (j in n - 1 downTo 0) {
|
|
||||||
val c = z[j] - mu[j] * cOld
|
|
||||||
val a = points.y[j]
|
|
||||||
val b = (points.y[j + 1] - points.y[j]) / h[j] - h[j] * (cOld + 2.0 * c) / 3.0
|
|
||||||
val d = (cOld - c) / (3.0 * h[j])
|
|
||||||
val x0 = points.x[j]
|
|
||||||
val x02 = x0 * x0
|
|
||||||
val x03 = x02 * x0
|
|
||||||
//Shift coefficients to represent absolute polynomial instead of one with an offset
|
|
||||||
val polynomial = ListPolynomial(
|
|
||||||
a - b * x0 + c * x02 - d * x03,
|
|
||||||
b - 2 * c * x0 + 3 * d * x02,
|
|
||||||
c - 3 * d * x0,
|
|
||||||
d
|
|
||||||
)
|
|
||||||
cOld = c
|
|
||||||
putLeft(x0, polynomial)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> Field<T>.splineInterpolator(
|
|
||||||
bufferFactory: MutableBufferFactory<T>,
|
|
||||||
): SplineInterpolator<T> = SplineInterpolator(this, bufferFactory)
|
|
||||||
|
|
||||||
public val DoubleField.splineInterpolator: SplineInterpolator<Double>
|
|
||||||
get() = SplineInterpolator(this, ::DoubleBuffer)
|
|
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.integration
|
|
||||||
|
|
||||||
import space.kscience.kmath.functions.ListPolynomial
|
|
||||||
import space.kscience.kmath.functions.integrate
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.DoubleField
|
|
||||||
import kotlin.math.PI
|
|
||||||
import kotlin.math.sin
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
class SplineIntegralTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun integratePolynomial(){
|
|
||||||
val polynomial = ListPolynomial(1.0, 2.0, 3.0)
|
|
||||||
val integral = polynomial.integrate(DoubleField,1.0..2.0)
|
|
||||||
assertEquals(11.0, integral, 0.001)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun gaussSin() {
|
|
||||||
val res = DoubleField.splineIntegrator.integrate(0.0..2 * PI, IntegrandMaxCalls(5)) { x ->
|
|
||||||
sin(x)
|
|
||||||
}
|
|
||||||
assertEquals(0.0, res.value, 1e-2)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun gaussUniform() {
|
|
||||||
val res = DoubleField.splineIntegrator.integrate(35.0..100.0, IntegrandMaxCalls(20)) { x ->
|
|
||||||
if(x in 30.0..50.0){
|
|
||||||
1.0
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertEquals(15.0, res.value, 0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.interpolation
|
|
||||||
|
|
||||||
import space.kscience.kmath.operations.DoubleField
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
internal class LinearInterpolatorTest {
|
|
||||||
@Test
|
|
||||||
fun testInterpolation() {
|
|
||||||
val data = listOf(
|
|
||||||
0.0 to 0.0,
|
|
||||||
1.0 to 1.0,
|
|
||||||
2.0 to 3.0,
|
|
||||||
3.0 to 4.0
|
|
||||||
)
|
|
||||||
|
|
||||||
//val polynomial: PiecewisePolynomial<Double> = DoubleField.linearInterpolator.interpolatePolynomials(data)
|
|
||||||
val function = DoubleField.linearInterpolator.interpolate(data)
|
|
||||||
assertEquals(null, function(-1.0))
|
|
||||||
assertEquals(0.5, function(0.5))
|
|
||||||
assertEquals(2.0, function(1.5))
|
|
||||||
assertEquals(3.0, function(2.0))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.interpolation
|
|
||||||
|
|
||||||
import space.kscience.kmath.operations.DoubleField
|
|
||||||
import kotlin.math.PI
|
|
||||||
import kotlin.math.sin
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
internal class SplineInterpolatorTest {
|
|
||||||
@Test
|
|
||||||
fun testInterpolation() {
|
|
||||||
val data = (0..10).map {
|
|
||||||
val x = it.toDouble() / 5 * PI
|
|
||||||
x to sin(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
//val polynomial: PiecewisePolynomial<Double> = DoubleField.splineInterpolator.interpolatePolynomials(data)
|
|
||||||
|
|
||||||
val function = DoubleField.splineInterpolator.interpolate(data, Double.NaN)
|
|
||||||
|
|
||||||
assertEquals(Double.NaN, function(-1.0))
|
|
||||||
assertEquals(sin(0.5), function(0.5), 0.1)
|
|
||||||
assertEquals(sin(1.5), function(1.5), 0.1)
|
|
||||||
assertEquals(sin(2.0), function(2.0), 0.1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,142 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.test.misc
|
|
||||||
|
|
||||||
import space.kscience.kmath.functions.ListPolynomial
|
|
||||||
import space.kscience.kmath.functions.ListPolynomialSpace
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
|
|
||||||
|
|
||||||
class IntModulo {
|
|
||||||
val residue: Int
|
|
||||||
val modulus: Int
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal constructor(residue: Int, modulus: Int, toCheckInput: Boolean = true) {
|
|
||||||
if (toCheckInput) {
|
|
||||||
require(modulus != 0) { "modulus can not be zero" }
|
|
||||||
this.modulus = if (modulus < 0) -modulus else modulus
|
|
||||||
this.residue = residue.mod(modulus)
|
|
||||||
} else {
|
|
||||||
this.residue = residue
|
|
||||||
this.modulus = modulus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(residue: Int, modulus: Int) : this(residue, modulus, true)
|
|
||||||
|
|
||||||
operator fun unaryPlus(): IntModulo = this
|
|
||||||
operator fun unaryMinus(): IntModulo =
|
|
||||||
IntModulo(
|
|
||||||
if (residue == 0) 0 else modulus - residue,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
operator fun plus(other: IntModulo): IntModulo {
|
|
||||||
require(modulus == other.modulus) { "can not add two residue different modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue + other.residue) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
operator fun plus(other: Int): IntModulo =
|
|
||||||
IntModulo(
|
|
||||||
(residue + other) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
operator fun minus(other: IntModulo): IntModulo {
|
|
||||||
require(modulus == other.modulus) { "can not subtract two residue different modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue - other.residue) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
operator fun minus(other: Int): IntModulo =
|
|
||||||
IntModulo(
|
|
||||||
(residue - other) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
operator fun times(other: IntModulo): IntModulo {
|
|
||||||
require(modulus == other.modulus) { "can not multiply two residue different modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue * other.residue) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
operator fun times(other: Int): IntModulo =
|
|
||||||
IntModulo(
|
|
||||||
(residue * other) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
operator fun div(other: IntModulo): IntModulo {
|
|
||||||
require(modulus == other.modulus) { "can not divide two residue different modulo" }
|
|
||||||
val (reciprocalCandidate, gcdOfOtherResidueAndModulus) = bezoutIdentityWithGCD(other.residue, modulus)
|
|
||||||
require(gcdOfOtherResidueAndModulus == 1) { "can not divide to residue that has non-trivial GCD with modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue * reciprocalCandidate) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
operator fun div(other: Int): IntModulo {
|
|
||||||
val (reciprocalCandidate, gcdOfOtherResidueAndModulus) = bezoutIdentityWithGCD(other, modulus)
|
|
||||||
require(gcdOfOtherResidueAndModulus == 1) { "can not divide to residue that has non-trivial GCD with modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue * reciprocalCandidate) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override fun equals(other: Any?): Boolean =
|
|
||||||
when (other) {
|
|
||||||
is IntModulo -> residue == other.residue && modulus == other.modulus
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = residue.hashCode()
|
|
||||||
|
|
||||||
override fun toString(): String = "$residue mod $modulus"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
class IntModuloRing : Ring<IntModulo> {
|
|
||||||
|
|
||||||
val modulus: Int
|
|
||||||
|
|
||||||
constructor(modulus: Int) {
|
|
||||||
require(modulus != 0) { "modulus can not be zero" }
|
|
||||||
this.modulus = if (modulus < 0) -modulus else modulus
|
|
||||||
}
|
|
||||||
|
|
||||||
override inline val zero: IntModulo get() = IntModulo(0, modulus, toCheckInput = false)
|
|
||||||
override inline val one: IntModulo get() = IntModulo(1, modulus, toCheckInput = false)
|
|
||||||
|
|
||||||
fun number(arg: Int) = IntModulo(arg, modulus, toCheckInput = false)
|
|
||||||
|
|
||||||
override inline fun add(left: IntModulo, right: IntModulo): IntModulo = left + right
|
|
||||||
override inline fun multiply(left: IntModulo, right: IntModulo): IntModulo = left * right
|
|
||||||
|
|
||||||
override inline fun IntModulo.unaryMinus(): IntModulo = -this
|
|
||||||
override inline fun IntModulo.plus(arg: IntModulo): IntModulo = this + arg
|
|
||||||
override inline fun IntModulo.minus(arg: IntModulo): IntModulo = this - arg
|
|
||||||
override inline fun IntModulo.times(arg: IntModulo): IntModulo = this * arg
|
|
||||||
inline fun IntModulo.div(arg: IntModulo): IntModulo = this / arg
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ListPolynomialSpace<IntModulo, IntModuloRing>.number(arg: Int) = IntModulo(arg, ring.modulus, toCheckInput = false)
|
|
||||||
|
|
||||||
fun ListPolynomialSpace<IntModulo, IntModuloRing>.ListPolynomial(vararg coefs: Int): ListPolynomial<IntModulo> =
|
|
||||||
ListPolynomial(coefs.map { IntModulo(it, ring.modulus) })
|
|
||||||
fun IntModuloRing.ListPolynomial(vararg coefs: Int): ListPolynomial<IntModulo> =
|
|
||||||
ListPolynomial(coefs.map { IntModulo(it, modulus) })
|
|
@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.test.misc
|
|
||||||
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.Field
|
|
||||||
import space.kscience.kmath.operations.NumbersAddOps
|
|
||||||
|
|
||||||
class Rational {
|
|
||||||
companion object {
|
|
||||||
val ZERO: Rational = Rational(0L)
|
|
||||||
val ONE: Rational = Rational(1L)
|
|
||||||
}
|
|
||||||
|
|
||||||
val numerator: Long
|
|
||||||
val denominator: Long
|
|
||||||
|
|
||||||
internal constructor(numerator: Long, denominator: Long, toCheckInput: Boolean = true) {
|
|
||||||
if (toCheckInput) {
|
|
||||||
if (denominator == 0L) throw ArithmeticException("/ by zero")
|
|
||||||
|
|
||||||
val greatestCommonDivider = gcd(numerator, denominator).let { if (denominator < 0L) -it else it }
|
|
||||||
|
|
||||||
this.numerator = numerator / greatestCommonDivider
|
|
||||||
this.denominator = denominator / greatestCommonDivider
|
|
||||||
} else {
|
|
||||||
this.numerator = numerator
|
|
||||||
this.denominator = denominator
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(numerator: Int, denominator: Int) : this(numerator.toLong(), denominator.toLong(), true)
|
|
||||||
constructor(numerator: Int, denominator: Long) : this(numerator.toLong(), denominator, true)
|
|
||||||
constructor(numerator: Long, denominator: Int) : this(numerator, denominator.toLong(), true)
|
|
||||||
constructor(numerator: Long, denominator: Long) : this(numerator, denominator, true)
|
|
||||||
constructor(numerator: Int) : this(numerator.toLong(), 1L, false)
|
|
||||||
constructor(numerator: Long) : this(numerator, 1L, false)
|
|
||||||
|
|
||||||
operator fun unaryPlus(): Rational = this
|
|
||||||
operator fun unaryMinus(): Rational = Rational(-this.numerator, this.denominator)
|
|
||||||
operator fun plus(other: Rational): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator * other.denominator + denominator * other.numerator,
|
|
||||||
denominator * other.denominator
|
|
||||||
)
|
|
||||||
operator fun plus(other: Int): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator + denominator * other.toLong(),
|
|
||||||
denominator
|
|
||||||
)
|
|
||||||
operator fun plus(other: Long): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator + denominator * other,
|
|
||||||
denominator
|
|
||||||
)
|
|
||||||
operator fun minus(other: Rational): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator * other.denominator - denominator * other.numerator,
|
|
||||||
denominator * other.denominator
|
|
||||||
)
|
|
||||||
operator fun minus(other: Int): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator - denominator * other.toLong(),
|
|
||||||
denominator
|
|
||||||
)
|
|
||||||
operator fun minus(other: Long): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator - denominator * other,
|
|
||||||
denominator
|
|
||||||
)
|
|
||||||
operator fun times(other: Rational): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator * other.numerator,
|
|
||||||
denominator * other.denominator
|
|
||||||
)
|
|
||||||
operator fun times(other: Int): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator * other.toLong(),
|
|
||||||
denominator
|
|
||||||
)
|
|
||||||
operator fun times(other: Long): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator * other,
|
|
||||||
denominator
|
|
||||||
)
|
|
||||||
operator fun div(other: Rational): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator * other.denominator,
|
|
||||||
denominator * other.numerator
|
|
||||||
)
|
|
||||||
operator fun div(other: Int): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator,
|
|
||||||
denominator * other.toLong()
|
|
||||||
)
|
|
||||||
operator fun div(other: Long): Rational =
|
|
||||||
Rational(
|
|
||||||
numerator,
|
|
||||||
denominator * other
|
|
||||||
)
|
|
||||||
override fun equals(other: Any?): Boolean =
|
|
||||||
when (other) {
|
|
||||||
is Rational -> numerator == other.numerator && denominator == other.denominator
|
|
||||||
is Int -> numerator == other && denominator == 1L
|
|
||||||
is Long -> numerator == other && denominator == 1L
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = 31 * numerator.hashCode() + denominator.hashCode()
|
|
||||||
|
|
||||||
override fun toString(): String = if (denominator == 1L) "$numerator" else "$numerator/$denominator"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
object RationalField : Field<Rational>, NumbersAddOps<Rational> {
|
|
||||||
override inline val zero: Rational get() = Rational.ZERO
|
|
||||||
override inline val one: Rational get() = Rational.ONE
|
|
||||||
|
|
||||||
override inline fun number(value: Number): Rational = Rational(value.toLong())
|
|
||||||
|
|
||||||
override inline fun add(left: Rational, right: Rational): Rational = left + right
|
|
||||||
override inline fun multiply(left: Rational, right: Rational): Rational = left * right
|
|
||||||
override inline fun divide(left: Rational, right: Rational): Rational = left / right
|
|
||||||
override inline fun scale(a: Rational, value: Double): Rational = a * number(value)
|
|
||||||
|
|
||||||
override inline fun Rational.unaryMinus(): Rational = -this
|
|
||||||
override inline fun Rational.plus(arg: Rational): Rational = this + arg
|
|
||||||
override inline fun Rational.minus(arg: Rational): Rational = this - arg
|
|
||||||
override inline fun Rational.times(arg: Rational): Rational = this * arg
|
|
||||||
override inline fun Rational.div(arg: Rational): Rational = this / arg
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.test.misc
|
|
||||||
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.*
|
|
||||||
|
|
||||||
class RationalWithMemorization private constructor(
|
|
||||||
val value: Rational,
|
|
||||||
override val memory : OperationsMemory
|
|
||||||
): WithMemorization {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Constant containing the zero (the additive identity) of the [Rational] field.
|
|
||||||
*/
|
|
||||||
public val ZERO: RationalWithMemorization = RationalWithMemorization(Rational.ZERO, object : Endpoint {})
|
|
||||||
/**
|
|
||||||
* Constant containing the one (the multiplicative identity) of the [Rational] field.
|
|
||||||
*/
|
|
||||||
public val ONE: RationalWithMemorization = RationalWithMemorization(Rational.ONE, object : Endpoint {})
|
|
||||||
}
|
|
||||||
constructor(numerator: Int, denominator: Int) : this(Rational(numerator, denominator), object : Endpoint {})
|
|
||||||
constructor(numerator: Int, denominator: Long) : this(Rational(numerator, denominator), object : Endpoint {})
|
|
||||||
constructor(numerator: Long, denominator: Int) : this(Rational(numerator, denominator), object : Endpoint {})
|
|
||||||
constructor(numerator: Long, denominator: Long) : this(Rational(numerator, denominator), object : Endpoint {})
|
|
||||||
constructor(numerator: Int) : this(Rational(numerator), object : Endpoint {})
|
|
||||||
constructor(numerator: Long) : this(Rational(numerator), object : Endpoint {})
|
|
||||||
|
|
||||||
operator fun unaryPlus(): RationalWithMemorization = this
|
|
||||||
operator fun unaryMinus(): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
-value,
|
|
||||||
object : Negation {
|
|
||||||
override val negated: OperationsMemory = memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
operator fun plus(other: RationalWithMemorization): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
value + other.value,
|
|
||||||
object : Sum {
|
|
||||||
override val augend: OperationsMemory = memory
|
|
||||||
override val addend: OperationsMemory = other.memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
operator fun minus(other: RationalWithMemorization): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
value - other.value,
|
|
||||||
object : Difference {
|
|
||||||
override val minuend: OperationsMemory = memory
|
|
||||||
override val subtrahend: OperationsMemory = other.memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
operator fun times(other: RationalWithMemorization): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
value * other.value,
|
|
||||||
object : Product {
|
|
||||||
override val multiplicand: OperationsMemory = memory
|
|
||||||
override val multiplier: OperationsMemory = other.memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
operator fun div(other: RationalWithMemorization): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
value / other.value,
|
|
||||||
object : Quotient {
|
|
||||||
override val dividend: OperationsMemory = memory
|
|
||||||
override val divisor: OperationsMemory = other.memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
|
||||||
other is RationalWithMemorization && value == other.value
|
|
||||||
|
|
||||||
override fun hashCode(): Int = value.hashCode()
|
|
||||||
|
|
||||||
override fun toString(): String = value.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
|
|
||||||
object RationalWithMemorizationRing : Ring<RationalWithMemorization> {
|
|
||||||
override inline val zero: RationalWithMemorization get() = RationalWithMemorization.ZERO
|
|
||||||
override inline val one: RationalWithMemorization get() = RationalWithMemorization.ONE
|
|
||||||
|
|
||||||
override inline fun add(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left + right
|
|
||||||
override inline fun multiply(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left * right
|
|
||||||
|
|
||||||
override inline fun RationalWithMemorization.unaryMinus(): RationalWithMemorization = -this
|
|
||||||
override inline fun RationalWithMemorization.plus(arg: RationalWithMemorization): RationalWithMemorization = this + arg
|
|
||||||
override inline fun RationalWithMemorization.minus(arg: RationalWithMemorization): RationalWithMemorization = this - arg
|
|
||||||
override inline fun RationalWithMemorization.times(arg: RationalWithMemorization): RationalWithMemorization = this * arg
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
|
|
||||||
object RationalWithMemorizationField : Field<RationalWithMemorization> {
|
|
||||||
override inline val zero: RationalWithMemorization get() = RationalWithMemorization.ZERO
|
|
||||||
override inline val one: RationalWithMemorization get() = RationalWithMemorization.ONE
|
|
||||||
|
|
||||||
override inline fun number(value: Number): RationalWithMemorization = RationalWithMemorization(value.toLong())
|
|
||||||
|
|
||||||
override inline fun add(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left + right
|
|
||||||
override inline fun multiply(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left * right
|
|
||||||
override inline fun divide(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left / right
|
|
||||||
override inline fun scale(a: RationalWithMemorization, value: Double): RationalWithMemorization = a * number(value)
|
|
||||||
|
|
||||||
override inline fun RationalWithMemorization.unaryMinus(): RationalWithMemorization = -this
|
|
||||||
override inline fun RationalWithMemorization.plus(arg: RationalWithMemorization): RationalWithMemorization = this + arg
|
|
||||||
override inline fun RationalWithMemorization.minus(arg: RationalWithMemorization): RationalWithMemorization = this - arg
|
|
||||||
override inline fun RationalWithMemorization.times(arg: RationalWithMemorization): RationalWithMemorization = this * arg
|
|
||||||
override inline fun RationalWithMemorization.div(arg: RationalWithMemorization): RationalWithMemorization = this / arg
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.test.misc
|
|
||||||
|
|
||||||
sealed interface OperationsMemory
|
|
||||||
|
|
||||||
interface Endpoint: OperationsMemory
|
|
||||||
|
|
||||||
interface Negation: OperationsMemory {
|
|
||||||
val negated: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Sum: OperationsMemory {
|
|
||||||
val augend: OperationsMemory
|
|
||||||
val addend: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Difference: OperationsMemory {
|
|
||||||
val minuend: OperationsMemory
|
|
||||||
val subtrahend: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Product: OperationsMemory {
|
|
||||||
val multiplicand: OperationsMemory
|
|
||||||
val multiplier: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Quotient: OperationsMemory {
|
|
||||||
val dividend: OperationsMemory
|
|
||||||
val divisor: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun equalMemories(one: OperationsMemory, other: OperationsMemory) : Boolean =
|
|
||||||
when(one) {
|
|
||||||
is Negation -> other is Negation && equalMemories(one.negated, other.negated)
|
|
||||||
is Sum -> other is Sum && equalMemories(one.augend, other.augend) && equalMemories(one.addend, other.addend)
|
|
||||||
is Difference -> other is Difference && equalMemories(one.minuend, other.minuend) && equalMemories(one.subtrahend, other.subtrahend)
|
|
||||||
is Product -> other is Product && equalMemories(one.multiplicand, other.multiplicand) && equalMemories(one.multiplier, other.multiplier)
|
|
||||||
is Quotient -> other is Quotient && equalMemories(one.dividend, other.dividend) && equalMemories(one.divisor, other.divisor)
|
|
||||||
is Endpoint -> one === other
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WithMemorization {
|
|
||||||
val memory: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
fun equalMemories(one: WithMemorization, other: WithMemorization) : Boolean = equalMemories(one.memory, other.memory)
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.test.misc
|
|
||||||
|
|
||||||
// TODO: Move to corresponding module "kmath-number-theory"
|
|
||||||
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
|
|
||||||
data class BezoutIdentityWithGCD<T>(val first: T, val second: T, val gcd: T)
|
|
||||||
|
|
||||||
tailrec fun gcd(a: Long, b: Long): Long = if (a == 0L) abs(b) else gcd(b % a, a)
|
|
||||||
|
|
||||||
fun bezoutIdentityWithGCD(a: Int, b: Int): BezoutIdentityWithGCD<Int> =
|
|
||||||
when {
|
|
||||||
a < 0 && b < 0 -> with(bezoutIdentityWithGCDInternalLogic(-a, -b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(-first, -second, gcd) }
|
|
||||||
a < 0 -> with(bezoutIdentityWithGCDInternalLogic(-a, b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(-first, second, gcd) }
|
|
||||||
b < 0 -> with(bezoutIdentityWithGCDInternalLogic(a, -b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(first, -second, gcd) }
|
|
||||||
else -> bezoutIdentityWithGCDInternalLogic(a, b, 1, 0, 0, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal tailrec fun bezoutIdentityWithGCDInternalLogic(a: Int, b: Int, m1: Int, m2: Int, m3: Int, m4: Int): BezoutIdentityWithGCD<Int> =
|
|
||||||
if (b == 0) BezoutIdentityWithGCD(m1, m3, a)
|
|
||||||
else {
|
|
||||||
val quotient = a / b
|
|
||||||
val reminder = a % b
|
|
||||||
bezoutIdentityWithGCDInternalLogic(b, reminder, m2, m1 - quotient * m2, m4, m3 - quotient * m4)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user