ValueAndErrorField

This commit is contained in:
Alexander Nozik 2022-04-10 23:00:55 +03:00
parent ff58985d78
commit b509dc917d
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
6 changed files with 73 additions and 15 deletions

View File

@ -19,6 +19,7 @@
- Complex power - Complex power
- Separate methods for UInt, Int and Number powers. NaN safety. - Separate methods for UInt, Int and Number powers. NaN safety.
- Tensorflow prototype - Tensorflow prototype
- `ValueAndErrorField`
### Changed ### Changed
- Exponential operations merged with hyperbolic functions - Exponential operations merged with hyperbolic functions
@ -50,6 +51,7 @@
- Tensor algebra takes read-only structures as input and inherits AlgebraND - Tensor algebra takes read-only structures as input and inherits AlgebraND
- `UnivariateDistribution` renamed to `Distribution1D` - `UnivariateDistribution` renamed to `Distribution1D`
- Rework of histograms. - Rework of histograms.
- `UnivariateFunction` -> `Function1D`, `MultivariateFunction` -> `FunctionND`
### Deprecated ### Deprecated
- Specialized `DoubleBufferAlgebra` - Specialized `DoubleBufferAlgebra`

View File

@ -13,7 +13,7 @@ import kotlin.math.pow
fun main() { fun main() {
//Define a function //Define a function
val function: UnivariateFunction<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 } val function: Function1D<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 }
//get the result of the integration //get the result of the integration
val result = DoubleField.gaussIntegrator.integrate(0.0..10.0, function = function) val result = DoubleField.gaussIntegrator.integrate(0.0..10.0, function = function)

View File

