forked from kscience/kmath
Optional math operations.
This commit is contained in:
parent
eb4c9a4b94
commit
0d1825a044
@ -1,5 +1,16 @@
|
||||
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.
|
||||
* 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
|
||||
* @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>> {
|
||||
/**
|
||||
* The context this element belongs to
|
||||
*/
|
||||
val context: Space<S>
|
||||
interface SpaceElement<T, S : Space<T>>: MathElement<T,S> {
|
||||
|
||||
/**
|
||||
* 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 minus(b: S): S = context.add(self, context.multiply(b, -1.0))
|
||||
operator fun times(k: Number): S = context.multiply(self, k.toDouble())
|
||||
operator fun div(k: Number): S = context.multiply(self, 1.0 / k.toDouble())
|
||||
operator fun plus(b: T): T = context.add(self, b)
|
||||
operator fun minus(b: T): T = context.add(self, context.multiply(b, -1.0))
|
||||
operator fun times(k: Number): T = context.multiply(self, 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
|
||||
*/
|
||||
interface RingElement<S : RingElement<S>> : SpaceElement<S> {
|
||||
override val context: Ring<S>
|
||||
interface RingElement<T, S : Ring<T>> : SpaceElement<T, 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
|
||||
*/
|
||||
interface FieldElement<S : FieldElement<S>> : RingElement<S> {
|
||||
override val context: Field<S>
|
||||
interface FieldElement<T, S : Field<T>> : RingElement<T, 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
|
||||
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
/**
|
||||
* 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 fun add(a: Real, b: Real): Real = Real(a.value + b.value)
|
||||
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, k: Double): Real = Real(a.value * k)
|
||||
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 toChar(): Char = value.toChar()
|
||||
override fun toDouble(): Double = value
|
||||
@ -29,8 +45,10 @@ class Real(val value: Double) : FieldElement<Real>, Number() {
|
||||
//values are dynamically calculated to save memory
|
||||
override val self
|
||||
get() = this
|
||||
|
||||
override val context
|
||||
get() = RealField
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,10 +72,9 @@ object ComplexField : Field<Complex> {
|
||||
/**
|
||||
* Complex number class
|
||||
*/
|
||||
data class Complex(val re: Double, val im: Double) : FieldElement<Complex> {
|
||||
override val self: Complex
|
||||
get() = this
|
||||
override val context: Field<Complex>
|
||||
data class Complex(val re: Double, val im: Double) : FieldElement<Complex, ComplexField> {
|
||||
override val self: Complex get() = this
|
||||
override val context: ComplexField
|
||||
get() = ComplexField
|
||||
|
||||
/**
|
||||
@ -72,15 +89,15 @@ data class Complex(val re: Double, val im: Double) : FieldElement<Complex> {
|
||||
val module: Double
|
||||
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> {
|
||||
override val zero: Double = 0.0
|
||||
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 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