diff --git a/common/src/main/kotlin/scientifik/kmath/structures/LinearAlgrebra.kt b/common/src/main/kotlin/scientifik/kmath/structures/LinearAlgrebra.kt index 3de6d7560..416a5d477 100644 --- a/common/src/main/kotlin/scientifik/kmath/structures/LinearAlgrebra.kt +++ b/common/src/main/kotlin/scientifik/kmath/structures/LinearAlgrebra.kt @@ -1,45 +1,117 @@ package scientifik.kmath.structures +import scientifik.kmath.operations.DoubleField import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space import scientifik.kmath.operations.SpaceElement -abstract class LinearSpace>(val rows: Int, val columns: Int, val field: Field) : Space { +/** + * The space for linear elements. Supports scalar product alongside with standard linear operations. + */ +abstract class LinearSpace>(val rows: Int, val columns: Int, val field: Field) : Space { - abstract fun produce(initializer: (Int, Int) -> T): E + /** + * Produce the element of this space + */ + abstract fun produce(initializer: (Int, Int) -> T): V - override val zero: E by lazy { + /** + * Produce new linear space with given dimensions + */ + abstract fun produceSpace(rows: Int, columns: Int): LinearSpace + + override val zero: V by lazy { produce { _, _ -> field.zero } } - override fun add(a: E, b: E): E { + override fun add(a: V, b: V): V { return produce { i, j -> with(field) { a[i, j] + b[i, j] } } } - override fun multiply(a: E, k: Double): E { + override fun multiply(a: V, k: Double): V { //TODO it is possible to implement scalable linear elements which normed values and adjustable scale to save memory and processing poser return produce { i, j -> with(field) { a[i, j] * k } } } + + /** + * Dot product + */ + fun multiply(a: V, b: V): V { + if (a.columns != b.rows) { + //TODO replace by specific exception + throw RuntimeException("Dimension mismatch in vector dot product") + } + return produceSpace(a.rows, b.columns).produce { i, j -> + (0..a.columns).asSequence().map { k -> field.multiply(a[i, k], b[k, j]) }.reduce { first, second -> field.add(first, second) } + } + } + + infix fun V.dot(b: V): V = multiply(this, b) } /** - * An element of linear algebra with fixed dimension. The linear space allows linear operations on objects of the same dimensions. - * Scalar product operations are performed outside space. - * - * @param T the type of linear object element type. + * A matrix-like structure that is not dependent on specific space implementation */ -interface LinearObject : SpaceElement> { +interface LinearStructure { val rows: Int val columns: Int operator fun get(i: Int, j: Int): T - /** - * Get a transposed object with switched dimensions - */ - fun transpose(): LinearObject - - /** - * Perform scalar multiplication (dot) operation, checking dimensions. The argument object and result both could be outside initial space. - */ - operator fun times(other: LinearObject): LinearObject + fun transpose(): LinearStructure { + return object : LinearStructure { + override val rows: Int = this@LinearStructure.columns + override val columns: Int = this@LinearStructure.rows + override fun get(i: Int, j: Int): T = this@LinearStructure.get(j, i) + } + } } + +class RealArraySpace(rows: Int, columns: Int) : LinearSpace(rows, columns, DoubleField) { + override fun produce(initializer: (Int, Int) -> Double): RealArray { + return RealArray(this, initializer) + } + + override fun produceSpace(rows: Int, columns: Int): LinearSpace { + return RealArraySpace(rows, columns) + } +} + +class RealArray(override val context: RealArraySpace, initializer: (Int, Int) -> Double): LinearStructure, SpaceElement { + + val array: Array> = Array(context.rows) { i -> Array(context.columns) { j -> initializer(i, j) } } + + override val rows: Int = context.rows + + override val columns: Int = context.columns + + override fun get(i: Int, j: Int): Double { + return array[i][j] + } + + override val self: RealArray = this +} + + +///** +// * An element of linear algebra with fixed dimension. The linear space allows linear operations on objects of the same dimensions. +// * Scalar product operations are performed outside space. +// * +// * @param E the type of linear object element type. +// */ +//interface LinearObject : SpaceElement> { +// override val context: LinearSpace<> +// +// val rows: Int +// val columns: Int +// operator fun get(i: Int, j: Int): E +// +// /** +// * Get a transposed object with switched dimensions +// */ +// fun transpose(): LinearObject +// +// /** +// * Perform scalar multiplication (dot) operation, checking dimensions. The argument object and result both could be outside initial space. +// */ +// operator fun times(other: LinearObject): LinearObject +//} diff --git a/common/src/main/kotlin/scientifik/kmath/structures/NDArray.kt b/common/src/main/kotlin/scientifik/kmath/structures/NDArray.kt index 4e2458fff..0005565c1 100644 --- a/common/src/main/kotlin/scientifik/kmath/structures/NDArray.kt +++ b/common/src/main/kotlin/scientifik/kmath/structures/NDArray.kt @@ -9,6 +9,7 @@ class ShapeMismatchException(val expected: List, val actual: List) : R * Field for n-dimensional arrays. * @param shape - the list of dimensions of the array * @param field - operations field defined on individual array element + * @param T the type of the element contained in NDArray */ abstract class NDField(val shape: List, val field: Field) : Field> { /**