@ -18,7 +18,7 @@ import space.kscience.plotly.scatter
@OptIn(UnstablePlotlyAPI::class) @OptIn(UnstablePlotlyAPI::class)
fun main() { fun main() {
val function: UnivariateFunction<Double> = { x -> val function: Function1D<Double> = { x ->
if (x in 30.0..50.0) { if (x in 30.0..50.0) {
1.0 1.0
} else { } else {

View File

@ -5,7 +5,6 @@
package space.kscience.kmath.operations package space.kscience.kmath.operations
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
@ -53,7 +52,7 @@ public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
*/ */
private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapInline( private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapInline(
buffer: Buffer<T>, buffer: Buffer<T>,
crossinline block: A.(T) -> T crossinline block: A.(T) -> T,
): Buffer<T> = bufferFactory(buffer.size) { elementAlgebra.block(buffer[it]) } ): Buffer<T> = bufferFactory(buffer.size) { elementAlgebra.block(buffer[it]) }
/** /**
@ -61,7 +60,7 @@ private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapInline(
*/ */
private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapIndexedInline( private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapIndexedInline(
buffer: Buffer<T>, buffer: Buffer<T>,
crossinline block: A.(index: Int, arg: T) -> T crossinline block: A.(index: Int, arg: T) -> T,
): Buffer<T> = bufferFactory(buffer.size) { elementAlgebra.block(it, buffer[it]) } ): Buffer<T> = bufferFactory(buffer.size) { elementAlgebra.block(it, buffer[it]) }
/** /**
@ -70,7 +69,7 @@ private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapIndexedInline(
private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.zipInline( private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.zipInline(
l: Buffer<T>, l: Buffer<T>,
r: Buffer<T>, r: Buffer<T>,
crossinline block: A.(l: T, r: T) -> T crossinline block: A.(l: T, r: T) -> T,
): Buffer<T> { ): Buffer<T> {
require(l.size == r.size) { "Incompatible buffer sizes. left: ${l.size}, right: ${r.size}" } require(l.size == r.size) { "Incompatible buffer sizes. left: ${l.size}, right: ${r.size}" }
return bufferFactory(l.size) { elementAlgebra.block(l[it], r[it]) } return bufferFactory(l.size) { elementAlgebra.block(l[it], r[it]) }
@ -127,13 +126,13 @@ public fun <T, A : ExponentialOperations<T>> BufferAlgebra<T, A>.atanh(arg: Buff
mapInline(arg) { atanh(it) } mapInline(arg) { atanh(it) }
public fun <T, A : PowerOperations<T>> BufferAlgebra<T, A>.pow(arg: Buffer<T>, pow: Number): Buffer<T> = public fun <T, A : PowerOperations<T>> BufferAlgebra<T, A>.pow(arg: Buffer<T>, pow: Number): Buffer<T> =
mapInline(arg) {it.pow(pow) } mapInline(arg) { it.pow(pow) }
public open class BufferRingOps<T, A: Ring<T>>( public open class BufferRingOps<T, A : Ring<T>>(
override val elementAlgebra: A, override val elementAlgebra: A,
override val bufferFactory: BufferFactory<T>, override val bufferFactory: BufferFactory<T>,
) : BufferAlgebra<T, A>, RingOps<Buffer<T>>{ ) : BufferAlgebra<T, A>, RingOps<Buffer<T>> {
override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l + r } override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l + r }
override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l * r } override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l * r }
@ -152,10 +151,11 @@ public val ShortRing.bufferAlgebra: BufferRingOps<Short, ShortRing>
public open class BufferFieldOps<T, A : Field<T>>( public open class BufferFieldOps<T, A : Field<T>>(
elementAlgebra: A, elementAlgebra: A,
bufferFactory: BufferFactory<T>, bufferFactory: BufferFactory<T>,
) : BufferRingOps<T, A>(elementAlgebra, bufferFactory), BufferAlgebra<T, A>, FieldOps<Buffer<T>>, ScaleOperations<Buffer<T>> { ) : BufferRingOps<T, A>(elementAlgebra, bufferFactory), BufferAlgebra<T, A>, FieldOps<Buffer<T>>,
ScaleOperations<Buffer<T>> {
override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l + r } // override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l + r }
override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l * r } // override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l * r }
override fun divide(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l / r } override fun divide(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l / r }
override fun scale(a: Buffer<T>, value: Double): Buffer<T> = a.map { scale(it, value) } override fun scale(a: Buffer<T>, value: Double): Buffer<T> = a.map { scale(it, value) }
@ -168,7 +168,7 @@ public open class BufferFieldOps<T, A : Field<T>>(
public class BufferField<T, A : Field<T>>( public class BufferField<T, A : Field<T>>(
elementAlgebra: A, elementAlgebra: A,
bufferFactory: BufferFactory<T>, bufferFactory: BufferFactory<T>,
override val size: Int override val size: Int,
) : BufferFieldOps<T, A>(elementAlgebra, bufferFactory), Field<Buffer<T>>, WithSize { ) : BufferFieldOps<T, A>(elementAlgebra, bufferFactory), Field<Buffer<T>>, WithSize {
override val zero: Buffer<T> = bufferFactory(size) { elementAlgebra.zero } override val zero: Buffer<T> = bufferFactory(size) { elementAlgebra.zero }

View File

@ -7,6 +7,6 @@ package space.kscience.kmath.functions
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
public typealias UnivariateFunction<T> = (T) -> T public typealias Function1D<T> = (T) -> T
public typealias MultivariateFunction<T> = (Buffer<T>) -> T public typealias FunctionND<T> = (Buffer<T>) -> T

View File

@ -0,0 +1,56 @@
/*
* 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.stat
import space.kscience.kmath.operations.Field
import kotlin.math.pow
import kotlin.math.sqrt
/**
* A combination of a random [value] and its [dispersion].
*
* [dispersion] must be positive.
*/
public data class ValueAndError(val value: Double, val dispersion: Double) {
init {
require(dispersion >= 0) { "Dispersion must be non-negative" }
}
val error: Double get() = sqrt(dispersion)
}
/**
* An algebra for double value + its error combination. The multiplication assumes linear error propagation
*/
public object ValueAndErrorField : Field<ValueAndError> {
override val zero: ValueAndError = ValueAndError(0.0, 0.0)
override val one: ValueAndError = ValueAndError(1.0, 0.0)
override fun add(left: ValueAndError, right: ValueAndError): ValueAndError =
ValueAndError(left.value + right.value, left.dispersion + right.dispersion)
override fun ValueAndError.unaryMinus(): ValueAndError =
ValueAndError(-value, dispersion)
//TODO study performance impact of pow(2). On JVM it does not exist: https://stackoverflow.com/questions/29144275/xx-vs-math-powx-2-java-performance
override fun multiply(left: ValueAndError, right: ValueAndError): ValueAndError {
val value = left.value * right.value
val dispersion = (left.dispersion / left.value.pow(2) + right.dispersion / right.value.pow(2)) * value.pow(2)
return ValueAndError(value, dispersion)
}
override fun divide(left: ValueAndError, right: ValueAndError): ValueAndError {
val value = left.value / right.value
val dispersion = (left.dispersion / left.value.pow(2) + right.dispersion / right.value.pow(2)) * value.pow(2)
return ValueAndError(value, dispersion)
}
override fun scale(a: ValueAndError, value: Double): ValueAndError =
ValueAndError(a.value * value, a.dispersion * value.pow(2))
}