diff --git a/doc/contexts.md b/doc/contexts.md index d4076a7c9..b6b6be44c 100644 --- a/doc/contexts.md +++ b/doc/contexts.md @@ -36,7 +36,7 @@ fun ComplexOperations.doSomethingWithComplex(c1: Complex, c2: Complex, c3: Compl ComplexOperations.doComethingWithComplex(c1,c2,c3) ``` -In fact, whole parts of proram could run in a mathematical context or even multiple nested contexts. +In fact, whole parts of program could run in a mathematical context or even multiple nested contexts. In `kmath` contexts are responsible not only for operations, but also for raw object creation and advanced features. diff --git a/doc/operations.md b/doc/operations.md new file mode 100644 index 000000000..99c8eacac --- /dev/null +++ b/doc/operations.md @@ -0,0 +1,38 @@ +## Spaces and fields + +An obvious first choice of mathematical objects to implement in context-oriented style are algebra elements like spaces, +rings and fields. Those are located in a `scientifik.kmath.operations.Algebra.kt` file. Alongside algebric context +themselves, the file includes definitions for algebra elements such as `FieldElement`. A `FieldElement` object +stores a reference to the `Field` which contains a additive and multiplicative operations for it, meaning +it has one fixed context attached to it and does not require explicit external context. So those `MathElements` could be +operated without context: +```kotlin +val c1 = Complex(1.0, 2.0) +val c2 = ComplexField.i +val c3 = c1 + c2 +``` +`ComplexField` also features special operations to mix complex numbers with real numbers like: +```kotlin +val c1 = Complex(1.0,2.0) +val c2 = ComplexField.run{ c1 - 1.0} //returns [re:0.0, im: 2.0] +val c3 = ComplexField.run{ c1 - i*2.0} +``` + +**Note**: In theory it is possible to add behaviors directly to the context, but currently kotlin syntax does not support +that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) for news. + +## Nested fields + +Algebra contexts allow to create more complex structures. For example, it is possible to create a `Matrix` from complex +elements like this: +```kotlin +val element = NDElements.create(field = ComplexField, shape = intArrayOf(2,2)){index: IntArray -> + Complex(index[0] - index[1], index[0] + index[1]) +} +``` +The `element` in this example is a member of `Field` of 2-d structures, each element of which is a member of its own +`ComplexField`. The important thing is that one does not need to create a special nd-structure to hold complex +numbers and implements operations on it, one need just to provide a field for its elements. + +**Note**: Fields themselves do not solve problem of JVM boxing, but it is possible to solve with special contexts like +`BufferSpec`. This feature is in development phase. \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 46388edaf..e8a80a451 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -18,6 +18,15 @@ object ComplexField : Field { override fun divide(a: Complex, b: Complex): Complex = Complex(a.re * b.re + a.im * b.im, a.re * b.im - a.im * b.re) / b.square + operator fun Double.plus(c: Complex) = this.toComplex() + c + + operator fun Double.minus(c: Complex) = this.toComplex() - c + + operator fun Complex.plus(d: Double) = d + this + + operator fun Complex.minus(d: Double) = this - d.toComplex() + + operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this) } /** @@ -45,14 +54,4 @@ data class Complex(val re: Double, val im: Double) : FieldElement>(shape: IntArray, field: F) : NDField //typealias NDFieldFactory = (IntArray)->NDField -object NDArrays { +object NDElements { /** * Create a platform-optimized NDArray of doubles */ - fun realNDArray(shape: IntArray, initializer: DoubleField.(IntArray) -> Double = { 0.0 }): NDElement { + fun realNDElement(shape: IntArray, initializer: DoubleField.(IntArray) -> Double = { 0.0 }): NDElement { return ExtendedNDField(shape, DoubleField).produce(initializer) } - fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDElement { - return realNDArray(intArrayOf(dim)) { initializer(it[0]) } + fun real1DElement(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDElement { + return realNDElement(intArrayOf(dim)) { initializer(it[0]) } } - fun real2DArray(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): NDElement { - return realNDArray(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } + fun real2DElement(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): NDElement { + return realNDElement(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } } - fun real3DArray(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }): NDElement { - return realNDArray(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } + fun real3DElement(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }): NDElement { + return realNDElement(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } } - inline fun produceReal(shape: IntArray, block: ExtendedNDField.() -> NDStructure): NDElement { + inline fun real(shape: IntArray, block: ExtendedNDField.() -> NDStructure): NDElement { val field = ExtendedNDField(shape, DoubleField) return NDStructureElement(field, field.run(block)) } diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/GenericNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/GenericNDFieldTest.kt index 338f5f052..0bc118e3b 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/GenericNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/GenericNDFieldTest.kt @@ -1,7 +1,7 @@ package scientifik.kmath.structures import scientifik.kmath.operations.DoubleField -import scientifik.kmath.structures.NDArrays.create +import scientifik.kmath.structures.NDElements.create import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt index 17546a30d..5d5ee97cf 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt @@ -1,16 +1,16 @@ package scientifik.kmath.structures import scientifik.kmath.operations.Norm -import scientifik.kmath.structures.NDArrays.produceReal -import scientifik.kmath.structures.NDArrays.real2DArray +import scientifik.kmath.structures.NDElements.real +import scientifik.kmath.structures.NDElements.real2DElement import kotlin.math.abs import kotlin.math.pow import kotlin.test.Test import kotlin.test.assertEquals class NumberNDFieldTest { - val array1 = real2DArray(3, 3) { i, j -> (i + j).toDouble() } - val array2 = real2DArray(3, 3) { i, j -> (i - j).toDouble() } + val array1 = real2DElement(3, 3) { i, j -> (i + j).toDouble() } + val array2 = real2DElement(3, 3) { i, j -> (i - j).toDouble() } @Test fun testSum() { @@ -27,7 +27,7 @@ class NumberNDFieldTest { @Test fun testGeneration() { - val array = real2DArray(3, 3) { i, j -> (i * 10 + j).toDouble() } + val array = real2DElement(3, 3) { i, j -> (i * 10 + j).toDouble() } for (i in 0..2) { for (j in 0..2) { @@ -64,7 +64,7 @@ class NumberNDFieldTest { @Test fun testInternalContext() { - produceReal(array1.shape) { + real(array1.shape) { with(L2Norm) { 1 + norm(array1) + exp(array2) } diff --git a/kmath-coroutines/src/commonTest/kotlin/scientifik/kmath/structures/LazyNDFieldTest.kt b/kmath-coroutines/src/commonTest/kotlin/scientifik/kmath/structures/LazyNDFieldTest.kt index 403fe2d31..6dce9b533 100644 --- a/kmath-coroutines/src/commonTest/kotlin/scientifik/kmath/structures/LazyNDFieldTest.kt +++ b/kmath-coroutines/src/commonTest/kotlin/scientifik/kmath/structures/LazyNDFieldTest.kt @@ -9,7 +9,7 @@ class LazyNDFieldTest { @Test fun testLazyStructure() { var counter = 0 - val regularStructure = NDArrays.create(IntField, intArrayOf(2, 2, 2)) { it[0] + it[1] - it[2] } + val regularStructure = NDElements.create(IntField, intArrayOf(2, 2, 2)) { it[0] + it[1] - it[2] } val result = (regularStructure.lazy() + 2).transform { counter++ it * it