Minor piecewise rework

This commit is contained in:
Alexander Nozik 2021-05-19 22:35:06 +03:00
parent 0898285542
commit 8a07140f7c
8 changed files with 91 additions and 32 deletions

View File

@ -10,12 +10,14 @@ import space.kscience.kmath.interpolation.interpolatePolynomials
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.plotly.Plotly
import space.kscience.plotly.UnstablePlotlyAPI
import space.kscience.plotly.makeFile
import space.kscience.plotly.models.functionXY
import space.kscience.plotly.scatter
import kotlin.math.PI
import kotlin.math.sin
@OptIn(UnstablePlotlyAPI::class)
fun main() {
val data = (0..10).map {
val x = it.toDouble() / 5 * PI

View File

@ -22,8 +22,20 @@ public fun interface Piecewise<T, R> {
/**
* Represents piecewise-defined function where all the sub-functions are polynomials.
* @param pieces An ordered list of range-polynomial pairs. The list does not in general guarantee that there are no "holes" in it.
*/
public fun interface PiecewisePolynomial<T : Any> : Piecewise<T, Polynomial<T>>
public class PiecewisePolynomial<T : Comparable<T>>(
public val pieces: List<Pair<ClosedRange<T>, Polynomial<T>>>,
) : Piecewise<T, Polynomial<T>> {
public override fun findPiece(arg: T): Polynomial<T>? {
return if (arg < pieces.first().first.start || arg >= pieces.last().first.endInclusive)
null
else {
pieces.firstOrNull { arg in it.first }?.second
}
}
}
/**
* A [Piecewise] builder where all the pieces are ordered by the [Comparable] type instances.
@ -31,7 +43,7 @@ public fun interface PiecewisePolynomial<T : Any> : Piecewise<T, Polynomial<T>>
* @param T the comparable piece key type.
* @param delimiter the initial piecewise separator
*/
public class OrderedPiecewisePolynomial<T : Comparable<T>>(delimiter: T) : PiecewisePolynomial<T> {
public class PiecewiseBuilder<T : Comparable<T>>(delimiter: T) {
private val delimiters: MutableList<T> = arrayListOf(delimiter)
private val pieces: MutableList<Polynomial<T>> = arrayListOf()
@ -59,17 +71,19 @@ public class OrderedPiecewisePolynomial<T : Comparable<T>>(delimiter: T) : Piece
pieces.add(0, piece)
}
public override fun findPiece(arg: T): Polynomial<T>? {
if (arg < delimiters.first() || arg >= delimiters.last())
return null
else {
for (index in 1 until delimiters.size)
if (arg < delimiters[index]) return pieces[index - 1]
error("Piece not found")
}
public fun build(): PiecewisePolynomial<T> {
return PiecewisePolynomial(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] an given [arg] or null if argument is outside of piecewise
* definition.

View File

@ -5,10 +5,8 @@
package space.kscience.kmath.functions
import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.*
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.math.max
@ -32,7 +30,9 @@ public fun <T> Polynomial(vararg coefficients: T): Polynomial<T> = Polynomial(co
/**
* Evaluates the value of the given double polynomial for given double argument.
*/
public fun Polynomial<Double>.value(): Double = coefficients.reduceIndexed { index, acc, d -> acc + d.pow(index) }
public fun Polynomial<Double>.value(arg: Double): Double = coefficients.reduceIndexed { index, acc, c ->
acc + c * arg.pow(index)
}
/**
* Evaluates the value of the given polynomial for given argument.
@ -52,7 +52,36 @@ public fun <T, C : Ring<T>> Polynomial<T>.value(ring: C, arg: T): T = ring {
*/
public fun <T, C : Ring<T>> Polynomial<T>.asFunction(ring: C): (T) -> T = { value(ring, it) }
//public fun <T: Any>
/**
* Create a polynomial witch represents differentiated version of this polynomial
*/
@UnstableKMathAPI
public fun <T, A> Polynomial<T>.differentiate(
algebra: A,
): Polynomial<T> where A : Ring<T>, A : NumericAlgebra<T> = algebra {
Polynomial(coefficients.drop(1).mapIndexed { index, t -> number(index) * t })
}
/**
* Create a polynomial witch represents indefinite integral version of this polynomial
*/
@UnstableKMathAPI
public fun <T, A> Polynomial<T>.integrate(
algebra: A,
): Polynomial<T> where A : Field<T>, A : NumericAlgebra<T> = algebra {
Polynomial(coefficients.mapIndexed { index, t -> t / number(index) })
}
/**
* Compute a definite integral of a given polynomial in a [range]
*/
@UnstableKMathAPI
public fun <T : Comparable<T>, A> Polynomial<T>.integrate(
algebra: A,
range: ClosedRange<T>,
): T where A : Field<T>, A : NumericAlgebra<T> = algebra {
value(algebra, range.endInclusive) - value(algebra, range.start)
}
/**
* Space of polynomials.
@ -87,6 +116,9 @@ public class PolynomialSpace<T, C>(
* Evaluates the polynomial for the given value [arg].
*/
public operator fun Polynomial<T>.invoke(arg: T): T = value(ring, arg)
public fun Polynomial<T>.asFunction(): (T) -> T = asFunction(ring)
}
public inline fun <T, C, R> C.polynomial(block: PolynomialSpace<T, C>.() -> R): R where C : Ring<T>, C : ScaleOperations<T> {

View File

@ -6,7 +6,6 @@
package space.kscience.kmath.interpolation
import space.kscience.kmath.data.XYColumnarData
import space.kscience.kmath.functions.OrderedPiecewisePolynomial
import space.kscience.kmath.functions.PiecewisePolynomial
import space.kscience.kmath.functions.Polynomial
import space.kscience.kmath.misc.UnstableKMathAPI
@ -28,7 +27,7 @@ public class LinearInterpolator<T : Comparable<T>>(public override val algebra:
require(points.size > 0) { "Point array should not be empty" }
insureSorted(points)
OrderedPiecewisePolynomial(points.x[0]).apply {
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]

View File

@ -6,7 +6,6 @@
package space.kscience.kmath.interpolation
import space.kscience.kmath.data.XYColumnarData
import space.kscience.kmath.functions.OrderedPiecewisePolynomial
import space.kscience.kmath.functions.PiecewisePolynomial
import space.kscience.kmath.functions.Polynomial
import space.kscience.kmath.misc.UnstableKMathAPI
@ -50,7 +49,7 @@ public class SplineInterpolator<T : Comparable<T>>(
// cubic spline coefficients -- b is linear, c quadratic, d is cubic (original y's are constants)
OrderedPiecewisePolynomial(points.x[points.size - 1]).apply {
PiecewisePolynomial(points.x[points.size - 1]) {
var cOld = zero
for (j in n - 1 downTo 0) {

View File

@ -0,0 +1,17 @@
/*
* 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 kotlin.test.Test
import kotlin.test.assertEquals
class PolynomialTest {
@Test
fun testIntegration() {
val polynomial = Polynomial(1.0, -2.0, 1.0)
assertEquals(0.0, polynomial.value(1.0), 0.001)
}
}

View File

@ -28,7 +28,6 @@ private fun <B : ClosedFloatingPointRange<Double>> TreeMap<Double, B>.getBin(val
@UnstableKMathAPI
public class TreeHistogram(
override val context: TreeHistogramSpace,
private val binMap: TreeMap<Double, out UnivariateBin>,
) : UnivariateHistogram {
override fun get(value: Double): UnivariateBin? = binMap.getBin(value)
@ -79,15 +78,15 @@ public class TreeHistogramSpace(
val count = binCounter.counter.value
resBins[key] = UnivariateBin(binCounter.domain, count, sqrt(count))
}
return TreeHistogram(this, resBins)
return TreeHistogram(resBins)
}
override fun add(
a: UnivariateHistogram,
b: UnivariateHistogram,
): UnivariateHistogram {
require(a.context == this) { "Histogram $a does not belong to this context" }
require(b.context == this) { "Histogram $b does not belong to this context" }
// require(a.context == this) { "Histogram $a does not belong to this context" }
// require(b.context == this) { "Histogram $b does not belong to this context" }
val bins = TreeMap<Double, UnivariateBin>().apply {
(a.bins.map { it.domain } union b.bins.map { it.domain }).forEach { def ->
put(def.center,
@ -100,7 +99,7 @@ public class TreeHistogramSpace(
)
}
}
return TreeHistogram(this, bins)
return TreeHistogram(bins)
}
override fun scale(a: UnivariateHistogram, value: Double): UnivariateHistogram {
@ -116,7 +115,7 @@ public class TreeHistogramSpace(
}
}
return TreeHistogram(this, bins)
return TreeHistogram(bins)
}
override fun UnivariateHistogram.unaryMinus(): UnivariateHistogram = this * (-1)

View File

@ -7,8 +7,6 @@ package space.kscience.kmath.histogram
import space.kscience.kmath.domains.UnivariateDomain
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.GroupElement
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asSequence
@ -35,8 +33,7 @@ public class UnivariateBin(
}
@OptIn(UnstableKMathAPI::class)
public interface UnivariateHistogram : Histogram<Double, UnivariateBin>,
GroupElement<UnivariateHistogram, Group<UnivariateHistogram>> {
public interface UnivariateHistogram : Histogram<Double, UnivariateBin>{
public operator fun get(value: Double): UnivariateBin?
public override operator fun get(point: Buffer<Double>): UnivariateBin? = get(point[0])