Optional math operations.

This commit is contained in:
Alexander Nozik 2018-07-18 12:52:58 +03:00
parent eb4c9a4b94
commit 0d1825a044
4 changed files with 115 additions and 28 deletions

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)

View File

@ -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())
}
}