From 2386ecba41ff00e234bf5a282e71deda3ec135aa Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 4 Nov 2023 11:49:31 +0300 Subject: [PATCH] 0.4 WIP --- .../space/kscience/attributes/Attributes.kt | 7 + .../space/kscience/attributes/SafeType.kt | 9 +- .../benchmarks/MatrixInverseBenchmark.kt | 2 +- .../space/kscience/kmath/ast/TestParser.kt | 3 + kmath-complex/build.gradle.kts | 1 + .../space/kscience/kmath/complex/Complex.kt | 23 +- .../kscience/kmath/complex/Quaternion.kt | 16 +- kmath-core/build.gradle.kts | 1 - .../space/kscience/kmath/annotations.kt | 0 .../kscience/kmath/expressions/DSAlgebra.kt | 2 +- .../kmath/expressions/SimpleAutoDiff.kt | 1 + .../kmath/linear/BufferedLinearSpace.kt | 3 - .../kscience/kmath/linear/LinearSpace.kt | 8 +- .../kscience/kmath/linear/LupDecomposition.kt | 210 +++++++----------- .../kscience/kmath/linear/MatrixBuilder.kt | 2 +- .../kscience/kmath/linear/MatrixWrapper.kt | 2 +- .../space/kscience/kmath/nd/StructureND.kt | 19 +- .../kscience/kmath/operations/Algebra.kt | 9 +- .../space/kscience/kmath/operations/BigInt.kt | 6 +- .../kmath/operations/BufferAlgebra.kt | 6 +- .../kscience/kmath/operations/LogicAlgebra.kt | 5 +- .../kscience/kmath/operations/numbers.kt | 12 +- .../kscience/kmath/structures/ArrayBuffer.kt | 13 +- .../space/kscience/kmath/structures/Buffer.kt | 2 +- .../kscience/kmath/structures/BufferView.kt | 7 +- .../kmath/structures/MutableBuffer.kt | 24 +- .../space/kscience/kmath/linear/MatrixTest.kt | 9 +- .../kscience/kmath/operations/BigNumbers.kt | 4 + .../kscience/kmath/chains/BlockingChain.kt | 4 +- .../kscience/kmath/streaming/BufferFlow.kt | 5 +- .../kscience/kmath/streaming/RingBuffer.kt | 42 ++-- .../kmath/structures/LazyStructureND.kt | 12 +- .../kscience/kmath/dimensions/Wrappers.kt | 10 +- .../kscience/kmath/ejml/EjmlLinearSpace.kt | 14 +- .../space/kscience/kmath/ejml/_generated.kt | 56 ++--- .../kscience/kmath/ejml/EjmlMatrixTest.kt | 2 +- .../space/kscience/kmath/real/RealMatrix.kt | 2 +- .../kscience/kmath/functions/Polynomial.kt | 5 +- .../integration/GaussIntegratorRuleFactory.kt | 5 +- .../kmath/integration/SplineIntegrator.kt | 4 +- .../kmath/interpolation/Interpolator.kt | 10 +- .../kmath/interpolation/SplineInterpolator.kt | 3 +- kmath-memory/build.gradle.kts | 4 + .../kscience/kmath/memory}/MemoryBuffer.kt | 17 +- .../space/kscience/kmath/memory/MemorySpec.kt | 5 +- 45 files changed, 297 insertions(+), 309 deletions(-) rename {kmath-memory => kmath-core}/src/commonMain/kotlin/space/kscience/kmath/annotations.kt (100%) rename {kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures => kmath-memory/src/commonMain/kotlin/space/kscience/kmath/memory}/MemoryBuffer.kt (84%) diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt index 705e61436..a1bccc211 100644 --- a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt @@ -106,4 +106,11 @@ public fun > Attributes( attrValue: T, ): Attributes = Attributes(mapOf(attribute to attrValue)) +/** + * Create Attributes with a single [Unit] valued attribute + */ +public fun > Attributes( + attribute: A +): Attributes = Attributes(mapOf(attribute to Unit)) + public operator fun Attributes.plus(other: Attributes): Attributes = Attributes(content + other.content) \ No newline at end of file diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/SafeType.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/SafeType.kt index 0fac2477c..1c7b0991a 100644 --- a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/SafeType.kt +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/SafeType.kt @@ -25,4 +25,11 @@ public inline fun safeTypeOf(): SafeType = SafeType(typeOf()) */ @Suppress("UNCHECKED_CAST") @UnstableAttributesAPI -public val SafeType.kClass: KClass get() = kType.classifier as KClass \ No newline at end of file +public val SafeType.kClass: KClass get() = kType.classifier as KClass + +/** + * An interface containing [type] for dynamic type checking. + */ +public interface WithType { + public val type: SafeType +} \ No newline at end of file diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt index f7aac8199..16e017432 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt @@ -47,7 +47,7 @@ internal class MatrixInverseBenchmark { @Benchmark fun ejmlInverse(blackhole: Blackhole) { EjmlLinearSpaceDDRM { - blackhole.consume(matrix.toEjml().inverse()) + blackhole.consume(matrix.toEjml().inverted()) } } } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt index 784dcece9..045890e71 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt @@ -10,6 +10,7 @@ import space.kscience.kmath.complex.ComplexField import space.kscience.kmath.expressions.interpret import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.test.Test import kotlin.test.assertEquals @@ -39,6 +40,8 @@ internal class TestParser { @Test fun evaluateMstBinary() { val magicalAlgebra = object : Algebra { + override val bufferFactory: MutableBufferFactory get() = MutableBufferFactory() + override fun bindSymbolOrNull(value: String): String = value override fun unaryOperationFunction(operation: String): (arg: String) -> String { diff --git a/kmath-complex/build.gradle.kts b/kmath-complex/build.gradle.kts index 3e8b3b75e..9ac2d5ab4 100644 --- a/kmath-complex/build.gradle.kts +++ b/kmath-complex/build.gradle.kts @@ -10,6 +10,7 @@ kscience { dependencies { api(projects.kmathCore) + api(projects.kmathMemory) } testDependencies { diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt index b5f1aabe7..3a524bbb3 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt @@ -5,12 +5,14 @@ package space.kscience.kmath.complex +import space.kscience.attributes.SafeType +import space.kscience.attributes.safeTypeOf import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.memory.MemoryReader -import space.kscience.kmath.memory.MemorySpec -import space.kscience.kmath.memory.MemoryWriter +import space.kscience.kmath.memory.* import space.kscience.kmath.operations.* -import space.kscience.kmath.structures.* +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.MutableBuffer +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.* /** @@ -51,8 +53,11 @@ public object ComplexField : Norm, NumbersAddOps, ScaleOperations { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory { size, init -> - MutableMemoryBuffer.create(Complex, size, init) + override val bufferFactory: MutableBufferFactory = object : MutableBufferFactory { + override fun invoke(size: Int, builder: (Int) -> Complex): MutableBuffer = + MutableMemoryBuffer.create(Complex, size, builder) + + override val type: SafeType = safeTypeOf() } override val zero: Complex = 0.0.toComplex() @@ -202,8 +207,10 @@ public data class Complex(val re: Double, val im: Double) { override fun toString(): String = "($re + i * $im)" public companion object : MemorySpec { - override val objectSize: Int - get() = 16 + + override val type: SafeType get() = safeTypeOf() + + override val objectSize: Int get() = 16 override fun MemoryReader.read(offset: Int): Complex = Complex(readDouble(offset), readDouble(offset + 8)) diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt index 8f3d15a26..f6f9e9047 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt @@ -5,15 +5,14 @@ package space.kscience.kmath.complex +import space.kscience.attributes.SafeType +import space.kscience.attributes.safeTypeOf import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.memory.MemoryReader -import space.kscience.kmath.memory.MemorySpec -import space.kscience.kmath.memory.MemoryWriter +import space.kscience.kmath.memory.* import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.MemoryBuffer import space.kscience.kmath.structures.MutableBuffer -import space.kscience.kmath.structures.MutableMemoryBuffer +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.* /** @@ -37,6 +36,8 @@ public class Quaternion( require(!z.isNaN()) { "z-component of quaternion is not-a-number" } } + override val type: SafeType get() = safeTypeOf() + /** * Returns a string representation of this quaternion. */ @@ -78,6 +79,7 @@ public class Quaternion( public companion object : MemorySpec { override val objectSize: Int get() = 32 + override val type: SafeType get() = safeTypeOf() override fun MemoryReader.read(offset: Int): Quaternion = Quaternion( readDouble(offset), @@ -122,7 +124,7 @@ public val Quaternion.reciprocal: Quaternion /** * Produce a normalized version of this quaternion */ -public fun Quaternion.normalized(): Quaternion = with(QuaternionAlgebra){ this@normalized / norm(this@normalized) } +public fun Quaternion.normalized(): Quaternion = with(QuaternionAlgebra) { this@normalized / norm(this@normalized) } /** * A field of [Quaternion]. @@ -131,6 +133,8 @@ public fun Quaternion.normalized(): Quaternion = with(QuaternionAlgebra){ this@n public object QuaternionAlgebra : Group, Norm, PowerOperations, ExponentialOperations, NumbersAddOps, ScaleOperations { + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() + override val zero: Quaternion = Quaternion(0.0) public val one: Quaternion = Quaternion(1.0) diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 398465fbe..23fedbda4 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -9,7 +9,6 @@ kscience{ wasm() dependencies { - api(projects.kmathMemory) api(projects.attributesKt) } diff --git a/kmath-memory/src/commonMain/kotlin/space/kscience/kmath/annotations.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/annotations.kt similarity index 100% rename from kmath-memory/src/commonMain/kotlin/space/kscience/kmath/annotations.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/annotations.kt diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DSAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DSAlgebra.kt index 817f38ff0..8ef751859 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DSAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DSAlgebra.kt @@ -188,7 +188,7 @@ public abstract class DSAlgebra>( vararg derivatives: T, ): DS { require(derivatives.size == compiler.size) { "dimension mismatch: ${derivatives.size} and ${compiler.size}" } - val data = derivatives.asBuffer() + val data = derivatives.asList().asBuffer(algebra.type) return DS(data) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt index 6d22b18dc..fd7bf9fdc 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.expressions import space.kscience.attributes.SafeType +import space.kscience.attributes.WithType import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.Point import space.kscience.kmath.operations.* diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt index 6bfe7581b..4bba47a91 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/BufferedLinearSpace.kt @@ -5,15 +5,12 @@ package space.kscience.kmath.linear -import space.kscience.attributes.SafeType import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.VirtualBuffer import space.kscience.kmath.structures.indices -import kotlin.reflect.KType -import kotlin.reflect.typeOf public class BufferedLinearSpace>( diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt index ddf4f17d4..00ebad5ee 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt @@ -6,11 +6,11 @@ package space.kscience.kmath.linear import space.kscience.attributes.SafeType +import space.kscience.attributes.WithType import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.* import space.kscience.kmath.operations.BufferRingOps import space.kscience.kmath.operations.Ring -import space.kscience.kmath.operations.WithType import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer @@ -173,15 +173,15 @@ public interface LinearSpace> : MatrixOperations { public operator fun T.times(v: Point): Point = v * this /** - * Get an attribute value for the structure in this scope. Structure features take precedence other context features. + * Get an attribute value for the structure in this scope. Structure attributes are preferred to computed attributes. * * @param structure the structure. * @param attribute to be computed. * @return a feature object or `null` if it isn't present. */ @UnstableKMathAPI - public fun > attributeFor(structure: StructureND<*>, attribute: A): T? = - structure.attributes[attribute] + public fun > attributeFor(structure: StructureND<*>, attribute: A): T = + structure.attributes[attribute] ?: error("Can't compute attribute $attribute for $structure") public companion object { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt index 1ac2ca5c6..6592a3da4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.linear +import space.kscience.attributes.Attributes import space.kscience.attributes.PolymorphicAttribute import space.kscience.attributes.SafeType import space.kscience.attributes.safeTypeOf @@ -19,32 +20,42 @@ import space.kscience.kmath.structures.* * *a* is the owning matrix. * * @param T the type of matrices' items. - * @param l The lower triangular matrix in this decomposition. It may have [LowerTriangular]. - * @param u The upper triangular matrix in this decomposition. It may have [UpperTriangular]. + * @param lu combined L and U matrix */ public class LupDecomposition( public val linearSpace: LinearSpace>, - public val l: Matrix, - public val u: Matrix, + private val lu: Matrix, public val pivot: IntBuffer, + private val even: Boolean, ) { public val elementAlgebra: Ring get() = linearSpace.elementAlgebra - public val pivotMatrix: VirtualMatrix + public val l: Matrix + get() = VirtualMatrix(lu.type, lu.rowNum, lu.colNum, attributes = Attributes(LowerTriangular)) { i, j -> + when { + j < i -> lu[i, j] + j == i -> elementAlgebra.one + else -> elementAlgebra.zero + } + } + + public val u: Matrix + get() = VirtualMatrix(lu.type, lu.rowNum, lu.colNum, attributes = Attributes(UpperTriangular)) { i, j -> + if (j >= i) lu[i, j] else elementAlgebra.zero + } + + public val pivotMatrix: Matrix get() = VirtualMatrix(linearSpace.type, l.rowNum, l.colNum) { row, column -> if (column == pivot[row]) elementAlgebra.one else elementAlgebra.zero } - public val LupDecomposition.determinant by lazy { + public val determinant: T by lazy { elementAlgebra { (0 until l.shape[0]).fold(if (even) one else -one) { value, i -> value * lu[i, i] } } } } - - - public class LupDecompositionAttribute(type: SafeType>) : PolymorphicAttribute>(type), MatrixAttribute> @@ -52,59 +63,6 @@ public class LupDecompositionAttribute(type: SafeType>) : public val MatrixOperations.LUP: LupDecompositionAttribute get() = LupDecompositionAttribute(safeTypeOf()) - -///** -// * Common implementation of [LupDecomposition]. -// */ -//private class LupDecompositionImpl( -// public val elementContext: Field, -// public val lu: Matrix, -// public val pivot: IntBuffer, -// private val even: Boolean, -//) : LupDecomposition { -// /** -// * Returns the matrix L of the decomposition. -// * -// * L is a lower-triangular matrix with [Ring.one] in diagonal -// */ -// override val l: Matrix = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j -> -// when { -// j < i -> lu[i, j] -// j == i -> elementContext.one -// else -> elementContext.zero -// } -// }.withFeature(LowerTriangular) -// -// -// /** -// * Returns the matrix U of the decomposition. -// * -// * U is an upper-triangular matrix including the diagonal -// */ -// override val u: Matrix = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j -> -// if (j >= i) lu[i, j] else elementContext.zero -// }.withFeature(UpperTriangular) -// -// /** -// * Returns the P rows permutation matrix. -// * -// * P is a sparse matrix with exactly one element set to [Ring.one] in -// * each row and each column, all other elements being set to [Ring.zero]. -// */ -// override val p: Matrix = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j -> -// if (j == pivot[i]) elementContext.one else elementContext.zero -// } -// -// /** -// * Return the determinant of the matrix -// * @return determinant of the matrix -// */ -// override val determinant: T by lazy { -// elementContext { (0 until lu.shape[0]).fold(if(even) one else -one) { value, i -> value * lu[i, i] } } -// } -// -//} - @PublishedApi internal fun > LinearSpace>.abs(value: T): T = if (value > elementAlgebra.zero) value else elementAlgebra { -value } @@ -115,97 +73,81 @@ internal fun > LinearSpace>.abs(value: T): T = public fun > LinearSpace>.lup( matrix: Matrix, checkSingular: (T) -> Boolean, -): LupDecomposition { +): LupDecomposition = elementAlgebra { require(matrix.rowNum == matrix.colNum) { "LU decomposition supports only square matrices" } val m = matrix.colNum val pivot = IntArray(matrix.rowNum) //TODO just waits for multi-receivers - BufferAccessor2D(matrix.rowNum, matrix.colNum, elementAlgebra.bufferFactory).run { - elementAlgebra { - val lu = create(matrix) + with(BufferAccessor2D(matrix.rowNum, matrix.colNum, elementAlgebra.bufferFactory)){ - // Initialize the permutation array and parity - for (row in 0 until m) pivot[row] = row - var even = true + val lu = create(matrix) - // Initialize the permutation array and parity - for (row in 0 until m) pivot[row] = row + // Initialize the permutation array and parity + for (row in 0 until m) pivot[row] = row + var even = true - // Loop over columns - for (col in 0 until m) { - // upper - for (row in 0 until col) { - val luRow = lu.row(row) - var sum = luRow[col] - for (i in 0 until row) sum -= luRow[i] * lu[i, col] - luRow[col] = sum - } + // Initialize the permutation array and parity + for (row in 0 until m) pivot[row] = row - // lower - var max = col // permutation row - var largest = -one - - for (row in col until m) { - val luRow = lu.row(row) - var sum = luRow[col] - for (i in 0 until col) sum -= luRow[i] * lu[i, col] - luRow[col] = sum - - // maintain the best permutation choice - if (abs(sum) > largest) { - largest = abs(sum) - max = row - } - } - - // Singularity check - check(!checkSingular(abs(lu[max, col]))) { "The matrix is singular" } - - // Pivot if necessary - if (max != col) { - val luMax = lu.row(max) - val luCol = lu.row(col) - - for (i in 0 until m) { - val tmp = luMax[i] - luMax[i] = luCol[i] - luCol[i] = tmp - } - - val temp = pivot[max] - pivot[max] = pivot[col] - pivot[col] = temp - even = !even - } - - // Divide the lower elements by the "winning" diagonal elt. - val luDiag = lu[col, col] - for (row in col + 1 until m) lu[row, col] /= luDiag + // Loop over columns + for (col in 0 until m) { + // upper + for (row in 0 until col) { + val luRow = lu.row(row) + var sum = luRow[col] + for (i in 0 until row) sum -= luRow[i] * lu[i, col] + luRow[col] = sum } - val l: MatrixWrapper = VirtualMatrix(type, rowNum, colNum) { i, j -> - when { - j < i -> lu[i, j] - j == i -> one - else -> zero + // lower + var max = col // permutation row + var largest = -one + + for (row in col until m) { + val luRow = lu.row(row) + var sum = luRow[col] + for (i in 0 until col) sum -= luRow[i] * lu[i, col] + luRow[col] = sum + + // maintain the best permutation choice + if (abs(sum) > largest) { + largest = abs(sum) + max = row } - }.withAttribute(LowerTriangular) + } - val u = VirtualMatrix(type, rowNum, colNum) { i, j -> - if (j >= i) lu[i, j] else zero - }.withAttribute(UpperTriangular) -// -// val p = VirtualMatrix(rowNum, colNum) { i, j -> -// if (j == pivot[i]) one else zero -// }.withAttribute(Determinant, if (even) one else -one) + // Singularity check + check(!checkSingular(abs(lu[max, col]))) { "The matrix is singular" } - return LupDecomposition(this@lup, l, u, pivot.asBuffer()) + // Pivot if necessary + if (max != col) { + val luMax = lu.row(max) + val luCol = lu.row(col) + + for (i in 0 until m) { + val tmp = luMax[i] + luMax[i] = luCol[i] + luCol[i] = tmp + } + + val temp = pivot[max] + pivot[max] = pivot[col] + pivot[col] = temp + even = !even + } + + // Divide the lower elements by the "winning" diagonal elt. + val luDiag = lu[col, col] + for (row in col + 1 until m) lu[row, col] /= luDiag } + + return LupDecomposition(this@lup, lu.toStructure2D(), pivot.asBuffer(), even) } } + public fun LinearSpace.lup( matrix: Matrix, singularityThreshold: Double = 1e-11, diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt index 9543555e0..b15d4883c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt @@ -7,9 +7,9 @@ package space.kscience.kmath.linear import space.kscience.attributes.FlagAttribute import space.kscience.attributes.SafeType +import space.kscience.attributes.WithType import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.Ring -import space.kscience.kmath.operations.WithType import space.kscience.kmath.structures.BufferAccessor2D import space.kscience.kmath.structures.MutableBufferFactory diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt index 9d89f7636..eb14de3c7 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt @@ -16,7 +16,7 @@ import space.kscience.kmath.operations.Ring * * @param T the type of items. */ -public class MatrixWrapper internal constructor( +public class MatrixWrapper internal constructor( public val origin: Matrix, override val attributes: Attributes, ) : Matrix by origin { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt index d0d078609..1456d5ed8 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt @@ -5,17 +5,12 @@ package space.kscience.kmath.nd -import space.kscience.attributes.Attribute -import space.kscience.attributes.AttributeContainer -import space.kscience.attributes.Attributes -import space.kscience.attributes.SafeType +import space.kscience.attributes.* import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.operations.Ring -import space.kscience.kmath.operations.WithType import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer -import kotlin.jvm.JvmName import kotlin.math.abs public interface StructureAttribute : Attribute @@ -148,28 +143,20 @@ public inline fun BufferND( crossinline initializer: (IntArray) -> T, ): BufferND = BufferND(strides, Buffer(strides.linearSize) { i -> initializer(strides.index(i)) }) -public inline fun BufferND( - type: SafeType, - strides: Strides, - crossinline initializer: (IntArray) -> T, -): BufferND = BufferND(strides, Buffer(type, strides.linearSize) { i -> initializer(strides.index(i)) }) - - public inline fun BufferND( shape: ShapeND, crossinline initializer: (IntArray) -> T, ): BufferND = BufferND(ColumnStrides(shape), initializer) -@JvmName("autoVarArg") public inline fun BufferND( vararg shape: Int, crossinline initializer: (IntArray) -> T, ): BufferND = BufferND(ColumnStrides(ShapeND(shape)), initializer) -public inline fun BufferND( +public fun BufferND( type: SafeType, vararg shape: Int, - crossinline initializer: (IntArray) -> T, + initializer: (IntArray) -> T, ): BufferND = BufferND(type, ColumnStrides(ShapeND(shape)), initializer) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt index 44e9a0b17..310daa65b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt @@ -6,19 +6,12 @@ package space.kscience.kmath.operations import space.kscience.attributes.SafeType +import space.kscience.attributes.WithType import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.operations.Ring.Companion.optimizedPower import space.kscience.kmath.structures.MutableBufferFactory -/** - * An interface containing [type] for dynamic type checking. - */ -public interface WithType { - public val type: SafeType -} - - /** * Represents an algebraic structure. * diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt index 25554477c..eee991d27 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt @@ -5,20 +5,18 @@ package space.kscience.kmath.operations -import space.kscience.attributes.SafeType -import space.kscience.attributes.safeTypeOf import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.BufferedRingOpsND import space.kscience.kmath.operations.BigInt.Companion.BASE import space.kscience.kmath.operations.BigInt.Companion.BASE_SIZE import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.log2 import kotlin.math.max import kotlin.math.min import kotlin.math.sign private typealias Magnitude = UIntArray -private typealias TBase = ULong /** * Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger). @@ -29,7 +27,7 @@ private typealias TBase = ULong @OptIn(UnstableKMathAPI::class) public object BigIntField : Field, NumbersAddOps, ScaleOperations { - override val type: SafeType = safeTypeOf() + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override val zero: BigInt = BigInt.ZERO override val one: BigInt = BigInt.ONE diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt index 5a8b26571..42e2d9876 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt @@ -5,8 +5,6 @@ package space.kscience.kmath.operations -import space.kscience.attributes.SafeType -import space.kscience.attributes.safeTypeOf import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBufferFactory @@ -21,10 +19,10 @@ public interface WithSize { public interface BufferAlgebra> : Algebra> { public val elementAlgebra: A - override val type: SafeType> get() = safeTypeOf>() - public val elementBufferFactory: MutableBufferFactory get() = elementAlgebra.bufferFactory + override val bufferFactory: MutableBufferFactory> get() = MutableBufferFactory() + public fun buffer(size: Int, vararg elements: T): Buffer { require(elements.size == size) { "Expected $size elements but found ${elements.size}" } return elementBufferFactory(size) { elements[it] } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/LogicAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/LogicAlgebra.kt index 312fd1396..1538b096b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/LogicAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/LogicAlgebra.kt @@ -5,10 +5,9 @@ package space.kscience.kmath.operations -import space.kscience.attributes.SafeType -import space.kscience.attributes.safeTypeOf import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.structures.MutableBufferFactory /** * An algebra for generic boolean logic @@ -74,7 +73,7 @@ public interface LogicAlgebra : Algebra { @Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object BooleanAlgebra : LogicAlgebra { - override val type: SafeType get() = safeTypeOf() + override val bufferFactory: MutableBufferFactory get() = MutableBufferFactory() override fun const(boolean: Boolean): Boolean = boolean diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt index 7d8e12139..28b87aa2f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt @@ -4,7 +4,7 @@ */ package space.kscience.kmath.operations -import space.kscience.kmath.structures.* +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.pow as kpow @@ -129,7 +129,7 @@ public val Double.Companion.algebra: Float64Field get() = Float64Field */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Float32Field : ExtendedField, Norm { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Float32Buffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override val zero: Float get() = 0.0f override val one: Float get() = 1.0f @@ -187,7 +187,7 @@ public val Float.Companion.algebra: Float32Field get() = Float32Field */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Int32Ring : Ring, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int32Buffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override val zero: Int get() = 0 override val one: Int get() = 1 @@ -212,7 +212,7 @@ public val Int.Companion.algebra: Int32Ring get() = Int32Ring */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Int16Ring : Ring, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int16Buffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override val zero: Short get() = 0 override val one: Short get() = 1 @@ -237,7 +237,7 @@ public val Short.Companion.algebra: Int16Ring get() = Int16Ring */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Int8Ring : Ring, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int8Buffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override val zero: Byte get() = 0 override val one: Byte get() = 1 @@ -262,7 +262,7 @@ public val Byte.Companion.algebra: Int8Ring get() = Int8Ring */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Int64Ring : Ring, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int64Buffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override val zero: Long get() = 0L override val one: Long get() = 1L diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ArrayBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ArrayBuffer.kt index 8e81dd941..47817661f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ArrayBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ArrayBuffer.kt @@ -5,13 +5,16 @@ package space.kscience.kmath.structures +import space.kscience.attributes.SafeType +import space.kscience.attributes.safeTypeOf + /** * [MutableBuffer] implementation over [Array]. * * @param T the type of elements contained in the buffer. * @property array The underlying array. */ -public class ArrayBuffer(internal val array: Array) : MutableBuffer { +public class ArrayBuffer(override val type: SafeType, internal val array: Array) : MutableBuffer { // Can't inline because array is invariant override val size: Int get() = array.size @@ -22,13 +25,17 @@ public class ArrayBuffer(internal val array: Array) : MutableBuffer { } override operator fun iterator(): Iterator = array.iterator() - override fun copy(): MutableBuffer = ArrayBuffer(array.copyOf()) + override fun copy(): MutableBuffer = ArrayBuffer(type, array.copyOf()) override fun toString(): String = Buffer.toString(this) } +/** + * Returns an [ArrayBuffer] that wraps the original array. + */ +public fun Array.asBuffer(type: SafeType): ArrayBuffer = ArrayBuffer(type, this) /** * Returns an [ArrayBuffer] that wraps the original array. */ -public fun Array.asBuffer(): ArrayBuffer = ArrayBuffer(this) \ No newline at end of file +public inline fun Array.asBuffer(): ArrayBuffer = ArrayBuffer(safeTypeOf(), this) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt index c3dbfbfe2..26e238145 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt @@ -6,9 +6,9 @@ package space.kscience.kmath.structures import space.kscience.attributes.SafeType +import space.kscience.attributes.WithType import space.kscience.attributes.safeTypeOf import space.kscience.kmath.operations.WithSize -import space.kscience.kmath.operations.WithType import space.kscience.kmath.operations.asSequence import kotlin.reflect.typeOf diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt index f538fdd01..db22bd41a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt @@ -1,5 +1,6 @@ package space.kscience.kmath.structures +import space.kscience.attributes.SafeType import space.kscience.kmath.UnstableKMathAPI /** @@ -8,6 +9,8 @@ import space.kscience.kmath.UnstableKMathAPI public interface BufferView : Buffer { public val origin: Buffer + override val type: SafeType get() = origin.type + /** * Get the index in [origin] buffer from index in this buffer. * Return -1 if element not present in the original buffer @@ -36,6 +39,7 @@ public class BufferSlice( } } + override fun get(index: Int): T = if (index >= size) { throw IndexOutOfBoundsException("$index is out of ${0 until size} rage") } else { @@ -100,7 +104,8 @@ public fun Buffer.slice(range: IntRange): BufferView = if (this is Buf * Dynamically create a range from the initial range */ @UnstableKMathAPI -public inline fun Buffer.slice(rangeBuilder: IntRange.() -> IntRange): BufferView = slice(rangeBuilder(indices)) +public inline fun Buffer.slice(rangeBuilder: IntRange.() -> IntRange): BufferView = + slice(rangeBuilder(indices)) /** * Resize original buffer to a given range using given [range], filling additional segments with [defaultValue]. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt index 21d3f9c6f..0a2117d5d 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt @@ -72,15 +72,21 @@ public interface MutableBuffer : Buffer { * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ @Suppress("UNCHECKED_CAST") -public inline fun MutableBuffer(type: SafeType, size: Int, initializer: (Int) -> T): MutableBuffer = - when (type.kType) { - typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as MutableBuffer - typeOf() -> MutableBuffer.short(size) { initializer(it) as Short } as MutableBuffer - typeOf() -> MutableBuffer.int(size) { initializer(it) as Int } as MutableBuffer - typeOf() -> MutableBuffer.float(size) { initializer(it) as Float } as MutableBuffer - typeOf() -> MutableBuffer.long(size) { initializer(it) as Long } as MutableBuffer - else -> MutableListBuffer(type, MutableList(size, initializer)) - } +public inline fun MutableBuffer( + type: SafeType, + size: Int, + initializer: (Int) -> T, +): MutableBuffer = when (type.kType) { + typeOf() -> TODO() + typeOf() -> Int8Buffer(size) { initializer(it) as Int8 } as MutableBuffer + typeOf() -> MutableBuffer.short(size) { initializer(it) as Int16 } as MutableBuffer + typeOf() -> MutableBuffer.int(size) { initializer(it) as Int32 } as MutableBuffer + typeOf() -> MutableBuffer.long(size) { initializer(it) as Int64 } as MutableBuffer + typeOf() -> MutableBuffer.float(size) { initializer(it) as Float } as MutableBuffer + typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as MutableBuffer + //TODO add unsigned types + else -> MutableListBuffer(type, MutableList(size, initializer)) +} /** * Creates a [MutableBuffer] of given type [T]. If the type is primitive, specialized buffers are used diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/MatrixTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/MatrixTest.kt index 531aee259..d0e5fa884 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/MatrixTest.kt @@ -8,7 +8,6 @@ package space.kscience.kmath.linear import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.as2D import space.kscience.kmath.operations.algebra import kotlin.test.Test import kotlin.test.assertEquals @@ -22,7 +21,7 @@ class MatrixTest { @Test fun testTranspose() = Double.algebra.linearSpace.run { val matrix = one(3, 3) - val transposed = matrix.transpose() + val transposed = matrix.transposed assertTrue { StructureND.contentEquals(matrix, transposed) } } @@ -38,7 +37,7 @@ class MatrixTest { @Test fun testMatrixExtension() = Double.algebra.linearSpace.run { - val transitionMatrix: Matrix = VirtualMatrix(6, 6) { row, col -> + val transitionMatrix: Matrix = VirtualMatrix(type,6, 6) { row, col -> when { col == 0 -> .50 row + 1 == col -> .50 @@ -60,8 +59,8 @@ class MatrixTest { @Test fun test2DDot() = Double.algebra.linearSpace.run { - val firstMatrix = StructureND.auto(2, 3) { (i, j) -> (i + j).toDouble() }.as2D() - val secondMatrix = StructureND.auto(3, 2) { (i, j) -> (i + j).toDouble() }.as2D() + val firstMatrix = buildMatrix(2, 3) { i, j -> (i + j).toDouble() } + val secondMatrix = buildMatrix(3, 2) { i, j -> (i + j).toDouble() } // val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() } // val secondMatrix = produce(3, 2) { i, j -> (i + j).toDouble() } diff --git a/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/operations/BigNumbers.kt index 584748bd7..59c4380ab 100644 --- a/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/operations/BigNumbers.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.operations +import space.kscience.kmath.structures.MutableBufferFactory import java.math.BigDecimal import java.math.BigInteger import java.math.MathContext @@ -13,6 +14,8 @@ import java.math.MathContext * A field over [BigInteger]. */ public object JBigIntegerField : Ring, NumericAlgebra { + + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override val zero: BigInteger get() = BigInteger.ZERO override val one: BigInteger get() = BigInteger.ONE @@ -33,6 +36,7 @@ public object JBigIntegerField : Ring, NumericAlgebra { public abstract class JBigDecimalFieldBase internal constructor( private val mathContext: MathContext = MathContext.DECIMAL64, ) : Field, PowerOperations, NumericAlgebra, ScaleOperations { + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override val zero: BigDecimal get() = BigDecimal.ZERO diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingChain.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingChain.kt index 2e9a15eed..c1fb097d9 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingChain.kt @@ -43,7 +43,7 @@ public interface BlockingBufferChain : BlockingChain, BufferChain { public suspend inline fun Chain.nextBuffer(size: Int): Buffer = if (this is BufferChain) { nextBuffer(size) } else { - Buffer.auto(size) { next() } + Buffer(size) { next() } } public inline fun BlockingChain.nextBufferBlocking( @@ -51,5 +51,5 @@ public inline fun BlockingChain.nextBufferBlocking( ): Buffer = if (this is BlockingBufferChain) { nextBufferBlocking(size) } else { - Buffer.auto(size) { nextBlocking() } + Buffer(size) { nextBlocking() } } \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt index c52b6c3d5..5ab03cc4a 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.flow import space.kscience.kmath.chains.BlockingDoubleChain +import space.kscience.kmath.operations.Group import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.Float64Buffer @@ -84,9 +85,9 @@ public fun Flow.chunked(bufferSize: Int): Flow = flow { * Map a flow to a moving window buffer. The window step is one. * To get different steps, one could use skip operation. */ -public fun Flow.windowed(window: Int): Flow> = flow { +public fun Flow.windowed(window: Int, algebra: Group): Flow> = flow { require(window > 1) { "Window size must be more than one" } - val ringBuffer = RingBuffer.boxing(window) + val ringBuffer = RingBuffer(window, algebra) this@windowed.collect { element -> ringBuffer.push(element) diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/RingBuffer.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/RingBuffer.kt index bb07fede1..57f42819e 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/RingBuffer.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/RingBuffer.kt @@ -7,6 +7,8 @@ package space.kscience.kmath.streaming import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import space.kscience.attributes.SafeType +import space.kscience.kmath.operations.Group import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.VirtualBuffer @@ -14,12 +16,14 @@ import space.kscience.kmath.structures.VirtualBuffer /** * Thread-safe ring buffer */ -@Suppress("UNCHECKED_CAST") public class RingBuffer( - private val buffer: MutableBuffer, + private val buffer: MutableBuffer, private var startIndex: Int = 0, size: Int = 0, ) : Buffer { + + override val type: SafeType get() = buffer.type + private val mutex: Mutex = Mutex() override var size: Int = size @@ -28,7 +32,7 @@ public class RingBuffer( override operator fun get(index: Int): T { require(index >= 0) { "Index must be positive" } require(index < size) { "Index $index is out of circular buffer size $size" } - return buffer[startIndex.forward(index)] as T + return buffer[startIndex.forward(index)] } public fun isFull(): Boolean = size == buffer.size @@ -43,7 +47,7 @@ public class RingBuffer( override fun computeNext() { if (count == 0) done() else { - setNext(copy[index] as T) + setNext(copy[index]) index = index.forward(1) count-- } @@ -55,7 +59,7 @@ public class RingBuffer( */ public suspend fun snapshot(): Buffer = mutex.withLock { val copy = buffer.copy() - VirtualBuffer(size) { i -> copy[startIndex.forward(i)] as T } + VirtualBuffer(type, size) { i -> copy[startIndex.forward(i)] } } public suspend fun push(element: T) { @@ -68,19 +72,17 @@ public class RingBuffer( private fun Int.forward(n: Int): Int = (this + n) % (buffer.size) override fun toString(): String = Buffer.toString(this) - - public companion object { - public inline fun build(size: Int, empty: T): RingBuffer { - val buffer = MutableBuffer.auto(size) { empty } as MutableBuffer - return RingBuffer(buffer) - } - - /** - * Slow yet universal buffer - */ - public fun boxing(size: Int): RingBuffer { - val buffer: MutableBuffer = MutableBuffer.boxing(size) { null } - return RingBuffer(buffer) - } - } +} + +public inline fun RingBuffer(size: Int, empty: T): RingBuffer { + val buffer = MutableBuffer(size) { empty } + return RingBuffer(buffer) +} + +/** + * Slow yet universal buffer + */ +public fun RingBuffer(size: Int, algebra: Group): RingBuffer { + val buffer: MutableBuffer = MutableBuffer(algebra.type, size) { algebra.zero } + return RingBuffer(buffer) } diff --git a/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt b/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt index 22c2ac3ff..d87335a9b 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt @@ -6,6 +6,8 @@ package space.kscience.kmath.structures import kotlinx.coroutines.* +import space.kscience.attributes.SafeType +import space.kscience.attributes.safeTypeOf import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.coroutines.Math import space.kscience.kmath.nd.ColumnStrides @@ -14,9 +16,11 @@ import space.kscience.kmath.nd.StructureND public class LazyStructureND( public val scope: CoroutineScope, + override val type: SafeType, override val shape: ShapeND, public val function: suspend (IntArray) -> T, ) : StructureND { + private val cache: MutableMap> = HashMap() public fun async(index: IntArray): Deferred = cache.getOrPut(index) { @@ -47,13 +51,13 @@ public suspend fun StructureND.await(index: IntArray): T = * PENDING would benefit from KEEP-176 */ @OptIn(PerformancePitfall::class) -public inline fun StructureND.mapAsyncIndexed( +public inline fun StructureND.mapAsyncIndexed( scope: CoroutineScope, crossinline function: suspend (T, index: IntArray) -> R, -): LazyStructureND = LazyStructureND(scope, shape) { index -> function(get(index), index) } +): LazyStructureND = LazyStructureND(scope, safeTypeOf(), shape) { index -> function(get(index), index) } @OptIn(PerformancePitfall::class) -public inline fun StructureND.mapAsync( +public inline fun StructureND.mapAsync( scope: CoroutineScope, crossinline function: suspend (T) -> R, -): LazyStructureND = LazyStructureND(scope, shape) { index -> function(get(index)) } +): LazyStructureND = LazyStructureND(scope, safeTypeOf(), shape) { index -> function(get(index)) } diff --git a/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt b/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt index f9d0f624d..fe8896075 100644 --- a/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt +++ b/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.dimensions +import space.kscience.attributes.SafeType import space.kscience.kmath.linear.* import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.Structure2D @@ -48,6 +49,9 @@ public interface DMatrix : Structure2D { public value class DMatrixWrapper( private val structure: Structure2D, ) : DMatrix { + + override val type: SafeType get() = structure.type + override val shape: ShapeND get() = structure.shape override val rowNum: Int get() = shape[0] override val colNum: Int get() = shape[1] @@ -75,8 +79,10 @@ public interface DPoint : Point { * Dimension-safe point wrapper */ @JvmInline -public value class DPointWrapper(public val point: Point) : - DPoint { +public value class DPointWrapper(public val point: Point) : DPoint { + + override val type: SafeType get() = point.type + override val size: Int get() = point.size override operator fun get(index: Int): T = point[index] diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt index ec1883128..a8f316650 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt @@ -6,14 +6,9 @@ package space.kscience.kmath.ejml import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.linear.InverseMatrixFeature -import space.kscience.kmath.linear.LinearSpace -import space.kscience.kmath.linear.Matrix -import space.kscience.kmath.linear.Point -import space.kscience.kmath.nd.Structure2D +import space.kscience.kmath.linear.* +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Ring -import kotlin.reflect.KType -import kotlin.reflect.typeOf /** * [LinearSpace] implementation specialized for a certain EJML type. @@ -42,8 +37,7 @@ public abstract class EjmlLinearSpace, out M : org.ejml public abstract override fun buildVector(size: Int, initializer: A.(Int) -> T): EjmlVector - @Suppress("UNCHECKED_CAST") @UnstableKMathAPI - public fun EjmlMatrix.inverse(): Structure2D = - attributeFor(this, InverseMatrixFeature::class)?.inverse as Structure2D + public fun EjmlMatrix.inverted(): Matrix = + attributeFor(this, Float64Field.linearSpace.Inverted) } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt index 881649d01..bf797fed4 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt @@ -131,10 +131,10 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace.plus(other: Matrix): EjmlDoubleMatrix { val out = DMatrixRMaj(1, 1) - + CommonOps_DDRM.add( elementAlgebra.one, toEjml().origin, elementAlgebra.one, - other.toEjml().origin, + other.toEjml().origin, out, ) @@ -369,10 +369,10 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace.plus(other: Matrix): EjmlFloatMatrix { val out = FMatrixRMaj(1, 1) - + CommonOps_FDRM.add( elementAlgebra.one, toEjml().origin, elementAlgebra.one, - other.toEjml().origin, + other.toEjml().origin, out, ) @@ -607,12 +607,12 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.plus(other: Matrix): EjmlDoubleMatrix { val out = DMatrixSparseCSC(1, 1) - + CommonOps_DSCC.add( elementAlgebra.one, toEjml().origin, elementAlgebra.one, - other.toEjml().origin, + other.toEjml().origin, out, - null, + null, null, ) @@ -656,7 +656,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.plus(other: Matrix): EjmlFloatMatrix { val out = FMatrixSparseCSC(1, 1) - + CommonOps_FSCC.add( elementAlgebra.one, toEjml().origin, elementAlgebra.one, - other.toEjml().origin, + other.toEjml().origin, out, - null, + null, null, ) @@ -889,7 +889,7 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace if (i <= j) random.nextDouble() else 0.0 } val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 } val matrix = space { l dot u } - val inverted = matrix.toEjml().inverse() + val inverted = matrix.toEjml().inverted() val res = matrix dot inverted diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt index fe5593eaa..9b71e2500 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt @@ -48,7 +48,7 @@ public fun Sequence.toMatrix(): RealMatrix = toList().let { } public fun RealMatrix.repeatStackVertical(n: Int): RealMatrix = - VirtualMatrix(rowNum * n, colNum) { row, col -> + VirtualMatrix(type, rowNum * n, colNum) { row, col -> get(if (row == 0) 0 else row % rowNum, col) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt index afd96aced..d4a414a5e 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt @@ -7,14 +7,12 @@ package space.kscience.kmath.functions -import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.max import kotlin.math.min -import kotlin.reflect.KType -import kotlin.reflect.typeOf /** @@ -67,6 +65,7 @@ public data class Polynomial( public open class PolynomialSpace( public val ring: A, ) : Ring>, ScaleOperations> where A : Ring, A : ScaleOperations { + override val bufferFactory: MutableBufferFactory> get() = MutableBufferFactory() /** * Instance of zero constant (zero of the underlying ring). diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt index 91b3811c0..22dea15da 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.integration +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.mapToBuffer import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Float64Buffer @@ -32,11 +33,11 @@ public fun GaussIntegratorRuleFactory.build( val normalized: Pair, Buffer> = build(numPoints) val length = range.endInclusive - range.start - val points = normalized.first.mapToBuffer(::Float64Buffer) { + val points = normalized.first.mapToBuffer(Float64Field.bufferFactory) { range.start + length / 2 + length / 2 * it } - val weights = normalized.second.mapToBuffer(::Float64Buffer) { + val weights = normalized.second.mapToBuffer(Float64Field.bufferFactory) { it * length / 2 } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt index c03b248d4..f9a6edcb5 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt @@ -88,7 +88,7 @@ public class SplineIntegrator>( public object DoubleSplineIntegrator : UnivariateIntegrator { override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { val range = integrand[IntegrationRange] ?: 0.0..1.0 - val interpolator: PolynomialInterpolator = SplineInterpolator(Float64Field, ::Float64Buffer) + val interpolator: PolynomialInterpolator = SplineInterpolator(Float64Field, Float64Field.bufferFactory) val nodes: Buffer = integrand[UnivariateIntegrationNodes] ?: run { val numPoints = integrand[IntegrandMaxCalls] ?: 100 @@ -96,7 +96,7 @@ public object DoubleSplineIntegrator : UnivariateIntegrator { Float64Buffer(numPoints) { i -> range.start + i * step } } - val values = nodes.mapToBuffer(::Float64Buffer) { integrand.function(it) } + val values = nodes.mapToBuffer(Float64Field.bufferFactory) { integrand.function(it) } val polynomials = interpolator.interpolatePolynomials(nodes, values) val res = polynomials.integrate(Float64Field, range) return integrand.withAttributes { diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt index 191e7dfd9..a65a41a62 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt @@ -7,6 +7,8 @@ package space.kscience.kmath.interpolation +import space.kscience.attributes.SafeType +import space.kscience.attributes.WithType import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.data.XYColumnarData import space.kscience.kmath.functions.PiecewisePolynomial @@ -26,9 +28,11 @@ public fun interface Interpolator { /** * And interpolator returning [PiecewisePolynomial] function */ -public interface PolynomialInterpolator> : Interpolator { +public interface PolynomialInterpolator> : Interpolator, WithType { public val algebra: Ring + override val type: SafeType get() = algebra.type + public fun getDefaultValue(): T = error("Out of bounds") public fun interpolatePolynomials(points: XYColumnarData): PiecewisePolynomial @@ -50,14 +54,14 @@ public fun > PolynomialInterpolator.interpolatePolynomials( public fun > PolynomialInterpolator.interpolatePolynomials( data: Map, ): PiecewisePolynomial { - val pointSet = XYColumnarData.of(data.keys.toList().asBuffer(), data.values.toList().asBuffer()) + val pointSet = XYColumnarData.of(data.keys.toList().asBuffer(type), data.values.toList().asBuffer(type)) return interpolatePolynomials(pointSet) } public fun > PolynomialInterpolator.interpolatePolynomials( data: List>, ): PiecewisePolynomial { - val pointSet = XYColumnarData.of(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer()) + val pointSet = XYColumnarData.of(data.map { it.first }.asBuffer(type), data.map { it.second }.asBuffer(type)) return interpolatePolynomials(pointSet) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt index ed4657d53..e43e18050 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt @@ -12,7 +12,6 @@ import space.kscience.kmath.functions.Polynomial import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.MutableBufferFactory /** @@ -80,4 +79,4 @@ public fun > Field.splineInterpolator( ): SplineInterpolator = SplineInterpolator(this, bufferFactory) public val Float64Field.splineInterpolator: SplineInterpolator - get() = SplineInterpolator(this, ::Float64Buffer) \ No newline at end of file + get() = SplineInterpolator(this, bufferFactory) \ No newline at end of file diff --git a/kmath-memory/build.gradle.kts b/kmath-memory/build.gradle.kts index f1dff3b75..d2e50b10b 100644 --- a/kmath-memory/build.gradle.kts +++ b/kmath-memory/build.gradle.kts @@ -7,6 +7,10 @@ kscience { js() native() wasm() + + dependencies { + api(projects.kmathCore) + } } readme { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MemoryBuffer.kt b/kmath-memory/src/commonMain/kotlin/space/kscience/kmath/memory/MemoryBuffer.kt similarity index 84% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MemoryBuffer.kt rename to kmath-memory/src/commonMain/kotlin/space/kscience/kmath/memory/MemoryBuffer.kt index cbfd6b9cd..f462be40e 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MemoryBuffer.kt +++ b/kmath-memory/src/commonMain/kotlin/space/kscience/kmath/memory/MemoryBuffer.kt @@ -1,11 +1,13 @@ /* - * Copyright 2018-2022 KMath contributors. + * Copyright 2018-2023 KMath contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package space.kscience.kmath.structures +package space.kscience.kmath.memory -import space.kscience.kmath.memory.* +import space.kscience.attributes.SafeType +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.MutableBuffer /** * A non-boxing buffer over [Memory] object. @@ -15,6 +17,9 @@ import space.kscience.kmath.memory.* * @property spec the spec of [T] type. */ public open class MemoryBuffer(protected val memory: Memory, protected val spec: MemorySpec) : Buffer { + + override val type: SafeType get() = spec.type + override val size: Int get() = memory.size / spec.objectSize override operator fun get(index: Int): T = memory.read { read(spec, spec.objectSize * index) } @@ -43,8 +48,10 @@ public open class MemoryBuffer(protected val memory: Memory, protected * @property memory the underlying memory segment. * @property spec the spec of [T] type. */ -public class MutableMemoryBuffer(memory: Memory, spec: MemorySpec) : MemoryBuffer(memory, spec), - MutableBuffer { +public class MutableMemoryBuffer( + memory: Memory, + spec: MemorySpec, +) : MemoryBuffer(memory, spec), MutableBuffer { private val writer: MemoryWriter = memory.writer() diff --git a/kmath-memory/src/commonMain/kotlin/space/kscience/kmath/memory/MemorySpec.kt b/kmath-memory/src/commonMain/kotlin/space/kscience/kmath/memory/MemorySpec.kt index 19bc3bae4..a1d2b1f28 100644 --- a/kmath-memory/src/commonMain/kotlin/space/kscience/kmath/memory/MemorySpec.kt +++ b/kmath-memory/src/commonMain/kotlin/space/kscience/kmath/memory/MemorySpec.kt @@ -5,12 +5,15 @@ package space.kscience.kmath.memory +import space.kscience.attributes.WithType + /** * A specification to read or write custom objects with fixed size in bytes. * * @param T the type of object this spec manages. */ -public interface MemorySpec { +public interface MemorySpec: WithType { + /** * Size of [T] in bytes after serialization. */