forked from kscience/kmath
Optional math operations.
This commit is contained in:
parent
eb4c9a4b94
commit
0d1825a044
@ -1,5 +1,16 @@
|
|||||||
package scientifik.kmath.operations
|
package scientifik.kmath.operations
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The generic mathematics elements which is able to store its context
|
||||||
|
*/
|
||||||
|
interface MathElement<T, S>{
|
||||||
|
/**
|
||||||
|
* The context this element belongs to
|
||||||
|
*/
|
||||||
|
val context: S
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A general interface representing linear context of some kind.
|
* A general interface representing linear context of some kind.
|
||||||
* The context defines sum operation for its elements and multiplication by real value.
|
* The context defines sum operation for its elements and multiplication by real value.
|
||||||
@ -37,23 +48,20 @@ interface Space<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The element of linear context
|
* The element of linear context
|
||||||
* @param S self type of the element. Needed for static type checking
|
* @param T self type of the element. Needed for static type checking
|
||||||
|
* @param S the type of space
|
||||||
*/
|
*/
|
||||||
interface SpaceElement<S : SpaceElement<S>> {
|
interface SpaceElement<T, S : Space<T>>: MathElement<T,S> {
|
||||||
/**
|
|
||||||
* The context this element belongs to
|
|
||||||
*/
|
|
||||||
val context: Space<S>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Self value. Needed for static type checking. Needed to avoid type erasure on JVM.
|
* Self value. Needed for static type checking. Needed to avoid type erasure on JVM.
|
||||||
*/
|
*/
|
||||||
val self: S
|
val self: T
|
||||||
|
|
||||||
operator fun plus(b: S): S = context.add(self, b)
|
operator fun plus(b: T): T = context.add(self, b)
|
||||||
operator fun minus(b: S): S = context.add(self, context.multiply(b, -1.0))
|
operator fun minus(b: T): T = context.add(self, context.multiply(b, -1.0))
|
||||||
operator fun times(k: Number): S = context.multiply(self, k.toDouble())
|
operator fun times(k: Number): T = context.multiply(self, k.toDouble())
|
||||||
operator fun div(k: Number): S = context.multiply(self, 1.0 / k.toDouble())
|
operator fun div(k: Number): T = context.multiply(self, 1.0 / k.toDouble())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,10 +85,10 @@ interface Ring<T> : Space<T> {
|
|||||||
/**
|
/**
|
||||||
* Ring element
|
* Ring element
|
||||||
*/
|
*/
|
||||||
interface RingElement<S : RingElement<S>> : SpaceElement<S> {
|
interface RingElement<T, S : Ring<T>> : SpaceElement<T, S> {
|
||||||
override val context: Ring<S>
|
override val context: S
|
||||||
|
|
||||||
operator fun times(b: S): S = context.multiply(self, b)
|
operator fun times(b: T): T = context.multiply(self, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,8 +104,8 @@ interface Field<T> : Ring<T> {
|
|||||||
/**
|
/**
|
||||||
* Field element
|
* Field element
|
||||||
*/
|
*/
|
||||||
interface FieldElement<S : FieldElement<S>> : RingElement<S> {
|
interface FieldElement<T, S : Field<T>> : RingElement<T, S> {
|
||||||
override val context: Field<S>
|
override val context: S
|
||||||
|
|
||||||
operator fun div(b: S): S = context.divide(self, b)
|
operator fun div(b: T): T = context.divide(self, b)
|
||||||
}
|
}
|
@ -1,23 +1,39 @@
|
|||||||
package scientifik.kmath.operations
|
package scientifik.kmath.operations
|
||||||
|
|
||||||
|
import kotlin.math.pow
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field for real values
|
* Field for real values
|
||||||
*/
|
*/
|
||||||
object RealField : Field<Real> {
|
object RealField : Field<Real>, TrigonometricOperations<Real>, PowerOperations<Real>, ExponentialOperations<Real> {
|
||||||
override val zero: Real = Real(0.0)
|
override val zero: Real = Real(0.0)
|
||||||
override fun add(a: Real, b: Real): Real = Real(a.value + b.value)
|
override fun add(a: Real, b: Real): Real = Real(a.value + b.value)
|
||||||
override val one: Real = Real(1.0)
|
override val one: Real = Real(1.0)
|
||||||
override fun multiply(a: Real, b: Real): Real = Real(a.value * b.value)
|
override fun multiply(a: Real, b: Real): Real = Real(a.value * b.value)
|
||||||
override fun multiply(a: Real, k: Double): Real = Real(a.value * k)
|
override fun multiply(a: Real, k: Double): Real = Real(a.value * k)
|
||||||
override fun divide(a: Real, b: Real): Real = Real(a.value / b.value)
|
override fun divide(a: Real, b: Real): Real = Real(a.value / b.value)
|
||||||
|
|
||||||
|
override fun sin(arg: Real): Real = Real(kotlin.math.sin(arg.value))
|
||||||
|
override fun cos(arg: Real): Real = Real(kotlin.math.cos(arg.value))
|
||||||
|
|
||||||
|
override fun power(arg: Real, pow: Double): Real = Real(arg.value.pow(pow))
|
||||||
|
|
||||||
|
override fun exp(arg: Real): Real = Real(kotlin.math.exp(arg.value))
|
||||||
|
|
||||||
|
override fun ln(arg: Real): Real = Real(kotlin.math.ln(arg.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Real field element wrapping double
|
* Real field element wrapping double.
|
||||||
|
*
|
||||||
|
* TODO could be replaced by inline class in kotlin 1.3 if it would allow to avoid boxing
|
||||||
*/
|
*/
|
||||||
class Real(val value: Double) : FieldElement<Real>, Number() {
|
class Real(val value: Double) : Number(), FieldElement<Real, RealField> {
|
||||||
|
/*
|
||||||
|
* The class uses composition instead of inheritance since Double is final
|
||||||
|
*/
|
||||||
|
|
||||||
override fun toByte(): Byte = value.toByte()
|
override fun toByte(): Byte = value.toByte()
|
||||||
override fun toChar(): Char = value.toChar()
|
override fun toChar(): Char = value.toChar()
|
||||||
override fun toDouble(): Double = value
|
override fun toDouble(): Double = value
|
||||||
@ -29,8 +45,10 @@ class Real(val value: Double) : FieldElement<Real>, Number() {
|
|||||||
//values are dynamically calculated to save memory
|
//values are dynamically calculated to save memory
|
||||||
override val self
|
override val self
|
||||||
get() = this
|
get() = this
|
||||||
|
|
||||||
override val context
|
override val context
|
||||||
get() = RealField
|
get() = RealField
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,10 +72,9 @@ object ComplexField : Field<Complex> {
|
|||||||
/**
|
/**
|
||||||
* Complex number class
|
* Complex number class
|
||||||
*/
|
*/
|
||||||
data class Complex(val re: Double, val im: Double) : FieldElement<Complex> {
|
data class Complex(val re: Double, val im: Double) : FieldElement<Complex, ComplexField> {
|
||||||
override val self: Complex
|
override val self: Complex get() = this
|
||||||
get() = this
|
override val context: ComplexField
|
||||||
override val context: Field<Complex>
|
|
||||||
get() = ComplexField
|
get() = ComplexField
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,15 +89,15 @@ data class Complex(val re: Double, val im: Double) : FieldElement<Complex> {
|
|||||||
val module: Double
|
val module: Double
|
||||||
get() = sqrt(square)
|
get() = sqrt(square)
|
||||||
|
|
||||||
|
|
||||||
//TODO is it convenient?
|
|
||||||
operator fun not() = conjugate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A field for double without boxing. Does not produce appropriate field element
|
||||||
|
*/
|
||||||
object DoubleField : Field<Double> {
|
object DoubleField : Field<Double> {
|
||||||
override val zero: Double = 0.0
|
override val zero: Double = 0.0
|
||||||
override fun add(a: Double, b: Double): Double = a + b
|
override fun add(a: Double, b: Double): Double = a + b
|
||||||
override fun multiply(a: Double, b: Double): Double = a * b
|
override fun multiply(a: Double, @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") b: Double): Double = a * b
|
||||||
override val one: Double = 1.0
|
override val one: Double = 1.0
|
||||||
override fun divide(a: Double, b: Double): Double = a / b
|
override fun divide(a: Double, b: Double): Double = a / b
|
||||||
}
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package scientifik.kmath.operations
|
||||||
|
|
||||||
|
|
||||||
|
/* Trigonometric operations */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container for trigonometric operations for specific type. Trigonometric operations are limited to fields.
|
||||||
|
*
|
||||||
|
* The operations are not exposed to class directly to avoid method bloat but instead are declared in the field.
|
||||||
|
* It also allows to override behavior for optional operations
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
interface TrigonometricOperations<T>: Field<T> {
|
||||||
|
fun sin(arg: T): T
|
||||||
|
fun cos(arg: T): T
|
||||||
|
|
||||||
|
fun tg(arg: T): T = sin(arg) / cos(arg)
|
||||||
|
|
||||||
|
fun ctg(arg: T): T = cos(arg) / sin(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : FieldElement<T, out TrigonometricOperations<T>>> sin(arg: T): T = arg.context.sin(arg)
|
||||||
|
fun <T : FieldElement<T, out TrigonometricOperations<T>>> cos(arg: T): T = arg.context.cos(arg)
|
||||||
|
fun <T : FieldElement<T, out TrigonometricOperations<T>>> tg(arg: T): T = arg.context.tg(arg)
|
||||||
|
fun <T : FieldElement<T, out TrigonometricOperations<T>>> ctg(arg: T): T = arg.context.ctg(arg)
|
||||||
|
|
||||||
|
/* Power and roots */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context extension to include power operations like square roots, etc
|
||||||
|
*/
|
||||||
|
interface PowerOperations<T> {
|
||||||
|
fun power(arg: T, pow: Double): T
|
||||||
|
}
|
||||||
|
|
||||||
|
infix fun <T : MathElement<T, out PowerOperations<T>>> T.pow(power: Double): T = context.power(this, power)
|
||||||
|
fun <T : MathElement<T, out PowerOperations<T>>> sqrt(arg: T): T = arg pow 0.5
|
||||||
|
fun <T : MathElement<T, out PowerOperations<T>>> sqr(arg: T): T = arg pow 2.0
|
||||||
|
|
||||||
|
/* Exponential */
|
||||||
|
|
||||||
|
interface ExponentialOperations<T>{
|
||||||
|
fun exp(arg: T): T
|
||||||
|
fun ln(arg: T): T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T: MathElement<T, out ExponentialOperations<T>>> exp(arg:T): T = arg.context.exp(arg)
|
||||||
|
fun <T: MathElement<T, out ExponentialOperations<T>>> ln(arg:T): T = arg.context.ln(arg)
|
@ -0,0 +1,14 @@
|
|||||||
|
package scientifik.kmath.operations
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class RealFieldTest {
|
||||||
|
@Test
|
||||||
|
fun testSqrt() {
|
||||||
|
val sqrt = with(RealField) {
|
||||||
|
sqrt(25 * one)
|
||||||
|
}
|
||||||
|
assertEquals(5.0, sqrt.toDouble())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user