diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d9c7d6e..2001215ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,11 @@ - Kmath-memory is moved on top of core. ### Deprecated +- ND4J engine ### Removed - `asPolynomial` function due to scope pollution +- Codegend for ejml (450 lines of codegen for 1000 lines of code is too much) ### Fixed - Median statistics diff --git a/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt b/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt index d930c90c0..4fa5be9f1 100644 --- a/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt +++ b/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt @@ -99,7 +99,7 @@ class ExpressionsInterpretersBenchmark { private val estree = node.estreeCompileToExpression(Float64Field) private val raw = Expression { args -> - val x = args[x]!! + val x = args.getValue(x) x * 2.0 + 2.0 / x - 16.0 / sin(x) } } diff --git a/examples/src/main/kotlin/space/kscience/kmath/fit/chiSquared.kt b/examples/src/main/kotlin/space/kscience/kmath/fit/chiSquared.kt index 828017185..14cd5bc76 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/fit/chiSquared.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/fit/chiSquared.kt @@ -13,10 +13,7 @@ import space.kscience.kmath.expressions.autodiff import space.kscience.kmath.expressions.symbol import space.kscience.kmath.operations.asIterable import space.kscience.kmath.operations.toList -import space.kscience.kmath.optimization.FunctionOptimizationTarget -import space.kscience.kmath.optimization.optimizeWith -import space.kscience.kmath.optimization.result -import space.kscience.kmath.optimization.resultValue +import space.kscience.kmath.optimization.* import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.real.DoubleVector import space.kscience.kmath.real.map @@ -80,8 +77,9 @@ suspend fun main() { val result = chi2.optimizeWith( CMOptimizer, mapOf(a to 1.5, b to 0.9, c to 1.0), - FunctionOptimizationTarget.MINIMIZE - ) + ){ + FunctionOptimizationTarget(OptimizationDirection.MINIMIZE) + } //display a page with plot and numerical results val page = Plotly.page { diff --git a/examples/src/main/kotlin/space/kscience/kmath/fit/qowFit.kt b/examples/src/main/kotlin/space/kscience/kmath/fit/qowFit.kt index 108c1f12e..4f3ce5443 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/fit/qowFit.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/fit/qowFit.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.fit import kotlinx.html.br import kotlinx.html.h3 +import space.kscience.attributes.Attributes import space.kscience.kmath.data.XYErrorColumnarData import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.expressions.Symbol @@ -64,7 +65,7 @@ suspend fun main() { QowOptimizer, Double.autodiff, mapOf(a to 0.9, b to 1.2, c to 2.0, e to 1.0, d to 1.0, e to 0.0), - OptimizationParameters(a, b, c, d) + attributes = Attributes(OptimizationParameters, listOf(a, b, c, d)) ) { arg -> //bind variables to autodiff context val a by binding diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt index 817f33dbe..c2e33afd4 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt @@ -8,7 +8,6 @@ package space.kscience.kmath.functions import space.kscience.kmath.interpolation.SplineInterpolator import space.kscience.kmath.interpolation.interpolatePolynomials import space.kscience.kmath.operations.Float64Field -import space.kscience.kmath.structures.Float64Buffer import space.kscience.plotly.Plotly import space.kscience.plotly.UnstablePlotlyAPI import space.kscience.plotly.makeFile @@ -24,9 +23,7 @@ fun main() { x to sin(x) } - val polynomial: PiecewisePolynomial = SplineInterpolator( - Float64Field, ::Float64Buffer - ).interpolatePolynomials(data) + val polynomial: PiecewisePolynomial = SplineInterpolator(Float64Field).interpolatePolynomials(data) val function = polynomial.asFunction(Float64Field, 0.0) diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt index c97ba8383..9b48e44a0 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.structures import space.kscience.kmath.complex.* -import space.kscience.kmath.linear.transpose +import space.kscience.kmath.linear.transposed import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.ndAlgebra @@ -60,7 +60,7 @@ fun complexExample() { val sum = matrix + x + 1.0 //Represent the sum as 2d-structure and transpose - sum.as2D().transpose() + sum.as2D().transposed() } } } diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt index 226ec602e..8c34ad4f1 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.structures import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* +import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.NumbersAddOps diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StructureWriteBenchmark.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StructureWriteBenchmark.kt index 9304e9f4c..8d642d892 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StructureWriteBenchmark.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StructureWriteBenchmark.kt @@ -6,20 +6,18 @@ package space.kscience.kmath.structures import space.kscience.kmath.nd.BufferND -import space.kscience.kmath.nd.ShapeND -import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.mapToBuffer import kotlin.system.measureTimeMillis private inline fun BufferND.mapToBufferND( - bufferFactory: BufferFactory = BufferFactory.auto(), + bufferFactory: BufferFactory = BufferFactory(), crossinline block: (T) -> R, ): BufferND = BufferND(indices, buffer.mapToBuffer(bufferFactory, block)) @Suppress("UNUSED_VARIABLE") fun main() { val n = 6000 - val structure = StructureND.buffered(ShapeND(n, n), Buffer.Companion::auto) { 1.0 } + val structure = BufferND(n, n) { 1.0 } structure.mapToBufferND { it + 1 } // warm-up val time1 = measureTimeMillis { val res = structure.mapToBufferND { it + 1 } } println("Structure mapping finished in $time1 millis") diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt index aafd43fd9..d4dfa60cb 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt @@ -13,7 +13,7 @@ import space.kscience.kmath.operations.withSize inline fun MutableBuffer.Companion.same( n: Int, value: R -): MutableBuffer = auto(n) { value } +): MutableBuffer = MutableBuffer(n) { value } fun main() { diff --git a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt index de6b406fd..589dc19e0 100644 --- a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt @@ -7,16 +7,20 @@ package space.kscience.kmath.commons.linear import org.apache.commons.math3.linear.* import org.apache.commons.math3.linear.LUDecomposition +import org.apache.commons.math3.linear.SingularValueDecomposition import space.kscience.attributes.SafeType import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.* +import space.kscience.kmath.linear.CholeskyDecomposition +import space.kscience.kmath.linear.QRDecomposition import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.StructureAttribute import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.Float64Buffer -import kotlin.reflect.cast +import space.kscience.kmath.structures.Float64 +import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.asBuffer public class CMMatrix(public val origin: RealMatrix) : Matrix { override val type: SafeType get() = DoubleField.type @@ -109,45 +113,44 @@ public object CMLinearSpace : LinearSpace { val origin = structure.toCM().origin - return when (attribute) { - IsDiagonal -> if (origin is DiagonalMatrix) IsDiagonal else null + val raw: Any? = when (attribute) { + IsDiagonal -> if (origin is DiagonalMatrix) Unit else null Determinant -> LUDecomposition(origin).determinant - LUP -> GenericLupDecomposition { - private val lup by lazy { LUDecomposition(origin) } - override val determinant: Double by lazy { lup.determinant } - override val l: Matrix by lazy> { CMMatrix(lup.l).withAttribute(LowerTriangular) } - override val u: Matrix by lazy> { CMMatrix(lup.u).withAttribute(UpperTriangular) } - override val p: Matrix by lazy { CMMatrix(lup.p) } + + LUP -> object : LupDecomposition { + val lup by lazy { LUDecomposition(origin) } + override val pivot: IntBuffer get() = lup.pivot.asBuffer() + override val l: Matrix get() = lup.l.wrap() + override val u: Matrix get() = lup.u.wrap() } - CholeskyDecompositionAttribute -> object : CholeskyDecompositionAttribute { - override val l: Matrix by lazy> { - val cholesky = CholeskyDecomposition(origin) - CMMatrix(cholesky.l).withAttribute(LowerTriangular) - } + Cholesky -> object : CholeskyDecomposition { + val cmCholesky by lazy { org.apache.commons.math3.linear.CholeskyDecomposition(origin) } + override val l: Matrix get() = cmCholesky.l.wrap() } - QRDecompositionAttribute -> object : QRDecompositionAttribute { - private val qr by lazy { QRDecomposition(origin) } - override val q: Matrix by lazy> { - CMMatrix(qr.q).withAttribute( - OrthogonalAttribute - ) - } - override val r: Matrix by lazy> { CMMatrix(qr.r).withAttribute(UpperTriangular) } + QR -> object : QRDecomposition { + val cmQr by lazy { org.apache.commons.math3.linear.QRDecomposition(origin) } + override val q: Matrix get() = cmQr.q.wrap().withAttribute(OrthogonalAttribute) + override val r: Matrix get() = cmQr.r.wrap().withAttribute(UpperTriangular) } - SVDAttribute -> object : SVDAttribute { - private val sv by lazy { SingularValueDecomposition(origin) } - override val u: Matrix by lazy { CMMatrix(sv.u) } - override val s: Matrix by lazy { CMMatrix(sv.s) } - override val v: Matrix by lazy { CMMatrix(sv.v) } - override val singularValues: Point by lazy { Float64Buffer(sv.singularValues) } + SVD -> object : space.kscience.kmath.linear.SingularValueDecomposition { + val cmSvd by lazy { SingularValueDecomposition(origin) } + + override val u: Matrix get() = cmSvd.u.wrap() + override val s: Matrix get() = cmSvd.s.wrap() + override val v: Matrix get() = cmSvd.v.wrap() + override val singularValues: Point get() = cmSvd.singularValues.asBuffer() + } else -> null - }?.let(type::cast) + } + @Suppress("UNCHECKED_CAST") + return raw as V? } + } public operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CMMatrix(origin.add(other.origin)) diff --git a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/optimization/CMOptimizer.kt b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/optimization/CMOptimizer.kt index 83ccea035..afbb96d9b 100644 --- a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/optimization/CMOptimizer.kt +++ b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/optimization/CMOptimizer.kt @@ -3,6 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @file:OptIn(UnstableKMathAPI::class) + package space.kscience.kmath.commons.optimization import org.apache.commons.math3.optim.* @@ -20,7 +21,6 @@ import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.SymbolIndexer import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.withSymbols -import space.kscience.kmath.misc.log import space.kscience.kmath.optimization.* import kotlin.collections.set import kotlin.reflect.KClass @@ -28,7 +28,7 @@ import kotlin.reflect.KClass public operator fun PointValuePair.component1(): DoubleArray = point public operator fun PointValuePair.component2(): Double = value -public object CMOptimizerEngine: OptimizationAttribute<() -> MultivariateOptimizer> +public object CMOptimizerEngine : OptimizationAttribute<() -> MultivariateOptimizer> /** * Specify a Commons-maths optimization engine @@ -37,7 +37,7 @@ public fun AttributesBuilder>.cmEngine(optimizerBui set(CMOptimizerEngine, optimizerBuilder) } -public object CMOptimizerData: SetAttribute OptimizationData> +public object CMOptimizerData : SetAttribute OptimizationData> /** * Specify Commons-maths optimization data. @@ -118,21 +118,24 @@ public object CMOptimizer : Optimizer> { val logger = problem.attributes[OptimizationLog] - for (feature in problem.attributes) { - when (feature) { - is CMOptimizerData -> feature.data.forEach { dataBuilder -> - addOptimizationData(dataBuilder()) - } - is FunctionOptimizationTarget -> when (feature) { - FunctionOptimizationTarget.MAXIMIZE -> addOptimizationData(GoalType.MAXIMIZE) - FunctionOptimizationTarget.MINIMIZE -> addOptimizationData(GoalType.MINIMIZE) - } - else -> logger?.log { "The feature $feature is unused in optimization" } + problem.attributes[CMOptimizerData]?.let { builders: Set OptimizationData> -> + builders.forEach { dataBuilder -> + addOptimizationData(dataBuilder()) + } + } + + problem.attributes[FunctionOptimizationTarget]?.let { direction: OptimizationDirection -> + when (direction) { + OptimizationDirection.MAXIMIZE -> addOptimizationData(GoalType.MAXIMIZE) + OptimizationDirection.MINIMIZE -> addOptimizationData(GoalType.MINIMIZE) } } val (point, value) = cmOptimizer.optimize(*optimizationData.values.toTypedArray()) - return problem.withAttributes(OptimizationResult(point.toMap()), OptimizationValue(value)) + return problem.withAttributes { + result(point.toMap()) + value(value) + } } } } diff --git a/kmath-commons/src/jvmTest/kotlin/space/kscience/kmath/commons/integration/IntegrationTest.kt b/kmath-commons/src/jvmTest/kotlin/space/kscience/kmath/commons/integration/IntegrationTest.kt index c2e630f38..43857c539 100644 --- a/kmath-commons/src/jvmTest/kotlin/space/kscience/kmath/commons/integration/IntegrationTest.kt +++ b/kmath-commons/src/jvmTest/kotlin/space/kscience/kmath/commons/integration/IntegrationTest.kt @@ -7,6 +7,8 @@ package space.kscience.kmath.commons.integration import org.junit.jupiter.api.Test import space.kscience.kmath.UnstableKMathAPI +import space.kscience.kmath.integration.IntegrandAbsoluteAccuracy +import space.kscience.kmath.integration.IntegrandRelativeAccuracy import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.value import space.kscience.kmath.operations.Float64Field.sin @@ -27,8 +29,8 @@ internal class IntegrationTest { @Test fun customSimpson() { val res = CMIntegrator.simpson().integrate(0.0..PI, { - targetRelativeAccuracy = 1e-4 - targetAbsoluteAccuracy = 1e-4 + IntegrandRelativeAccuracy(1e-4) + IntegrandAbsoluteAccuracy(1e-4) }, function).value assertTrue { abs(res - 2) < 1e-3 } assertTrue { abs(res - 2) > 1e-12 } diff --git a/kmath-commons/src/jvmTest/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/jvmTest/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt index e17ba48e9..177a01b43 100644 --- a/kmath-commons/src/jvmTest/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/jvmTest/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -73,8 +73,9 @@ internal class OptimizeTest { val result: FunctionOptimization = chi2.optimizeWith( CMOptimizer, mapOf(a to 1.5, b to 0.9, c to 1.0), - FunctionOptimizationTarget.MINIMIZE - ) + ){ + FunctionOptimizationTarget(OptimizationDirection.MINIMIZE) + } println(result) println("Chi2/dof = ${result.resultValue / (x.size - 3)}") } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Expression.kt index 7bf48fcc1..f2e134963 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Expression.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.expressions import space.kscience.attributes.SafeType import space.kscience.attributes.WithType +import space.kscience.attributes.safeTypeOf import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.DoubleField @@ -30,12 +31,18 @@ public interface Expression : WithType { public operator fun invoke(arguments: Map): T } +/** + * Create an expression from a functional block. + */ public fun Expression(type: SafeType, block: (Map) -> T): Expression = object : Expression { override fun invoke(arguments: Map): T = block(arguments) override val type: SafeType = type } +public inline fun Expression(noinline block: (Map) -> T): Expression = + Expression(safeTypeOf(), block) + /** * Specialization of [Expression] for [Double] allowing better performance because of using array. */ 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 4d2768e02..14828f2df 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 @@ -15,14 +15,19 @@ import space.kscience.kmath.operations.* import space.kscience.kmath.structures.* public interface LupDecomposition { - public val linearSpace: LinearSpace> - public val elementAlgebra: Field get() = linearSpace.elementAlgebra - public val pivot: IntBuffer public val l: Matrix public val u: Matrix } +/** + * Create a pivot matrix from pivot vector using provided [LinearSpace] + */ +public fun LupDecomposition.pivotMatrix(linearSpace: LinearSpace>): Matrix = + VirtualMatrix(linearSpace.type, l.rowNum, l.colNum) { row, column -> + if (column == pivot[row]) linearSpace.elementAlgebra.one else linearSpace.elementAlgebra.zero + } + /** * Matrices with this feature support LU factorization with partial pivoting: *[p] · a = [l] · [u]* where * *a* is the owning matrix. @@ -31,12 +36,14 @@ public interface LupDecomposition { * @param lu combined L and U matrix */ public class GenericLupDecomposition( - override val linearSpace: LinearSpace>, + public val linearSpace: LinearSpace>, private val lu: Matrix, override val pivot: IntBuffer, private val even: Boolean, ) : LupDecomposition { + private val elementAlgebra get() = linearSpace.elementAlgebra + override val l: Matrix get() = VirtualMatrix(lu.type, lu.rowNum, lu.colNum, attributes = Attributes(LowerTriangular)) { i, j -> when { @@ -51,11 +58,6 @@ public class GenericLupDecomposition( 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 determinant: T by lazy { elementAlgebra { (0 until l.shape[0]).fold(if (even) one else -one) { value, i -> value * lu[i, i] } } } @@ -79,7 +81,7 @@ internal fun > LinearSpace>.abs(value: T): T = public fun > LinearSpace>.lup( matrix: Matrix, checkSingular: (T) -> Boolean, -): LupDecomposition = elementAlgebra { +): GenericLupDecomposition = elementAlgebra { require(matrix.rowNum == matrix.colNum) { "LU decomposition supports only square matrices" } val m = matrix.colNum val pivot = IntArray(matrix.rowNum) @@ -156,7 +158,7 @@ public fun > LinearSpace>.lup( public fun LinearSpace.lup( matrix: Matrix, singularityThreshold: Double = 1e-11, -): LupDecomposition = lup(matrix) { it < singularityThreshold } +): GenericLupDecomposition = lup(matrix) { it < singularityThreshold } internal fun LinearSpace>.solve( lup: LupDecomposition, diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Transposed.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Transposed.kt index 2cc566f92..28821a52a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Transposed.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Transposed.kt @@ -25,5 +25,4 @@ public class TransposedMatrix(public val origin: Matrix) : Matrix { /** * Create a virtual transposed matrix without copying anything. `A.transpose().transpose() === A` */ -public val Matrix.transposed: Matrix - get() = (this as? TransposedMatrix)?.origin ?: TransposedMatrix(this) \ No newline at end of file +public fun Matrix.transposed(): Matrix = (this as? TransposedMatrix)?.origin ?: TransposedMatrix(this) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Float64FieldND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Float64FieldND.kt index 309f9cb37..ab9b25663 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Float64FieldND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Float64FieldND.kt @@ -5,9 +5,11 @@ package space.kscience.kmath.nd +import space.kscience.attributes.SafeType import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.* +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -20,7 +22,10 @@ import kotlin.math.pow as kpow public class Float64BufferND( indexes: ShapeIndexer, override val buffer: Float64Buffer, -) : MutableBufferND(indexes, buffer), MutableStructureNDOfDouble{ +) : MutableBufferND(indexes, buffer), MutableStructureNDOfDouble { + + override val type: SafeType get() = Float64Field.type + override fun getDouble(index: IntArray): Double = buffer[indices.offset(index)] override fun setDouble(index: IntArray, value: Double) { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/primitiveStructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/primitiveStructureND.kt index 9f70b6dca..ca299f12f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/primitiveStructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/primitiveStructureND.kt @@ -5,9 +5,15 @@ package space.kscience.kmath.nd +import space.kscience.attributes.SafeType import space.kscience.kmath.PerformancePitfall +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Field +import space.kscience.kmath.structures.Float64 public interface StructureNDOfDouble : StructureND { + override val type: SafeType get() = Float64Field.type + /** * Guaranteed non-blocking access to content */ @@ -22,6 +28,7 @@ public fun StructureND.getDouble(index: IntArray): Double = if (this is StructureNDOfDouble) getDouble(index) else get(index) public interface MutableStructureNDOfDouble : StructureNDOfDouble, MutableStructureND { + /** * Guaranteed non-blocking access to content */ @@ -34,6 +41,9 @@ public fun MutableStructureND.getDouble(index: IntArray): Double = public interface StructureNDOfInt : StructureND { + + override val type: SafeType get() = Int32Field.type + /** * Guaranteed non-blocking access to content */ diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/DoubleLUSolverTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/DoubleLUSolverTest.kt index 9989afd6c..e50d1230d 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/DoubleLUSolverTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/DoubleLUSolverTest.kt @@ -40,7 +40,7 @@ class DoubleLUSolverTest { //Check determinant assertEquals(7.0, lup.determinant) - assertMatrixEquals(lup.pivotMatrix dot matrix, lup.l dot lup.u) + assertMatrixEquals(lup.pivotMatrix(this) dot matrix, lup.l dot lup.u) } @Test 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 9d69d0379..4e9cc2011 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 @@ -21,7 +21,7 @@ class MatrixTest { @Test fun testTranspose() = Double.algebra.linearSpace.run { val matrix = one(3, 3) - val transposed = matrix.transposed + val transposed = matrix.transposed() assertTrue { StructureND.contentEquals(matrix, transposed) } } diff --git a/kmath-coroutines/build.gradle.kts b/kmath-coroutines/build.gradle.kts index 1e901ca98..91b2afd5e 100644 --- a/kmath-coroutines/build.gradle.kts +++ b/kmath-coroutines/build.gradle.kts @@ -6,6 +6,7 @@ kscience { jvm() js() native() + wasm() dependencies { api(project(":kmath-core")) diff --git a/kmath-coroutines/src/jvmTest/kotlin/space/kscience/kmath/streaming/RingBufferTest.kt b/kmath-coroutines/src/jvmTest/kotlin/space/kscience/kmath/streaming/RingBufferTest.kt index 6219a5886..701715123 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/space/kscience/kmath/streaming/RingBufferTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/space/kscience/kmath/streaming/RingBufferTest.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.streaming import kotlinx.coroutines.flow.* import kotlinx.coroutines.runBlocking +import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.operations.asSequence import kotlin.test.Test import kotlin.test.assertEquals @@ -14,7 +15,7 @@ import kotlin.test.assertEquals internal class RingBufferTest { @Test fun push() { - val buffer = RingBuffer.build(20, Double.NaN) + val buffer = RingBuffer(20, Double.NaN) runBlocking { for (i in 1..30) { buffer.push(i.toDouble()) @@ -30,7 +31,7 @@ internal class RingBufferTest { while (true) emit(i++) } - val windowed = flow.windowed(10) + val windowed = flow.windowed(10, Int32Ring) runBlocking { @Suppress("UNUSED_VARIABLE") val first = windowed.take(1).single() diff --git a/kmath-dimensions/build.gradle.kts b/kmath-dimensions/build.gradle.kts index be1fc65a0..0ed6a8949 100644 --- a/kmath-dimensions/build.gradle.kts +++ b/kmath-dimensions/build.gradle.kts @@ -6,6 +6,7 @@ kscience{ jvm() js() native() + wasm() dependencies{ api(projects.kmathCore) 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 cd24d25e7..d1de77d54 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 @@ -153,7 +153,7 @@ public value class DMatrixContext>(public val context: context.run { this@unaryMinus.unaryMinus() }.coerce() public inline fun DMatrix.transposed(): DMatrix = - context.run { (this@transposed as Matrix).transposed }.coerce() + context.run { (this@transposed as Matrix).transposed() }.coerce() public companion object { public val real: DMatrixContext = DMatrixContext(Double.algebra.linearSpace) diff --git a/kmath-dimensions/src/wasmJsMain/kotlin/space/kscience/kmath/dimensions/Dimension.wasmJs.kt b/kmath-dimensions/src/wasmJsMain/kotlin/space/kscience/kmath/dimensions/Dimension.wasmJs.kt new file mode 100644 index 000000000..cbf404b7f --- /dev/null +++ b/kmath-dimensions/src/wasmJsMain/kotlin/space/kscience/kmath/dimensions/Dimension.wasmJs.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2018-2024 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.dimensions + +import kotlin.reflect.KClass + +private val dimensionMap: MutableMap = hashMapOf(1 to D1, 2 to D2, 3 to D3) + +@Suppress("UNCHECKED_CAST") +public actual fun Dimension.Companion.resolve(type: KClass): D = dimensionMap + .entries + .map(MutableMap.MutableEntry::value) + .find { it::class == type } as? D + ?: error("Can't resolve dimension $type") + +public actual fun Dimension.Companion.of(dim: Int): Dimension = dimensionMap.getOrPut(dim) { + object : Dimension { + override val dim: Int get() = dim + } +} diff --git a/kmath-ejml/build.gradle.kts b/kmath-ejml/build.gradle.kts index d7f780d79..54b8f7038 100644 --- a/kmath-ejml/build.gradle.kts +++ b/kmath-ejml/build.gradle.kts @@ -1,15 +1,15 @@ -import space.kscience.kmath.ejml.codegen.ejmlCodegen - plugins { id("space.kscience.gradle.jvm") } +val ejmlVerision = "0.43.1" + dependencies { - api("org.ejml:ejml-ddense:0.41") - api("org.ejml:ejml-fdense:0.41") - api("org.ejml:ejml-dsparse:0.41") - api("org.ejml:ejml-fsparse:0.41") - api(project(":kmath-core")) + api("org.ejml:ejml-ddense:$ejmlVerision") + api("org.ejml:ejml-fdense:$ejmlVerision") + api("org.ejml:ejml-dsparse:$ejmlVerision") + api("org.ejml:ejml-fsparse:$ejmlVerision") + api(projects.kmathCore) } readme { @@ -32,10 +32,10 @@ readme { ) { "LinearSpace implementations." } } -kotlin.sourceSets.main { - val codegen by tasks.creating { - ejmlCodegen(kotlin.srcDirs.first().absolutePath + "/space/kscience/kmath/ejml/_generated.kt") - } - - kotlin.srcDirs(files().builtBy(codegen)) -} +//kotlin.sourceSets.main { +// val codegen by tasks.creating { +// ejmlCodegen(kotlin.srcDirs.first().absolutePath + "/space/kscience/kmath/ejml/_generated.kt") +// } +// +// kotlin.srcDirs(files().builtBy(codegen)) +//} diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt similarity index 72% rename from kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt rename to kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt index c86d9bc44..d33a74199 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt @@ -17,21 +17,18 @@ import org.ejml.sparse.csc.CommonOps_DSCC import org.ejml.sparse.csc.CommonOps_FSCC import org.ejml.sparse.csc.factory.DecompositionFactory_DSCC import org.ejml.sparse.csc.factory.DecompositionFactory_FSCC -import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC -import org.ejml.sparse.csc.factory.LinearSolverFactory_FSCC import space.kscience.attributes.SafeType import space.kscience.attributes.safeTypeOf -import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.* import space.kscience.kmath.linear.Matrix -import space.kscience.kmath.nd.StructureFeature +import space.kscience.kmath.nd.Structure2D +import space.kscience.kmath.nd.StructureAttribute import space.kscience.kmath.operations.Float32Field import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.DoubleBuffer -import space.kscience.kmath.structures.FloatBuffer -import kotlin.reflect.KClass -import kotlin.reflect.cast +import space.kscience.kmath.structures.Float32 +import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.asBuffer /** * [EjmlVector] specialization for [Double]. @@ -78,7 +75,6 @@ public class EjmlFloatMatrix(override val origin: M) : EjmlMatr } - /** * [EjmlLinearSpace] implementation based on [CommonOps_DDRM], [DecompositionFactory_DDRM] operations and * [DMatrixRMaj] matrices. @@ -143,10 +139,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, ) @@ -217,77 +213,65 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace): EjmlDoubleVector = v * this - @UnstableKMathAPI - override fun computeFeature(structure: Matrix, type: KClass): F? { - structure.getFeature(type)?.let { return it } + override fun > computeAttribute(structure: Structure2D, attribute: A): V? { val origin = structure.toEjml().origin - return when (type) { - InverseMatrixFeature::class -> object : InverseMatrixFeature { - override val inverse: Matrix by lazy { - val res = origin.copy() - CommonOps_DDRM.invert(res) - res.wrapMatrix() - } + val raw: Any? = when (attribute) { + Inverted -> { + val res = origin.copy() + CommonOps_DDRM.invert(res) + res.wrapMatrix() } - DeterminantFeature::class -> object : DeterminantFeature { - override val determinant: Double by lazy { CommonOps_DDRM.det(origin) } - } - - SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature { - private val svd by lazy { - DecompositionFactory_DDRM.svd(origin.numRows, origin.numCols, true, true, false) + Determinant -> CommonOps_DDRM.det(origin) + SVD -> object : SingularValueDecomposition { + val ejmlSvd by lazy { + DecompositionFactory_DDRM + .svd(origin.numRows, origin.numCols, true, true, false) .apply { decompose(origin.copy()) } } + override val u: Matrix get() = ejmlSvd.getU(null, false).wrapMatrix() + + override val s: Matrix get() = ejmlSvd.getW(null).wrapMatrix() + override val v: Matrix get() = ejmlSvd.getV(null, false).wrapMatrix() + override val singularValues: Point get() = ejmlSvd.singularValues.asBuffer() - override val u: Matrix by lazy { svd.getU(null, false).wrapMatrix() } - override val s: Matrix by lazy { svd.getW(null).wrapMatrix() } - override val v: Matrix by lazy { svd.getV(null, false).wrapMatrix() } - override val singularValues: Point by lazy { DoubleBuffer(svd.singularValues) } } - QRDecompositionFeature::class -> object : QRDecompositionFeature { - private val qr by lazy { - DecompositionFactory_DDRM.qr().apply { decompose(origin.copy()) } - } - - override val q: Matrix by lazy { - qr.getQ(null, false).wrapMatrix().withFeature(OrthogonalFeature) - } - - override val r: Matrix by lazy { qr.getR(null, false).wrapMatrix().withFeature(UFeature) } + QR -> object : QRDecomposition { + val ejmlQr by lazy { DecompositionFactory_DDRM.qr().apply { decompose(origin.copy()) } } + override val q: Matrix get() = ejmlQr.getQ(null, false).wrapMatrix() + override val r: Matrix get() = ejmlQr.getR(null, false).wrapMatrix() } - CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { + Cholesky -> object : CholeskyDecomposition { override val l: Matrix by lazy { val cholesky = DecompositionFactory_DDRM.chol(structure.rowNum, true).apply { decompose(origin.copy()) } - cholesky.getT(null).wrapMatrix().withFeature(LFeature) + cholesky.getT(null).wrapMatrix().withAttribute(LowerTriangular) } } - LupDecompositionFeature::class -> object : LupDecompositionFeature { + LUP -> object : LupDecomposition { private val lup by lazy { DecompositionFactory_DDRM.lu(origin.numRows, origin.numCols).apply { decompose(origin.copy()) } } - override val l: Matrix by lazy { - lup.getLower(null).wrapMatrix().withFeature(LFeature) - } + override val l: Matrix + get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) - override val u: Matrix by lazy { - lup.getUpper(null).wrapMatrix().withFeature(UFeature) - } - override val p: Matrix by lazy { lup.getRowPivot(null).wrapMatrix() } + override val u: Matrix + get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) + override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } else -> null - }?.let{ - type.cast(it) } + + @Suppress("UNCHECKED_CAST") + return raw as V? } /** @@ -318,7 +302,6 @@ public object EjmlLinearSpaceDDRM : 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, ) @@ -457,77 +440,65 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace): EjmlFloatVector = v * this - @UnstableKMathAPI - override fun computeFeature(structure: Matrix, type: KClass): F? { - structure.getFeature(type)?.let { return it } + override fun > computeAttribute(structure: Structure2D, attribute: A): V? { val origin = structure.toEjml().origin - return when (type) { - InverseMatrixFeature::class -> object : InverseMatrixFeature { - override val inverse: Matrix by lazy { - val res = origin.copy() - CommonOps_FDRM.invert(res) - res.wrapMatrix() - } + val raw: Any? = when (attribute) { + Inverted -> { + val res = origin.copy() + CommonOps_FDRM.invert(res) + res.wrapMatrix() } - DeterminantFeature::class -> object : DeterminantFeature { - override val determinant: Float by lazy { CommonOps_FDRM.det(origin) } - } - - SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature { - private val svd by lazy { - DecompositionFactory_FDRM.svd(origin.numRows, origin.numCols, true, true, false) + Determinant -> CommonOps_FDRM.det(origin) + SVD -> object : SingularValueDecomposition { + val ejmlSvd by lazy { + DecompositionFactory_FDRM + .svd(origin.numRows, origin.numCols, true, true, false) .apply { decompose(origin.copy()) } } + override val u: Matrix get() = ejmlSvd.getU(null, false).wrapMatrix() + + override val s: Matrix get() = ejmlSvd.getW(null).wrapMatrix() + override val v: Matrix get() = ejmlSvd.getV(null, false).wrapMatrix() + override val singularValues: Point get() = ejmlSvd.singularValues.asBuffer() - override val u: Matrix by lazy { svd.getU(null, false).wrapMatrix() } - override val s: Matrix by lazy { svd.getW(null).wrapMatrix() } - override val v: Matrix by lazy { svd.getV(null, false).wrapMatrix() } - override val singularValues: Point by lazy { FloatBuffer(svd.singularValues) } } - QRDecompositionFeature::class -> object : QRDecompositionFeature { - private val qr by lazy { - DecompositionFactory_FDRM.qr().apply { decompose(origin.copy()) } - } - - override val q: Matrix by lazy { - qr.getQ(null, false).wrapMatrix().withFeature(OrthogonalFeature) - } - - override val r: Matrix by lazy { qr.getR(null, false).wrapMatrix().withFeature(UFeature) } + QR -> object : QRDecomposition { + val ejmlQr by lazy { DecompositionFactory_FDRM.qr().apply { decompose(origin.copy()) } } + override val q: Matrix get() = ejmlQr.getQ(null, false).wrapMatrix() + override val r: Matrix get() = ejmlQr.getR(null, false).wrapMatrix() } - CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { - override val l: Matrix by lazy { + Cholesky -> object : CholeskyDecomposition { + override val l: Matrix by lazy { val cholesky = DecompositionFactory_FDRM.chol(structure.rowNum, true).apply { decompose(origin.copy()) } - cholesky.getT(null).wrapMatrix().withFeature(LFeature) + cholesky.getT(null).wrapMatrix().withAttribute(LowerTriangular) } } - LupDecompositionFeature::class -> object : LupDecompositionFeature { + LUP -> object : LupDecomposition { private val lup by lazy { DecompositionFactory_FDRM.lu(origin.numRows, origin.numCols).apply { decompose(origin.copy()) } } - override val l: Matrix by lazy { - lup.getLower(null).wrapMatrix().withFeature(LFeature) - } + override val l: Matrix + get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) - override val u: Matrix by lazy { - lup.getUpper(null).wrapMatrix().withFeature(UFeature) - } - override val p: Matrix by lazy { lup.getRowPivot(null).wrapMatrix() } + override val u: Matrix + get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) + override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } else -> null - }?.let{ - type.cast(it) } + + @Suppress("UNCHECKED_CAST") + return raw as V? } /** @@ -558,7 +529,6 @@ public object EjmlLinearSpaceFDRM : 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, ) @@ -672,7 +642,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace): EjmlDoubleVector = v * this - @UnstableKMathAPI - override fun computeFeature(structure: Matrix, type: KClass): F? { - structure.getFeature(type)?.let { return it } + override fun > computeAttribute(structure: Structure2D, attribute: A): V? { val origin = structure.toEjml().origin - return when (type) { - QRDecompositionFeature::class -> object : QRDecompositionFeature { - private val qr by lazy { - DecompositionFactory_DSCC.qr(FillReducing.NONE).apply { decompose(origin.copy()) } - } - - override val q: Matrix by lazy { - qr.getQ(null, false).wrapMatrix().withFeature(OrthogonalFeature) - } - - override val r: Matrix by lazy { qr.getR(null, false).wrapMatrix().withFeature(UFeature) } + val raw: Any? = when (attribute) { + Inverted -> { + val res = DMatrixRMaj(origin.numRows,origin.numCols) + CommonOps_DSCC.invert(origin,res) + res.wrapMatrix() } - CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { + Determinant -> CommonOps_DSCC.det(origin) + + QR -> object : QRDecomposition { + val ejmlQr by lazy { DecompositionFactory_DSCC.qr(FillReducing.NONE).apply { decompose(origin.copy()) } } + override val q: Matrix get() = ejmlQr.getQ(null, false).wrapMatrix() + override val r: Matrix get() = ejmlQr.getR(null, false).wrapMatrix() + } + + Cholesky -> object : CholeskyDecomposition { override val l: Matrix by lazy { val cholesky = DecompositionFactory_DSCC.cholesky().apply { decompose(origin.copy()) } - (cholesky.getT(null) as DMatrix).wrapMatrix().withFeature(LFeature) + (cholesky.getT(null) as DMatrix).wrapMatrix().withAttribute(LowerTriangular) } } - LUDecompositionFeature::class, DeterminantFeature::class, InverseMatrixFeature::class -> object : - LUDecompositionFeature, DeterminantFeature, InverseMatrixFeature { - private val lu by lazy { + LUP -> object : LupDecomposition { + private val lup by lazy { DecompositionFactory_DSCC.lu(FillReducing.NONE).apply { decompose(origin.copy()) } } - override val l: Matrix by lazy { - lu.getLower(null).wrapMatrix().withFeature(LFeature) - } + override val l: Matrix + get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) - override val u: Matrix by lazy { - lu.getUpper(null).wrapMatrix().withFeature(UFeature) - } - override val inverse: Matrix by lazy { - var a = origin - val inverse = DMatrixRMaj(1, 1) - val solver = LinearSolverFactory_DSCC.lu(FillReducing.NONE) - if (solver.modifiesA()) a = a.copy() - val i = CommonOps_DDRM.identity(a.numRows) - solver.solve(i, inverse) - inverse.wrapMatrix() - } - - override val determinant: Double by lazy { elementAlgebra.number(lu.computeDeterminant().real) } + override val u: Matrix + get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) + override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } else -> null - }?.let{ - type.cast(it) } + + @Suppress("UNCHECKED_CAST") + return raw as V? } /** @@ -793,7 +751,6 @@ 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, ) @@ -907,7 +864,7 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace): EjmlFloatVector = v * this - - @UnstableKMathAPI - override fun computeFeature(structure: Matrix, type: KClass): F? { - structure.getFeature(type)?.let { return it } + override fun > computeAttribute(structure: Structure2D, attribute: A): V? { val origin = structure.toEjml().origin - return when (type) { - QRDecompositionFeature::class -> object : QRDecompositionFeature { - private val qr by lazy { - DecompositionFactory_FSCC.qr(FillReducing.NONE).apply { decompose(origin.copy()) } - } - - override val q: Matrix by lazy { - qr.getQ(null, false).wrapMatrix().withFeature(OrthogonalFeature) - } - - override val r: Matrix by lazy { qr.getR(null, false).wrapMatrix().withFeature(UFeature) } + val raw: Any? = when (attribute) { + Inverted -> { + val res = FMatrixRMaj(origin.numRows,origin.numCols) + CommonOps_FSCC.invert(origin,res) + res.wrapMatrix() } - CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { - override val l: Matrix by lazy { + Determinant -> CommonOps_FSCC.det(origin) + + QR -> object : QRDecomposition { + val ejmlQr by lazy { DecompositionFactory_FSCC.qr(FillReducing.NONE).apply { decompose(origin.copy()) } } + override val q: Matrix get() = ejmlQr.getQ(null, false).wrapMatrix() + override val r: Matrix get() = ejmlQr.getR(null, false).wrapMatrix() + } + + Cholesky -> object : CholeskyDecomposition { + override val l: Matrix by lazy { val cholesky = DecompositionFactory_FSCC.cholesky().apply { decompose(origin.copy()) } - (cholesky.getT(null) as FMatrix).wrapMatrix().withFeature(LFeature) + (cholesky.getT(null) as FMatrix).wrapMatrix().withAttribute(LowerTriangular) } } - LUDecompositionFeature::class, DeterminantFeature::class, InverseMatrixFeature::class -> object : - LUDecompositionFeature, DeterminantFeature, InverseMatrixFeature { - private val lu by lazy { + LUP -> object : LupDecomposition { + private val lup by lazy { DecompositionFactory_FSCC.lu(FillReducing.NONE).apply { decompose(origin.copy()) } } - override val l: Matrix by lazy { - lu.getLower(null).wrapMatrix().withFeature(LFeature) - } + override val l: Matrix + get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) - override val u: Matrix by lazy { - lu.getUpper(null).wrapMatrix().withFeature(UFeature) - } - override val inverse: Matrix by lazy { - var a = origin - val inverse = FMatrixRMaj(1, 1) - val solver = LinearSolverFactory_FSCC.lu(FillReducing.NONE) - if (solver.modifiesA()) a = a.copy() - val i = CommonOps_FDRM.identity(a.numRows) - solver.solve(i, inverse) - inverse.wrapMatrix() - } - - override val determinant: Float by lazy { elementAlgebra.number(lu.computeDeterminant().real) } + override val u: Matrix + get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) + override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } else -> null - }?.let{ - type.cast(it) } + + @Suppress("UNCHECKED_CAST") + return raw as V? } /** diff --git a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt index 03b9b57b1..8b1b28e7d 100644 --- a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt +++ b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt @@ -58,19 +58,19 @@ internal class EjmlMatrixTest { @OptIn(UnstableKMathAPI::class) @Test - fun features() { + fun features() = EjmlLinearSpaceDDRM { val m = randomMatrix val w = EjmlDoubleMatrix(m) - val det: Determinant = EjmlLinearSpaceDDRM.attributeForOrNull(w) ?: fail() - assertEquals(CommonOps_DDRM.det(m), det.determinant) - val lup: LupDecompositionAttribute = EjmlLinearSpaceDDRM.attributeForOrNull(w) ?: fail() + val det: Double = w.getOrComputeAttribute(Determinant) ?: fail() + assertEquals(CommonOps_DDRM.det(m), det) + val lup: LupDecomposition = w.getOrComputeAttribute(LUP) ?: fail() val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows, m.numCols) .also { it.decompose(m.copy()) } assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getLower(null)), lup.l) assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getUpper(null)), lup.u) - assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getRowPivot(null)), lup.p) + assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getRowPivot(null)), lup.pivotMatrix(this)) } @Test diff --git a/kmath-for-real/build.gradle.kts b/kmath-for-real/build.gradle.kts index 99ce5903f..a0426b516 100644 --- a/kmath-for-real/build.gradle.kts +++ b/kmath-for-real/build.gradle.kts @@ -6,6 +6,7 @@ kscience { jvm() js() native() + wasm() dependencies { api(projects.kmathCore) diff --git a/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleVectorTest.kt b/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleVectorTest.kt index 29aa2ec2e..d4051c8d9 100644 --- a/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleVectorTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleVectorTest.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.real import space.kscience.kmath.linear.asMatrix import space.kscience.kmath.linear.linearSpace -import space.kscience.kmath.linear.transpose +import space.kscience.kmath.linear.transposed import space.kscience.kmath.operations.algebra import space.kscience.kmath.structures.Float64Buffer import kotlin.test.Test @@ -34,7 +34,7 @@ internal class DoubleVectorTest { val vector1 = Float64Buffer(5) { it.toDouble() } val vector2 = Float64Buffer(5) { 5 - it.toDouble() } val matrix1 = vector1.asMatrix() - val matrix2 = vector2.asMatrix().transpose() + val matrix2 = vector2.asMatrix().transposed() val product = matrix1 dot matrix2 assertEquals(5.0, product[1, 0]) assertEquals(6.0, product[2, 2]) diff --git a/kmath-functions/build.gradle.kts b/kmath-functions/build.gradle.kts index 3c1fbb07c..9296aaaf0 100644 --- a/kmath-functions/build.gradle.kts +++ b/kmath-functions/build.gradle.kts @@ -6,7 +6,6 @@ kscience{ jvm() js() native() - wasm() dependencies { diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt index a4e659545..e2f5c77ba 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt @@ -92,7 +92,7 @@ public inline fun GaussIntegrator.integrate( range: ClosedRange, order: Int = 10, intervals: Int = 10, - attributesBuilder: AttributesBuilder>.() -> Unit, + attributesBuilder: AttributesBuilder>.() -> Unit = {}, noinline function: (Double) -> T, ): UnivariateIntegrand { require(range.endInclusive > range.start) { "The range upper bound should be higher than lower bound" } 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 e77a2820b..2b58f0b05 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 @@ -23,7 +23,7 @@ import space.kscience.kmath.structures.MutableBufferFactory */ public class SplineInterpolator>( override val algebra: Field, - public val bufferFactory: MutableBufferFactory, + public val bufferFactory: MutableBufferFactory = algebra.bufferFactory, ) : PolynomialInterpolator { //TODO possibly optimize zeroed buffers diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/testUtils/IntModulo.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/testUtils/IntModulo.kt index 1f1e1a83f..d53a1ac2b 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/testUtils/IntModulo.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/testUtils/IntModulo.kt @@ -9,6 +9,7 @@ package space.kscience.kmath.functions.testUtils import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.ScaleOperations +import space.kscience.kmath.structures.MutableBufferFactory class IntModulo { @@ -109,15 +110,17 @@ class IntModulo { } @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") -class IntModuloRing : Ring, ScaleOperations { +class IntModuloRing(modulus: Int) : Ring, ScaleOperations { val modulus: Int - constructor(modulus: Int) { + init { require(modulus != 0) { "modulus can not be zero" } this.modulus = if (modulus < 0) -modulus else modulus } + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() + override inline val zero: IntModulo get() = IntModulo(0, modulus, toCheckInput = false) override inline val one: IntModulo get() = IntModulo(1, modulus, toCheckInput = false) diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/testUtils/Rational.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/testUtils/Rational.kt index 98150355c..c810cba2e 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/testUtils/Rational.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/testUtils/Rational.kt @@ -10,6 +10,7 @@ package space.kscience.kmath.functions.testUtils import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.NumbersAddOps +import space.kscience.kmath.structures.MutableBufferFactory @Suppress("NAME_SHADOWING") class Rational { @@ -159,6 +160,7 @@ class Rational { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") @OptIn(UnstableKMathAPI::class) object RationalField : Field, NumbersAddOps { + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override inline val zero: Rational get() = Rational.ZERO override inline val one: Rational get() = Rational.ONE diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SplineIntegralTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SplineIntegralTest.kt index 7e161f7c9..946f8811c 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SplineIntegralTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SplineIntegralTest.kt @@ -18,15 +18,15 @@ import kotlin.test.assertEquals class SplineIntegralTest { @Test - fun integratePolynomial(){ + fun integratePolynomial() { val polynomial = Polynomial(1.0, 2.0, 3.0) - val integral = polynomial.integrate(Float64Field,1.0..2.0) + val integral = polynomial.integrate(Float64Field, 1.0..2.0) assertEquals(11.0, integral, 0.001) } @Test fun gaussSin() { - val res = Float64Field.splineIntegrator.integrate(0.0..2 * PI, IntegrandMaxCalls(5)) { x -> + val res = Float64Field.splineIntegrator.integrate(0.0..2 * PI, { IntegrandMaxCalls(5) }) { x -> sin(x) } assertEquals(0.0, res.value, 1e-2) @@ -34,8 +34,8 @@ class SplineIntegralTest { @Test fun gaussUniform() { - val res = Float64Field.splineIntegrator.integrate(35.0..100.0, IntegrandMaxCalls(20)) { x -> - if(x in 30.0..50.0){ + val res = Float64Field.splineIntegrator.integrate(35.0..100.0, { IntegrandMaxCalls(20) }) { x -> + if (x in 30.0..50.0) { 1.0 } else { 0.0 diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt index c3ab61d89..a226ab04d 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt @@ -7,8 +7,8 @@ package space.kscience.kmath.geometry import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.kmath.geometry.euclidean2d.DoubleVector2D -import space.kscience.kmath.geometry.euclidean3d.DoubleVector3D +import space.kscience.kmath.geometry.euclidean2d.Float64Vector2D +import space.kscience.kmath.geometry.euclidean3d.Float64Vector3D /** * A line formed by [start] vector of start and a [direction] vector. Direction vector is not necessarily normalized, @@ -25,8 +25,8 @@ private data class LineImpl(override val start: V, override val dir public fun Line(base: V, direction: V): Line = LineImpl(base, direction) -public typealias Line2D = Line -public typealias Line3D = Line +public typealias Line2D = Line +public typealias Line3D = Line /** * A directed line segment between [begin] and [end] @@ -49,5 +49,5 @@ public fun LineSegment.line(algebra: GeometrySpace): Line Line(begin, end - begin) } -public typealias LineSegment2D = LineSegment -public typealias LineSegment3D = LineSegment +public typealias LineSegment2D = LineSegment +public typealias LineSegment3D = LineSegment diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector3D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector3D.kt index 71442cf63..64396baff 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector3D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector3D.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.geometry +import space.kscience.attributes.SafeType import space.kscience.kmath.linear.Point import space.kscience.kmath.structures.Buffer @@ -33,6 +34,9 @@ public fun Buffer.asVector3D(): Vector3D = object : Vector3D { require(this@asVector3D.size == 3) { "Buffer of size 3 is required for Vector3D" } } + override val type: SafeType = this@asVector3D.type + + override val x: T get() = this@asVector3D[0] override val y: T get() = this@asVector3D[1] override val z: T get() = this@asVector3D[2] diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/angles.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/angles.kt index 576a891fe..ff3efc126 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/angles.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/angles.kt @@ -11,6 +11,8 @@ import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import space.kscience.kmath.operations.Group +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.jvm.JvmInline import kotlin.math.PI import kotlin.math.floor @@ -28,11 +30,19 @@ public sealed interface Angle : Comparable { public operator fun div(other: Angle): Double public operator fun unaryMinus(): Angle - public companion object { - public val zero: Radians = Radians(0.0) + public companion object: Group { + override val zero: Radians = Radians(0.0) public val pi: Radians = Radians(PI) public val piTimes2: Radians = Radians(PI * 2) public val piDiv2: Radians = Radians(PI / 2) + + + override fun add(left: Angle, right: Angle): Angle = left + right + + @Suppress("EXTENSION_SHADOWED_BY_MEMBER") + override fun Angle.unaryMinus(): Angle = -this + + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() } } @@ -43,7 +53,7 @@ public object AngleSerializer : KSerializer { override fun deserialize(decoder: Decoder): Angle = decoder.decodeDouble().degrees override fun serialize(encoder: Encoder, value: Angle) { - encoder.encodeDouble(value.degrees) + encoder.encodeDouble(value.toDegrees().value) } } @@ -56,16 +66,16 @@ public value class Radians(public val value: Double) : Angle { override fun toRadians(): Radians = this override fun toDegrees(): Degrees = Degrees(value * 180 / PI) - public override fun plus(other: Angle): Radians = Radians(value + other.radians) - public override fun minus(other: Angle): Radians = Radians(value - other.radians) + public override fun plus(other: Angle): Radians = Radians(value + other.toRadians().value) + public override fun minus(other: Angle): Radians = Radians(value - other.toRadians().value) public override fun times(other: Number): Radians = Radians(value * other.toDouble()) public override fun div(other: Number): Radians = Radians(value / other.toDouble()) - override fun div(other: Angle): Double = value / other.radians + override fun div(other: Angle): Double = value / other.toRadians().value public override fun unaryMinus(): Radians = Radians(-value) - override fun compareTo(other: Angle): Int = value.compareTo(other.radians) + override fun compareTo(other: Angle): Int = value.compareTo(other.toRadians().value) } public fun sin(angle: Angle): Double = kotlin.math.sin(angle.toRadians().value) @@ -85,16 +95,16 @@ public value class Degrees(public val value: Double) : Angle { override fun toRadians(): Radians = Radians(value * PI / 180) override fun toDegrees(): Degrees = this - public override fun plus(other: Angle): Degrees = Degrees(value + other.degrees) - public override fun minus(other: Angle): Degrees = Degrees(value - other.degrees) + public override fun plus(other: Angle): Degrees = Degrees(value + other.toDegrees().value) + public override fun minus(other: Angle): Degrees = Degrees(value - other.toDegrees().value) public override fun times(other: Number): Degrees = Degrees(value * other.toDouble()) public override fun div(other: Number): Degrees = Degrees(value / other.toDouble()) - override fun div(other: Angle): Double = value / other.degrees + override fun div(other: Angle): Double = value / other.toDegrees().value public override fun unaryMinus(): Degrees = Degrees(-value) - override fun compareTo(other: Angle): Int = value.compareTo(other.degrees) + override fun compareTo(other: Angle): Int = value.compareTo(other.toDegrees().value) } public val Number.degrees: Degrees get() = Degrees(toDouble()) @@ -106,6 +116,6 @@ public val Angle.degrees: Double get() = toDegrees().value * Normalized angle 2 PI range symmetric around [center]. By default, uses (0, 2PI) range. */ public fun Angle.normalized(center: Angle = Angle.pi): Angle = - this - Angle.piTimes2 * floor((radians + PI - center.radians) / PI / 2) + this - Angle.piTimes2 * floor((toRadians().value + PI - center.toRadians().value) / PI / 2) public fun abs(angle: Angle): Angle = if (angle < Angle.zero) -angle else angle \ No newline at end of file diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float32Space2D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float32Space2D.kt index d50e6c4b2..d687a3574 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float32Space2D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float32Space2D.kt @@ -11,19 +11,22 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import space.kscience.attributes.SafeType import space.kscience.kmath.geometry.GeometrySpace import space.kscience.kmath.geometry.Vector2D import space.kscience.kmath.operations.Float32Field import space.kscience.kmath.structures.Float32 +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.pow import kotlin.math.sqrt @Serializable(Float32Space2D.VectorSerializer::class) -public interface Float32Vector2D : Vector2D +public interface Float32Vector2D : Vector2D{ + override val type: SafeType get() = Float32Field.type +} public object Float32Space2D : GeometrySpace { - @Serializable @SerialName("Float32Vector2D") private data class Vector2DImpl( @@ -72,6 +75,8 @@ public object Float32Space2D : GeometrySpace { public val yAxis: Float32Vector2D = vector(0.0, 1.0) override val defaultPrecision: Float32 = 1e-3f + + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() } public fun Float32Vector2D(x: Number, y: Number): Float32Vector2D = Float32Space2D.vector(x, y) diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float64Space2D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float64Space2D.kt index 0a43a46ea..05b9401d7 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float64Space2D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float64Space2D.kt @@ -11,66 +11,77 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import space.kscience.attributes.SafeType import space.kscience.kmath.geometry.GeometrySpace import space.kscience.kmath.geometry.Vector2D -import space.kscience.kmath.linear.Float64LinearSpace import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.ScaleOperations +import space.kscience.kmath.structures.Float64 +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.pow import kotlin.math.sqrt -public typealias DoubleVector2D = Vector2D -public typealias Float64Vector2D = Vector2D +@Serializable(Float64Space2D.VectorSerializer::class) +public interface Float64Vector2D : Vector2D { + override val type: SafeType get() = Float64Field.type +} + +@Deprecated("Use Float64Vector2D", ReplaceWith("Float64Vector2D")) +public typealias DoubleVector2D = Float64Vector2D -public val Vector2D.r: Double get() = Float64Space2D.norm(this) /** * 2D Euclidean space */ -public object Float64Space2D : GeometrySpace, ScaleOperations { +public object Float64Space2D : GeometrySpace, ScaleOperations { + @Serializable @SerialName("Float64Vector2D") private data class Vector2DImpl( override val x: Double, override val y: Double, - ) : DoubleVector2D + ) : Float64Vector2D - public object VectorSerializer : KSerializer { + public object VectorSerializer : KSerializer { private val proxySerializer = Vector2DImpl.serializer() override val descriptor: SerialDescriptor get() = proxySerializer.descriptor - override fun deserialize(decoder: Decoder): DoubleVector2D = decoder.decodeSerializableValue(proxySerializer) + override fun deserialize(decoder: Decoder): Float64Vector2D = decoder.decodeSerializableValue(proxySerializer) - override fun serialize(encoder: Encoder, value: DoubleVector2D) { + override fun serialize(encoder: Encoder, value: Float64Vector2D) { val vector = value as? Vector2DImpl ?: Vector2DImpl(value.x, value.y) encoder.encodeSerializableValue(proxySerializer, vector) } } - public fun vector(x: Number, y: Number): DoubleVector2D = Vector2DImpl(x.toDouble(), y.toDouble()) + public fun vector(x: Number, y: Number): Float64Vector2D = Vector2DImpl(x.toDouble(), y.toDouble()) - override val zero: DoubleVector2D by lazy { vector(0.0, 0.0) } + override val zero: Float64Vector2D by lazy { vector(0.0, 0.0) } - override fun norm(arg: DoubleVector2D): Double = sqrt(arg.x.pow(2) + arg.y.pow(2)) + override fun norm(arg: Float64Vector2D): Double = sqrt(arg.x.pow(2) + arg.y.pow(2)) - override fun DoubleVector2D.unaryMinus(): DoubleVector2D = vector(-x, -y) + override fun Float64Vector2D.unaryMinus(): Float64Vector2D = vector(-x, -y) - override fun DoubleVector2D.distanceTo(other: DoubleVector2D): Double = norm(this - other) - override fun add(left: DoubleVector2D, right: DoubleVector2D): DoubleVector2D = + override fun Float64Vector2D.distanceTo(other: Float64Vector2D): Double = norm(this - other) + override fun add(left: Float64Vector2D, right: Float64Vector2D): Float64Vector2D = vector(left.x + right.x, left.y + right.y) - override fun scale(a: DoubleVector2D, value: Double): DoubleVector2D = vector(a.x * value, a.y * value) - override fun DoubleVector2D.dot(other: DoubleVector2D): Double = x * other.x + y * other.y + override fun scale(a: Float64Vector2D, value: Double): Float64Vector2D = vector(a.x * value, a.y * value) + override fun Float64Vector2D.dot(other: Float64Vector2D): Double = x * other.x + y * other.y - public val xAxis: DoubleVector2D = vector(1.0, 0.0) - public val yAxis: DoubleVector2D = vector(0.0, 1.0) + public val xAxis: Float64Vector2D = vector(1.0, 0.0) + public val yAxis: Float64Vector2D = vector(0.0, 1.0) override val defaultPrecision: Double = 1e-6 + + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() } public fun Float64Vector2D(x: Number, y: Number): Float64Vector2D = Float64Space2D.vector(x, y) +public val Float64Vector2D.r: Float64 get() = Float64Space2D.norm(this) + public val Float64Field.euclidean2D: Float64Space2D get() = Float64Space2D \ No newline at end of file diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float32Space3D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float32Space3D.kt index ee4b965cd..7204ac4f9 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float32Space3D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float32Space3D.kt @@ -11,15 +11,19 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import space.kscience.attributes.SafeType import space.kscience.kmath.geometry.GeometrySpace import space.kscience.kmath.geometry.Vector3D import space.kscience.kmath.operations.Float32Field import space.kscience.kmath.structures.Float32 +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.pow import kotlin.math.sqrt @Serializable(Float32Space3D.VectorSerializer::class) -public interface Float32Vector3D : Vector3D +public interface Float32Vector3D : Vector3D{ + override val type: SafeType get() = Float32Field.type +} public object Float32Space3D : GeometrySpace { @@ -101,6 +105,8 @@ public object Float32Space3D : GeometrySpace { public val zAxis: Float32Vector3D = vector(0.0, 0.0, 1.0) override val defaultPrecision: Float32 = 1e-3f + + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() } public fun Float32Vector3D(x: Number, y: Number, z: Number): Float32Vector3D = Float32Space3D.vector(x, y, z) diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float64Space3D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float64Space3D.kt index d0b1ac581..a2c23926a 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float64Space3D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float64Space3D.kt @@ -11,10 +11,13 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import space.kscience.attributes.SafeType import space.kscience.kmath.geometry.GeometrySpace import space.kscience.kmath.geometry.Vector3D import space.kscience.kmath.linear.Float64LinearSpace import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.pow import kotlin.math.sqrt @@ -31,12 +34,16 @@ internal fun leviCivita(i: Int, j: Int, k: Int): Int = when { else -> 0 } -public typealias DoubleVector3D = Vector3D -public typealias Float64Vector3D = Vector3D +@Serializable(Float64Space3D.VectorSerializer::class) +public interface Float64Vector3D : Vector3D { + override val type: SafeType get() = Float64Field.type +} -public val DoubleVector3D.r: Double get() = Float64Space3D.norm(this) +@Deprecated("Use Float64Vector3D", ReplaceWith("Float64Vector3D")) +public typealias DoubleVector3D = Float64Vector3D -public object Float64Space3D : GeometrySpace{ + +public object Float64Space3D : GeometrySpace, Double> { public val linearSpace: Float64LinearSpace = Float64LinearSpace @@ -46,52 +53,52 @@ public object Float64Space3D : GeometrySpace{ override val x: Double, override val y: Double, override val z: Double, - ) : DoubleVector3D + ) : Float64Vector3D - public object VectorSerializer : KSerializer { + public object VectorSerializer : KSerializer { private val proxySerializer = Vector3DImpl.serializer() override val descriptor: SerialDescriptor get() = proxySerializer.descriptor - override fun deserialize(decoder: Decoder): DoubleVector3D = decoder.decodeSerializableValue(proxySerializer) + override fun deserialize(decoder: Decoder): Float64Vector3D = decoder.decodeSerializableValue(proxySerializer) - override fun serialize(encoder: Encoder, value: DoubleVector3D) { + override fun serialize(encoder: Encoder, value: Float64Vector3D) { val vector = value as? Vector3DImpl ?: Vector3DImpl(value.x, value.y, value.z) encoder.encodeSerializableValue(proxySerializer, vector) } } - public fun vector(x: Double, y: Double, z: Double): DoubleVector3D = + public fun vector(x: Double, y: Double, z: Double): Float64Vector3D = Vector3DImpl(x, y, z) - public fun vector(x: Number, y: Number, z: Number): DoubleVector3D = + public fun vector(x: Number, y: Number, z: Number): Float64Vector3D = vector(x.toDouble(), y.toDouble(), z.toDouble()) - override val zero: DoubleVector3D by lazy { vector(0.0, 0.0, 0.0) } + override val zero: Float64Vector3D by lazy { vector(0.0, 0.0, 0.0) } - override fun norm(arg: DoubleVector3D): Double = sqrt(arg.x.pow(2) + arg.y.pow(2) + arg.z.pow(2)) + override fun norm(arg: Vector3D): Double = sqrt(arg.x.pow(2) + arg.y.pow(2) + arg.z.pow(2)) - public fun DoubleVector3D.norm(): Double = norm(this) + public fun Vector3D.norm(): Double = norm(this) - override fun DoubleVector3D.unaryMinus(): DoubleVector3D = vector(-x, -y, -z) + override fun Vector3D.unaryMinus(): Float64Vector3D = vector(-x, -y, -z) - override fun DoubleVector3D.distanceTo(other: DoubleVector3D): Double = (this - other).norm() + override fun Vector3D.distanceTo(other: Vector3D): Double = (this - other).norm() - override fun add(left: DoubleVector3D, right: DoubleVector3D): DoubleVector3D = + override fun add(left: Vector3D, right: Vector3D): Float64Vector3D = vector(left.x + right.x, left.y + right.y, left.z + right.z) - override fun scale(a: DoubleVector3D, value: Double): DoubleVector3D = + override fun scale(a: Vector3D, value: Double): Float64Vector3D = vector(a.x * value, a.y * value, a.z * value) - override fun DoubleVector3D.dot(other: DoubleVector3D): Double = + override fun Vector3D.dot(other: Vector3D): Double = x * other.x + y * other.y + z * other.z /** * Compute vector product of [first] and [second]. The basis is assumed to be right-handed. */ public fun vectorProduct( - first: DoubleVector3D, - second: DoubleVector3D, - ): DoubleVector3D { + first: Vector3D, + second: Vector3D, + ): Float64Vector3D { var x = 0.0 var y = 0.0 var z = 0.0 @@ -110,13 +117,18 @@ public object Float64Space3D : GeometrySpace{ /** * Vector product with the right basis */ - public infix fun DoubleVector3D.cross(other: DoubleVector3D): Vector3D = vectorProduct(this, other) + public infix fun Vector3D.cross(other: Vector3D): Vector3D = vectorProduct(this, other) - public val xAxis: DoubleVector3D = vector(1.0, 0.0, 0.0) - public val yAxis: DoubleVector3D = vector(0.0, 1.0, 0.0) - public val zAxis: DoubleVector3D = vector(0.0, 0.0, 1.0) + public val xAxis: Float64Vector3D = vector(1.0, 0.0, 0.0) + public val yAxis: Float64Vector3D = vector(0.0, 1.0, 0.0) + public val zAxis: Float64Vector3D = vector(0.0, 0.0, 1.0) override val defaultPrecision: Double = 1e-6 + + override val bufferFactory: MutableBufferFactory> = MutableBufferFactory() } public val Float64Field.euclidean3D: Float64Space3D get() = Float64Space3D + + +public val Float64Vector3D.r: Double get() = Float64Space3D.norm(this) diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/rotations3D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/rotations3D.kt index abb531944..212cc251e 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/rotations3D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/rotations3D.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.geometry.euclidean3d +import space.kscience.attributes.SafeType import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.complex.* import space.kscience.kmath.geometry.* @@ -13,6 +14,7 @@ import space.kscience.kmath.linear.Matrix import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.linear.matrix import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 import kotlin.math.* public operator fun Quaternion.times(other: Quaternion): Quaternion = QuaternionAlgebra.multiply(this, other) @@ -35,7 +37,7 @@ public infix fun Quaternion.dot(other: Quaternion): Double = w * other.w + x * o /** * Represent a vector as quaternion with zero a rotation angle. */ -internal fun DoubleVector3D.asQuaternion(): Quaternion = Quaternion(0.0, x, y, z) +internal fun Float64Vector3D.asQuaternion(): Quaternion = Quaternion(0.0, x, y, z) /** * Angle in radians denoted by this quaternion rotation @@ -45,7 +47,7 @@ public val Quaternion.theta: Radians get() = (kotlin.math.acos(normalized().w) * /** * Create a normalized Quaternion from rotation angle and rotation vector */ -public fun Quaternion.Companion.fromRotation(theta: Angle, vector: DoubleVector3D): Quaternion { +public fun Quaternion.Companion.fromRotation(theta: Angle, vector: Float64Vector3D): Quaternion { val s = sin(theta / 2) val c = cos(theta / 2) val norm = with(Float64Space3D) { vector.norm() } @@ -55,9 +57,9 @@ public fun Quaternion.Companion.fromRotation(theta: Angle, vector: DoubleVector3 /** * An axis of quaternion rotation */ -public val Quaternion.vector: DoubleVector3D +public val Quaternion.vector: Float64Vector3D get() { - return object : DoubleVector3D { + return object : Float64Vector3D { private val sint2 = sqrt(1 - w * w) override val x: Double get() = this@vector.x / sint2 override val y: Double get() = this@vector.y / sint2 @@ -69,7 +71,7 @@ public val Quaternion.vector: DoubleVector3D /** * Rotate a vector in a [Float64Space3D] with [quaternion] */ -public fun Float64Space3D.rotate(vector: DoubleVector3D, quaternion: Quaternion): DoubleVector3D = +public fun Float64Space3D.rotate(vector: Float64Vector3D, quaternion: Quaternion): Float64Vector3D = with(QuaternionAlgebra) { val p = vector.asQuaternion() (quaternion * p * quaternion.reciprocal).vector @@ -80,15 +82,15 @@ public fun Float64Space3D.rotate(vector: DoubleVector3D, quaternion: Quaternion) */ @UnstableKMathAPI public fun Float64Space3D.rotate( - vector: DoubleVector3D, + vector: Float64Vector3D, composition: QuaternionAlgebra.() -> Quaternion, -): DoubleVector3D = +): Float64Vector3D = rotate(vector, QuaternionAlgebra.composition()) /** * Rotate a [Float64] vector in 3D space with a rotation matrix */ -public fun Float64Space3D.rotate(vector: DoubleVector3D, matrix: Matrix): DoubleVector3D { +public fun Float64Space3D.rotate(vector: Float64Vector3D, matrix: Matrix): Vector3D { require(matrix.colNum == 3 && matrix.rowNum == 3) { "Square 3x3 rotation matrix is required" } return with(linearSpace) { (matrix dot vector).asVector3D() } } @@ -242,6 +244,8 @@ public fun Quaternion.Companion.fromEuler( * A vector consisting of angles */ public data class AngleVector(override val x: Angle, override val y: Angle, override val z: Angle) : Vector3D { + override val type: SafeType get() = Angle.type + public companion object } diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/testUtils.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/testUtils.kt index 613a88b2f..3f9c86add 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/testUtils.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/testUtils.kt @@ -5,8 +5,7 @@ package space.kscience.kmath.geometry -import space.kscience.kmath.geometry.euclidean2d.DoubleVector2D -import space.kscience.kmath.geometry.euclidean3d.DoubleVector3D +import space.kscience.kmath.structures.Float64 import kotlin.math.abs import kotlin.test.assertEquals @@ -27,12 +26,12 @@ fun grid( return xs.flatMap { x -> ys.map { y -> x to y } } } -fun assertVectorEquals(expected: DoubleVector2D, actual: DoubleVector2D, absoluteTolerance: Double = 1e-3) { +fun assertVectorEquals(expected: Vector2D, actual: Vector2D, absoluteTolerance: Double = 1e-3) { assertEquals(expected.x, actual.x, absoluteTolerance) assertEquals(expected.y, actual.y, absoluteTolerance) } -fun assertVectorEquals(expected: DoubleVector3D, actual: DoubleVector3D, absoluteTolerance: Double = 1e-6) { +fun assertVectorEquals(expected: Vector3D, actual: Vector3D, absoluteTolerance: Double = 1e-6) { assertEquals(expected.x, actual.x, absoluteTolerance) assertEquals(expected.y, actual.y, absoluteTolerance) assertEquals(expected.z, actual.z, absoluteTolerance) diff --git a/kmath-histograms/build.gradle.kts b/kmath-histograms/build.gradle.kts index 33704c29e..63791d61c 100644 --- a/kmath-histograms/build.gradle.kts +++ b/kmath-histograms/build.gradle.kts @@ -6,6 +6,8 @@ kscience{ jvm() js() native() + wasm() + useCoroutines() } //apply(plugin = "kotlinx-atomicfu") @@ -21,7 +23,6 @@ kotlin.sourceSets { dependencies { implementation(project(":kmath-for-real")) implementation(projects.kmath.kmathStat) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.0") } } } diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram.kt index 79f145792..7f6fb2d26 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram.kt @@ -47,7 +47,7 @@ public interface Histogram> { } } -public interface HistogramBuilder { +public interface HistogramBuilder { /** * The default value increment for a bin @@ -61,9 +61,9 @@ public interface HistogramBuilder { } -public fun HistogramBuilder.put(point: Point): Unit = putValue(point) +public fun HistogramBuilder.put(point: Point): Unit = putValue(point) -public fun HistogramBuilder.put(vararg point: T): Unit = put(point.asBuffer()) +public inline fun HistogramBuilder.put(vararg point: T): Unit = put(point.asList().asBuffer()) public fun HistogramBuilder.put(vararg point: Number): Unit = put(Float64Buffer(point.map { it.toDouble() }.toDoubleArray())) diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogram1D.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogram1D.kt index 0326e8757..770fd70a0 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogram1D.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogram1D.kt @@ -12,6 +12,7 @@ import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.floor @OptIn(UnstableKMathAPI::class) @@ -46,6 +47,8 @@ public class UniformHistogram1DGroup( public val startPoint: Double = 0.0, ) : Group>, ScaleOperations> where A : Ring, A : ScaleOperations { + override val bufferFactory: MutableBufferFactory> = MutableBufferFactory() + override val zero: UniformHistogram1D = UniformHistogram1D(this, emptyMap()) /** diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt index 839744f89..8a914530b 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt @@ -37,6 +37,8 @@ public class UniformHistogramGroupND>( require(!lower.indices.any { upper[it] - lower[it] < 0 }) { "Range for one of axis is not strictly positive" } } + override val bufferFactory: MutableBufferFactory> = MutableBufferFactory() + public val dimension: Int get() = lower.size override val shape: ShapeND = ShapeND(IntArray(binNums.size) { binNums[it] + 2 }) @@ -87,7 +89,7 @@ public class UniformHistogramGroupND>( builder: HistogramBuilder.() -> Unit, ): HistogramND { val ndCounter: BufferND> = - StructureND.buffered(shape) { Counter.of(valueAlgebraND.elementAlgebra) } + BufferND(shape) { Counter.of(valueAlgebraND.elementAlgebra) } val hBuilder = object : HistogramBuilder { override val defaultValue: V get() = valueAlgebraND.elementAlgebra.one @@ -97,7 +99,8 @@ public class UniformHistogramGroupND>( } } hBuilder.apply(builder) - val values: BufferND = BufferND(ndCounter.indices, ndCounter.buffer.mapToBuffer(valueBufferFactory) { it.value }) + val values: BufferND = + BufferND(ndCounter.indices, ndCounter.buffer.mapToBuffer(valueBufferFactory) { it.value }) return HistogramND(this, values) } @@ -128,8 +131,7 @@ public fun > Histogram.Companion.uniformNDFromRanges( public fun Histogram.Companion.uniformDoubleNDFromRanges( vararg ranges: ClosedFloatingPointRange, -): UniformHistogramGroupND = - uniformNDFromRanges(Floa64FieldOpsND, *ranges, bufferFactory = ::Float64Buffer) +): UniformHistogramGroupND = uniformNDFromRanges(Floa64FieldOpsND, *ranges) /** @@ -147,21 +149,18 @@ public fun > Histogram.Companion.uniformNDFromRanges( bufferFactory: BufferFactory = valueAlgebraND.elementAlgebra.bufferFactory, ): UniformHistogramGroupND = UniformHistogramGroupND( valueAlgebraND, - DoubleBuffer( - ranges - .map(Pair, Int>::first) - .map(ClosedFloatingPointRange::start) - ), - DoubleBuffer( - ranges - .map(Pair, Int>::first) - .map(ClosedFloatingPointRange::endInclusive) - ), + ranges + .map(Pair, Int>::first) + .map(ClosedFloatingPointRange::start) + .asBuffer(), + ranges + .map(Pair, Int>::first) + .map(ClosedFloatingPointRange::endInclusive) + .asBuffer(), ranges.map(Pair, Int>::second).toIntArray(), valueBufferFactory = bufferFactory ) public fun Histogram.Companion.uniformDoubleNDFromRanges( vararg ranges: Pair, Int>, -): UniformHistogramGroupND = - uniformNDFromRanges(Floa64FieldOpsND, *ranges, bufferFactory = ::Float64Buffer) \ No newline at end of file +): UniformHistogramGroupND = uniformNDFromRanges(Floa64FieldOpsND, *ranges) \ No newline at end of file diff --git a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramGroup.kt b/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramGroup.kt index 95658141a..c8be4f846 100644 --- a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramGroup.kt +++ b/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramGroup.kt @@ -14,10 +14,7 @@ import space.kscience.kmath.misc.sorted import space.kscience.kmath.operations.Group import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.ScaleOperations -import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.first -import space.kscience.kmath.structures.indices -import space.kscience.kmath.structures.last +import space.kscience.kmath.structures.* import java.util.* private fun > TreeMap.getBin(value: Double): B? { @@ -53,6 +50,8 @@ public class TreeHistogramGroup( @PublishedApi internal val binFactory: (Double) -> DoubleDomain1D, ) : Group>, ScaleOperations> where A : Ring, A : ScaleOperations { + override val bufferFactory: MutableBufferFactory> = MutableBufferFactory() + internal inner class DomainCounter(val domain: DoubleDomain1D, val counter: Counter = Counter.of(valueAlgebra)) : ClosedRange by domain.range diff --git a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt index 53e0beca1..07b77683d 100644 --- a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt +++ b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.kotlingrad import ai.hypergraph.kotlingrad.api.SFun import ai.hypergraph.kotlingrad.api.SVar +import space.kscience.attributes.SafeType import space.kscience.kmath.expressions.* import space.kscience.kmath.operations.NumericAlgebra @@ -25,6 +26,8 @@ public class KotlingradExpression>( public val algebra: A, public val mst: MST, ) : SpecialDifferentiableExpression> { + override val type: SafeType = algebra.type + override fun invoke(arguments: Map): T = mst.interpret(algebra, arguments) override fun derivativeOrNull( diff --git a/kmath-nd4j/build.gradle.kts b/kmath-nd4j/build.gradle.kts index e5c4af891..bfa09e771 100644 --- a/kmath-nd4j/build.gradle.kts +++ b/kmath-nd4j/build.gradle.kts @@ -12,7 +12,7 @@ dependencies { } readme { - maturity = space.kscience.gradle.Maturity.EXPERIMENTAL + maturity = space.kscience.gradle.Maturity.DEPRECATED propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) feature(id = "nd4jarraystructure") { "NDStructure wrapper for INDArray" } feature(id = "nd4jarrayrings") { "Rings over Nd4jArrayStructure of Int and Long" } diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayStructure.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayStructure.kt index 34bbcaefb..9bfc2f8f5 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayStructure.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayStructure.kt @@ -6,8 +6,10 @@ package space.kscience.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray +import space.kscience.attributes.SafeType import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* +import space.kscience.kmath.operations.Float32Field /** * Represents a [StructureND] wrapping an [INDArray] object. @@ -31,6 +33,7 @@ public sealed class Nd4jArrayStructure : MutableStructureND { } public data class Nd4jArrayIntStructure(override val ndArray: INDArray) : Nd4jArrayStructure(), StructureNDOfInt { + override fun elementsIterator(): Iterator> = ndArray.intIterator() @OptIn(PerformancePitfall::class) @@ -47,8 +50,10 @@ public data class Nd4jArrayIntStructure(override val ndArray: INDArray) : Nd4jAr */ public fun INDArray.asIntStructure(): Nd4jArrayIntStructure = Nd4jArrayIntStructure(this) -public data class Nd4jArrayDoubleStructure(override val ndArray: INDArray) : Nd4jArrayStructure(), StructureNDOfDouble { +public data class Nd4jArrayDoubleStructure(override val ndArray: INDArray) : Nd4jArrayStructure(), + StructureNDOfDouble { override fun elementsIterator(): Iterator> = ndArray.realIterator() + @OptIn(PerformancePitfall::class) override fun get(index: IntArray): Double = ndArray.getDouble(*index) @@ -64,7 +69,11 @@ public data class Nd4jArrayDoubleStructure(override val ndArray: INDArray) : Nd4 public fun INDArray.asDoubleStructure(): Nd4jArrayStructure = Nd4jArrayDoubleStructure(this) public data class Nd4jArrayFloatStructure(override val ndArray: INDArray) : Nd4jArrayStructure() { + + override val type: SafeType get() = Float32Field.type + override fun elementsIterator(): Iterator> = ndArray.floatIterator() + @PerformancePitfall override fun get(index: IntArray): Float = ndArray.getFloat(*index) diff --git a/kmath-optimization/build.gradle.kts b/kmath-optimization/build.gradle.kts index 7250d1f72..f908b127d 100644 --- a/kmath-optimization/build.gradle.kts +++ b/kmath-optimization/build.gradle.kts @@ -6,6 +6,7 @@ kscience{ jvm() js() native() + wasm() } kotlin.sourceSets { diff --git a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt index d8b3f29eb..a38e9e6e8 100644 --- a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt +++ b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt @@ -11,6 +11,10 @@ import space.kscience.kmath.expressions.Symbol public class OptimizationValue(type: SafeType) : PolymorphicAttribute(type) +public inline fun AttributesBuilder>.value(value: T) { + set(OptimizationValue(safeTypeOf()), value) +} + public enum class OptimizationDirection { MAXIMIZE, MINIMIZE diff --git a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationProblem.kt b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationProblem.kt index 49678cd57..18f552bda 100644 --- a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationProblem.kt +++ b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationProblem.kt @@ -34,10 +34,18 @@ public fun AttributesBuilder>.startAt(startingPoint: public class OptimizationCovariance : OptimizationAttribute>, PolymorphicAttribute>(safeTypeOf()) +public fun AttributesBuilder>.covariance(covariance: NamedMatrix) { + set(OptimizationCovariance(),covariance) +} + public class OptimizationResult() : OptimizationAttribute>, PolymorphicAttribute>(safeTypeOf()) +public fun AttributesBuilder>.result(result: Map) { + set(OptimizationResult(), result) +} + public val OptimizationProblem.resultOrNull: Map? get() = attributes[OptimizationResult()] public val OptimizationProblem.result: Map diff --git a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt index 46394132b..eba637a83 100644 --- a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt +++ b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt @@ -264,10 +264,10 @@ public object QowOptimizer : Optimizer { qow = QoWeight(problem, res.freeParameters) res = qow.newtonianRun(maxSteps = iterations) } - val covariance = res.covariance() + return res.problem.withAttributes { - set(OptimizationResult(), res.freeParameters) - set(OptimizationCovariance(), covariance) + result(res.freeParameters) + covariance(res.covariance()) } } } \ No newline at end of file diff --git a/kmath-stat/build.gradle.kts b/kmath-stat/build.gradle.kts index 000280def..7d396b460 100644 --- a/kmath-stat/build.gradle.kts +++ b/kmath-stat/build.gradle.kts @@ -6,6 +6,7 @@ kscience{ jvm() js() native() + wasm() } kotlin.sourceSets { diff --git a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/MCScopeTest.kt b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/MCScopeTest.kt index 545e89814..a95829eff 100644 --- a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/MCScopeTest.kt +++ b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/MCScopeTest.kt @@ -68,7 +68,7 @@ internal class MCScopeTest { } - @OptIn(DelicateCoroutinesApi::class) + @OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class) fun compareResult(test: ATest) { val res1 = runBlocking(Dispatchers.Default) { test() } val res2 = runBlocking(newSingleThreadContext("test")) { test() } diff --git a/kmath-symja/src/main/kotlin/space/kscience/kmath/symja/SymjaExpression.kt b/kmath-symja/src/main/kotlin/space/kscience/kmath/symja/SymjaExpression.kt index e5ba411e6..b23da37f5 100644 --- a/kmath-symja/src/main/kotlin/space/kscience/kmath/symja/SymjaExpression.kt +++ b/kmath-symja/src/main/kotlin/space/kscience/kmath/symja/SymjaExpression.kt @@ -7,8 +7,9 @@ package space.kscience.kmath.symja import org.matheclipse.core.eval.ExprEvaluator import org.matheclipse.core.expression.F -import space.kscience.kmath.expressions.SpecialDifferentiableExpression +import space.kscience.attributes.SafeType import space.kscience.kmath.expressions.MST +import space.kscience.kmath.expressions.SpecialDifferentiableExpression import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.interpret import space.kscience.kmath.operations.NumericAlgebra @@ -30,6 +31,8 @@ public class SymjaExpression>( public val mst: MST, public val evaluator: ExprEvaluator = DEFAULT_EVALUATOR, ) : SpecialDifferentiableExpression> { + override val type: SafeType get() = algebra.type + override fun invoke(arguments: Map): T = mst.interpret(algebra, arguments) override fun derivativeOrNull(symbols: List): SymjaExpression = SymjaExpression( diff --git a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt index 4c06404b1..9a559bc28 100644 --- a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt +++ b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt @@ -10,6 +10,7 @@ import org.tensorflow.Output import org.tensorflow.ndarray.NdArray import org.tensorflow.op.core.Constant import org.tensorflow.types.TFloat64 +import space.kscience.attributes.SafeType import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.expressions.Symbol @@ -25,9 +26,9 @@ public class DoubleTensorFlowOutput( graph: Graph, output: Output, ) : TensorFlowOutput(graph, output) { + override val type: SafeType get() = Float64Field.type override fun org.tensorflow.Tensor.actualizeTensor(): NdArray = this as TFloat64 - } internal fun ShapeND.toLongArray(): LongArray = LongArray(size) { get(it).toLong() } diff --git a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/IntTensorFlowAlgebra.kt b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/IntTensorFlowAlgebra.kt index 186a794e9..3695a2640 100644 --- a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/IntTensorFlowAlgebra.kt +++ b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/IntTensorFlowAlgebra.kt @@ -10,11 +10,19 @@ import org.tensorflow.Output import org.tensorflow.ndarray.NdArray import org.tensorflow.types.TInt32 import org.tensorflow.types.TInt64 +import space.kscience.attributes.SafeType +import space.kscience.kmath.operations.Int32Ring +import space.kscience.kmath.operations.Int64Ring +import space.kscience.kmath.structures.Int32 +import space.kscience.kmath.structures.Int64 public class IntTensorFlowOutput( graph: Graph, output: Output, ) : TensorFlowOutput(graph, output) { + + override val type: SafeType get() = Int32Ring.type + override fun org.tensorflow.Tensor.actualizeTensor(): NdArray = this as TInt32 } @@ -22,5 +30,7 @@ public class LongTensorFlowOutput( graph: Graph, output: Output, ) : TensorFlowOutput(graph, output) { + + override val type: SafeType get() = Int64Ring.type override fun org.tensorflow.Tensor.actualizeTensor(): NdArray = this as TInt64 } \ No newline at end of file diff --git a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/TensorFlowAlgebra.kt b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/TensorFlowAlgebra.kt index 158c928c4..7e413c29c 100644 --- a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/TensorFlowAlgebra.kt +++ b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/TensorFlowAlgebra.kt @@ -17,6 +17,7 @@ import org.tensorflow.op.core.* import org.tensorflow.types.TInt32 import org.tensorflow.types.family.TNumber import org.tensorflow.types.family.TType +import space.kscience.attributes.SafeType import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnsafeKMathAPI import space.kscience.kmath.UnstableKMathAPI @@ -39,8 +40,8 @@ public sealed interface TensorFlowTensor : Tensor /** * Static (eager) in-memory TensorFlow tensor */ -@JvmInline -public value class TensorFlowArray(public val tensor: NdArray) : Tensor { +public class TensorFlowArray(override val type: SafeType, public val tensor: NdArray) : Tensor { + override val shape: ShapeND get() = ShapeND(tensor.shape().asArray().toIntArray()) @PerformancePitfall @@ -73,7 +74,7 @@ public abstract class TensorFlowOutput( internal val actualTensor by lazy { Session(graph).use { session -> - TensorFlowArray(session.runner().fetch(output).run().first().actualizeTensor()) + TensorFlowArray(type, session.runner().fetch(output).run().first().actualizeTensor()) } } diff --git a/kmath-tensors/build.gradle.kts b/kmath-tensors/build.gradle.kts index 8977183da..82213f408 100644 --- a/kmath-tensors/build.gradle.kts +++ b/kmath-tensors/build.gradle.kts @@ -12,6 +12,7 @@ kscience{ } } native() + wasm() dependencies { api(projects.kmathCore) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/Tensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/Tensor.kt deleted file mode 100644 index 2a7f463bc..000000000 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/Tensor.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2018-2024 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.tensors.api - -import space.kscience.kmath.nd.MutableStructureND - -public typealias Tensor = MutableStructureND diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt index 6e515f3f8..c960090d2 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt @@ -5,11 +5,14 @@ package space.kscience.kmath.tensors.api +import space.kscience.kmath.nd.MutableStructureND import space.kscience.kmath.nd.RingOpsND import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.Ring +public typealias Tensor = MutableStructureND + /** * Algebra over a ring on [Tensor]. * For more information: https://proofwiki.org/wiki/Definition:Algebra_over_Ring diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index a47c9364a..b2106200e 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -6,7 +6,6 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall -import space.kscience.kmath.linear.transposed import space.kscience.kmath.nd.* import kotlin.math.abs import kotlin.math.max @@ -139,7 +138,7 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI if (inputData.nargin < 5) { weight = fromArray( ShapeND(intArrayOf(1, 1)), - doubleArrayOf((inputData.realValues.transposed dot inputData.realValues).as1D()[0]) + doubleArrayOf((inputData.realValues.transposed() dot inputData.realValues).as1D()[0]) ).as2D() } @@ -266,12 +265,12 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI settings.funcCalls += 1 // val tmp = deltaY.times(weight) - var X2Try = deltaY.as2D().transposed dot deltaY.times(weight) // Chi-squared error criteria + var X2Try = deltaY.as2D().transposed() dot deltaY.times(weight) // Chi-squared error criteria val alpha = 1.0 if (updateType == 2) { // Quadratic // One step of quadratic line update in the h direction for minimum X2 - val alphaTensor = (jtWdy.transposed dot h) / ((X2Try - x2) / 2.0 + 2 * (jtWdy.transposed dot h)) + val alphaTensor = (jtWdy.transposed() dot h) / ((X2Try - x2) / 2.0 + 2 * (jtWdy.transposed() dot h)) h = h dot alphaTensor pTry = (p + h).as2D() // update only [idx] elements pTry = smallestElementComparison( @@ -289,7 +288,7 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI ) // residual error using p_try settings.funcCalls += 1 - X2Try = deltaY.as2D().transposed dot deltaY * weight // Chi-squared error criteria + X2Try = deltaY.as2D().transposed() dot deltaY * weight // Chi-squared error criteria } val rho = when (updateType) { // Nielsen diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorBuffer.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorBuffer.kt index 2b5741ed4..88112af2a 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorBuffer.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorBuffer.kt @@ -6,12 +6,16 @@ package space.kscience.kmath.viktor import org.jetbrains.bio.viktor.F64FlatArray +import space.kscience.attributes.SafeType +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.MutableBuffer @Suppress("NOTHING_TO_INLINE", "OVERRIDE_BY_INLINE") @JvmInline public value class ViktorBuffer(public val flatArray: F64FlatArray) : MutableBuffer { + override val type: SafeType get() = Float64Field.type + override val size: Int get() = flatArray.length diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt index 328a0ab01..9e01fd88a 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorStructureND.kt @@ -6,13 +6,17 @@ package space.kscience.kmath.viktor import org.jetbrains.bio.viktor.F64Array +import space.kscience.attributes.SafeType import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.MutableStructureND import space.kscience.kmath.nd.ShapeND +import space.kscience.kmath.operations.Float64Field @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") public class ViktorStructureND(public val f64Buffer: F64Array) : MutableStructureND { + override val type: SafeType get() = Float64Field.type + override val shape: ShapeND get() = ShapeND(f64Buffer.shape) @OptIn(PerformancePitfall::class)