From 9e88bff668595b90a55ed83d7e2ce978334cb8e1 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 22 Nov 2023 09:22:28 +0300 Subject: [PATCH 01/20] Kotlin 2.0.0-Beta1 --- benchmarks/build.gradle.kts | 2 +- gradle.properties | 2 +- .../kscience/kmath/internal/base64/base64.kt | 5 -- .../kmath/internal/binaryen/index.binaryen.kt | 4 -- .../lib.dom.WebAssembly.module_dukat.kt | 58 ++----------------- .../nonDeclarations.WebAssembly.kt | 7 --- .../kmath/functions/testUtils/Rational.kt | 2 +- .../geometry/euclidean2d/Float64Space2D.kt | 2 +- .../kscience/kmath/histogram/Histogram.kt | 6 +- .../kscience/kmath/histogram/Histogram1D.kt | 2 +- .../histogram/UniformHistogramGroupND.kt | 2 +- .../space/kscience/kmath/stat/MCScopeTest.kt | 2 +- 12 files changed, 15 insertions(+), 79 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 24471a9e4..4894cd265 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -81,7 +81,7 @@ benchmark { // Setup configurations targets { register("jvm") - register("js") +// register("js") } fun kotlinx.benchmark.gradle.BenchmarkConfiguration.commonConfiguration() { diff --git a/gradle.properties b/gradle.properties index 5d62bf88e..af735ea2d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,6 @@ org.gradle.jvmargs=-Xmx4096m org.gradle.parallel=true org.gradle.workers.max=4 -toolsVersion=0.15.0-kotlin-1.9.20 +toolsVersion=0.15.0-kotlin-2.0.0-Beta1 #kotlin.experimental.tryK2=true #kscience.wasm.disabled=true \ No newline at end of file diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/base64/base64.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/base64/base64.kt index 8ea699837..03cd76611 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/base64/base64.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/base64/base64.kt @@ -4,11 +4,6 @@ */ @file:Suppress( - "INTERFACE_WITH_SUPERCLASS", - "OVERRIDING_FINAL_MEMBER", - "RETURN_TYPE_MISMATCH_ON_OVERRIDE", - "CONFLICTING_OVERLOADS", - "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", "ObjectPropertyName", "ClassName", ) diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.kt index 59c9a40a6..8c50de9bb 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.kt @@ -4,10 +4,6 @@ */ @file:Suppress( - "INTERFACE_WITH_SUPERCLASS", - "OVERRIDING_FINAL_MEMBER", - "RETURN_TYPE_MISMATCH_ON_OVERRIDE", - "CONFLICTING_OVERLOADS", "PropertyName", "ClassName", "ENUM_CLASS_IN_EXTERNAL_DECLARATION_WARNING", ) diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/lib.dom.WebAssembly.module_dukat.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/lib.dom.WebAssembly.module_dukat.kt index 873a5e1d2..628df09b0 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/lib.dom.WebAssembly.module_dukat.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/lib.dom.WebAssembly.module_dukat.kt @@ -6,94 +6,46 @@ @file:JsQualifier("WebAssembly") @file:Suppress( - "INTERFACE_WITH_SUPERCLASS", - "OVERRIDING_FINAL_MEMBER", - "RETURN_TYPE_MISMATCH_ON_OVERRIDE", - "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", "ClassName", ) package space.kscience.kmath.internal.webassembly -import space.kscience.kmath.internal.tsstdlib.PromiseLike import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.ArrayBufferView import org.khronos.webgl.Uint8Array import org.w3c.fetch.Response +import space.kscience.kmath.internal.tsstdlib.PromiseLike import kotlin.js.Promise -@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") -internal external interface CompileError { - companion object { - var prototype: CompileError - } -} +internal external interface CompileError -@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") internal external interface Global { var value: Any fun valueOf(): Any - - companion object { - var prototype: Global - } } -@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") @JsName("Instance") internal external interface Instance1 { var exports: Exports - - companion object { - var prototype: Instance - } } -@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") -internal external interface LinkError { - companion object { - var prototype: LinkError - } -} +internal external interface LinkError -@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") internal external interface Memory { var buffer: ArrayBuffer fun grow(delta: Number): Number - - companion object { - var prototype: Memory - } } -@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") -@JsName("Module") -internal external interface Module1 { - companion object { - var prototype: Module - fun customSections(moduleObject: Module, sectionName: String): Array - fun exports(moduleObject: Module): Array - fun imports(moduleObject: Module): Array - } -} -@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") -internal external interface RuntimeError { - companion object { - var prototype: RuntimeError - } -} +internal external interface RuntimeError + -@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") internal external interface Table { var length: Number fun get(index: Number): Function<*>? fun grow(delta: Number): Number fun set(index: Number, value: Function<*>?) - - companion object { - var prototype: Table - } } internal external interface GlobalDescriptor { diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/nonDeclarations.WebAssembly.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/nonDeclarations.WebAssembly.kt index ba86e977b..4cef2d85a 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/nonDeclarations.WebAssembly.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/nonDeclarations.WebAssembly.kt @@ -3,13 +3,6 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -@file:Suppress( - "INTERFACE_WITH_SUPERCLASS", - "OVERRIDING_FINAL_MEMBER", - "RETURN_TYPE_MISMATCH_ON_OVERRIDE", - "CONFLICTING_OVERLOADS", - "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", -) package space.kscience.kmath.internal.webassembly 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 ff05805da..2d21c2224 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 @@ -146,7 +146,7 @@ class Rational { override fun equals(other: Any?): Boolean = when (other) { is Rational -> numerator == other.numerator && denominator == other.denominator - is Int -> numerator == other && denominator == 1L + is Int -> numerator == other.toLong() && denominator == 1L is Long -> numerator == other && denominator == 1L else -> false } 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 423e43c30..ba8ea1396 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 @@ -37,7 +37,7 @@ public object Float64Space2D : GeometrySpace, ScaleOperations { + public object VectorSerializer : KSerializer { private val proxySerializer = Vector2DImpl.serializer() override val descriptor: SerialDescriptor get() = proxySerializer.descriptor 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 3303fa172..2a0523e46 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 @@ -33,7 +33,7 @@ public interface Histogram> { /** * Find existing bin, corresponding to given coordinates */ - public operator fun get(point: Point): B? + public operator fun get(point: Point): B? /** * Dimension of the histogram @@ -57,11 +57,11 @@ public interface HistogramBuilder { /** * Increment appropriate bin with given value */ - public fun putValue(point: Point, value: V = defaultValue) + public fun putValue(point: Point, value: V = defaultValue) } -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()) diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram1D.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram1D.kt index f50610a17..7b1496fce 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram1D.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram1D.kt @@ -42,7 +42,7 @@ public interface Histogram1DBuilder : HistogramBuilder, value: V) { + override fun putValue(point: Point, value: V) { require(point.size == 1) { "Only points with single value could be used in Histogram1D" } putValue(point[0], value) } 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 f7094f7a9..2ca3540c2 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 @@ -91,7 +91,7 @@ public class UniformHistogramGroupND>( val hBuilder = object : HistogramBuilder { override val defaultValue: V get() = valueAlgebraND.elementAlgebra.one - override fun putValue(point: Point, value: V) = with(valueAlgebraND.elementAlgebra) { + override fun putValue(point: Point, value: V) = with(valueAlgebraND.elementAlgebra) { val index = getIndexOrNull(point) ndCounter[index].add(value) } 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 dbcf32e27..5eba0fed8 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() } From 9f9c4a347be1c15bfb23d99adc9a782a80cd4681 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 28 Jan 2024 18:35:47 +0300 Subject: [PATCH 02/20] Fix all issues for 2.0.0 --- gradle.properties | 2 +- kmath-histograms/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index af735ea2d..3afebb73b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,6 @@ org.gradle.jvmargs=-Xmx4096m org.gradle.parallel=true org.gradle.workers.max=4 -toolsVersion=0.15.0-kotlin-2.0.0-Beta1 +toolsVersion=0.15.2-kotlin-2.0.0-Beta3 #kotlin.experimental.tryK2=true #kscience.wasm.disabled=true \ No newline at end of file diff --git a/kmath-histograms/build.gradle.kts b/kmath-histograms/build.gradle.kts index 33704c29e..cf68fbb47 100644 --- a/kmath-histograms/build.gradle.kts +++ b/kmath-histograms/build.gradle.kts @@ -6,6 +6,7 @@ kscience{ jvm() js() native() + useCoroutines() } //apply(plugin = "kotlinx-atomicfu") @@ -21,7 +22,6 @@ kotlin.sourceSets { dependencies { implementation(project(":kmath-for-real")) implementation(projects.kmath.kmathStat) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.0") } } } From 0af8147be63096966656aa73a4db64f818f17bce Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 26 Mar 2024 09:58:50 +0300 Subject: [PATCH 03/20] Remove unnecessary internal dependencies --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3afebb73b..57610c9fb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,6 @@ org.gradle.jvmargs=-Xmx4096m org.gradle.parallel=true org.gradle.workers.max=4 -toolsVersion=0.15.2-kotlin-2.0.0-Beta3 +toolsVersion=0.15.2-kotlin-2.0.0-Beta5 #kotlin.experimental.tryK2=true #kscience.wasm.disabled=true \ No newline at end of file From d0d250c67fab9cbe54f88b59b7b679e3d4c53fe7 Mon Sep 17 00:00:00 2001 From: "vasilev.ilia" Date: Mon, 22 Apr 2024 21:57:24 +0300 Subject: [PATCH 04/20] MHS first implementation | STUD-7 --- .../samplers/MetropolisHastingsSampler.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt new file mode 100644 index 000000000..29fea9c13 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt @@ -0,0 +1,42 @@ +/* + * 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.samplers + +import space.kscience.kmath.chains.BlockingDoubleChain +import space.kscience.kmath.distributions.NormalDistribution +import space.kscience.kmath.random.RandomGenerator +import space.kscience.kmath.structures.Float64Buffer + +/** + * [Metropolis–Hastings algorithm](https://en.wikipedia.org/wiki/Metropolis%E2%80%93Hastings_algorithm) for sampling + * target distribution [distribution]. + * + */ +public class MetropolisHastingsSampler( + public val distribution: (x : Double) -> Double, + public val initState : Double = 0.0, +) : BlockingDoubleSampler { + override fun sample(generator: RandomGenerator): BlockingDoubleChain = object : BlockingDoubleChain { + var currState = initState + + override fun nextBufferBlocking(size: Int): Float64Buffer { + val u = generator.nextDoubleBuffer(size) + + return Float64Buffer(size) {index -> + val proposalDist = NormalDistribution(currState, 0.01) + val newState = proposalDist.sample(RandomGenerator.default(1)).nextBufferBlocking(1).get(0) + val acceptanceRatio = distribution(newState) / distribution(currState) + if (u[index] <= acceptanceRatio) { + currState = newState + } + currState + } + } + + override suspend fun fork(): BlockingDoubleChain = BoxMullerSampler.sample(generator.fork()) + } + +} \ No newline at end of file From dbc5488eb2af2842618dbd8dc754b1f3c8a213ca Mon Sep 17 00:00:00 2001 From: "vasilev.ilia" Date: Wed, 24 Apr 2024 23:29:14 +0300 Subject: [PATCH 05/20] Minor edits. Tests added. | STUD-7 --- .../samplers/MetropolisHastingsSampler.kt | 30 +++++++++++-------- .../samplers/TestMetropolisHastingsSampler.kt | 26 ++++++++++++++++ 2 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt index 29fea9c13..74dfa822f 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt @@ -6,33 +6,39 @@ package space.kscience.kmath.samplers import space.kscience.kmath.chains.BlockingDoubleChain +import space.kscience.kmath.distributions.Distribution1D import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.structures.Float64Buffer +import kotlin.math.* /** * [Metropolis–Hastings algorithm](https://en.wikipedia.org/wiki/Metropolis%E2%80%93Hastings_algorithm) for sampling - * target distribution [distribution]. + * target distribution [targetDist]. * + * params: + * - targetDist: function close to the density of the sampled distribution; + * - initialState: initial value of the chain of sampled values. */ public class MetropolisHastingsSampler( - public val distribution: (x : Double) -> Double, - public val initState : Double = 0.0, + public val targetDist: (arg : Double) -> Double, + public val initialState : Double = 0.0, ) : BlockingDoubleSampler { override fun sample(generator: RandomGenerator): BlockingDoubleChain = object : BlockingDoubleChain { - var currState = initState + var currentState = initialState + fun proposalDist(arg : Double) = NormalDistribution(arg, 0.01) override fun nextBufferBlocking(size: Int): Float64Buffer { - val u = generator.nextDoubleBuffer(size) + val acceptanceProb = generator.nextDoubleBuffer(size) return Float64Buffer(size) {index -> - val proposalDist = NormalDistribution(currState, 0.01) - val newState = proposalDist.sample(RandomGenerator.default(1)).nextBufferBlocking(1).get(0) - val acceptanceRatio = distribution(newState) / distribution(currState) - if (u[index] <= acceptanceRatio) { - currState = newState - } - currState + val newState = proposalDist(currentState).sample(RandomGenerator.default(0)).nextBufferBlocking(5).get(4) + val firstComp = targetDist(newState) / targetDist(currentState) + val secondComp = proposalDist(newState).probability(currentState) / proposalDist(currentState).probability(newState) + val acceptanceRatio = min(1.0, firstComp * secondComp) + + currentState = if (acceptanceProb[index] <= acceptanceRatio) newState else currentState + currentState } } diff --git a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt new file mode 100644 index 000000000..7bc1eb877 --- /dev/null +++ b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt @@ -0,0 +1,26 @@ +/* + * 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.samplers +import space.kscience.kmath.distributions.NormalDistribution +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.random.DefaultGenerator +import space.kscience.kmath.stat.invoke +import space.kscience.kmath.stat.mean +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestMetropolisHastingsSampler { + + @Test + fun samplingNormalTest1() { + fun myDist(arg : Double) = NormalDistribution(0.0, 1.0).probability(arg) + val sampler = MetropolisHastingsSampler(::myDist) + + + val sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(10) + assertEquals(0.05, Float64Field.mean(sampledValues), 0.01) + } +} \ No newline at end of file From c418607bf674af50bbe31b3b62af0aff4294c4dc Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 30 Apr 2024 19:41:34 +0300 Subject: [PATCH 06/20] 2.0.0-RC2 --- build.gradle.kts | 2 +- buildSrc/build.gradle.kts | 1 + gradle.properties | 2 +- kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt | 3 --- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d90529e08..57ff022f2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,7 +16,7 @@ allprojects { } group = "space.kscience" - version = "0.4.0" + version = "0.4.1-dev" } subprojects { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index ba4c391d8..c0f9d9631 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + kotlin("jvm") version "1.9.23" `kotlin-dsl` `version-catalog` } diff --git a/gradle.properties b/gradle.properties index 295dbc060..9a76404a9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,6 +10,6 @@ org.gradle.jvmargs=-Xmx4096m org.gradle.parallel=true org.gradle.workers.max=4 -toolsVersion=0.15.2-kotlin-2.0.0-RC1 +toolsVersion=0.15.2-kotlin-2.0.0-RC2 #kotlin.experimental.tryK2=true #kscience.wasm.disabled=true \ No newline at end of file diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt index f06137c4a..b48a8fdba 100644 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt +++ b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -12,8 +12,6 @@ import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Int32Ring -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract import space.kscience.kmath.asm.compile as asmCompile import space.kscience.kmath.asm.compileToExpression as asmCompileToExpression @@ -43,7 +41,6 @@ private object PrimitiveAsmCompilerTestContext : CompilerTestContext { internal actual inline fun runCompilerTest(action: CompilerTestContext.() -> Unit) { - contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } action(GenericAsmCompilerTestContext) action(PrimitiveAsmCompilerTestContext) } From 3417d8cdc188a8151f2a3c90c3c9a40c515bdc41 Mon Sep 17 00:00:00 2001 From: "vasilev.ilya" Date: Mon, 20 May 2024 01:12:27 +0300 Subject: [PATCH 07/20] Minor edits. Tests added. | STUD-7 --- .../samplers/MetropolisHastingsSampler.kt | 18 +++--- .../samplers/TestMetropolisHastingsSampler.kt | 60 +++++++++++++++++-- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt index 74dfa822f..d8ded96eb 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt @@ -13,36 +13,38 @@ import space.kscience.kmath.structures.Float64Buffer import kotlin.math.* /** - * [Metropolis–Hastings algorithm](https://en.wikipedia.org/wiki/Metropolis%E2%80%93Hastings_algorithm) for sampling + * [Metropolis–Hastings algorithm](https://en.wikipedia.org/wiki/Metropolis-Hastings_algorithm) for sampling * target distribution [targetDist]. * + * The normal distribution is used as the proposal function. + * * params: * - targetDist: function close to the density of the sampled distribution; - * - initialState: initial value of the chain of sampled values. + * - initialState: initial value of the chain of sampled values; + * - proposalStd: standard deviation of the proposal function. */ public class MetropolisHastingsSampler( public val targetDist: (arg : Double) -> Double, public val initialState : Double = 0.0, + public val proposalStd : Double = 1.0, ) : BlockingDoubleSampler { override fun sample(generator: RandomGenerator): BlockingDoubleChain = object : BlockingDoubleChain { var currentState = initialState - fun proposalDist(arg : Double) = NormalDistribution(arg, 0.01) + fun proposalDist(arg : Double) = NormalDistribution(arg, proposalStd) override fun nextBufferBlocking(size: Int): Float64Buffer { val acceptanceProb = generator.nextDoubleBuffer(size) return Float64Buffer(size) {index -> - val newState = proposalDist(currentState).sample(RandomGenerator.default(0)).nextBufferBlocking(5).get(4) - val firstComp = targetDist(newState) / targetDist(currentState) - val secondComp = proposalDist(newState).probability(currentState) / proposalDist(currentState).probability(newState) - val acceptanceRatio = min(1.0, firstComp * secondComp) + val newState = proposalDist(currentState).sample(generator).nextBufferBlocking(1).get(0) + val acceptanceRatio = min(1.0, targetDist(newState) / targetDist(currentState)) currentState = if (acceptanceProb[index] <= acceptanceRatio) newState else currentState currentState } } - override suspend fun fork(): BlockingDoubleChain = BoxMullerSampler.sample(generator.fork()) + override suspend fun fork(): BlockingDoubleChain = sample(generator.fork()) } } \ No newline at end of file diff --git a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt index 7bc1eb877..9ab44f87d 100644 --- a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt @@ -9,18 +9,68 @@ import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.random.DefaultGenerator import space.kscience.kmath.stat.invoke import space.kscience.kmath.stat.mean +import kotlin.math.exp +import kotlin.math.pow import kotlin.test.Test import kotlin.test.assertEquals class TestMetropolisHastingsSampler { @Test - fun samplingNormalTest1() { - fun myDist(arg : Double) = NormalDistribution(0.0, 1.0).probability(arg) - val sampler = MetropolisHastingsSampler(::myDist) + fun samplingNormalTest() { + fun normalDist1(arg : Double) = NormalDistribution(0.5, 1.0).probability(arg) + var sampler = MetropolisHastingsSampler(::normalDist1, proposalStd = 1.0) + var sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) + assertEquals(0.5, Float64Field.mean(sampledValues), 1e-2) - val sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(10) - assertEquals(0.05, Float64Field.mean(sampledValues), 0.01) + fun normalDist2(arg : Double) = NormalDistribution(68.13, 1.0).probability(arg) + sampler = MetropolisHastingsSampler(::normalDist2, initialState = 63.0, proposalStd = 1.0) + sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) + + assertEquals(68.13, Float64Field.mean(sampledValues), 1e-2) + } + + @Test + fun samplingExponentialTest() { + fun expDist(arg : Double, param : Double) : Double { + if (arg < 0.0) { return 0.0 } + return param * exp(-param * arg) + } + + fun expDist1(arg : Double) = expDist(arg, 0.5) + var sampler = MetropolisHastingsSampler(::expDist1, initialState = 2.0, proposalStd = 1.0) + var sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) + + assertEquals(2.0, Float64Field.mean(sampledValues), 1e-2) + + fun expDist2(arg : Double) = expDist(arg, 2.0) + sampler = MetropolisHastingsSampler(::expDist2, initialState = 9.0, proposalStd = 1.0) + sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) + + assertEquals(0.5, Float64Field.mean(sampledValues), 1e-2) + + } + + @Test + fun samplingRayleighTest() { + fun rayleighDist(arg : Double, sigma : Double) : Double { + if (arg < 0.0) { return 0.0 } + + val expArg = (arg / sigma).pow(2) + return arg * exp(-expArg / 2.0) / sigma.pow(2) + } + + fun rayleighDist1(arg : Double) = rayleighDist(arg, 1.0) + var sampler = MetropolisHastingsSampler(::rayleighDist1, initialState = 2.0, proposalStd = 1.0) + var sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) + + assertEquals(1.25, Float64Field.mean(sampledValues), 1e-2) + + fun rayleighDist2(arg : Double) = rayleighDist(arg, 2.0) + sampler = MetropolisHastingsSampler(::rayleighDist2, proposalStd = 1.0) + sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(10_000_000) + + assertEquals(2.5, Float64Field.mean(sampledValues), 1e-2) } } \ No newline at end of file From c585c59552e895432b13105532ca00e8186f820b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 4 Jun 2024 10:31:42 +0300 Subject: [PATCH 08/20] 2.0.0 --- benchmarks/build.gradle.kts | 19 ++++++------------- buildSrc/build.gradle.kts | 8 +++----- gradle.properties | 2 +- .../kotlin/space/kscience/kmath/nd/ShapeND.kt | 4 +--- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 4894cd265..020ef745f 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -1,6 +1,5 @@ @file:Suppress("UNUSED_VARIABLE") -import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile import space.kscience.kmath.benchmarks.addBenchmarkProperties plugins { @@ -152,18 +151,12 @@ benchmark { } } -kotlin.sourceSets.all { - with(languageSettings) { - optIn("kotlin.contracts.ExperimentalContracts") - optIn("kotlin.ExperimentalUnsignedTypes") - optIn("space.kscience.kmath.UnstableKMathAPI") - } -} - -tasks.withType { - kotlinOptions { - jvmTarget = "11" - freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" + "-Xlambdas=indy" +kotlin { + jvmToolchain(11) + compilerOptions { + optIn.addAll( + "space.kscience.kmath.UnstableKMathAPI" + ) } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index c0f9d9631..beb2af7f6 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -26,10 +26,8 @@ dependencies { } kotlin { - jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } - sourceSets.all { - languageSettings.optIn("kotlin.OptIn") + jvmToolchain(11) + compilerOptions { + optIn.add("kotlin.OptIn") } } diff --git a/gradle.properties b/gradle.properties index 9a76404a9..2b3b0c9ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,6 +10,6 @@ org.gradle.jvmargs=-Xmx4096m org.gradle.parallel=true org.gradle.workers.max=4 -toolsVersion=0.15.2-kotlin-2.0.0-RC2 +toolsVersion=0.15.4-kotlin-2.0.0 #kotlin.experimental.tryK2=true #kscience.wasm.disabled=true \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeND.kt index d5cdc6286..3a99aef83 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeND.kt @@ -6,13 +6,11 @@ package space.kscience.kmath.nd import space.kscience.kmath.UnsafeKMathAPI -import kotlin.jvm.JvmInline /** * A read-only ND shape */ -@JvmInline -public value class ShapeND(@PublishedApi internal val array: IntArray) { +public class ShapeND(@PublishedApi internal val array: IntArray) { public val size: Int get() = array.size public operator fun get(index: Int): Int = array[index] override fun toString(): String = array.contentToString() From 6e24b563b2c296b6c560f1e82fd562147dc45fe5 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Jul 2024 11:06:20 +0300 Subject: [PATCH 09/20] optimize attributes plus --- .../kotlin/space/kscience/attributes/Attributes.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt index ab6185520..5eb93bfc0 100644 --- a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt @@ -140,4 +140,8 @@ public fun > Attributes( attribute: A, ): Attributes = MapAttributes(mapOf(attribute to Unit)) -public operator fun Attributes.plus(other: Attributes): Attributes = MapAttributes(content + other.content) \ No newline at end of file +public operator fun Attributes.plus(other: Attributes): Attributes = when{ + isEmpty() -> other + other.isEmpty() -> this + else -> MapAttributes(content + other.content) +} \ No newline at end of file From 3e8f44166c4a4ed6e99e1676a7200ed044645224 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Jul 2024 11:02:49 +0300 Subject: [PATCH 10/20] add Attributes minus --- .../space/kscience/attributes/Attributes.kt | 14 +++++++-- benchmarks/build.gradle.kts | 2 +- kmath-ast/build.gradle.kts | 30 +++++++++---------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt index 5eb93bfc0..936cf2ee2 100644 --- a/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt +++ b/attributes-kt/src/commonMain/kotlin/space/kscience/attributes/Attributes.kt @@ -140,8 +140,18 @@ public fun > Attributes( attribute: A, ): Attributes = MapAttributes(mapOf(attribute to Unit)) -public operator fun Attributes.plus(other: Attributes): Attributes = when{ +/** + * Create a new [Attributes] that overlays [other] on top of this set of attributes. New attributes are added. + * Existing attribute keys are replaced. + */ +public operator fun Attributes.plus(other: Attributes): Attributes = when { isEmpty() -> other other.isEmpty() -> this else -> MapAttributes(content + other.content) -} \ No newline at end of file +} + +/** + * Create a new [Attributes] with removed [key] (if it is present). + */ +public operator fun Attributes.minus(key: Attribute<*>): Attributes = + if (content.contains(key)) MapAttributes(content.minus(key)) else this \ No newline at end of file diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 020ef745f..9e0d095d6 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -80,7 +80,7 @@ benchmark { // Setup configurations targets { register("jvm") -// register("js") + register("js") } fun kotlinx.benchmark.gradle.BenchmarkConfiguration.commonConfiguration() { diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index a770912f3..2cd0979ae 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -4,7 +4,20 @@ plugins { kscience { jvm() - js() + js{ + nodejs { + testTask { + useMocha().timeout = "0" + } + } + + browser { + useCommonJs() + testTask { + useMocha().timeout = "0" + } + } + } native() dependencies { @@ -18,7 +31,7 @@ kscience { dependencies(jsMain) { implementation(npm("astring", "1.7.5")) - implementation(npm("binaryen", "101.0.0")) + implementation(npm("binaryen", "117.0.0")) implementation(npm("js-base64", "3.6.1")) } @@ -29,19 +42,6 @@ kscience { } kotlin { - js { - nodejs { - testTask { - useMocha().timeout = "0" - } - } - - browser { - testTask { - useMocha().timeout = "0" - } - } - } sourceSets { filter { it.name.contains("test", true) } From 57d1cd8c87a43aa5406459a654b8d17316805acc Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 4 Aug 2024 15:01:47 +0300 Subject: [PATCH 11/20] Add Metropolis-Hastings sampler. Minor fixes. --- CHANGELOG.md | 1 + gradle/libs.versions.toml | 9 ++ .../kscience/kmath/chains/BlockingChain.kt | 4 +- .../space/kscience/kmath/chains/Chain.kt | 13 +- kmath-stat/build.gradle.kts | 18 ++- .../samplers/MetropolisHastingsSampler.kt | 86 +++++++++----- .../space/kscience/kmath/samplers/Sampler.kt | 2 +- .../samplers/TestMetropolisHastingsSampler.kt | 111 +++++++++++------- 8 files changed, 152 insertions(+), 92 deletions(-) create mode 100644 gradle/libs.versions.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bf936c10..6c2e987f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ### Added +- Metropolis-Hastings sampler ### Changed diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..9796ed176 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,9 @@ +[versions] + +commons-rng = "1.6" + + +[libraries] + +commons-rng-simple = {module ="org.apache.commons:commons-rng-simple", version.ref = "commons-rng"} +commons-rng-sampling = {module ="org.apache.commons:commons-rng-sampling", version.ref = "commons-rng"} \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingChain.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingChain.kt index f843e8af8..d58da67a5 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingChain.kt @@ -40,13 +40,13 @@ public interface BlockingBufferChain : BlockingChain, BufferChain { } -public suspend inline fun Chain.nextBuffer(size: Int): Buffer = if (this is BufferChain) { +public suspend inline fun Chain.nextBuffer(size: Int): Buffer = if (this is BufferChain) { nextBuffer(size) } else { Buffer(size) { next() } } -public inline fun BlockingChain.nextBufferBlocking( +public inline fun BlockingChain.nextBufferBlocking( size: Int, ): Buffer = if (this is BlockingBufferChain) { nextBufferBlocking(size) diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/Chain.kt index 0158c6752..3927795e3 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/Chain.kt @@ -23,7 +23,7 @@ public interface Chain : Flow { public suspend fun next(): T /** - * Create a copy of current chain state. Consuming resulting chain does not affect initial chain. + * Create a copy of the current chain state. Consuming the resulting chain does not affect the initial chain. */ public suspend fun fork(): Chain @@ -47,7 +47,7 @@ public class SimpleChain(private val gen: suspend () -> R) : Chain { /** * A stateless Markov chain */ -public class MarkovChain(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain { +public class MarkovChain(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain { private val mutex: Mutex = Mutex() private var value: R? = null @@ -71,8 +71,8 @@ public class MarkovChain(private val seed: suspend () -> R, private */ public class StatefulChain( private val state: S, - private val seed: S.() -> R, - private val forkState: ((S) -> S), + private val seed: suspend S.() -> R, + private val forkState: suspend ((S) -> S), private val gen: suspend S.(R) -> R, ) : Chain { private val mutex: Mutex = Mutex() @@ -97,6 +97,11 @@ public class ConstantChain(public val value: T) : Chain { override suspend fun fork(): Chain = this } +/** + * Discard a fixed number of samples + */ +public suspend inline fun Chain.discard(number: Int): Chain = apply { nextBuffer(number) } + /** * Map the chain result using suspended transformation. Initial chain result can no longer be safely consumed * since mapped chain consumes tokens. Accepts regular transformation function. diff --git a/kmath-stat/build.gradle.kts b/kmath-stat/build.gradle.kts index 8116a8925..5552ed9a6 100644 --- a/kmath-stat/build.gradle.kts +++ b/kmath-stat/build.gradle.kts @@ -7,21 +7,17 @@ kscience { js() native() wasm() -} -kotlin.sourceSets { + useCoroutines() + commonMain { - dependencies { - api(projects.kmathCoroutines) - //implementation(spclibs.atomicfu) - } + api(projects.kmathCoroutines) + //implementation(spclibs.atomicfu) } - getByName("jvmMain") { - dependencies { - api("org.apache.commons:commons-rng-sampling:1.3") - api("org.apache.commons:commons-rng-simple:1.3") - } + jvmMain { + api(libs.commons.rng.simple) + api(libs.commons.rng.sampling) } } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt index d8ded96eb..4e00bbad2 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt @@ -5,46 +5,72 @@ package space.kscience.kmath.samplers -import space.kscience.kmath.chains.BlockingDoubleChain -import space.kscience.kmath.distributions.Distribution1D -import space.kscience.kmath.distributions.NormalDistribution +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.chains.StatefulChain +import space.kscience.kmath.operations.Group +import space.kscience.kmath.operations.algebra import space.kscience.kmath.random.RandomGenerator -import space.kscience.kmath.structures.Float64Buffer -import kotlin.math.* +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.structures.Float64 /** * [Metropolis–Hastings algorithm](https://en.wikipedia.org/wiki/Metropolis-Hastings_algorithm) for sampling - * target distribution [targetDist]. + * target distribution [targetPdf]. * - * The normal distribution is used as the proposal function. - * - * params: - * - targetDist: function close to the density of the sampled distribution; - * - initialState: initial value of the chain of sampled values; - * - proposalStd: standard deviation of the proposal function. + * @param stepSampler a sampler for proposal point (offset to previous point) */ -public class MetropolisHastingsSampler( - public val targetDist: (arg : Double) -> Double, - public val initialState : Double = 0.0, - public val proposalStd : Double = 1.0, -) : BlockingDoubleSampler { - override fun sample(generator: RandomGenerator): BlockingDoubleChain = object : BlockingDoubleChain { - var currentState = initialState - fun proposalDist(arg : Double) = NormalDistribution(arg, proposalStd) +public class MetropolisHastingsSampler( + public val algebra: Group, + public val startPoint: T, + public val stepSampler: Sampler, + public val targetPdf: suspend (T) -> Float64, +) : Sampler { - override fun nextBufferBlocking(size: Int): Float64Buffer { - val acceptanceProb = generator.nextDoubleBuffer(size) + //TODO consider API for conditional step probability - return Float64Buffer(size) {index -> - val newState = proposalDist(currentState).sample(generator).nextBufferBlocking(1).get(0) - val acceptanceRatio = min(1.0, targetDist(newState) / targetDist(currentState)) - - currentState = if (acceptanceProb[index] <= acceptanceRatio) newState else currentState - currentState + override fun sample(generator: RandomGenerator): Chain = StatefulChain, T>( + state = stepSampler.sample(generator), + seed = { startPoint }, + forkState = Chain::fork + ) { previousPoint: T -> + val proposalPoint = with(algebra) { previousPoint + next() } + val ratio = targetPdf(proposalPoint) / targetPdf(previousPoint) + if (ratio >= 1.0) { + proposalPoint + } else { + val acceptanceProbability = generator.nextDouble() + if (acceptanceProbability <= ratio) { + proposalPoint + } else { + previousPoint } } - - override suspend fun fork(): BlockingDoubleChain = sample(generator.fork()) } + + public companion object { + + /** + * A Metropolis-Hastings sampler for univariate [Float64] values + */ + public fun univariate( + startPoint: Float64, + stepSampler: Sampler, + targetPdf: suspend (Float64) -> Float64, + ): MetropolisHastingsSampler = MetropolisHastingsSampler( + algebra = Float64.algebra, + startPoint = startPoint, + stepSampler = stepSampler, + targetPdf = targetPdf + ) + + /** + * A Metropolis-Hastings sampler for univariate [Float64] values with normal step distribution + */ + public fun univariateNormal( + startPoint: Float64, + stepSigma: Float64, + targetPdf: suspend (Float64) -> Float64, + ): MetropolisHastingsSampler = univariate(startPoint, GaussianSampler(0.0, stepSigma), targetPdf) + } } \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt index 44e099a52..9bc1a6bc2 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt @@ -17,7 +17,7 @@ import kotlin.jvm.JvmName /** * Sampler that generates chains of values of type [T]. */ -public fun interface Sampler { +public fun interface Sampler { /** * Generates a chain of samples. * diff --git a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt index 9ab44f87d..b03b0c4c1 100644 --- a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt @@ -4,73 +4,96 @@ */ package space.kscience.kmath.samplers + +import kotlinx.coroutines.test.runTest +import space.kscience.kmath.chains.discard +import space.kscience.kmath.chains.nextBuffer import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.operations.Float64Field -import space.kscience.kmath.random.DefaultGenerator +import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.invoke import space.kscience.kmath.stat.mean +import kotlin.math.PI import kotlin.math.exp import kotlin.math.pow +import kotlin.math.sqrt import kotlin.test.Test import kotlin.test.assertEquals class TestMetropolisHastingsSampler { + data class TestSetup(val mean: Double, val startPoint: Double, val sigma: Double = 0.5) + + val sample = 1e6.toInt() + val burnIn = sample / 5 + @Test - fun samplingNormalTest() { - fun normalDist1(arg : Double) = NormalDistribution(0.5, 1.0).probability(arg) - var sampler = MetropolisHastingsSampler(::normalDist1, proposalStd = 1.0) - var sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) + fun samplingNormalTest() = runTest { + val generator = RandomGenerator.default(1) - assertEquals(0.5, Float64Field.mean(sampledValues), 1e-2) + listOf( + TestSetup(0.5, 0.0), + TestSetup(68.13, 60.0), + ).forEach { + val distribution = NormalDistribution(it.mean, 1.0) + val sampler = MetropolisHastingsSampler + .univariateNormal(it.startPoint, it.sigma, distribution::probability) + val sampledValues = sampler.sample(generator).discard(burnIn).nextBuffer(sample) - fun normalDist2(arg : Double) = NormalDistribution(68.13, 1.0).probability(arg) - sampler = MetropolisHastingsSampler(::normalDist2, initialState = 63.0, proposalStd = 1.0) - sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) - - assertEquals(68.13, Float64Field.mean(sampledValues), 1e-2) + assertEquals(it.mean, Float64Field.mean(sampledValues), 1e-2) + } } @Test - fun samplingExponentialTest() { - fun expDist(arg : Double, param : Double) : Double { - if (arg < 0.0) { return 0.0 } - return param * exp(-param * arg) + fun samplingExponentialTest() = runTest { + val generator = RandomGenerator.default(1) + + fun expDist(lambda: Double, arg: Double): Double = if (arg <= 0.0) 0.0 else lambda * exp(-arg * lambda) + + listOf( + TestSetup(0.5, 2.0), + TestSetup(2.0, 1.0) + ).forEach { setup -> + val sampler = MetropolisHastingsSampler.univariateNormal(setup.startPoint, setup.sigma) { + expDist(setup.mean, it) + } + val sampledValues = sampler.sample(generator).discard(burnIn).nextBuffer(sample) + + assertEquals(1.0 / setup.mean, Float64Field.mean(sampledValues), 1e-2) } - - fun expDist1(arg : Double) = expDist(arg, 0.5) - var sampler = MetropolisHastingsSampler(::expDist1, initialState = 2.0, proposalStd = 1.0) - var sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) - - assertEquals(2.0, Float64Field.mean(sampledValues), 1e-2) - - fun expDist2(arg : Double) = expDist(arg, 2.0) - sampler = MetropolisHastingsSampler(::expDist2, initialState = 9.0, proposalStd = 1.0) - sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) - - assertEquals(0.5, Float64Field.mean(sampledValues), 1e-2) - } @Test - fun samplingRayleighTest() { - fun rayleighDist(arg : Double, sigma : Double) : Double { - if (arg < 0.0) { return 0.0 } - - val expArg = (arg / sigma).pow(2) - return arg * exp(-expArg / 2.0) / sigma.pow(2) + fun samplingRayleighTest() = runTest { + val generator = RandomGenerator.default(1) + fun rayleighDist(sigma: Double, arg: Double): Double = if (arg < 0.0) { + 0.0 + } else { + arg * exp(-(arg / sigma).pow(2) / 2.0) / sigma.pow(2) } - fun rayleighDist1(arg : Double) = rayleighDist(arg, 1.0) - var sampler = MetropolisHastingsSampler(::rayleighDist1, initialState = 2.0, proposalStd = 1.0) - var sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) + listOf( + TestSetup(0.5, 1.0), + TestSetup(2.0, 1.0) + ).forEach { setup -> + val sampler = MetropolisHastingsSampler.univariateNormal(setup.startPoint, setup.sigma) { + rayleighDist(setup.mean, it) + } + val sampledValues = sampler.sample(generator).discard(burnIn).nextBuffer(sample) - assertEquals(1.25, Float64Field.mean(sampledValues), 1e-2) - - fun rayleighDist2(arg : Double) = rayleighDist(arg, 2.0) - sampler = MetropolisHastingsSampler(::rayleighDist2, proposalStd = 1.0) - sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(10_000_000) - - assertEquals(2.5, Float64Field.mean(sampledValues), 1e-2) + assertEquals(setup.mean * sqrt(PI / 2), Float64Field.mean(sampledValues), 1e-2) + } +// +// fun rayleighDist1(arg: Double) = rayleighDist(arg, 1.0) +// var sampler = MetropolisHastingsSampler(::rayleighDist1, initialPoint = 2.0, proposalStd = 1.0) +// var sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) +// +// assertEquals(1.25, Float64Field.mean(sampledValues), 1e-2) +// +// fun rayleighDist2(arg: Double) = rayleighDist(arg, 2.0) +// sampler = MetropolisHastingsSampler(::rayleighDist2, proposalStd = 1.0) +// sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(10_000_000) +// +// assertEquals(2.5, Float64Field.mean(sampledValues), 1e-2) } } \ No newline at end of file From 48d0ee81268eba45e9a30fc4ee903a1310675c87 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 4 Aug 2024 21:26:51 +0300 Subject: [PATCH 12/20] Add Metropolis-Hastings sampler. Minor fixes. --- .../samplers/TestMetropolisHastingsSampler.kt | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt index b03b0c4c1..3011687ef 100644 --- a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt @@ -24,8 +24,8 @@ class TestMetropolisHastingsSampler { data class TestSetup(val mean: Double, val startPoint: Double, val sigma: Double = 0.5) - val sample = 1e6.toInt() - val burnIn = sample / 5 + private val sample = 1e6.toInt() + private val burnIn = sample / 5 @Test fun samplingNormalTest() = runTest { @@ -66,6 +66,7 @@ class TestMetropolisHastingsSampler { @Test fun samplingRayleighTest() = runTest { val generator = RandomGenerator.default(1) + fun rayleighDist(sigma: Double, arg: Double): Double = if (arg < 0.0) { 0.0 } else { @@ -83,17 +84,5 @@ class TestMetropolisHastingsSampler { assertEquals(setup.mean * sqrt(PI / 2), Float64Field.mean(sampledValues), 1e-2) } -// -// fun rayleighDist1(arg: Double) = rayleighDist(arg, 1.0) -// var sampler = MetropolisHastingsSampler(::rayleighDist1, initialPoint = 2.0, proposalStd = 1.0) -// var sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(1_000_000) -// -// assertEquals(1.25, Float64Field.mean(sampledValues), 1e-2) -// -// fun rayleighDist2(arg: Double) = rayleighDist(arg, 2.0) -// sampler = MetropolisHastingsSampler(::rayleighDist2, proposalStd = 1.0) -// sampledValues = sampler.sample(DefaultGenerator()).nextBufferBlocking(10_000_000) -// -// assertEquals(2.5, Float64Field.mean(sampledValues), 1e-2) } } \ No newline at end of file From 6619db3f455da670a3cf363141b8b57c6dade365 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 9 Aug 2024 10:22:37 +0300 Subject: [PATCH 13/20] Reimplement random-forging chain --- benchmarks/build.gradle.kts | 4 +- build.gradle.kts | 4 +- examples/build.gradle.kts | 4 +- gradle/libs.versions.toml | 9 ++- kmath-multik/build.gradle.kts | 6 +- .../kmath/samplers/RandomForkingEvent.kt | 71 +++++++++++++++++++ 6 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 9e0d095d6..b0446946a 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -15,8 +15,6 @@ repositories { mavenCentral() } -val multikVersion: String by rootProject.extra - kotlin { jvm() @@ -45,7 +43,7 @@ kotlin { implementation(project(":kmath-for-real")) implementation(project(":kmath-tensors")) implementation(project(":kmath-multik")) - implementation("org.jetbrains.kotlinx:multik-default:$multikVersion") + implementation(libs.multik.default) implementation(spclibs.kotlinx.benchmark.runtime) } } diff --git a/build.gradle.kts b/build.gradle.kts index 57ff022f2..1b4b4b3ab 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ import space.kscience.gradle.useSPCTeam plugins { id("space.kscience.gradle.project") - id("org.jetbrains.kotlinx.kover") version "0.7.6" + alias(spclibs.plugins.kotlinx.kover) } val attributesVersion by extra("0.2.0") @@ -70,5 +70,3 @@ ksciencePublish { } apiValidation.nonPublicMarkers.add("space.kscience.kmath.UnstableKMathAPI") - -val multikVersion by extra("0.2.3") diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index b74d6edca..74ff891d5 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -10,8 +10,6 @@ repositories { maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers") } -val multikVersion: String by rootProject.extra - dependencies { implementation(project(":kmath-ast")) implementation(project(":kmath-kotlingrad")) @@ -33,7 +31,7 @@ dependencies { implementation(project(":kmath-jafama")) //multik implementation(project(":kmath-multik")) - implementation("org.jetbrains.kotlinx:multik-default:$multikVersion") + implementation(libs.multik.default) //datetime implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9796ed176..e1add1777 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,12 @@ [versions] commons-rng = "1.6" - +multik = "0.2.3" [libraries] -commons-rng-simple = {module ="org.apache.commons:commons-rng-simple", version.ref = "commons-rng"} -commons-rng-sampling = {module ="org.apache.commons:commons-rng-sampling", version.ref = "commons-rng"} \ No newline at end of file +commons-rng-simple = { module = "org.apache.commons:commons-rng-simple", version.ref = "commons-rng" } +commons-rng-sampling = { module = "org.apache.commons:commons-rng-sampling", version.ref = "commons-rng" } + +multik-core = { module = "org.jetbrains.kotlinx:multik-core", version.ref = "multik" } +multik-default = { module = "org.jetbrains.kotlinx:multik-default", version.ref = "multik" } \ No newline at end of file diff --git a/kmath-multik/build.gradle.kts b/kmath-multik/build.gradle.kts index e3f27effe..941ac403d 100644 --- a/kmath-multik/build.gradle.kts +++ b/kmath-multik/build.gradle.kts @@ -4,8 +4,6 @@ plugins { description = "JetBrains Multik connector" -val multikVersion: String by rootProject.extra - kscience { jvm() js() @@ -16,12 +14,12 @@ kotlin { commonMain { dependencies { api(projects.kmathTensors) - api("org.jetbrains.kotlinx:multik-core:$multikVersion") + api(libs.multik.core) } } commonTest { dependencies { - api("org.jetbrains.kotlinx:multik-default:$multikVersion") + api(libs.multik.default) } } } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt new file mode 100644 index 000000000..b4d56cf73 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt @@ -0,0 +1,71 @@ +/* + * 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package space.kscience.kmath.samplers + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.random.RandomGenerator +import space.kscience.kmath.stat.Sampler +import kotlin.coroutines.coroutineContext + + +/** + * A sampler that creates a chain that could be split at each computation + */ +public class RandomForkingSampler( + private val scope: CoroutineScope, + private val initialValue: T, + private val makeStep: suspend RandomGenerator.(T) -> List +) : Sampler { + + override fun sample(generator: RandomGenerator): Chain = buildChain(scope, initialValue) { generator.makeStep(it) } + + public companion object { + private suspend fun Channel.receiveEvents( + initial: T, + makeStep: suspend (T) -> List + ) { + send(initial) + //inner dispatch queue + val innerChannel = Channel(50) + innerChannel.send(initial) + while (coroutineContext.isActive && !innerChannel.isEmpty) { + val current = innerChannel.receive() + //add event immediately, but it does not mean that the value is computed immediately as well + makeStep(current).forEach { + innerChannel.send(it) + send(it) + } + } + innerChannel.close() + close() + } + + + public fun buildChain( + scope: CoroutineScope, + initial: T, + makeStep: suspend (T) -> List + ): Chain { + val channel = Channel(Channel.RENDEZVOUS) + scope.launch { + channel.receiveEvents(initial, makeStep) + } + + return object : Chain { + override suspend fun next(): T? = channel.receiveCatching().getOrNull() + + override suspend fun fork(): Chain = buildChain(scope, channel.receive(), makeStep) + } + } + } +} From 2becee7f596c218c2263957efcbd988247b557f9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 9 Aug 2024 22:14:24 +0300 Subject: [PATCH 14/20] Reimplement random-forking chain --- .../kmath/samplers/RandomForkingEvent.kt | 82 +++++++++++++++++-- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt index b4d56cf73..9260b67fe 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt @@ -8,35 +8,51 @@ package space.kscience.kmath.samplers import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.chains.Chain +import space.kscience.kmath.operations.Group +import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.structures.Float64 import kotlin.coroutines.coroutineContext +@UnstableKMathAPI +public data class RandomForkingSample( + val value: Deferred, + val generation: Int, + val energy: Float64, + val stepChain: Chain +) /** * A sampler that creates a chain that could be split at each computation */ -public class RandomForkingSampler( +@UnstableKMathAPI +public class RandomForkingSampler( private val scope: CoroutineScope, - private val initialValue: T, + private val initialValue: suspend (RandomGenerator) -> T, private val makeStep: suspend RandomGenerator.(T) -> List ) : Sampler { - override fun sample(generator: RandomGenerator): Chain = buildChain(scope, initialValue) { generator.makeStep(it) } + override fun sample(generator: RandomGenerator): Chain = + buildChain(scope, initial = { initialValue(generator) }) { generator.makeStep(it) } public companion object { private suspend fun Channel.receiveEvents( initial: T, + buffer: Int = 50, makeStep: suspend (T) -> List ) { send(initial) //inner dispatch queue - val innerChannel = Channel(50) + val innerChannel = Channel(buffer) innerChannel.send(initial) while (coroutineContext.isActive && !innerChannel.isEmpty) { val current = innerChannel.receive() @@ -51,21 +67,71 @@ public class RandomForkingSampler( } - public fun buildChain( + internal fun buildChain( scope: CoroutineScope, - initial: T, + initial: suspend () -> T, makeStep: suspend (T) -> List ): Chain { val channel = Channel(Channel.RENDEZVOUS) scope.launch { - channel.receiveEvents(initial, makeStep) + channel.receiveEvents(initial(), makeStep = makeStep) } return object : Chain { override suspend fun next(): T? = channel.receiveCatching().getOrNull() - override suspend fun fork(): Chain = buildChain(scope, channel.receive(), makeStep) + override suspend fun fork(): Chain = buildChain(scope, { channel.receive() }, makeStep) } } + + + public fun metropolisHastings( + scope: CoroutineScope, + algebra: A, + startPoint: suspend (RandomGenerator) -> T, + stepSampler: Sampler, + initialEnergy: Float64, + energySplitRule: suspend RandomForkingSample.() -> List, + stepScaleRule: suspend (Float64) -> Float64 = { 1.0 }, + targetPdf: suspend (T) -> Float64, + ): RandomForkingSampler> where A : Group, A : ScaleOperations = + RandomForkingSampler>( + scope = scope, + initialValue = { generator -> + RandomForkingSample( + value = scope.async { startPoint(generator) }, + generation = 0, + energy = initialEnergy, + stepChain = stepSampler.sample(generator) + ) + } + ) { previousSample: RandomForkingSample -> + val value = previousSample.value.await() + previousSample.energySplitRule().map { energy -> + RandomForkingSample( + value = scope.async { + val proposalPoint = with(algebra) { + value + previousSample.stepChain.next() * stepScaleRule(previousSample.energy) + } + val ratio = targetPdf(proposalPoint) / targetPdf(value) + + if (ratio >= 1.0) { + proposalPoint + } else { + val acceptanceProbability = nextDouble() + if (acceptanceProbability <= ratio) { + proposalPoint + } else { + value + } + } + }, + generation = previousSample.generation + 1, + energy = 0.0, + stepChain = previousSample.stepChain + ) + } + } + } } From 91513a1629b110ca0632eaa6fa110234c38ab527 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 14 Aug 2024 19:20:05 +0300 Subject: [PATCH 15/20] Reimplement random-forking chain --- .../kscience/kmath/linear/matrixAttributes.kt | 2 +- kmath-ejml/build.gradle.kts | 5 +---- .../kmath/geometry/euclidean2d/Circle2D.kt | 2 +- .../distributions/FactorizedDistribution.kt | 11 ++++++---- .../kmath/distributions/NormalDistribution.kt | 15 ++++++++++++- .../kscience/kmath/samplers/InternalErf.kt | 20 ----------------- .../samplers/MetropolisHastingsSampler.kt | 6 ++--- .../kmath/samplers/RandomForkingEvent.kt | 22 +++++++++---------- .../samplers/TestMetropolisHastingsSampler.kt | 3 +++ 9 files changed, 40 insertions(+), 46 deletions(-) delete mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalErf.kt diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/matrixAttributes.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/matrixAttributes.kt index dadb2f3d7..fdb7e318b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/matrixAttributes.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/matrixAttributes.kt @@ -124,7 +124,7 @@ public val MatrixScope.QR: QRDecompositionAttribute public interface CholeskyDecomposition { /** - * The triangular matrix in this decomposition. It may have either [UpperTriangular] or [LowerTriangular]. + * The lower triangular matrix in this decomposition. It should have [LowerTriangular]. */ public val l: Matrix } diff --git a/kmath-ejml/build.gradle.kts b/kmath-ejml/build.gradle.kts index 54b8f7038..50416eaf3 100644 --- a/kmath-ejml/build.gradle.kts +++ b/kmath-ejml/build.gradle.kts @@ -5,10 +5,7 @@ plugins { val ejmlVerision = "0.43.1" dependencies { - 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("org.ejml:ejml-all:$ejmlVerision") api(projects.kmathCore) } diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt index fcffc7714..ca9a37dcb 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt @@ -27,7 +27,7 @@ public data class Float64Circle2D( override val radius: Float64, ) : Circle2D -public fun Circle2D(center: Vector2D, radius: Double): Circle2D = Float64Circle2D( +public fun Circle2D(center: Vector2D, radius: Double): Float64Circle2D = Float64Circle2D( center as? Float64Vector2D ?: Float64Vector2D(center.x, center.y), radius ) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/FactorizedDistribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/FactorizedDistribution.kt index 8f5a378bb..3e0aa1c06 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/FactorizedDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/FactorizedDistribution.kt @@ -17,8 +17,9 @@ public interface NamedDistribution : Distribution> /** * A multivariate distribution that has independent distributions for separate axis. */ -public class FactorizedDistribution(public val distributions: Collection>) : - NamedDistribution { +public class FactorizedDistribution( + public val distributions: Collection> +) : NamedDistribution { override fun probability(arg: Map): Double = distributions.fold(1.0) { acc, dist -> acc * dist.probability(arg) } @@ -28,8 +29,10 @@ public class FactorizedDistribution(public val distributions: Collection(public val name: String, public val distribution: Distribution) : - NamedDistribution { +public class NamedDistributionWrapper( + public val name: String, + public val distribution: Distribution +) : NamedDistribution { override fun probability(arg: Map): Double = distribution.probability( arg[name] ?: error("Argument with name $name not found in input parameters") ) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt index f425c6285..89f1dd8d7 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt @@ -9,11 +9,24 @@ import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.operations.Float64Field.pow import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.samplers.GaussianSampler -import space.kscience.kmath.samplers.InternalErf +import space.kscience.kmath.samplers.InternalGamma import space.kscience.kmath.samplers.NormalizedGaussianSampler import space.kscience.kmath.samplers.ZigguratNormalizedGaussianSampler import kotlin.math.* + +/** + * Based on Commons Math implementation. + * See [https://commons.apache.org/proper/commons-math/javadocs/api-3.3/org/apache/commons/math3/special/Erf.html]. + */ +internal object InternalErf { + fun erfc(x: Double): Double { + if (abs(x) > 40) return if (x > 0) 0.0 else 2.0 + val ret = InternalGamma.regularizedGammaQ(0.5, x * x, 10000) + return if (x < 0) 2 - ret else ret + } +} + /** * Implements [Distribution1D] for the normal (gaussian) distribution. */ diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalErf.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalErf.kt deleted file mode 100644 index c28b4be50..000000000 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/InternalErf.kt +++ /dev/null @@ -1,20 +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.samplers - -import kotlin.math.abs - -/** - * Based on Commons Math implementation. - * See [https://commons.apache.org/proper/commons-math/javadocs/api-3.3/org/apache/commons/math3/special/Erf.html]. - */ -internal object InternalErf { - fun erfc(x: Double): Double { - if (abs(x) > 40) return if (x > 0) 0.0 else 2.0 - val ret = InternalGamma.regularizedGammaQ(0.5, x * x, 10000) - return if (x < 0) 2 - ret else ret - } -} \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt index 4e00bbad2..a6bba910b 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt @@ -21,7 +21,7 @@ import space.kscience.kmath.structures.Float64 */ public class MetropolisHastingsSampler( public val algebra: Group, - public val startPoint: T, + public val startPoint: suspend (RandomGenerator) ->T, public val stepSampler: Sampler, public val targetPdf: suspend (T) -> Float64, ) : Sampler { @@ -30,7 +30,7 @@ public class MetropolisHastingsSampler( override fun sample(generator: RandomGenerator): Chain = StatefulChain, T>( state = stepSampler.sample(generator), - seed = { startPoint }, + seed = { startPoint(generator) }, forkState = Chain::fork ) { previousPoint: T -> val proposalPoint = with(algebra) { previousPoint + next() } @@ -59,7 +59,7 @@ public class MetropolisHastingsSampler( targetPdf: suspend (Float64) -> Float64, ): MetropolisHastingsSampler = MetropolisHastingsSampler( algebra = Float64.algebra, - startPoint = startPoint, + startPoint = {startPoint}, stepSampler = stepSampler, targetPdf = targetPdf ) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt index 9260b67fe..a9331276c 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt @@ -7,13 +7,8 @@ package space.kscience.kmath.samplers -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.async +import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.chains.Chain import space.kscience.kmath.operations.Group @@ -28,6 +23,7 @@ public data class RandomForkingSample( val value: Deferred, val generation: Int, val energy: Float64, + val generator: RandomGenerator, val stepChain: Chain ) @@ -38,11 +34,11 @@ public data class RandomForkingSample( public class RandomForkingSampler( private val scope: CoroutineScope, private val initialValue: suspend (RandomGenerator) -> T, - private val makeStep: suspend RandomGenerator.(T) -> List + private val makeStep: suspend (T) -> List ) : Sampler { override fun sample(generator: RandomGenerator): Chain = - buildChain(scope, initial = { initialValue(generator) }) { generator.makeStep(it) } + buildChain(scope, initial = { initialValue(generator) }) { makeStep(it) } public companion object { private suspend fun Channel.receiveEvents( @@ -95,13 +91,14 @@ public class RandomForkingSampler( stepScaleRule: suspend (Float64) -> Float64 = { 1.0 }, targetPdf: suspend (T) -> Float64, ): RandomForkingSampler> where A : Group, A : ScaleOperations = - RandomForkingSampler>( + RandomForkingSampler( scope = scope, initialValue = { generator -> RandomForkingSample( value = scope.async { startPoint(generator) }, generation = 0, energy = initialEnergy, + generator = generator, stepChain = stepSampler.sample(generator) ) } @@ -111,14 +108,14 @@ public class RandomForkingSampler( RandomForkingSample( value = scope.async { val proposalPoint = with(algebra) { - value + previousSample.stepChain.next() * stepScaleRule(previousSample.energy) + value + previousSample.stepChain.next() * stepScaleRule(energy) } val ratio = targetPdf(proposalPoint) / targetPdf(value) if (ratio >= 1.0) { proposalPoint } else { - val acceptanceProbability = nextDouble() + val acceptanceProbability = previousSample.generator.nextDouble() if (acceptanceProbability <= ratio) { proposalPoint } else { @@ -127,7 +124,8 @@ public class RandomForkingSampler( } }, generation = previousSample.generation + 1, - energy = 0.0, + energy = energy, + generator = previousSample.generator, stepChain = previousSample.stepChain ) } diff --git a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt index 3011687ef..1261c75e5 100644 --- a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt @@ -85,4 +85,7 @@ class TestMetropolisHastingsSampler { assertEquals(setup.mean * sqrt(PI / 2), Float64Field.mean(sampledValues), 1e-2) } } + + + } \ No newline at end of file From b818a8981f6cccfccff1444bcd7b6055f6183078 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 17 Aug 2024 21:11:13 +0300 Subject: [PATCH 16/20] Eigenvalue decomposition API Cosmetic change Double -> Float64 --- CHANGELOG.md | 2 +- .../ExpressionsInterpretersBenchmark.kt | 5 +- .../ExpressionsInterpretersBenchmark.kt | 7 +- .../kmath/benchmarks/NDFieldBenchmark.kt | 11 +- .../kmath/benchmarks/ViktorBenchmark.kt | 3 +- docs/nd-structure.md | 4 +- .../kscience/kmath/functions/integrate.kt | 3 +- .../kscience/kmath/functions/interpolate.kt | 3 +- .../kmath/functions/interpolateSquare.kt | 5 +- .../kmath/functions/matrixIntegration.kt | 3 +- .../space/kscience/kmath/linear/gradient.kt | 5 +- .../kmath/operations/mixedNDOperations.kt | 3 +- .../space/kscience/kmath/series/analyzeDif.kt | 7 +- .../kscience/kmath/series/seriesBuilder.kt | 9 +- .../kmath/stat/DistributionBenchmark.kt | 2 +- .../kscience/kmath/stat/DistributionDemo.kt | 3 +- .../kscience/kmath/structures/ComplexND.kt | 2 +- .../kscience/kmath/structures/NDField.kt | 10 +- .../kmath/structures/StreamDoubleFieldND.kt | 64 +++--- .../kscience/kmath/structures/mutableNd.kt | 2 +- .../StreamingLm/streamLm.kt | 7 +- .../LevenbergMarquardt/functionsToOptimize.kt | 35 ++-- .../space/kscience/kmath/tensors/multik.kt | 3 +- .../space/kscience/kmath/ast/TestFolding.kt | 3 +- .../kotlin/space/kscience/kmath/ast/utils.kt | 3 +- .../kmath/wasm/internal/WasmBuilder.kt | 9 +- .../kotlin/space/kscience/kmath/wasm/wasm.kt | 3 +- .../kotlin/space/kscience/kmath/ast/utils.kt | 5 +- .../kmath/asm/internal/PrimitiveAsmBuilder.kt | 7 +- .../kotlin/space/kscience/kmath/ast/utils.kt | 9 +- .../commons/expressions/CmDsExpression.kt | 7 +- .../integration/CMGaussRuleIntegrator.kt | 9 +- .../kmath/commons/integration/CMIntegrator.kt | 7 +- .../kscience/kmath/commons/linear/CMMatrix.kt | 46 +++-- .../kscience/kmath/commons/linear/CMSolver.kt | 23 +-- .../kmath/commons/optimization/CMOptimizer.kt | 15 +- .../commons/transform/Transformations.kt | 6 +- .../commons/optimization/OptimizeTest.kt | 3 +- .../kscience/kmath/complex/Quaternion.kt | 5 +- .../space/kscience/kmath/domains/Domain1D.kt | 7 +- .../kscience/kmath/domains/Float64Domain.kt | 3 +- .../kmath/domains/HyperSquareDomain.kt | 5 +- .../kmath/domains/UnconstrainedDomain.kt | 3 +- .../kscience/kmath/expressions/Expression.kt | 5 +- .../FunctionalExpressionAlgebra.kt | 5 +- .../kmath/linear/EigenDecomposition.kt | 28 +++ .../kmath/linear/Float64LinearSpace.kt | 35 ++-- .../kscience/kmath/linear/LupDecomposition.kt | 6 +- .../space/kscience/kmath/misc/cumulative.kt | 7 +- .../space/kscience/kmath/nd/Float64FieldND.kt | 109 ++++++----- .../space/kscience/kmath/nd/StructureND.kt | 7 +- .../kscience/kmath/nd/VirtualStructureND.kt | 3 +- .../kscience/kmath/nd/primitiveStructureND.kt | 9 +- .../kmath/operations/BufferAlgebra.kt | 3 +- .../kmath/operations/Float64BufferField.kt | 23 +-- .../kmath/operations/Float64BufferOps.kt | 82 ++++---- .../kscience/kmath/operations/numbers.kt | 5 +- .../space/kscience/kmath/structures/Buffer.kt | 4 +- .../kmath/structures/Float64Buffer.kt | 8 +- .../kmath/structures/MutableBuffer.kt | 2 +- .../kmath/structures/bufferPrimitiveAccess.kt | 2 +- .../kmath/expressions/ExpressionFieldTest.kt | 3 +- .../kmath/expressions/SimpleAutoDiffTest.kt | 11 +- .../space/kscience/kmath/linear/MatrixTest.kt | 5 +- .../linear/Float64ParallelLinearSpace.kt | 27 +-- .../kmath/structures/parallelMutableBuffer.kt | 2 +- .../kmath/linear/ParallelMatrixTest.kt | 5 +- .../kmath/chains/BlockingDoubleChain.kt | 3 +- .../kscience/kmath/streaming/BufferFlow.kt | 3 +- kmath-ejml/build.gradle.kts | 3 +- .../kscience/kmath/ejml/EjmlLinearSpace.kt | 12 +- .../kscience/kmath/ejml/implementations.kt | 185 ++++++++++++------ .../kscience/kmath/ejml/EjmlMatrixTest.kt | 10 +- .../space/kscience/kmath/real/DoubleVector.kt | 5 +- .../space/kscience/kmath/real/RealMatrix.kt | 5 +- .../kotlin/space/kscience/kmath/real/dot.kt | 3 +- .../kotlin/space/kscience/kmath/real/grids.kt | 9 +- .../space/kscience/kmath/real/realND.kt | 9 +- .../kscience/kmath/real/DoubleMatrixTest.kt | 2 +- .../kmath/functions/polynomialUtil.kt | 3 +- .../kmath/integration/GaussIntegrator.kt | 5 +- .../integration/GaussIntegratorRuleFactory.kt | 21 +- .../kscience/kmath/integration/Integrand.kt | 5 +- .../kmath/integration/SimpsonIntegrator.kt | 9 +- .../kmath/integration/SplineIntegrator.kt | 13 +- .../kmath/integration/UnivariateIntegrand.kt | 11 +- .../kmath/interpolation/SplineInterpolator.kt | 3 +- .../interpolation/LinearInterpolatorTest.kt | 2 +- .../interpolation/SplineInterpolatorTest.kt | 2 +- .../kmath/geometry/euclidean2d/Circle2D.kt | 2 +- .../geometry/euclidean3d/Float64Space3D.kt | 2 +- .../kmath/geometry/euclidean3d/rotations3D.kt | 8 +- .../kscience/kmath/geometry/testUtils.kt | 6 +- .../space/kscience/kmath/histogram/Counter.kt | 3 +- .../kscience/kmath/histogram/Histogram1D.kt | 3 +- .../kmath/histogram/UniformHistogram1D.kt | 3 +- .../histogram/UniformHistogramGroupND.kt | 30 +-- .../kmath/histogram/TreeHistogramGroup.kt | 6 +- .../kscience/kmath/jafama/KMathJafama.kt | 9 +- .../kmath/multik/MultikDoubleAlgebra.kt | 33 ++-- .../kscience/kmath/nd4j/Nd4jArrayAlgebra.kt | 21 +- .../kscience/kmath/nd4j/Nd4jArrayIterator.kt | 3 +- .../kscience/kmath/nd4j/Nd4jArrayStructure.kt | 5 +- .../kscience/kmath/nd4j/Nd4jTensorAlgebra.kt | 32 +-- .../kmath/optimization/QowOptimizer.kt | 23 +-- .../kscience/kmath/optimization/XYFit.kt | 37 ++-- .../kmath/optimization/logLikelihood.kt | 13 +- .../src/commonMain/tmp/QowFit.kt | 36 ++-- .../tmp/minuit/MnUserParameterState.kt | 18 +- .../tmp/minuit/MnUserTransformation.kt | 8 +- .../kmath/distributions/NormalDistribution.kt | 3 +- .../distributions/UniformDistribution.kt | 7 +- .../AhrensDieterExponentialSampler.kt | 3 +- .../AhrensDieterMarsagliaTsangGammaSampler.kt | 13 +- .../samplers/MetropolisHastingsSampler.kt | 4 +- .../samplers/NormalizedGaussianSampler.kt | 3 +- .../space/kscience/kmath/samplers/Sampler.kt | 3 +- .../kmath/series/VarianceRatioTest.kt | 3 +- .../kotlin/space/kscience/kmath/stat/Mean.kt | 5 +- .../space/kscience/kmath/stat/Median.kt | 5 +- .../kmath/stat/chiSquaredExpression.kt | 9 +- .../space/kscience/kmath/series/TestSeries.kt | 3 +- .../tensorflow/DoubleTensorFlowAlgebra.kt | 23 +-- .../tensors/api/LinearOpsTensorAlgebra.kt | 3 +- .../core/BroadcastDoubleTensorAlgebra.kt | 17 +- .../kmath/tensors/core/DoubleTensor.kt | 6 +- .../kmath/tensors/core/DoubleTensor1D.kt | 3 +- .../kmath/tensors/core/DoubleTensor2D.kt | 5 +- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 172 ++++++++-------- .../core/LevenbergMarquardtAlgorithm.kt | 93 ++++----- .../kmath/tensors/core/internal/checks.kt | 3 +- .../core/internal/doubleTensorHelpers.kt | 7 +- .../kmath/tensors/core/internal/linUtils.kt | 43 ++-- .../kscience/kmath/tensors/core/tensorOps.kt | 33 ++-- .../kmath/tensors/core/tensorTransform.kt | 5 +- .../kmath/tensors/core/TestDoubleTensor.kt | 3 +- .../tensors/core/offsetBufferEquality.kt | 3 +- .../kmath/tensors/core/TestLmAlgorithm.kt | 19 +- .../kscience/kmath/viktor/ViktorBuffer.kt | 5 +- .../kscience/kmath/viktor/ViktorFieldOpsND.kt | 59 +++--- .../kmath/viktor/ViktorStructureND.kt | 3 +- test-utils/src/commonMain/kotlin/asserts.kt | 3 +- 142 files changed, 1094 insertions(+), 871 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c2e987f0..2163afe94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -201,7 +201,7 @@ - Full autodiff refactoring based on `Symbol` - `kmath-prob` renamed to `kmath-stat` - Grid generators moved to `kmath-for-real` -- Use `Point` instead of specialized type in `kmath-for-real` +- Use `Point` instead of specialized type in `kmath-for-real` - Optimized dot product for buffer matrices moved to `kmath-for-real` - EjmlMatrix context is an object - Matrix LUP `inverse` renamed to `inverseWithLup` 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 29d4cde79..6a525dd0a 100644 --- a/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt +++ b/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt @@ -14,6 +14,7 @@ import space.kscience.kmath.expressions.* import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.Float64 import kotlin.math.sin import kotlin.random.Random import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression @@ -67,7 +68,7 @@ class ExpressionsInterpretersBenchmark { blackhole.consume(sum) } - private fun invokeAndSum(expr: Expression, blackhole: Blackhole) { + private fun invokeAndSum(expr: Expression, blackhole: Blackhole) { val random = Random(0) var sum = 0.0 val m = HashMap() @@ -99,7 +100,7 @@ class ExpressionsInterpretersBenchmark { private val wasm = node.wasmCompileToExpression(Float64Field) private val estree = node.estreeCompileToExpression(Float64Field) - private val raw = Expression { args -> + private val raw = Expression { args -> val x = args.getValue(x) x * 2.0 + 2.0 / x - 16.0 / sin(x) } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt index 45b3916dc..1c5ac71e3 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt @@ -15,6 +15,7 @@ import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.Float64 import kotlin.math.sin import kotlin.random.Random @@ -83,7 +84,7 @@ internal class ExpressionsInterpretersBenchmark { blackhole.consume(sum) } - private fun invokeAndSum(expr: Expression, blackhole: Blackhole) { + private fun invokeAndSum(expr: Expression, blackhole: Blackhole) { val random = Random(0) var sum = 0.0 val m = HashMap() @@ -114,9 +115,9 @@ internal class ExpressionsInterpretersBenchmark { private val asmPrimitive = node.compileToExpression(Float64Field) private val xIdx = asmPrimitive.indexer.indexOf(x) - private val asmGeneric = node.compileToExpression(Float64Field as Algebra) + private val asmGeneric = node.compileToExpression(Float64Field as Algebra) - private val raw = Expression { args -> + private val raw = Expression { args -> val x = args[x]!! x * 2.0 + 2.0 / x - 16.0 / sin(x) } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt index 77ca03099..06089ef2e 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt @@ -17,6 +17,7 @@ import space.kscience.kmath.UnsafeKMathAPI import space.kscience.kmath.nd.* import space.kscience.kmath.nd4j.nd4j import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.tensors.core.DoubleTensor import space.kscience.kmath.tensors.core.one import space.kscience.kmath.tensors.core.tensorAlgebra @@ -37,28 +38,28 @@ internal class NDFieldBenchmark { @Benchmark fun specializedFieldAdd(blackhole: Blackhole) = with(specializedField) { - var res: StructureND = one(shape) + var res: StructureND = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @Benchmark fun boxingFieldAdd(blackhole: Blackhole) = with(genericField) { - var res: StructureND = one(shape) + var res: StructureND = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @Benchmark fun multikAdd(blackhole: Blackhole) = with(multikAlgebra) { - var res: StructureND = one(shape) + var res: StructureND = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @Benchmark fun viktorAdd(blackhole: Blackhole) = with(viktorField) { - var res: StructureND = one(shape) + var res: StructureND = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } @@ -87,7 +88,7 @@ internal class NDFieldBenchmark { // @Benchmark // fun nd4jAdd(blackhole: Blackhole) = with(nd4jField) { -// var res: StructureND = one(dim, dim) +// var res: StructureND = one(dim, dim) // repeat(n) { res += 1.0 } // blackhole.consume(res) // } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt index bd5c2006a..21ba472b6 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt @@ -15,6 +15,7 @@ import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.one import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.viktor.ViktorFieldND @State(Scope.Benchmark) @@ -23,7 +24,7 @@ internal class ViktorBenchmark { @Benchmark fun doubleFieldAddition(blackhole: Blackhole) { with(doubleField) { - var res: StructureND = one(shape) + var res: StructureND = one(shape) repeat(n) { res += 1.0 } blackhole.consume(res) } diff --git a/docs/nd-structure.md b/docs/nd-structure.md index 8f2e504db..771cd6048 100644 --- a/docs/nd-structure.md +++ b/docs/nd-structure.md @@ -31,7 +31,7 @@ The code to run this looks like: ```kotlin specializedField.run { - var res: NDBuffer = one + var res: NDBuffer = one repeat(n) { res += 1.0 } @@ -103,7 +103,7 @@ The boxing field produced by ```kotlin genericField.run { - var res: NDBuffer = one + var res: NDBuffer = one repeat(n) { res += 1.0 } diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt index 07bacc45d..d3f6b19c4 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt @@ -14,11 +14,12 @@ import space.kscience.kmath.integration.gaussIntegrator import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.value import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 import kotlin.math.pow fun main() { //Define a function - val function: Function1D = { x -> 3 * x.pow(2) + 2 * x + 1 } + val function: Function1D = { x -> 3 * x.pow(2) + 2 * x + 1 } //get the result of the integration val result = Float64Field.gaussIntegrator.integrate(0.0..10.0, function = function) 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 c2e33afd4..9307e97e2 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt @@ -8,6 +8,7 @@ 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.Float64 import space.kscience.plotly.Plotly import space.kscience.plotly.UnstablePlotlyAPI import space.kscience.plotly.makeFile @@ -23,7 +24,7 @@ fun main() { x to sin(x) } - val polynomial: PiecewisePolynomial = SplineInterpolator(Float64Field).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/functions/interpolateSquare.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolateSquare.kt index 29a40498b..6470ee528 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolateSquare.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolateSquare.kt @@ -10,6 +10,7 @@ import space.kscience.kmath.interpolation.splineInterpolator import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.real.map import space.kscience.kmath.real.step +import space.kscience.kmath.structures.Float64 import space.kscience.plotly.Plotly import space.kscience.plotly.UnstablePlotlyAPI import space.kscience.plotly.makeFile @@ -18,7 +19,7 @@ import space.kscience.plotly.scatter @OptIn(UnstablePlotlyAPI::class) fun main() { - val function: Function1D = { x -> + val function: Function1D = { x -> if (x in 30.0..50.0) { 1.0 } else { @@ -28,7 +29,7 @@ fun main() { val xs = 0.0..100.0 step 0.5 val ys = xs.map(function) - val polynomial: PiecewisePolynomial = Float64Field.splineInterpolator.interpolatePolynomials(xs, ys) + val polynomial: PiecewisePolynomial = Float64Field.splineInterpolator.interpolatePolynomials(xs, ys) val polyFunction = polynomial.asFunction(Float64Field, 0.0) diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt index 0680f42b0..bf9e31c40 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt @@ -12,6 +12,7 @@ import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.structureND import space.kscience.kmath.nd.withNdAlgebra import space.kscience.kmath.operations.algebra +import space.kscience.kmath.structures.Float64 import kotlin.math.pow fun main(): Unit = Double.algebra.withNdAlgebra(2, 2) { @@ -22,7 +23,7 @@ fun main(): Unit = Double.algebra.withNdAlgebra(2, 2) { } //Define a function in a nd space - val function: (Double) -> StructureND = { x: Double -> 3 * x.pow(2) + 2 * diagonal(x) + 1 } + val function: (Double) -> StructureND = { x: Double -> 3 * x.pow(2) + 2 * diagonal(x) + 1 } //get the result of the integration val result = gaussIntegrator.integrate(0.0..10.0, function = function) diff --git a/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt b/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt index 6ebd1d221..1d79a2dfd 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt @@ -6,18 +6,19 @@ package space.kscience.kmath.linear import space.kscience.kmath.real.* +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer fun main() { val x0 = DoubleVector(0.0, 0.0, 0.0) val sigma = DoubleVector(1.0, 1.0, 1.0) - val gaussian: (Point) -> Double = { x -> + val gaussian: (Point) -> Double = { x -> require(x.size == x0.size) kotlin.math.exp(-((x - x0) / sigma).square().sum()) } - fun ((Point) -> Double).grad(x: Point): Point { + fun ((Point) -> Double).grad(x: Point): Point { require(x.size == x0.size) return Float64Buffer(x.size) { i -> val h = sigma[i] / 5 diff --git a/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt index 7af026759..6c87491eb 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt @@ -11,6 +11,7 @@ import space.kscience.kmath.nd.Float64BufferND import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.mutableStructureND import space.kscience.kmath.nd.ndAlgebra +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.viktor.viktorAlgebra import kotlin.collections.component1 import kotlin.collections.component2 @@ -20,7 +21,7 @@ fun main() { if (i == j) 2.0 else 0.0 } - val cmMatrix: Structure2D = CMLinearSpace.matrix(2, 2)(0.0, 1.0, 0.0, 3.0) + val cmMatrix: Structure2D = CMLinearSpace.matrix(2, 2)(0.0, 1.0, 0.0, 3.0) val res: Float64BufferND = Float64Field.ndAlgebra { exp(viktorStructure) + 2.0 * cmMatrix diff --git a/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt b/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt index e6d3ef56b..0f475f74e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/series/analyzeDif.kt @@ -14,6 +14,7 @@ import space.kscience.kmath.operations.toList import space.kscience.kmath.stat.KMComparisonResult import space.kscience.kmath.stat.ksComparisonStatistic import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.slice import space.kscience.plotly.* import kotlin.math.PI @@ -24,7 +25,7 @@ fun Double.Companion.seriesAlgebra() = Double.algebra.bufferAlgebra.seriesAlgebr fun main() = with(Double.seriesAlgebra()) { - fun Plot.plotSeries(name: String, buffer: Buffer) { + fun Plot.plotSeries(name: String, buffer: Buffer) { scatter { this.name = name x.numbers = buffer.labels @@ -37,10 +38,10 @@ fun main() = with(Double.seriesAlgebra()) { val s2 = s1.slice(20..50).moveTo(40) - val s3: Buffer = s1.zip(s2) { l, r -> l + r } //s1 + s2 + val s3: Buffer = s1.zip(s2) { l, r -> l + r } //s1 + s2 val s4 = s3.map { ln(it) } - val kmTest: KMComparisonResult = ksComparisonStatistic(s1, s2) + val kmTest: KMComparisonResult = ksComparisonStatistic(s1, s2) Plotly.page { h1 { +"This is my plot" } diff --git a/examples/src/main/kotlin/space/kscience/kmath/series/seriesBuilder.kt b/examples/src/main/kotlin/space/kscience/kmath/series/seriesBuilder.kt index 9dfb0fdc9..43e90b53a 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/series/seriesBuilder.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/series/seriesBuilder.kt @@ -6,10 +6,7 @@ package space.kscience.kmath.series -import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.Float64Buffer -import space.kscience.kmath.structures.asBuffer -import space.kscience.kmath.structures.toDoubleArray +import space.kscience.kmath.structures.* import space.kscience.plotly.* import space.kscience.plotly.models.Scatter import space.kscience.plotly.models.ScatterMode @@ -22,7 +19,7 @@ fun main(): Unit = with(Double.seriesAlgebra()) { val arrayOfRandoms = DoubleArray(20) { random.nextDouble() } val series1: Float64Buffer = arrayOfRandoms.asBuffer() - val series2: Series = series1.moveBy(3) + val series2: Series = series1.moveBy(3) val res = series2 - series1 @@ -30,7 +27,7 @@ fun main(): Unit = with(Double.seriesAlgebra()) { println(res) - fun Plot.series(name: String, buffer: Buffer, block: Scatter.() -> Unit = {}) { + fun Plot.series(name: String, buffer: Buffer, block: Scatter.() -> Unit = {}) { scatter { this.name = name x.numbers = buffer.offsetIndices diff --git a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt index 19b763113..89a1cdc28 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt @@ -37,7 +37,7 @@ private suspend fun runKMathChained(): Duration { } private fun runCMDirect(): Duration { - val rng = RandomSource.create(RandomSource.MT, 123L) + val rng = RandomSource.MT.create(123L) val sampler = CMGaussianSampler.of( BoxMullerNormalizedGaussianSampler.of(rng), diff --git a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionDemo.kt b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionDemo.kt index 2f8e9d959..e9a988b35 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionDemo.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionDemo.kt @@ -10,13 +10,14 @@ import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.combineWithState import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.random.RandomGenerator +import space.kscience.kmath.structures.Float64 private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) /** * Averaging. */ -private fun Chain.mean(): Chain = combineWithState(AveragingChainState(), { it.copy() }) { chain -> +private fun Chain.mean(): Chain = combineWithState(AveragingChainState(), { it.copy() }) { chain -> val next = chain.next() num++ value += next 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 9b48e44a0..50b3ece1f 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt @@ -26,7 +26,7 @@ fun main() { val realTime = measureTimeMillis { realField { - var res: StructureND = one + var res: StructureND = one repeat(n) { res += 1.0 } diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt index 9ec8719ad..2c65c2287 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt @@ -45,35 +45,35 @@ fun main() { measureAndPrint("Boxing addition") { genericField { - var res: StructureND = one(shape) + var res: StructureND = one(shape) repeat(n) { res += 1.0 } } } measureAndPrint("Specialized addition") { doubleField { - var res: StructureND = one(shape) + var res: StructureND = one(shape) repeat(n) { res += 1.0 } } } measureAndPrint("Nd4j specialized addition") { nd4jField { - var res: StructureND = one(shape) + var res: StructureND = one(shape) repeat(n) { res += 1.0 } } } measureAndPrint("Viktor addition") { viktorField { - var res: StructureND = one + var res: StructureND = one repeat(n) { res += 1.0 } } } measureAndPrint("Parallel stream addition") { parallelField { - var res: StructureND = one + var res: StructureND = one repeat(n) { res += 1.0 } } } 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 1171231ab..2835a9bc9 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -19,21 +19,21 @@ import java.util.stream.IntStream * execution. */ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND, - NumbersAddOps>, - ExtendedField> { + NumbersAddOps>, + ExtendedField> { private val strides = ColumnStrides(shape) override val elementAlgebra: Float64Field get() = Float64Field - override val zero: BufferND by lazy { structureND(shape) { zero } } - override val one: BufferND by lazy { structureND(shape) { one } } + override val zero: BufferND by lazy { structureND(shape) { zero } } + override val one: BufferND by lazy { structureND(shape) { one } } - override fun number(value: Number): BufferND { + override fun number(value: Number): BufferND { val d = value.toDouble() // minimize conversions return structureND(shape) { d } } @OptIn(PerformancePitfall::class) - private val StructureND.buffer: Float64Buffer + private val StructureND.buffer: Float64Buffer get() = when { shape != this@StreamDoubleFieldND.shape -> throw ShapeMismatchException( this@StreamDoubleFieldND.shape, @@ -44,7 +44,7 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND Float64Buffer(strides.linearSize) { offset -> get(strides.index(offset)) } } - override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): BufferND { + override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): BufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> val index = strides.index(offset) Float64Field.initializer(index) @@ -56,7 +56,7 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND Double, - ): MutableBufferND { + ): MutableBufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> val index = strides.index(offset) DoubleField.initializer(index) @@ -66,17 +66,17 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND.map( + override fun StructureND.map( transform: Float64Field.(Double) -> Double, - ): BufferND { + ): BufferND { val array = Arrays.stream(buffer.array).parallel().map { Float64Field.transform(it) }.toArray() return BufferND(strides, array.asBuffer()) } @OptIn(PerformancePitfall::class) - override fun StructureND.mapIndexed( + override fun StructureND.mapIndexed( transform: Float64Field.(index: IntArray, Double) -> Double, - ): BufferND { + ): BufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> Float64Field.transform( strides.index(offset), @@ -89,39 +89,39 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND, - right: StructureND, + left: StructureND, + right: StructureND, transform: Float64Field.(Double, Double) -> Double, - ): BufferND { + ): BufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> Float64Field.transform(left.buffer.array[offset], right.buffer.array[offset]) }.toArray() return BufferND(strides, array.asBuffer()) } - override fun StructureND.unaryMinus(): StructureND = map { -it } + override fun StructureND.unaryMinus(): StructureND = map { -it } - override fun scale(a: StructureND, value: Double): StructureND = a.map { it * value } + override fun scale(a: StructureND, value: Double): StructureND = a.map { it * value } - override fun power(arg: StructureND, pow: Number): BufferND = arg.map { power(it, pow) } + override fun power(arg: StructureND, pow: Number): BufferND = arg.map { power(it, pow) } - override fun exp(arg: StructureND): BufferND = arg.map { exp(it) } + override fun exp(arg: StructureND): BufferND = arg.map { exp(it) } - override fun ln(arg: StructureND): BufferND = arg.map { ln(it) } + override fun ln(arg: StructureND): BufferND = arg.map { ln(it) } - override fun sin(arg: StructureND): BufferND = arg.map { sin(it) } - override fun cos(arg: StructureND): BufferND = arg.map { cos(it) } - override fun tan(arg: StructureND): BufferND = arg.map { tan(it) } - override fun asin(arg: StructureND): BufferND = arg.map { asin(it) } - override fun acos(arg: StructureND): BufferND = arg.map { acos(it) } - override fun atan(arg: StructureND): BufferND = arg.map { atan(it) } + override fun sin(arg: StructureND): BufferND = arg.map { sin(it) } + override fun cos(arg: StructureND): BufferND = arg.map { cos(it) } + override fun tan(arg: StructureND): BufferND = arg.map { tan(it) } + override fun asin(arg: StructureND): BufferND = arg.map { asin(it) } + override fun acos(arg: StructureND): BufferND = arg.map { acos(it) } + override fun atan(arg: StructureND): BufferND = arg.map { atan(it) } - override fun sinh(arg: StructureND): BufferND = arg.map { sinh(it) } - override fun cosh(arg: StructureND): BufferND = arg.map { cosh(it) } - override fun tanh(arg: StructureND): BufferND = arg.map { tanh(it) } - override fun asinh(arg: StructureND): BufferND = arg.map { asinh(it) } - override fun acosh(arg: StructureND): BufferND = arg.map { acosh(it) } - override fun atanh(arg: StructureND): BufferND = arg.map { atanh(it) } + override fun sinh(arg: StructureND): BufferND = arg.map { sinh(it) } + override fun cosh(arg: StructureND): BufferND = arg.map { cosh(it) } + override fun tanh(arg: StructureND): BufferND = arg.map { tanh(it) } + override fun asinh(arg: StructureND): BufferND = arg.map { asinh(it) } + override fun acosh(arg: StructureND): BufferND = arg.map { acosh(it) } + override fun atanh(arg: StructureND): BufferND = arg.map { atanh(it) } } fun Float64Field.ndStreaming(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(ShapeND(shape)) diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/mutableNd.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/mutableNd.kt index 0b0a4cac1..fdabfcdb1 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/mutableNd.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/mutableNd.kt @@ -11,7 +11,7 @@ import space.kscience.kmath.operations.algebra @OptIn(PerformancePitfall::class) fun main(): Unit = with(Double.algebra.ndAlgebra) { - val structure: MutableStructure2D = mutableStructureND(ShapeND(2, 2)) { (i, j) -> + val structure: MutableStructure2D = mutableStructureND(ShapeND(2, 2)) { (i, j) -> i.toDouble() + j.toDouble() }.as2D() diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt index dca7325ce..b90b6895f 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt @@ -12,6 +12,7 @@ import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.component1 +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.tensors.LevenbergMarquardt.StartDataLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.zeros import space.kscience.kmath.tensors.core.DoubleTensorAlgebra @@ -20,9 +21,9 @@ import space.kscience.kmath.tensors.core.levenbergMarquardt import kotlin.random.Random fun streamLm( - lm_func: (MutableStructure2D, MutableStructure2D, Int) -> (MutableStructure2D), + lm_func: (MutableStructure2D, MutableStructure2D, Int) -> (MutableStructure2D), startData: StartDataLm, launchFrequencyInMs: Long, numberOfLaunches: Int, -): Flow> = flow { +): Flow> = flow { var example_number = startData.example_number var p_init = startData.p_init @@ -64,7 +65,7 @@ fun streamLm( } } -fun generateNewYDat(y_dat: MutableStructure2D, delta: Double): MutableStructure2D { +fun generateNewYDat(y_dat: MutableStructure2D, delta: Double): MutableStructure2D { val n = y_dat.shape.component1() val y_dat_new = zeros(ShapeND(intArrayOf(n, 1))).as2D() for (i in 0 until n) { diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt index 9a4b613b2..9f25a2bc5 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt @@ -9,6 +9,7 @@ import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.component1 +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.DoubleTensorAlgebra @@ -19,24 +20,24 @@ import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times import space.kscience.kmath.tensors.core.asDoubleTensor public data class StartDataLm( - var lm_matx_y_dat: MutableStructure2D, + var lm_matx_y_dat: MutableStructure2D, var example_number: Int, - var p_init: MutableStructure2D, - var t: MutableStructure2D, - var y_dat: MutableStructure2D, + var p_init: MutableStructure2D, + var t: MutableStructure2D, + var y_dat: MutableStructure2D, var weight: Double, - var dp: MutableStructure2D, - var p_min: MutableStructure2D, - var p_max: MutableStructure2D, - var consts: MutableStructure2D, + var dp: MutableStructure2D, + var p_min: MutableStructure2D, + var p_max: MutableStructure2D, + var consts: MutableStructure2D, var opts: DoubleArray, ) fun funcEasyForLm( - t: MutableStructure2D, - p: MutableStructure2D, + t: MutableStructure2D, + p: MutableStructure2D, exampleNumber: Int, -): MutableStructure2D { +): MutableStructure2D { val m = t.shape.component1() var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) @@ -59,10 +60,10 @@ fun funcEasyForLm( } fun funcMiddleForLm( - t: MutableStructure2D, - p: MutableStructure2D, + t: MutableStructure2D, + p: MutableStructure2D, exampleNumber: Int, -): MutableStructure2D { +): MutableStructure2D { val m = t.shape.component1() var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) @@ -79,10 +80,10 @@ fun funcMiddleForLm( } fun funcDifficultForLm( - t: MutableStructure2D, - p: MutableStructure2D, + t: MutableStructure2D, + p: MutableStructure2D, exampleNumber: Int, -): MutableStructure2D { +): MutableStructure2D { val m = t.shape.component1() var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt index f8bf65ad7..bc1a493f0 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/multik.kt @@ -10,12 +10,13 @@ import org.jetbrains.kotlinx.multik.api.ndarray import org.jetbrains.kotlinx.multik.default.DefaultEngine import space.kscience.kmath.multik.MultikDoubleAlgebra import space.kscience.kmath.nd.one +import space.kscience.kmath.structures.Float64 val multikAlgebra = MultikDoubleAlgebra(DefaultEngine()) fun main(): Unit = with(multikAlgebra) { - val a = Multik.ndarray(intArrayOf(1, 2, 3)).asType().wrap() + val a = Multik.ndarray(intArrayOf(1, 2, 3)).asType().wrap() val b = Multik.ndarray(doubleArrayOf(1.0, 2.0, 3.0)).wrap() one(a.shape) - a + b * 3.0 } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt index 74fd4b56c..dfced7f14 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt @@ -9,6 +9,7 @@ import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.operations.Int8Ring import space.kscience.kmath.operations.pi +import space.kscience.kmath.structures.Float64 import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.fail @@ -41,7 +42,7 @@ internal class TestFolding { @Test fun foldSymbol() = assertEquals( Float64Field.pi, - ("pi".parseMath().evaluateConstants(Float64Field) as? TypedMst.Constant ?: fail()).value, + ("pi".parseMath().evaluateConstants(Float64Field) as? TypedMst.Constant ?: fail()).value, ) @Test diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt index ee2346b13..59239cf24 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -10,12 +10,13 @@ import space.kscience.kmath.expressions.MST import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Int32Ring +import space.kscience.kmath.structures.Float64 internal interface CompilerTestContext { fun MST.compileToExpression(algebra: Int32Ring): Expression fun MST.compile(algebra: Int32Ring, arguments: Map): Int fun MST.compile(algebra: Int32Ring, vararg arguments: Pair): Int = compile(algebra, mapOf(*arguments)) - fun MST.compileToExpression(algebra: Float64Field): Expression + fun MST.compileToExpression(algebra: Float64Field): Expression fun MST.compile(algebra: Float64Field, arguments: Map): Double fun MST.compile(algebra: Float64Field, vararg arguments: Pair): Double = diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt index 18ef35c4b..5a12ccb8f 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt @@ -11,6 +11,7 @@ import space.kscience.kmath.expressions.* import space.kscience.kmath.internal.binaryen.* import space.kscience.kmath.internal.webassembly.Instance import space.kscience.kmath.operations.* +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.internal.binaryen.Module as BinaryenModule import space.kscience.kmath.internal.webassembly.Module as WasmModule @@ -85,13 +86,13 @@ internal sealed class WasmBuilder>( } @UnstableKMathAPI -internal class DoubleWasmBuilder(target: TypedMst) : +internal class DoubleWasmBuilder(target: TypedMst) : WasmBuilder(f64, Float64Field, target) { override val instance by lazy { object : DoubleExpression { override val indexer = SimpleSymbolIndexer(keys) - override fun invoke(arguments: DoubleArray) = spreader(executable, arguments).unsafeCast() + override fun invoke(arguments: DoubleArray) = spreader(executable, arguments).unsafeCast() } } @@ -99,7 +100,7 @@ internal class DoubleWasmBuilder(target: TypedMst) : override fun visitNumber(number: Number) = ctx.f64.const(number.toDouble()) - override fun visitUnary(node: TypedMst.Unary): ExpressionRef = when (node.operation) { + override fun visitUnary(node: TypedMst.Unary): ExpressionRef = when (node.operation) { GroupOps.MINUS_OPERATION -> ctx.f64.neg(visit(node.value)) GroupOps.PLUS_OPERATION -> visit(node.value) PowerOperations.SQRT_OPERATION -> ctx.f64.sqrt(visit(node.value)) @@ -120,7 +121,7 @@ internal class DoubleWasmBuilder(target: TypedMst) : else -> super.visitUnary(node) } - override fun visitBinary(mst: TypedMst.Binary): ExpressionRef = when (mst.operation) { + override fun visitBinary(mst: TypedMst.Binary): ExpressionRef = when (mst.operation) { GroupOps.PLUS_OPERATION -> ctx.f64.add(visit(mst.left), visit(mst.right)) GroupOps.MINUS_OPERATION -> ctx.f64.sub(visit(mst.left), visit(mst.right)) RingOps.TIMES_OPERATION -> ctx.f64.mul(visit(mst.left), visit(mst.right)) diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt index ceb1ad69d..26e6a2486 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt @@ -13,6 +13,7 @@ import space.kscience.kmath.ast.evaluateConstants import space.kscience.kmath.expressions.* import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Int32Ring +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.wasm.internal.DoubleWasmBuilder import space.kscience.kmath.wasm.internal.IntWasmBuilder @@ -58,7 +59,7 @@ public fun MST.compile(algebra: Int32Ring, vararg arguments: Pair): * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compileToExpression(algebra: Float64Field): Expression { +public fun MST.compileToExpression(algebra: Float64Field): Expression { val typed = evaluateConstants(algebra) return if (typed is TypedMst.Constant) object : DoubleExpression { diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt index c747adc0c..dab84e94f 100644 --- a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt +++ b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -13,6 +13,7 @@ import space.kscience.kmath.expressions.MST import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Int32Ring +import space.kscience.kmath.structures.Float64 import kotlin.contracts.InvocationKind import kotlin.contracts.contract import space.kscience.kmath.estree.compile as estreeCompile @@ -24,7 +25,7 @@ import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression private object WasmCompilerTestContext : CompilerTestContext { override fun MST.compileToExpression(algebra: Int32Ring): Expression = wasmCompileToExpression(algebra) override fun MST.compile(algebra: Int32Ring, arguments: Map): Int = wasmCompile(algebra, arguments) - override fun MST.compileToExpression(algebra: Float64Field): Expression = wasmCompileToExpression(algebra) + override fun MST.compileToExpression(algebra: Float64Field): Expression = wasmCompileToExpression(algebra) override fun MST.compile(algebra: Float64Field, arguments: Map): Double = wasmCompile(algebra, arguments) @@ -33,7 +34,7 @@ private object WasmCompilerTestContext : CompilerTestContext { private object ESTreeCompilerTestContext : CompilerTestContext { override fun MST.compileToExpression(algebra: Int32Ring): Expression = estreeCompileToExpression(algebra) override fun MST.compile(algebra: Int32Ring, arguments: Map): Int = estreeCompile(algebra, arguments) - override fun MST.compileToExpression(algebra: Float64Field): Expression = estreeCompileToExpression(algebra) + override fun MST.compileToExpression(algebra: Float64Field): Expression = estreeCompileToExpression(algebra) override fun MST.compile(algebra: Float64Field, arguments: Map): Double = estreeCompile(algebra, arguments) diff --git a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/PrimitiveAsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/PrimitiveAsmBuilder.kt index 3f003bb09..ed0f361af 100644 --- a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/PrimitiveAsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/PrimitiveAsmBuilder.kt @@ -15,6 +15,7 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.ast.TypedMst import space.kscience.kmath.expressions.* import space.kscience.kmath.operations.* +import space.kscience.kmath.structures.Float64 import java.lang.invoke.MethodHandles import java.lang.invoke.MethodType import java.nio.file.Paths @@ -381,7 +382,7 @@ internal sealed class PrimitiveAsmBuilder>( } @UnstableKMathAPI -internal class DoubleAsmBuilder(target: TypedMst) : PrimitiveAsmBuilder( +internal class DoubleAsmBuilder(target: TypedMst) : PrimitiveAsmBuilder( Float64Field, java.lang.Double::class.java, java.lang.Double.TYPE, @@ -410,7 +411,7 @@ internal class DoubleAsmBuilder(target: TypedMst) : PrimitiveAsmBuilder< false, ) - override fun visitUnary(node: TypedMst.Unary) { + override fun visitUnary(node: TypedMst.Unary) { super.visitUnary(node) when (node.operation) { @@ -435,7 +436,7 @@ internal class DoubleAsmBuilder(target: TypedMst) : PrimitiveAsmBuilder< } } - override fun visitBinary(node: TypedMst.Binary) { + override fun visitBinary(node: TypedMst.Binary) { super.visitBinary(node) when (node.operation) { diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt index b48a8fdba..2c800f4ef 100644 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt +++ b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -12,6 +12,7 @@ import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Int32Ring +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.asm.compile as asmCompile import space.kscience.kmath.asm.compileToExpression as asmCompileToExpression @@ -22,18 +23,18 @@ private object GenericAsmCompilerTestContext : CompilerTestContext { override fun MST.compile(algebra: Int32Ring, arguments: Map): Int = asmCompile(algebra as Algebra, arguments) - override fun MST.compileToExpression(algebra: Float64Field): Expression = - asmCompileToExpression(algebra as Algebra) + override fun MST.compileToExpression(algebra: Float64Field): Expression = + asmCompileToExpression(algebra as Algebra) override fun MST.compile(algebra: Float64Field, arguments: Map): Double = - asmCompile(algebra as Algebra, arguments) + asmCompile(algebra as Algebra, arguments) } @OptIn(UnstableKMathAPI::class) private object PrimitiveAsmCompilerTestContext : CompilerTestContext { override fun MST.compileToExpression(algebra: Int32Ring): Expression = asmCompileToExpression(algebra) override fun MST.compile(algebra: Int32Ring, arguments: Map): Int = asmCompile(algebra, arguments) - override fun MST.compileToExpression(algebra: Float64Field): Expression = asmCompileToExpression(algebra) + override fun MST.compileToExpression(algebra: Float64Field): Expression = asmCompileToExpression(algebra) override fun MST.compile(algebra: Float64Field, arguments: Map): Double = asmCompile(algebra, arguments) diff --git a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/expressions/CmDsExpression.kt b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/expressions/CmDsExpression.kt index 855de14a0..65e01e2d5 100644 --- a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/expressions/CmDsExpression.kt +++ b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/expressions/CmDsExpression.kt @@ -14,6 +14,7 @@ import space.kscience.kmath.expressions.* import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.NumbersAddOps +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.MutableBufferFactory /** @@ -132,9 +133,9 @@ public object CmDsProcessor : AutoDiffProcessor DerivativeStructure, -) : DifferentiableExpression { +) : DifferentiableExpression { - override val type: SafeType get() = DoubleField.type + override val type: SafeType get() = DoubleField.type override operator fun invoke(arguments: Map): Double = CmDsField(0, arguments).function().value @@ -142,7 +143,7 @@ public class CmDsExpression( /** * Get the derivative expression with given orders */ - override fun derivativeOrNull(symbols: List): Expression = Expression(type) { arguments -> + override fun derivativeOrNull(symbols: List): Expression = Expression(type) { arguments -> with(CmDsField(symbols.size, arguments)) { function().derivative(symbols) } } } diff --git a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/integration/CMGaussRuleIntegrator.kt b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/integration/CMGaussRuleIntegrator.kt index b4680bace..9791539f9 100644 --- a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/integration/CMGaussRuleIntegrator.kt +++ b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/integration/CMGaussRuleIntegrator.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.commons.integration import org.apache.commons.math3.analysis.integration.gauss.GaussIntegrator import org.apache.commons.math3.analysis.integration.gauss.GaussIntegratorFactory import space.kscience.kmath.integration.* +import space.kscience.kmath.structures.Float64 /** * A simple one-pass integrator based on Gauss rule @@ -14,9 +15,9 @@ import space.kscience.kmath.integration.* public class CMGaussRuleIntegrator( private val numpoints: Int, private var type: GaussRule = GaussRule.LEGENDRE, -) : UnivariateIntegrator { +) : UnivariateIntegrator { - override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { + override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { val range = integrand[IntegrationRange] ?: error("Integration range is not provided") val integrator: GaussIntegrator = getIntegrator(range) @@ -28,7 +29,7 @@ public class CMGaussRuleIntegrator( } } - private fun getIntegrator(range: ClosedRange): GaussIntegrator { + private fun getIntegrator(range: ClosedRange): GaussIntegrator { return when (type) { GaussRule.LEGENDRE -> factory.legendre( numpoints, @@ -77,7 +78,7 @@ public class CMGaussRuleIntegrator( private val factory: GaussIntegratorFactory = GaussIntegratorFactory() public fun integrate( - range: ClosedRange, + range: ClosedRange, numPoints: Int = 100, type: GaussRule = GaussRule.LEGENDRE, function: (Double) -> Double, diff --git a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt index 83880287e..e37eed06d 100644 --- a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt +++ b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt @@ -10,6 +10,7 @@ import org.apache.commons.math3.analysis.integration.SimpsonIntegrator import space.kscience.attributes.Attributes import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.integration.* +import space.kscience.kmath.structures.Float64 import org.apache.commons.math3.analysis.integration.UnivariateIntegrator as CMUnivariateIntegrator /** @@ -17,10 +18,10 @@ import org.apache.commons.math3.analysis.integration.UnivariateIntegrator as CMU */ public class CMIntegrator( private val defaultMaxCalls: Int = 200, - public val integratorBuilder: (Integrand) -> CMUnivariateIntegrator, -) : UnivariateIntegrator { + public val integratorBuilder: (Integrand) -> CMUnivariateIntegrator, +) : UnivariateIntegrator { - override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { + override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { val integrator = integratorBuilder(integrand) val maxCalls = integrand[IntegrandMaxCalls] ?: defaultMaxCalls val remainingCalls = maxCalls - integrand.calls 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 1fe923cbb..d7470be56 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 @@ -12,6 +12,7 @@ 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.EigenDecomposition import space.kscience.kmath.linear.QRDecomposition import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.StructureAttribute @@ -22,7 +23,7 @@ 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 { +public class CMMatrix(public val origin: RealMatrix) : Matrix { override val rowNum: Int get() = origin.rowDimension override val colNum: Int get() = origin.columnDimension @@ -31,12 +32,12 @@ public class CMMatrix(public val origin: RealMatrix) : Matrix { } @JvmInline -public value class CMVector(public val origin: RealVector) : Point { +public value class CMVector(public val origin: RealVector) : Point { override val size: Int get() = origin.dimension override operator fun get(index: Int): Double = origin.getEntry(index) - override operator fun iterator(): Iterator = origin.toArray().iterator() + override operator fun iterator(): Iterator = origin.toArray().iterator() override fun toString(): String = Buffer.toString(this) } @@ -46,7 +47,7 @@ public fun RealVector.toPoint(): CMVector = CMVector(this) public object CMLinearSpace : LinearSpace { override val elementAlgebra: Float64Field get() = Float64Field - override val type: SafeType get() = DoubleField.type + override val type: SafeType get() = DoubleField.type override fun buildMatrix( rows: Int, @@ -58,7 +59,7 @@ public object CMLinearSpace : LinearSpace { } @OptIn(UnstableKMathAPI::class) - public fun Matrix.toCM(): CMMatrix = when (val matrix = origin) { + public fun Matrix.toCM(): CMMatrix = when (val matrix = origin) { is CMMatrix -> matrix else -> { //TODO add feature analysis @@ -67,7 +68,7 @@ public object CMLinearSpace : LinearSpace { } } - public fun Point.toCM(): CMVector = if (this is CMVector) this else { + public fun Point.toCM(): CMVector = if (this is CMVector) this else { val array = DoubleArray(size) { this[it] } ArrayRealVector(array).wrap() } @@ -75,40 +76,40 @@ public object CMLinearSpace : LinearSpace { internal fun RealMatrix.wrap(): CMMatrix = CMMatrix(this) internal fun RealVector.wrap(): CMVector = CMVector(this) - override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): Point = + override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): Point = ArrayRealVector(DoubleArray(size) { Float64Field.initializer(it) }).wrap() - override fun Matrix.plus(other: Matrix): CMMatrix = + override fun Matrix.plus(other: Matrix): CMMatrix = toCM().origin.add(other.toCM().origin).wrap() - override fun Point.plus(other: Point): CMVector = + override fun Point.plus(other: Point): CMVector = toCM().origin.add(other.toCM().origin).wrap() - override fun Point.minus(other: Point): CMVector = + override fun Point.minus(other: Point): CMVector = toCM().origin.subtract(other.toCM().origin).wrap() - override fun Matrix.dot(other: Matrix): CMMatrix = + override fun Matrix.dot(other: Matrix): CMMatrix = toCM().origin.multiply(other.toCM().origin).wrap() - override fun Matrix.dot(vector: Point): CMVector = + override fun Matrix.dot(vector: Point): CMVector = toCM().origin.preMultiply(vector.toCM().origin).wrap() - override operator fun Matrix.minus(other: Matrix): CMMatrix = + override operator fun Matrix.minus(other: Matrix): CMMatrix = toCM().origin.subtract(other.toCM().origin).wrap() - override operator fun Matrix.times(value: Double): CMMatrix = + override operator fun Matrix.times(value: Double): CMMatrix = toCM().origin.scalarMultiply(value).wrap() - override fun Double.times(m: Matrix): CMMatrix = + override fun Double.times(m: Matrix): CMMatrix = m * this - override fun Point.times(value: Double): CMVector = + override fun Point.times(value: Double): CMVector = toCM().origin.mapMultiply(value).wrap() - override fun Double.times(v: Point): CMVector = + override fun Double.times(v: Point): CMVector = v * this - override fun > computeAttribute(structure: Structure2D, attribute: A): V? { + override fun > computeAttribute(structure: Structure2D, attribute: A): V? { val origin = structure.toCM().origin @@ -125,7 +126,7 @@ public object CMLinearSpace : LinearSpace { Cholesky -> object : CholeskyDecomposition { val cmCholesky by lazy { org.apache.commons.math3.linear.CholeskyDecomposition(origin) } - override val l: Matrix get() = cmCholesky.l.wrap() + override val l: Matrix get() = cmCholesky.l.wrap() } QR -> object : QRDecomposition { @@ -144,6 +145,13 @@ public object CMLinearSpace : LinearSpace { } + EIG -> object : EigenDecomposition { + val cmEigen by lazy { org.apache.commons.math3.linear.EigenDecomposition(origin) } + + override val v: Matrix get() = cmEigen.v.wrap() + override val d: Matrix get() = cmEigen.d.wrap() + } + else -> null } @Suppress("UNCHECKED_CAST") diff --git a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/linear/CMSolver.kt b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/linear/CMSolver.kt index 0c15d1016..ec12de1f2 100644 --- a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/linear/CMSolver.kt +++ b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/linear/CMSolver.kt @@ -9,6 +9,7 @@ import org.apache.commons.math3.linear.* import space.kscience.kmath.linear.LinearSolver import space.kscience.kmath.linear.Matrix import space.kscience.kmath.linear.Point +import space.kscience.kmath.structures.Float64 public enum class CMDecomposition { LUP, @@ -19,7 +20,7 @@ public enum class CMDecomposition { } private fun CMLinearSpace.solver( - a: Matrix, + a: Matrix, decomposition: CMDecomposition = CMDecomposition.LUP, ): DecompositionSolver = when (decomposition) { CMDecomposition.LUP -> LUDecomposition(a.toCM().origin).solver @@ -30,31 +31,31 @@ private fun CMLinearSpace.solver( } public fun CMLinearSpace.solve( - a: Matrix, - b: Matrix, + a: Matrix, + b: Matrix, decomposition: CMDecomposition = CMDecomposition.LUP, ): CMMatrix = solver(a, decomposition).solve(b.toCM().origin).wrap() public fun CMLinearSpace.solve( - a: Matrix, - b: Point, + a: Matrix, + b: Point, decomposition: CMDecomposition = CMDecomposition.LUP, ): CMVector = solver(a, decomposition).solve(b.toCM().origin).toPoint() public fun CMLinearSpace.inverse( - a: Matrix, + a: Matrix, decomposition: CMDecomposition = CMDecomposition.LUP, ): CMMatrix = solver(a, decomposition).inverse.wrap() -public fun CMLinearSpace.solver(decomposition: CMDecomposition): LinearSolver = object : LinearSolver { - override fun solve(a: Matrix, b: Matrix): Matrix = +public fun CMLinearSpace.solver(decomposition: CMDecomposition): LinearSolver = object : LinearSolver { + override fun solve(a: Matrix, b: Matrix): Matrix = solver(a, decomposition).solve(b.toCM().origin).wrap() - override fun solve(a: Matrix, b: Point): Point = + override fun solve(a: Matrix, b: Point): Point = solver(a, decomposition).solve(b.toCM().origin).toPoint() - override fun inverse(matrix: Matrix): Matrix = solver(matrix, decomposition).inverse.wrap() + override fun inverse(matrix: Matrix): Matrix = solver(matrix, decomposition).inverse.wrap() } -public fun CMLinearSpace.lupSolver(): LinearSolver = solver((CMDecomposition.LUP)) \ No newline at end of file +public fun CMLinearSpace.lupSolver(): LinearSolver = solver((CMDecomposition.LUP)) \ No newline at end of file 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 4030e630f..db4b3b733 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 @@ -22,6 +22,7 @@ import space.kscience.kmath.expressions.SymbolIndexer import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.withSymbols import space.kscience.kmath.optimization.* +import space.kscience.kmath.structures.Float64 import kotlin.collections.set import kotlin.reflect.KClass @@ -33,7 +34,7 @@ public object CMOptimizerEngine : OptimizationAttribute<() -> MultivariateOptimi /** * Specify a Commons-maths optimization engine */ -public fun AttributesBuilder>.cmEngine(optimizerBuilder: () -> MultivariateOptimizer) { +public fun AttributesBuilder>.cmEngine(optimizerBuilder: () -> MultivariateOptimizer) { set(CMOptimizerEngine, optimizerBuilder) } @@ -42,18 +43,18 @@ public object CMOptimizerData : SetAttribute OptimizationDat /** * Specify Commons-maths optimization data. */ -public fun AttributesBuilder>.cmOptimizationData(data: SymbolIndexer.() -> OptimizationData) { +public fun AttributesBuilder>.cmOptimizationData(data: SymbolIndexer.() -> OptimizationData) { CMOptimizerData add data } -public fun AttributesBuilder>.simplexSteps(vararg steps: Pair) { +public fun AttributesBuilder>.simplexSteps(vararg steps: Pair) { //TODO use convergence checker from features cmEngine { SimplexOptimizer(CMOptimizer.defaultConvergenceChecker) } cmOptimizationData { NelderMeadSimplex(mapOf(*steps).toDoubleArray()) } } @OptIn(UnstableKMathAPI::class) -public object CMOptimizer : Optimizer> { +public object CMOptimizer : Optimizer> { public const val DEFAULT_RELATIVE_TOLERANCE: Double = 1e-4 public const val DEFAULT_ABSOLUTE_TOLERANCE: Double = 1e-4 @@ -67,12 +68,12 @@ public object CMOptimizer : Optimizer> { override suspend fun optimize( - problem: FunctionOptimization, - ): FunctionOptimization { + problem: FunctionOptimization, + ): FunctionOptimization { val startPoint = problem.startPoint val parameters = problem.attributes[OptimizationParameters] - ?: problem.attributes[OptimizationStartPoint()]?.keys + ?: problem.attributes[OptimizationStartPoint()]?.keys ?: startPoint.keys diff --git a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/transform/Transformations.kt b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/transform/Transformations.kt index 838ab95d5..a274e518a 100644 --- a/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/transform/Transformations.kt +++ b/kmath-commons/src/jvmMain/kotlin/space/kscience/kmath/commons/transform/Transformations.kt @@ -77,7 +77,7 @@ public fun Flow>.fft( } @JvmName("realFFT") -public fun Flow>.fft( +public fun Flow>.fft( normalization: DftNormalization = DftNormalization.STANDARD, direction: TransformType = TransformType.FORWARD, ): Flow> { @@ -89,7 +89,7 @@ public fun Flow>.fft( * Process a continuous flow of real numbers in FFT splitting it in chunks of [bufferSize]. */ @JvmName("realFFT") -public fun Flow.fft( +public fun Flow.fft( bufferSize: Int = Int.MAX_VALUE, normalization: DftNormalization = DftNormalization.STANDARD, direction: TransformType = TransformType.FORWARD, @@ -99,4 +99,4 @@ public fun Flow.fft( * Map a complex flow into real flow by taking real part of each number */ @FlowPreview -public fun Flow.real(): Flow = map { it.re } +public fun Flow.real(): Flow = map { it.re } 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 8b314cf1a..2fe7b85e7 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 @@ -18,6 +18,7 @@ import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.optimization.* import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.chiSquaredExpression +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer import kotlin.test.Test @@ -70,7 +71,7 @@ internal class OptimizeTest { bindSymbol(a) * arg.pow(2) + bindSymbol(b) * arg + cWithDefault } - val result: FunctionOptimization = chi2.optimizeWith( + val result: FunctionOptimization = chi2.optimizeWith( CMOptimizer, mapOf(a to 1.5, b to 0.9, c to 1.0), ) { diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt index 6a6171975..a8f83d7ed 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt @@ -11,6 +11,7 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.memory.* import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.* @@ -28,7 +29,7 @@ public class Quaternion( public val x: Double, public val y: Double, public val z: Double, -) : Buffer { +) : Buffer { init { require(!w.isNaN()) { "w-component of quaternion is not-a-number" } require(!x.isNaN()) { "x-component of quaternion is not-a-number" } @@ -51,7 +52,7 @@ public class Quaternion( else -> error("Index $index out of bounds [0,3]") } - override fun iterator(): Iterator = listOf(w, x, y, z).iterator() + override fun iterator(): Iterator = listOf(w, x, y, z).iterator() override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Domain1D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Domain1D.kt index 79f446f6d..62d97f7b7 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Domain1D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Domain1D.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.domains import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.Point +import space.kscience.kmath.structures.Float64 @UnstableKMathAPI public abstract class Domain1D>(public val range: ClosedRange) : Domain { @@ -22,8 +23,8 @@ public abstract class Domain1D>(public val range: ClosedRange< @UnstableKMathAPI public class DoubleDomain1D( - @Suppress("CanBeParameter") public val doubleRange: ClosedFloatingPointRange, -) : Domain1D(doubleRange), Float64Domain { + @Suppress("CanBeParameter") public val doubleRange: ClosedFloatingPointRange, +) : Domain1D(doubleRange), Float64Domain { override fun getLowerBound(num: Int): Double { require(num == 0) return range.start @@ -55,5 +56,5 @@ public class DoubleDomain1D( } @UnstableKMathAPI -public val Domain1D.center: Double +public val Domain1D.center: Double get() = (range.endInclusive + range.start) / 2 diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Float64Domain.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Float64Domain.kt index a522381e1..ee5c1cff6 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Float64Domain.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Float64Domain.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.domains import space.kscience.kmath.UnstableKMathAPI +import space.kscience.kmath.structures.Float64 /** * n-dimensional volume @@ -12,7 +13,7 @@ import space.kscience.kmath.UnstableKMathAPI * @author Alexander Nozik */ @UnstableKMathAPI -public interface Float64Domain : Domain { +public interface Float64Domain : Domain { /** * Global lower edge * @param num axis number diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt index 16dae5699..95e9fea19 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.domains import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.Point import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.indices @@ -15,7 +16,7 @@ import space.kscience.kmath.structures.indices * and a [Buffer] of upper boundaries. Upper should be greater or equals than lower. */ @UnstableKMathAPI -public class HyperSquareDomain(public val lower: Buffer, public val upper: Buffer) : Float64Domain { +public class HyperSquareDomain(public val lower: Buffer, public val upper: Buffer) : Float64Domain { init { require(lower.size == upper.size) { "Domain borders size mismatch. Lower borders size is ${lower.size}, but upper borders size is ${upper.size}." @@ -29,7 +30,7 @@ public class HyperSquareDomain(public val lower: Buffer, public val uppe public val center: Float64Buffer get() = Float64Buffer(dimension) { (lower[it] + upper[it]) / 2.0 } - override operator fun contains(point: Point): Boolean = point.indices.all { i -> + override operator fun contains(point: Point): Boolean = point.indices.all { i -> point[i] in lower[i]..upper[i] } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/UnconstrainedDomain.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/UnconstrainedDomain.kt index f651dcde3..535606a29 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/UnconstrainedDomain.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/UnconstrainedDomain.kt @@ -6,10 +6,11 @@ package space.kscience.kmath.domains import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.Point +import space.kscience.kmath.structures.Float64 @UnstableKMathAPI public class UnconstrainedDomain(override val dimension: Int) : Float64Domain { - override operator fun contains(point: Point): Boolean = true + override operator fun contains(point: Point): Boolean = true override fun getLowerBound(num: Int): Double = Double.NEGATIVE_INFINITY 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 f2e134963..1cc3b766b 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 @@ -13,6 +13,7 @@ import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.IntRing import space.kscience.kmath.operations.LongRing +import space.kscience.kmath.structures.Float64 import kotlin.jvm.JvmName import kotlin.properties.ReadOnlyProperty @@ -47,9 +48,9 @@ public inline fun Expression(noinline block: (Map) -> T): * Specialization of [Expression] for [Double] allowing better performance because of using array. */ @UnstableKMathAPI -public interface DoubleExpression : Expression { +public interface DoubleExpression : Expression { - override val type: SafeType get() = DoubleField.type + override val type: SafeType get() = DoubleField.type /** * The indexer of this expression's arguments that should be used to build array for [invoke]. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt index 8ec69c6de..5b6b1083e 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.expressions import space.kscience.kmath.operations.* +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.MutableBufferFactory import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -197,5 +198,5 @@ public inline fun > A.expressionInExtendedField( ): Expression = FunctionalExpressionExtendedField(this).block() public inline fun Float64Field.expression( - block: FunctionalExpressionExtendedField.() -> Expression, -): Expression = FunctionalExpressionExtendedField(this).block() + block: FunctionalExpressionExtendedField.() -> Expression, +): Expression = FunctionalExpressionExtendedField(this).block() diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt new file mode 100644 index 000000000..4675509bd --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt @@ -0,0 +1,28 @@ +/* + * 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.linear + +import space.kscience.attributes.PolymorphicAttribute +import space.kscience.attributes.safeTypeOf + +public interface EigenDecomposition { + /** + * Eigenvector matrix. + */ + public val v: Matrix + + /** + * A diagonal matrix of eigenvalues. Must have [IsDiagonal] + */ + public val d: Matrix +} + +public class EigenDecompositionAttribute : + PolymorphicAttribute>(safeTypeOf()), + MatrixAttribute> + +public val MatrixScope.EIG: EigenDecompositionAttribute + get() = EigenDecompositionAttribute() diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Float64LinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Float64LinearSpace.kt index 561cb5fd4..1b0e08f5c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Float64LinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Float64LinearSpace.kt @@ -6,12 +6,19 @@ package space.kscience.kmath.linear import space.kscience.kmath.PerformancePitfall -import space.kscience.kmath.nd.* +import space.kscience.kmath.nd.Floa64FieldOpsND +import space.kscience.kmath.nd.ShapeND +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.nd.asND import space.kscience.kmath.operations.Float64BufferOps import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.map public object Float64LinearSpace : LinearSpace { @@ -21,36 +28,36 @@ public object Float64LinearSpace : LinearSpace { rows: Int, columns: Int, initializer: Float64Field.(i: Int, j: Int) -> Double, - ): Matrix = Floa64FieldOpsND.structureND(ShapeND(rows, columns)) { (i, j) -> + ): Matrix = Floa64FieldOpsND.structureND(ShapeND(rows, columns)) { (i, j) -> Float64Field.initializer(i, j) }.as2D() override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): Float64Buffer = Float64Buffer(size) { Float64Field.initializer(it) } - override fun Matrix.unaryMinus(): Matrix = Floa64FieldOpsND { + override fun Matrix.unaryMinus(): Matrix = Floa64FieldOpsND { asND().map { -it }.as2D() } - override fun Matrix.plus(other: Matrix): Matrix = Floa64FieldOpsND { + override fun Matrix.plus(other: Matrix): Matrix = Floa64FieldOpsND { require(shape == other.shape) { "Shape mismatch on Matrix::plus. Expected $shape but found ${other.shape}" } asND().plus(other.asND()).as2D() } - override fun Matrix.minus(other: Matrix): Matrix = Floa64FieldOpsND { + override fun Matrix.minus(other: Matrix): Matrix = Floa64FieldOpsND { require(shape == other.shape) { "Shape mismatch on Matrix::minus. Expected $shape but found ${other.shape}" } asND().minus(other.asND()).as2D() } // Create a continuous in-memory representation of this vector for better memory layout handling - private fun Buffer.linearize() = if (this is Float64Buffer) { + private fun Buffer.linearize() = if (this is Float64Buffer) { this.array } else { DoubleArray(size) { get(it) } } @OptIn(PerformancePitfall::class) - override fun Matrix.dot(other: Matrix): Matrix { + override fun Matrix.dot(other: Matrix): Matrix { require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" } val rows = this@dot.rows.map { it.linearize() } val columns = other.columns.map { it.linearize() } @@ -67,7 +74,7 @@ public object Float64LinearSpace : LinearSpace { } @OptIn(PerformancePitfall::class) - override fun Matrix.dot(vector: Point): Float64Buffer { + override fun Matrix.dot(vector: Point): Float64Buffer { require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" } val rows = this@dot.rows.map { it.linearize() } val indices = 0 until this.colNum @@ -82,27 +89,27 @@ public object Float64LinearSpace : LinearSpace { } - override fun Matrix.times(value: Double): Matrix = Floa64FieldOpsND { + override fun Matrix.times(value: Double): Matrix = Floa64FieldOpsND { asND().map { it * value }.as2D() } - public override fun Point.plus(other: Point): Float64Buffer = Float64BufferOps.run { + public override fun Point.plus(other: Point): Float64Buffer = Float64BufferOps.run { this@plus + other } - public override fun Point.minus(other: Point): Float64Buffer = Float64BufferOps.run { + public override fun Point.minus(other: Point): Float64Buffer = Float64BufferOps.run { this@minus - other } - public override fun Point.times(value: Double): Float64Buffer = Float64BufferOps.run { + public override fun Point.times(value: Double): Float64Buffer = Float64BufferOps.run { scale(this@times, value) } - public operator fun Point.div(value: Double): Float64Buffer = Float64BufferOps.run { + public operator fun Point.div(value: Double): Float64Buffer = Float64BufferOps.run { scale(this@div, 1.0 / value) } - public override fun Double.times(v: Point): Float64Buffer = v * this + public override fun Double.times(v: Point): Float64Buffer = v * this } 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 03859ba83..f9ebcc452 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 @@ -163,9 +163,9 @@ public fun > Field.lup( public fun Field.lup( - matrix: Matrix, + matrix: Matrix, singularityThreshold: Double = 1e-11, -): GenericLupDecomposition = lup(matrix) { it < singularityThreshold } +): GenericLupDecomposition = lup(matrix) { it < singularityThreshold } private fun Field.solve( lup: LupDecomposition, @@ -235,5 +235,5 @@ public fun > LinearSpace>.lupSolver( override fun inverse(matrix: Matrix): Matrix = solve(matrix, one(matrix.rowNum, matrix.colNum)) } -public fun LinearSpace.lupSolver(singularityThreshold: Double = 1e-11): LinearSolver = +public fun LinearSpace.lupSolver(singularityThreshold: Double = 1e-11): LinearSolver = lupSolver { it < singularityThreshold } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/cumulative.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/cumulative.kt index 60f71b943..761478f9b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/cumulative.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/cumulative.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.misc import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import kotlin.jvm.JvmName /** @@ -47,7 +48,7 @@ public fun Iterable.cumulativeSum(ring: Ring): Iterable = ring { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -public fun Iterable.cumulativeSum(): Iterable = cumulative(0.0) { element, sum -> sum + element } +public fun Iterable.cumulativeSum(): Iterable = cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") public fun Iterable.cumulativeSum(): Iterable = cumulative(0) { element, sum -> sum + element } @@ -59,7 +60,7 @@ public fun Sequence.cumulativeSum(ring: Ring): Sequence = ring { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -public fun Sequence.cumulativeSum(): Sequence = cumulative(0.0) { element, sum -> sum + element } +public fun Sequence.cumulativeSum(): Sequence = cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") public fun Sequence.cumulativeSum(): Sequence = cumulative(0) { element, sum -> sum + element } @@ -71,7 +72,7 @@ public fun List.cumulativeSum(group: Ring): List = group { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -public fun List.cumulativeSum(): List = cumulative(0.0) { element, sum -> sum + element } +public fun List.cumulativeSum(): List = cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") public fun List.cumulativeSum(): List = cumulative(0) { element, sum -> sum + element } 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 4bb853af8..62e0f39e0 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 @@ -8,6 +8,7 @@ package space.kscience.kmath.nd 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 +21,7 @@ import kotlin.math.pow as kpow public class Float64BufferND( indexes: ShapeIndexer, override val buffer: Float64Buffer, -) : MutableBufferND(indexes, buffer), MutableStructureNDOfDouble { +) : MutableBufferND(indexes, buffer), MutableStructureNDOfDouble { override fun getDouble(index: IntArray): Double = buffer[indices.offset(index)] @@ -30,11 +31,11 @@ public class Float64BufferND( } -public sealed class Floa64FieldOpsND : BufferedFieldOpsND(Float64Field.bufferAlgebra), - ScaleOperations>, ExtendedFieldOps> { +public sealed class Floa64FieldOpsND : BufferedFieldOpsND(Float64Field.bufferAlgebra), + ScaleOperations>, ExtendedFieldOps> { @OptIn(PerformancePitfall::class) - override fun StructureND.toBufferND(): Float64BufferND = when (this) { + override fun StructureND.toBufferND(): Float64BufferND = when (this) { is Float64BufferND -> this else -> { val indexer = indexerBuilder(shape) @@ -64,16 +65,16 @@ public sealed class Floa64FieldOpsND : BufferedFieldOpsND( } @OptIn(PerformancePitfall::class) - override fun StructureND.map(transform: Float64Field.(Double) -> Double): BufferND = + override fun StructureND.map(transform: Float64Field.(Double) -> Double): BufferND = mapInline(toBufferND()) { Float64Field.transform(it) } @OptIn(PerformancePitfall::class) override fun zip( - left: StructureND, - right: StructureND, + left: StructureND, + right: StructureND, transform: Float64Field.(Double, Double) -> Double, - ): BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> Float64Field.transform(l, r) } + ): BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> Float64Field.transform(l, r) } override fun mutableStructureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): Float64BufferND { val indexer = indexerBuilder(shape) @@ -85,102 +86,102 @@ public sealed class Floa64FieldOpsND : BufferedFieldOpsND( ) } - override fun add(left: StructureND, right: StructureND): Float64BufferND = + override fun add(left: StructureND, right: StructureND): Float64BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> l + r } - override fun multiply(left: StructureND, right: StructureND): Float64BufferND = + override fun multiply(left: StructureND, right: StructureND): Float64BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> l * r } - override fun StructureND.unaryMinus(): Float64BufferND = mapInline(toBufferND()) { -it } + override fun StructureND.unaryMinus(): Float64BufferND = mapInline(toBufferND()) { -it } - override fun StructureND.div(arg: StructureND): Float64BufferND = + override fun StructureND.div(arg: StructureND): Float64BufferND = zipInline(toBufferND(), arg.toBufferND()) { l, r -> l / r } - override fun divide(left: StructureND, right: StructureND): Float64BufferND = + override fun divide(left: StructureND, right: StructureND): Float64BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l: Double, r: Double -> l / r } - override fun StructureND.div(arg: Double): Float64BufferND = + override fun StructureND.div(arg: Double): Float64BufferND = mapInline(toBufferND()) { it / arg } - override fun Double.div(arg: StructureND): Float64BufferND = + override fun Double.div(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { this / it } - override fun StructureND.unaryPlus(): Float64BufferND = toBufferND() + override fun StructureND.unaryPlus(): Float64BufferND = toBufferND() - override fun StructureND.plus(arg: StructureND): Float64BufferND = + override fun StructureND.plus(arg: StructureND): Float64BufferND = zipInline(toBufferND(), arg.toBufferND()) { l: Double, r: Double -> l + r } - override fun StructureND.minus(arg: StructureND): Float64BufferND = + override fun StructureND.minus(arg: StructureND): Float64BufferND = zipInline(toBufferND(), arg.toBufferND()) { l: Double, r: Double -> l - r } - override fun StructureND.times(arg: StructureND): Float64BufferND = + override fun StructureND.times(arg: StructureND): Float64BufferND = zipInline(toBufferND(), arg.toBufferND()) { l: Double, r: Double -> l * r } - override fun StructureND.times(k: Number): Float64BufferND = + override fun StructureND.times(k: Number): Float64BufferND = mapInline(toBufferND()) { it * k.toDouble() } - override fun StructureND.div(k: Number): Float64BufferND = + override fun StructureND.div(k: Number): Float64BufferND = mapInline(toBufferND()) { it / k.toDouble() } - override fun Number.times(arg: StructureND): Float64BufferND = arg * this + override fun Number.times(arg: StructureND): Float64BufferND = arg * this - override fun StructureND.plus(arg: Double): Float64BufferND = mapInline(toBufferND()) { it + arg } + override fun StructureND.plus(arg: Double): Float64BufferND = mapInline(toBufferND()) { it + arg } - override fun StructureND.minus(arg: Double): StructureND = mapInline(toBufferND()) { it - arg } + override fun StructureND.minus(arg: Double): StructureND = mapInline(toBufferND()) { it - arg } - override fun Double.plus(arg: StructureND): StructureND = arg + this + override fun Double.plus(arg: StructureND): StructureND = arg + this - override fun Double.minus(arg: StructureND): StructureND = mapInline(arg.toBufferND()) { this - it } + override fun Double.minus(arg: StructureND): StructureND = mapInline(arg.toBufferND()) { this - it } - override fun scale(a: StructureND, value: Double): Float64BufferND = + override fun scale(a: StructureND, value: Double): Float64BufferND = mapInline(a.toBufferND()) { it * value } - override fun exp(arg: StructureND): Float64BufferND = + override fun exp(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.exp(it) } - override fun ln(arg: StructureND): Float64BufferND = + override fun ln(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.ln(it) } - override fun sin(arg: StructureND): Float64BufferND = + override fun sin(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.sin(it) } - override fun cos(arg: StructureND): Float64BufferND = + override fun cos(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.cos(it) } - override fun tan(arg: StructureND): Float64BufferND = + override fun tan(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.tan(it) } - override fun asin(arg: StructureND): Float64BufferND = + override fun asin(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.asin(it) } - override fun acos(arg: StructureND): Float64BufferND = + override fun acos(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.acos(it) } - override fun atan(arg: StructureND): Float64BufferND = + override fun atan(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.atan(it) } - override fun sinh(arg: StructureND): Float64BufferND = + override fun sinh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.sinh(it) } - override fun cosh(arg: StructureND): Float64BufferND = + override fun cosh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.cosh(it) } - override fun tanh(arg: StructureND): Float64BufferND = + override fun tanh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.tanh(it) } - override fun asinh(arg: StructureND): Float64BufferND = + override fun asinh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.asinh(it) } - override fun acosh(arg: StructureND): Float64BufferND = + override fun acosh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.acosh(it) } - override fun atanh(arg: StructureND): Float64BufferND = + override fun atanh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.atanh(it) } override fun power( - arg: StructureND, + arg: StructureND, pow: Number, - ): StructureND = if (pow is Int) { + ): StructureND = if (pow is Int) { mapInline(arg.toBufferND()) { it.pow(pow) } } else { mapInline(arg.toBufferND()) { it.pow(pow.toDouble()) } @@ -191,18 +192,18 @@ public sealed class Floa64FieldOpsND : BufferedFieldOpsND( @OptIn(UnstableKMathAPI::class) public class Float64FieldND(override val shape: ShapeND) : - Floa64FieldOpsND(), FieldND, NumbersAddOps>, - ExtendedField> { + Floa64FieldOpsND(), FieldND, NumbersAddOps>, + ExtendedField> { - override fun power(arg: StructureND, pow: UInt): Float64BufferND = mapInline(arg.toBufferND()) { + override fun power(arg: StructureND, pow: UInt): Float64BufferND = mapInline(arg.toBufferND()) { it.kpow(pow.toInt()) } - override fun power(arg: StructureND, pow: Int): Float64BufferND = mapInline(arg.toBufferND()) { + override fun power(arg: StructureND, pow: Int): Float64BufferND = mapInline(arg.toBufferND()) { it.kpow(pow) } - override fun power(arg: StructureND, pow: Number): Float64BufferND = if (pow.isInteger()) { + override fun power(arg: StructureND, pow: Number): Float64BufferND = if (pow.isInteger()) { power(arg, pow.toInt()) } else { val dpow = pow.toDouble() @@ -212,17 +213,17 @@ public class Float64FieldND(override val shape: ShapeND) : } } - override fun sinh(arg: StructureND): Float64BufferND = super.sinh(arg) + override fun sinh(arg: StructureND): Float64BufferND = super.sinh(arg) - override fun cosh(arg: StructureND): Float64BufferND = super.cosh(arg) + override fun cosh(arg: StructureND): Float64BufferND = super.cosh(arg) - override fun tanh(arg: StructureND): Float64BufferND = super.tan(arg) + override fun tanh(arg: StructureND): Float64BufferND = super.tan(arg) - override fun asinh(arg: StructureND): Float64BufferND = super.asinh(arg) + override fun asinh(arg: StructureND): Float64BufferND = super.asinh(arg) - override fun acosh(arg: StructureND): Float64BufferND = super.acosh(arg) + override fun acosh(arg: StructureND): Float64BufferND = super.acosh(arg) - override fun atanh(arg: StructureND): Float64BufferND = super.atanh(arg) + override fun atanh(arg: StructureND): Float64BufferND = super.atanh(arg) override fun number(value: Number): Float64BufferND { val d = value.toDouble() // minimize conversions diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt index 5c1021147..be2856c3b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt @@ -14,6 +14,7 @@ import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import kotlin.math.abs public interface StructureAttribute : Attribute @@ -76,8 +77,8 @@ public interface StructureND : AttributeContainer, WithShape { @PerformancePitfall public fun contentEquals( - st1: StructureND, - st2: StructureND, + st1: StructureND, + st2: StructureND, tolerance: Double = 1e-11, ): Boolean { if (st1 === st2) return true @@ -210,7 +211,7 @@ public fun > LinearSpace>.contentEquals( @PerformancePitfall public operator fun StructureND.get(vararg index: Int): T = get(index) -public operator fun StructureND.get(vararg index: Int): Double = getDouble(index) +public operator fun StructureND.get(vararg index: Int): Double = getDouble(index) public operator fun StructureND.get(vararg index: Int): Int = getInt(index) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/VirtualStructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/VirtualStructureND.kt index a7e0b0053..3765707a8 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/VirtualStructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/VirtualStructureND.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.nd import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnstableKMathAPI +import space.kscience.kmath.structures.Float64 public open class VirtualStructureND( override val shape: ShapeND, @@ -24,7 +25,7 @@ public open class VirtualStructureND( public class VirtualDoubleStructureND( shape: ShapeND, producer: (IntArray) -> Double, -) : VirtualStructureND(shape, producer) +) : VirtualStructureND(shape, producer) @UnstableKMathAPI public class VirtualIntStructureND( 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 4831b4138..e9b104746 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 @@ -6,8 +6,9 @@ package space.kscience.kmath.nd import space.kscience.kmath.PerformancePitfall +import space.kscience.kmath.structures.Float64 -public interface StructureNDOfDouble : StructureND { +public interface StructureNDOfDouble : StructureND { /** * Guaranteed non-blocking access to content @@ -19,10 +20,10 @@ public interface StructureNDOfDouble : StructureND { * Optimized method to access primitive without boxing if possible */ @OptIn(PerformancePitfall::class) -public fun StructureND.getDouble(index: IntArray): Double = +public fun StructureND.getDouble(index: IntArray): Double = if (this is StructureNDOfDouble) getDouble(index) else get(index) -public interface MutableStructureNDOfDouble : StructureNDOfDouble, MutableStructureND { +public interface MutableStructureNDOfDouble : StructureNDOfDouble, MutableStructureND { /** * Guaranteed non-blocking access to content @@ -31,7 +32,7 @@ public interface MutableStructureNDOfDouble : StructureNDOfDouble, MutableStruct } @OptIn(PerformancePitfall::class) -public fun MutableStructureND.getDouble(index: IntArray): Double = +public fun MutableStructureND.getDouble(index: IntArray): Double = if (this is StructureNDOfDouble) getDouble(index) else get(index) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt index 6c260db9f..a55b63cf4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.operations import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBufferFactory @@ -182,7 +183,7 @@ public fun > BufferFieldOps.withSize(size: Int): BufferFie //Double buffer specialization -public fun BufferField.buffer(vararg elements: Number): Buffer { +public fun BufferField.buffer(vararg elements: Number): Buffer { require(elements.size == size) { "Expected $size elements but found ${elements.size}" } return elementBufferFactory(size) { elements[it].toDouble() } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferField.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferField.kt index 1c4bda18f..8ddb5d445 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferField.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferField.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.operations import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer /** @@ -13,23 +14,23 @@ import space.kscience.kmath.structures.Float64Buffer * * @property size the size of buffers to operate on. */ -public class Float64BufferField(public val size: Int) : ExtendedField>, Float64BufferOps() { - override val zero: Buffer by lazy { Float64Buffer(size) { 0.0 } } - override val one: Buffer by lazy { Float64Buffer(size) { 1.0 } } +public class Float64BufferField(public val size: Int) : ExtendedField>, Float64BufferOps() { + override val zero: Buffer by lazy { Float64Buffer(size) { 0.0 } } + override val one: Buffer by lazy { Float64Buffer(size) { 1.0 } } - override fun sinh(arg: Buffer): Float64Buffer = super.sinh(arg) + override fun sinh(arg: Buffer): Float64Buffer = super.sinh(arg) - override fun cosh(arg: Buffer): Float64Buffer = super.cosh(arg) + override fun cosh(arg: Buffer): Float64Buffer = super.cosh(arg) - override fun tanh(arg: Buffer): Float64Buffer = super.tanh(arg) + override fun tanh(arg: Buffer): Float64Buffer = super.tanh(arg) - override fun asinh(arg: Buffer): Float64Buffer = super.asinh(arg) + override fun asinh(arg: Buffer): Float64Buffer = super.asinh(arg) - override fun acosh(arg: Buffer): Float64Buffer = super.acosh(arg) + override fun acosh(arg: Buffer): Float64Buffer = super.acosh(arg) - override fun atanh(arg: Buffer): Float64Buffer = super.atanh(arg) + override fun atanh(arg: Buffer): Float64Buffer = super.atanh(arg) - override fun power(arg: Buffer, pow: Number): Float64Buffer = if (pow.isInteger()) { + override fun power(arg: Buffer, pow: Number): Float64Buffer = if (pow.isInteger()) { arg.map { it.pow(pow.toInt()) } } else { arg.map { @@ -38,6 +39,6 @@ public class Float64BufferField(public val size: Int) : ExtendedField) -> Buffer = + override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer = super.unaryOperationFunction(operation) } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferOps.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferOps.kt index ca4eacb8a..dfa542d83 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferOps.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferOps.kt @@ -14,43 +14,43 @@ import kotlin.math.sqrt /** * [ExtendedFieldOps] over [Float64Buffer]. */ -public abstract class Float64BufferOps : BufferAlgebra, ExtendedFieldOps>, - Norm, Double> { +public abstract class Float64BufferOps : BufferAlgebra, ExtendedFieldOps>, + Norm, Double> { override val elementAlgebra: Float64Field get() = Float64Field - override val elementBufferFactory: MutableBufferFactory get() = elementAlgebra.bufferFactory + override val elementBufferFactory: MutableBufferFactory get() = elementAlgebra.bufferFactory @Suppress("OVERRIDE_BY_INLINE") @OptIn(UnstableKMathAPI::class) - final override inline fun Buffer.map(block: Float64Field.(Double) -> Double): Float64Buffer = + final override inline fun Buffer.map(block: Float64Field.(Double) -> Double): Float64Buffer = DoubleArray(size) { Float64Field.block(getDouble(it)) }.asBuffer() @OptIn(UnstableKMathAPI::class) @Suppress("OVERRIDE_BY_INLINE") - final override inline fun Buffer.mapIndexed(block: Float64Field.(index: Int, arg: Double) -> Double): Float64Buffer = + final override inline fun Buffer.mapIndexed(block: Float64Field.(index: Int, arg: Double) -> Double): Float64Buffer = Float64Buffer(size) { Float64Field.block(it, getDouble(it)) } @OptIn(UnstableKMathAPI::class) @Suppress("OVERRIDE_BY_INLINE") - final override inline fun Buffer.zip( - other: Buffer, + final override inline fun Buffer.zip( + other: Buffer, block: Float64Field.(left: Double, right: Double) -> Double, ): Float64Buffer { require(size == other.size) { "Incompatible buffer sizes. left: ${size}, right: ${other.size}" } return Float64Buffer(size) { Float64Field.block(getDouble(it), other.getDouble(it)) } } - override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer = + override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer = super.unaryOperationFunction(operation) - override fun binaryOperationFunction(operation: String): (left: Buffer, right: Buffer) -> Buffer = + override fun binaryOperationFunction(operation: String): (left: Buffer, right: Buffer) -> Buffer = super.binaryOperationFunction(operation) - override fun Buffer.unaryMinus(): Float64Buffer = map { -it } + override fun Buffer.unaryMinus(): Float64Buffer = map { -it } - override fun add(left: Buffer, right: Buffer): Float64Buffer { + override fun add(left: Buffer, right: Buffer): Float64Buffer { require(right.size == left.size) { "The size of the first buffer ${left.size} should be the same as for second one: ${right.size} " } @@ -62,9 +62,9 @@ public abstract class Float64BufferOps : BufferAlgebra, Ex } else Float64Buffer(DoubleArray(left.size) { left[it] + right[it] }) } - override fun Buffer.plus(arg: Buffer): Float64Buffer = add(this, arg) + override fun Buffer.plus(arg: Buffer): Float64Buffer = add(this, arg) - override fun Buffer.minus(arg: Buffer): Float64Buffer { + override fun Buffer.minus(arg: Buffer): Float64Buffer { require(arg.size == this.size) { "The size of the first buffer ${this.size} should be the same as for second one: ${arg.size} " } @@ -77,7 +77,7 @@ public abstract class Float64BufferOps : BufferAlgebra, Ex } // -// override fun multiply(a: Buffer, k: Number): RealBuffer { +// override fun multiply(a: Buffer, k: Number): RealBuffer { // val kValue = k.toDouble() // // return if (a is RealBuffer) { @@ -86,7 +86,7 @@ public abstract class Float64BufferOps : BufferAlgebra, Ex // } else RealBuffer(DoubleArray(a.size) { a[it] * kValue }) // } // -// override fun divide(a: Buffer, k: Number): RealBuffer { +// override fun divide(a: Buffer, k: Number): RealBuffer { // val kValue = k.toDouble() // // return if (a is RealBuffer) { @@ -96,7 +96,7 @@ public abstract class Float64BufferOps : BufferAlgebra, Ex // } @UnstableKMathAPI - override fun multiply(left: Buffer, right: Buffer): Float64Buffer { + override fun multiply(left: Buffer, right: Buffer): Float64Buffer { require(right.size == left.size) { "The size of the first buffer ${left.size} should be the same as for second one: ${right.size} " } @@ -108,7 +108,7 @@ public abstract class Float64BufferOps : BufferAlgebra, Ex } else Float64Buffer(DoubleArray(left.size) { left[it] * right[it] }) } - override fun divide(left: Buffer, right: Buffer): Float64Buffer { + override fun divide(left: Buffer, right: Buffer): Float64Buffer { require(right.size == left.size) { "The size of the first buffer ${left.size} should be the same as for second one: ${right.size} " } @@ -120,39 +120,39 @@ public abstract class Float64BufferOps : BufferAlgebra, Ex } else Float64Buffer(DoubleArray(left.size) { left[it] / right[it] }) } - override fun sin(arg: Buffer): Float64Buffer = arg.map { sin(it) } + override fun sin(arg: Buffer): Float64Buffer = arg.map { sin(it) } - override fun cos(arg: Buffer): Float64Buffer = arg.map { cos(it) } + override fun cos(arg: Buffer): Float64Buffer = arg.map { cos(it) } - override fun tan(arg: Buffer): Float64Buffer = arg.map { tan(it) } + override fun tan(arg: Buffer): Float64Buffer = arg.map { tan(it) } - override fun asin(arg: Buffer): Float64Buffer = arg.map { asin(it) } + override fun asin(arg: Buffer): Float64Buffer = arg.map { asin(it) } - override fun acos(arg: Buffer): Float64Buffer = arg.map { acos(it) } + override fun acos(arg: Buffer): Float64Buffer = arg.map { acos(it) } - override fun atan(arg: Buffer): Float64Buffer = arg.map { atan(it) } + override fun atan(arg: Buffer): Float64Buffer = arg.map { atan(it) } - override fun sinh(arg: Buffer): Float64Buffer = arg.map { sinh(it) } + override fun sinh(arg: Buffer): Float64Buffer = arg.map { sinh(it) } - override fun cosh(arg: Buffer): Float64Buffer = arg.map { cosh(it) } + override fun cosh(arg: Buffer): Float64Buffer = arg.map { cosh(it) } - override fun tanh(arg: Buffer): Float64Buffer = arg.map { tanh(it) } + override fun tanh(arg: Buffer): Float64Buffer = arg.map { tanh(it) } - override fun asinh(arg: Buffer): Float64Buffer = arg.map { asinh(it) } + override fun asinh(arg: Buffer): Float64Buffer = arg.map { asinh(it) } - override fun acosh(arg: Buffer): Float64Buffer = arg.map { acosh(it) } + override fun acosh(arg: Buffer): Float64Buffer = arg.map { acosh(it) } - override fun atanh(arg: Buffer): Float64Buffer = arg.map { atanh(it) } + override fun atanh(arg: Buffer): Float64Buffer = arg.map { atanh(it) } - override fun exp(arg: Buffer): Float64Buffer = arg.map { exp(it) } + override fun exp(arg: Buffer): Float64Buffer = arg.map { exp(it) } - override fun ln(arg: Buffer): Float64Buffer = arg.map { ln(it) } + override fun ln(arg: Buffer): Float64Buffer = arg.map { ln(it) } - override fun norm(arg: Buffer): Double = Float64L2Norm.norm(arg) + override fun norm(arg: Buffer): Double = Float64L2Norm.norm(arg) - override fun scale(a: Buffer, value: Double): Float64Buffer = a.map { it * value } + override fun scale(a: Buffer, value: Double): Float64Buffer = a.map { it * value } - override fun power(arg: Buffer, pow: Number): Buffer = if (pow is Int) { + override fun power(arg: Buffer, pow: Number): Buffer = if (pow is Int) { arg.map { it.pow(pow) } } else { arg.map { it.pow(pow.toDouble()) } @@ -161,11 +161,11 @@ public abstract class Float64BufferOps : BufferAlgebra, Ex public companion object : Float64BufferOps() } -public object Float64L2Norm : Norm, Double> { - override fun norm(arg: Point): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) }) +public object Float64L2Norm : Norm, Double> { + override fun norm(arg: Point): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) }) } -public fun Float64BufferOps.sum(buffer: Buffer): Double = buffer.reduce(Double::plus) +public fun Float64BufferOps.sum(buffer: Buffer): Double = buffer.reduce(Double::plus) /** * Sum of elements using given [conversion] @@ -173,7 +173,7 @@ public fun Float64BufferOps.sum(buffer: Buffer): Double = buffer.reduce( public inline fun Float64BufferOps.sumOf(buffer: Buffer, conversion: (T) -> Double): Double = buffer.fold(0.0) { acc, value -> acc + conversion(value) } -public fun Float64BufferOps.average(buffer: Buffer): Double = sum(buffer) / buffer.size +public fun Float64BufferOps.average(buffer: Buffer): Double = sum(buffer) / buffer.size /** * Average of elements using given [conversion] @@ -181,14 +181,14 @@ public fun Float64BufferOps.average(buffer: Buffer): Double = sum(buffer public inline fun Float64BufferOps.averageOf(buffer: Buffer, conversion: (T) -> Double): Double = sumOf(buffer, conversion) / buffer.size -public fun Float64BufferOps.dispersion(buffer: Buffer): Double { +public fun Float64BufferOps.dispersion(buffer: Buffer): Double { val av = average(buffer) return buffer.fold(0.0) { acc, value -> acc + (value - av).pow(2) } / buffer.size } -public fun Float64BufferOps.std(buffer: Buffer): Double = sqrt(dispersion(buffer)) +public fun Float64BufferOps.std(buffer: Buffer): Double = sqrt(dispersion(buffer)) -public fun Float64BufferOps.covariance(x: Buffer, y: Buffer): Double { +public fun Float64BufferOps.covariance(x: Buffer, y: Buffer): Double { require(x.size == y.size) { "Expected buffers of the same size, but x.size == ${x.size} and y.size == ${y.size}" } val xMean = average(x) val yMean = average(y) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt index 96a080afd..9799c5428 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt @@ -4,6 +4,7 @@ */ package space.kscience.kmath.operations +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.pow as kpow @@ -66,8 +67,8 @@ public interface ExtendedField : ExtendedFieldOps, Field, NumericAlgebr * A field for [Double] without boxing. Does not produce an appropriate field element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") -public object Float64Field : ExtendedField, Norm, ScaleOperations { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory() +public object Float64Field : ExtendedField, Norm, ScaleOperations { + override val bufferFactory: MutableBufferFactory = MutableBufferFactory() override val zero: Double get() = 0.0 override val one: Double get() = 1.0 diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt index 7fc06c43e..73454ca2d 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt @@ -115,7 +115,7 @@ public fun Buffer( size: Int, initializer: (Int) -> T, ): Buffer = when (type.kType) { - typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as Buffer + typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as Buffer typeOf() -> MutableBuffer.short(size) { initializer(it) as Short } as Buffer typeOf() -> MutableBuffer.int(size) { initializer(it) as Int } as Buffer typeOf() -> MutableBuffer.long(size) { initializer(it) as Long } as Buffer @@ -134,7 +134,7 @@ public inline fun Buffer(size: Int, initializer: (Int) -> T): Buffer //code duplication here because we want to inline initializers val type = safeTypeOf() return when (type.kType) { - typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as Buffer + typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as Buffer typeOf() -> MutableBuffer.short(size) { initializer(it) as Short } as Buffer typeOf() -> MutableBuffer.int(size) { initializer(it) as Int } as Buffer typeOf() -> MutableBuffer.long(size) { initializer(it) as Long } as Buffer diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float64Buffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float64Buffer.kt index 38b737fd5..beeccbdcf 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float64Buffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float64Buffer.kt @@ -14,7 +14,7 @@ import kotlin.jvm.JvmInline * @property array the underlying array. */ @JvmInline -public value class Float64Buffer(public val array: DoubleArray) : PrimitiveBuffer { +public value class Float64Buffer(public val array: DoubleArray) : PrimitiveBuffer { override val size: Int get() = array.size @@ -54,7 +54,7 @@ public fun Float64Buffer(vararg doubles: Double): Float64Buffer = Float64Buffer( /** * Returns a new [DoubleArray] containing all the elements of this [Buffer]. */ -public fun Buffer.toDoubleArray(): DoubleArray = when (this) { +public fun Buffer.toDoubleArray(): DoubleArray = when (this) { is Float64Buffer -> array else -> DoubleArray(size, ::get) } @@ -62,7 +62,7 @@ public fun Buffer.toDoubleArray(): DoubleArray = when (this) { /** * Represent this buffer as [Float64Buffer]. Does not guarantee that changes in the original buffer are reflected on this buffer. */ -public fun Buffer.toFloat64Buffer(): Float64Buffer = when (this) { +public fun Buffer.toFloat64Buffer(): Float64Buffer = when (this) { is Float64Buffer -> this else -> DoubleArray(size, ::get).asBuffer() } @@ -79,5 +79,5 @@ public fun DoubleArray.asBuffer(): Float64Buffer = Float64Buffer(this) public fun interface Float64BufferTransform : BufferTransform { public fun transform(arg: Float64Buffer): Float64Buffer - override fun transform(arg: Buffer): Float64Buffer = arg.toFloat64Buffer() + override fun transform(arg: Buffer): Float64Buffer = arg.toFloat64Buffer() } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt index eeee5faad..a32cba214 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt @@ -99,7 +99,7 @@ public inline fun MutableBuffer( typeOf() -> MutableBuffer.int(size) { initializer(it) as Int32 } as MutableBuffer typeOf() -> MutableBuffer.long(size) { initializer(it) as Int64 } as MutableBuffer typeOf() -> MutableBuffer.float(size) { initializer(it) as Float } as MutableBuffer - typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as MutableBuffer + typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as MutableBuffer //TODO add unsigned types else -> MutableListBuffer(MutableList(size, initializer)) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt index 6035811b9..a590a1cf8 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt @@ -11,7 +11,7 @@ import space.kscience.kmath.UnstableKMathAPI * Non-boxing access to primitive [Double] */ @UnstableKMathAPI -public fun Buffer.getDouble(index: Int): Double = if (this is BufferView) { +public fun Buffer.getDouble(index: Int): Double = if (this is BufferView) { val originIndex = originIndex(index) if (originIndex >= 0) { origin.getDouble(originIndex) diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt index ca2e31d7d..efd993f85 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.expressions import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFails @@ -37,7 +38,7 @@ class ExpressionFieldTest { @Test fun valueExpression() { - val expressionBuilder: FunctionalExpressionField.() -> Expression = { + val expressionBuilder: FunctionalExpressionField.() -> Expression = { val x by binding x * x + 2 * x + one } diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt index dcfb16042..529ecbf73 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.expressions import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.asBuffer import kotlin.math.E import kotlin.math.PI @@ -21,18 +22,18 @@ internal class SimpleAutoDiffTest { fun dx( xBinding: Pair, - body: SimpleAutoDiffField.(x: AutoDiffValue) -> AutoDiffValue, - ): DerivationResult = Float64Field.simpleAutoDiff(xBinding) { body(bindSymbol(xBinding.first)) } + body: SimpleAutoDiffField.(x: AutoDiffValue) -> AutoDiffValue, + ): DerivationResult = Float64Field.simpleAutoDiff(xBinding) { body(bindSymbol(xBinding.first)) } fun dxy( xBinding: Pair, yBinding: Pair, - body: SimpleAutoDiffField.(x: AutoDiffValue, y: AutoDiffValue) -> AutoDiffValue, - ): DerivationResult = Float64Field.simpleAutoDiff(xBinding, yBinding) { + body: SimpleAutoDiffField.(x: AutoDiffValue, y: AutoDiffValue) -> AutoDiffValue, + ): DerivationResult = Float64Field.simpleAutoDiff(xBinding, yBinding) { body(bindSymbol(xBinding.first), bindSymbol(yBinding.first)) } - fun diff(block: SimpleAutoDiffField.() -> AutoDiffValue): SimpleAutoDiffExpression { + fun diff(block: SimpleAutoDiffField.() -> AutoDiffValue): SimpleAutoDiffExpression { return SimpleAutoDiffExpression(Float64Field, block) } 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 30b8aaffb..3489e92a4 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 @@ -10,6 +10,7 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.algebra +import space.kscience.kmath.structures.Float64 import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -38,7 +39,7 @@ class MatrixTest { @Test fun testMatrixExtension() = Double.algebra.linearSpace.run { - val transitionMatrix: Matrix = VirtualMatrix(6, 6) { row, col -> + val transitionMatrix: Matrix = VirtualMatrix(6, 6) { row, col -> when { col == 0 -> .50 row + 1 == col -> .50 @@ -47,7 +48,7 @@ class MatrixTest { } } - infix fun Matrix.pow(power: Int): Matrix { + infix fun Matrix.pow(power: Int): Matrix { var res = this repeat(power - 1) { res = res dot this@pow diff --git a/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/linear/Float64ParallelLinearSpace.kt b/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/linear/Float64ParallelLinearSpace.kt index 088b0fd9f..1a4c04bde 100644 --- a/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/linear/Float64ParallelLinearSpace.kt +++ b/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/linear/Float64ParallelLinearSpace.kt @@ -11,6 +11,7 @@ import space.kscience.kmath.operations.Float64BufferOps import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer import java.util.stream.IntStream @@ -25,7 +26,7 @@ public object Float64ParallelLinearSpace : LinearSpace { rows: Int, columns: Int, initializer: Float64Field.(i: Int, j: Int) -> Double, - ): Matrix { + ): Matrix { val shape = ShapeND(rows, columns) val indexer = BufferAlgebraND.defaultIndexerBuilder(shape) @@ -43,29 +44,29 @@ public object Float64ParallelLinearSpace : LinearSpace { override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): Float64Buffer = IntStream.range(0, size).parallel().mapToDouble { Float64Field.initializer(it) }.toArray().asBuffer() - override fun Matrix.unaryMinus(): Matrix = Floa64FieldOpsND { + override fun Matrix.unaryMinus(): Matrix = Floa64FieldOpsND { asND().map { -it }.as2D() } - override fun Matrix.plus(other: Matrix): Matrix = Floa64FieldOpsND { + override fun Matrix.plus(other: Matrix): Matrix = Floa64FieldOpsND { require(shape == other.shape) { "Shape mismatch on Matrix::plus. Expected $shape but found ${other.shape}" } asND().plus(other.asND()).as2D() } - override fun Matrix.minus(other: Matrix): Matrix = Floa64FieldOpsND { + override fun Matrix.minus(other: Matrix): Matrix = Floa64FieldOpsND { require(shape == other.shape) { "Shape mismatch on Matrix::minus. Expected $shape but found ${other.shape}" } asND().minus(other.asND()).as2D() } // Create a continuous in-memory representation of this vector for better memory layout handling - private fun Buffer.linearize() = if (this is Float64Buffer) { + private fun Buffer.linearize() = if (this is Float64Buffer) { this.array } else { DoubleArray(size) { get(it) } } @OptIn(PerformancePitfall::class) - override fun Matrix.dot(other: Matrix): Matrix { + override fun Matrix.dot(other: Matrix): Matrix { require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" } val rows = this@dot.rows.map { it.linearize() } val columns = other.columns.map { it.linearize() } @@ -82,7 +83,7 @@ public object Float64ParallelLinearSpace : LinearSpace { } @OptIn(PerformancePitfall::class) - override fun Matrix.dot(vector: Point): Float64Buffer { + override fun Matrix.dot(vector: Point): Float64Buffer { require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" } val rows = this@dot.rows.map { it.linearize() } val indices = 0 until this.colNum @@ -97,27 +98,27 @@ public object Float64ParallelLinearSpace : LinearSpace { } - override fun Matrix.times(value: Double): Matrix = Floa64FieldOpsND { + override fun Matrix.times(value: Double): Matrix = Floa64FieldOpsND { asND().map { it * value }.as2D() } - public override fun Point.plus(other: Point): Float64Buffer = Float64BufferOps.run { + public override fun Point.plus(other: Point): Float64Buffer = Float64BufferOps.run { this@plus + other } - public override fun Point.minus(other: Point): Float64Buffer = Float64BufferOps.run { + public override fun Point.minus(other: Point): Float64Buffer = Float64BufferOps.run { this@minus - other } - public override fun Point.times(value: Double): Float64Buffer = Float64BufferOps.run { + public override fun Point.times(value: Double): Float64Buffer = Float64BufferOps.run { scale(this@times, value) } - public operator fun Point.div(value: Double): Float64Buffer = Float64BufferOps.run { + public operator fun Point.div(value: Double): Float64Buffer = Float64BufferOps.run { scale(this@div, 1.0 / value) } - public override fun Double.times(v: Point): Float64Buffer = v * this + public override fun Double.times(v: Point): Float64Buffer = v * this } diff --git a/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/structures/parallelMutableBuffer.kt b/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/structures/parallelMutableBuffer.kt index d19f50dba..376f090c7 100644 --- a/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/structures/parallelMutableBuffer.kt +++ b/kmath-core/src/jvmMain/kotlin/space/kscience/kmath/structures/parallelMutableBuffer.kt @@ -30,7 +30,7 @@ public fun MutableBuffer.Companion.parallel( .asBuffer() as MutableBuffer typeOf() -> Float32Buffer(size) { initializer(it) as Float } as MutableBuffer - typeOf() -> IntStream.range(0, size).parallel().mapToDouble { initializer(it) as Float64 }.toArray() + typeOf() -> IntStream.range(0, size).parallel().mapToDouble { initializer(it) as Float64 }.toArray() .asBuffer() as MutableBuffer //TODO add unsigned types else -> IntStream.range(0, size).parallel().mapToObj { initializer(it) }.collect(Collectors.toList()) diff --git a/kmath-core/src/jvmTest/kotlin/space/kscience/kmath/linear/ParallelMatrixTest.kt b/kmath-core/src/jvmTest/kotlin/space/kscience/kmath/linear/ParallelMatrixTest.kt index 2abc36c8e..19a37c9fb 100644 --- a/kmath-core/src/jvmTest/kotlin/space/kscience/kmath/linear/ParallelMatrixTest.kt +++ b/kmath-core/src/jvmTest/kotlin/space/kscience/kmath/linear/ParallelMatrixTest.kt @@ -9,6 +9,7 @@ import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -37,7 +38,7 @@ class ParallelMatrixTest { @Test fun testMatrixExtension() = Float64Field.linearSpace.parallel { - val transitionMatrix: Matrix = VirtualMatrix(6, 6) { row, col -> + val transitionMatrix: Matrix = VirtualMatrix(6, 6) { row, col -> when { col == 0 -> .50 row + 1 == col -> .50 @@ -46,7 +47,7 @@ class ParallelMatrixTest { } } - infix fun Matrix.pow(power: Int): Matrix { + infix fun Matrix.pow(power: Int): Matrix { var res = this repeat(power - 1) { res = res dot this@pow diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt index b3e1b59b7..b3ac31e91 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt @@ -5,12 +5,13 @@ package space.kscience.kmath.chains +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer /** * Chunked, specialized chain for double values, which supports blocking [nextBlocking] operation */ -public interface BlockingDoubleChain : BlockingBufferChain { +public interface BlockingDoubleChain : BlockingBufferChain { /** * Returns an [DoubleArray] chunk of [size] values of [next]. diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt index 12665c8a5..3d6116595 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt @@ -17,6 +17,7 @@ import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.operations.Group import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer /** @@ -56,7 +57,7 @@ public fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory) /** * Specialized flow chunker for real buffer */ -public fun Flow.chunked(bufferSize: Int): Flow = flow { +public fun Flow.chunked(bufferSize: Int): Flow = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } if (this@chunked is BlockingDoubleChain) { diff --git a/kmath-ejml/build.gradle.kts b/kmath-ejml/build.gradle.kts index 50416eaf3..de0399244 100644 --- a/kmath-ejml/build.gradle.kts +++ b/kmath-ejml/build.gradle.kts @@ -5,8 +5,9 @@ plugins { val ejmlVerision = "0.43.1" dependencies { - api("org.ejml:ejml-all:$ejmlVerision") api(projects.kmathCore) + api(projects.kmathComplex) + api("org.ejml:ejml-all:$ejmlVerision") } readme { diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt index d5bd8ccb7..fe522844d 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt @@ -6,9 +6,13 @@ package space.kscience.kmath.ejml import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.linear.* -import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.linear.Inverted +import space.kscience.kmath.linear.LinearSpace +import space.kscience.kmath.linear.Matrix +import space.kscience.kmath.linear.Point +import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.operations.Ring +import space.kscience.kmath.structures.Float64 /** * [LinearSpace] implementation specialized for a certain EJML type. @@ -38,6 +42,6 @@ public abstract class EjmlLinearSpace, out M : org.ejml public abstract override fun buildVector(size: Int, initializer: A.(Int) -> T): EjmlVector @UnstableKMathAPI - public fun EjmlMatrix.inverted(): Matrix = - computeAttribute(this, Float64Field.linearSpace.Inverted)!! + public fun Structure2D.inverted(): Matrix = + computeAttribute(this, Inverted()) ?: error("Can't invert matrix") } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt index df4941c52..d84eeb589 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt @@ -12,6 +12,8 @@ import org.ejml.dense.row.CommonOps_DDRM import org.ejml.dense.row.CommonOps_FDRM import org.ejml.dense.row.factory.DecompositionFactory_DDRM import org.ejml.dense.row.factory.DecompositionFactory_FDRM +import org.ejml.interfaces.decomposition.EigenDecomposition_F32 +import org.ejml.interfaces.decomposition.EigenDecomposition_F64 import org.ejml.sparse.FillReducing import org.ejml.sparse.csc.CommonOps_DSCC import org.ejml.sparse.csc.CommonOps_FSCC @@ -19,6 +21,7 @@ import org.ejml.sparse.csc.factory.DecompositionFactory_DSCC import org.ejml.sparse.csc.factory.DecompositionFactory_FSCC import space.kscience.attributes.SafeType import space.kscience.attributes.safeTypeOf +import space.kscience.kmath.complex.Complex import space.kscience.kmath.linear.* import space.kscience.kmath.linear.Matrix import space.kscience.kmath.nd.Structure2D @@ -27,9 +30,15 @@ import space.kscience.kmath.operations.Float32Field import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Float32 +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.IntBuffer import space.kscience.kmath.structures.asBuffer +/** + * Copy EJML [Complex_F64] into KMath [Complex] + */ +public fun Complex_F64.toKMathComplex(): Complex = Complex(real, imaginary) + /** * [EjmlVector] specialization for [Double]. */ @@ -79,16 +88,16 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace get() = safeTypeOf() + override val type: SafeType get() = safeTypeOf() @Suppress("UNCHECKED_CAST") - override fun Matrix.toEjml(): EjmlDoubleMatrix = when { + override fun Matrix.toEjml(): EjmlDoubleMatrix = when { this is EjmlDoubleMatrix<*> && origin is DMatrixRMaj -> this as EjmlDoubleMatrix else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } } @Suppress("UNCHECKED_CAST") - override fun Point.toEjml(): EjmlDoubleVector = when { + override fun Point.toEjml(): EjmlDoubleVector = when { this is EjmlDoubleVector<*> && origin is DMatrixRMaj -> this as EjmlDoubleVector else -> EjmlDoubleVector(DMatrixRMaj(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } @@ -115,21 +124,21 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace T.wrapMatrix() = EjmlDoubleMatrix(this) private fun T.wrapVector() = EjmlDoubleVector(this) - override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } + override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } - override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { + override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { val out = DMatrixRMaj(1, 1) CommonOps_DDRM.mult(toEjml().origin, other.toEjml().origin, out) return out.wrapMatrix() } - override fun Matrix.dot(vector: Point): EjmlDoubleVector { + override fun Matrix.dot(vector: Point): EjmlDoubleVector { val out = DMatrixRMaj(1, 1) CommonOps_DDRM.mult(toEjml().origin, vector.toEjml().origin, out) return out.wrapVector() } - override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { + override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { val out = DMatrixRMaj(1, 1) CommonOps_DDRM.add( @@ -143,19 +152,19 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace.times(value: Double): EjmlDoubleMatrix { + override operator fun Matrix.times(value: Double): EjmlDoubleMatrix { val res = DMatrixRMaj(1, 1) CommonOps_DDRM.scale(value, toEjml().origin, res) return res.wrapMatrix() } - override fun Point.unaryMinus(): EjmlDoubleVector { + override fun Point.unaryMinus(): EjmlDoubleVector { val res = DMatrixRMaj(1, 1) CommonOps_DDRM.changeSign(toEjml().origin, res) return res.wrapVector() } - override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { + override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { val out = DMatrixRMaj(1, 1) CommonOps_DDRM.add( @@ -169,7 +178,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace.plus(other: Point): EjmlDoubleVector { + override fun Point.plus(other: Point): EjmlDoubleVector { val out = DMatrixRMaj(1, 1) CommonOps_DDRM.add( @@ -183,7 +192,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace.minus(other: Point): EjmlDoubleVector { + override fun Point.minus(other: Point): EjmlDoubleVector { val out = DMatrixRMaj(1, 1) CommonOps_DDRM.add( @@ -197,18 +206,18 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace): EjmlDoubleMatrix = m * this + override fun Double.times(m: Matrix): EjmlDoubleMatrix = m * this - override fun Point.times(value: Double): EjmlDoubleVector { + override fun Point.times(value: Double): EjmlDoubleVector { val res = DMatrixRMaj(1, 1) CommonOps_DDRM.scale(value, toEjml().origin, res) return res.wrapVector() } - override fun Double.times(v: Point): EjmlDoubleVector = v * this + override fun Double.times(v: Point): EjmlDoubleVector = v * this - override fun > computeAttribute(structure: Structure2D, attribute: A): V? { - val origin = structure.toEjml().origin + override fun > computeAttribute(structure: Structure2D, attribute: A): V? { + val origin: DMatrixRMaj = structure.toEjml().origin val raw: Any? = when (attribute) { Inverted -> { @@ -218,28 +227,28 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace CommonOps_DDRM.det(origin) - SVD -> object : SingularValueDecomposition { + 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 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 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() } - QR -> object : QRDecomposition { + 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() + 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 { + Cholesky -> object : CholeskyDecomposition { + override val l: Matrix by lazy { val cholesky = DecompositionFactory_DDRM.chol(structure.rowNum, true).apply { decompose(origin.copy()) } @@ -247,20 +256,49 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace object : LupDecomposition { + LUP -> object : LupDecomposition { private val lup by lazy { DecompositionFactory_DDRM.lu(origin.numRows, origin.numCols).apply { decompose(origin.copy()) } } - override val l: Matrix + override val l: Matrix get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) - - override val u: Matrix + override val u: Matrix get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) + override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } + EIG -> { + check(origin.numCols == origin.numRows) { "Eigenvalue decomposition requires symmetric matrix" } + object : EigenDecomposition { + val cmEigen: EigenDecomposition_F64 by lazy { + DecompositionFactory_DDRM.eig(origin.numRows, true).apply { decompose(origin) } + } + + override val v: Matrix by lazy { + val eigenvectors = List(origin.numRows) { cmEigen.getEigenVector(it) } + buildMatrix(origin.numRows, origin.numCols) { row, column -> + eigenvectors[row][column] + } + } + + override val d: Matrix by lazy { + val eigenvalues = List(origin.numRows) { cmEigen.getEigenvalue(it) } + + buildMatrix(origin.numRows, origin.numCols) { row, column -> + when (row) { + column -> eigenvalues[row].real + column - 1 -> eigenvalues[row].imaginary + column + 1 -> -eigenvalues[row].imaginary + else -> 0.0 + } + } + } + } + } + else -> null } @@ -275,7 +313,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace, b: Matrix): EjmlDoubleMatrix { + public fun solve(a: Matrix, b: Matrix): EjmlDoubleMatrix { val res = DMatrixRMaj(1, 1) CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) return res.wrapMatrix() @@ -288,7 +326,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace, b: Point): EjmlDoubleVector { + public fun solve(a: Matrix, b: Point): EjmlDoubleVector { val res = DMatrixRMaj(1, 1) CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) return EjmlDoubleVector(res) @@ -488,6 +526,35 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace { + check(origin.numCols == origin.numRows) { "Eigenvalue decomposition requires symmetric matrix" } + object : EigenDecomposition { + val cmEigen: EigenDecomposition_F32 by lazy { + DecompositionFactory_FDRM.eig(origin.numRows, true).apply { decompose(origin) } + } + + override val v by lazy { + val eigenvectors: List = List(origin.numRows) { cmEigen.getEigenVector(it) } + buildMatrix(origin.numRows, origin.numCols) { row, column -> + eigenvectors[row][column] + } + } + + override val d: Matrix by lazy { + val eigenvalues = List(origin.numRows) { cmEigen.getEigenvalue(it) } + + buildMatrix(origin.numRows, origin.numCols) { row, column -> + when (row) { + column -> eigenvalues[row].real + column - 1 -> eigenvalues[row].imaginary + column + 1 -> -eigenvalues[row].imaginary + else -> 0f + } + } + } + } + } + else -> null } @@ -533,16 +600,16 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace get() = safeTypeOf() + override val type: SafeType get() = safeTypeOf() @Suppress("UNCHECKED_CAST") - override fun Matrix.toEjml(): EjmlDoubleMatrix = when { + override fun Matrix.toEjml(): EjmlDoubleMatrix = when { this is EjmlDoubleMatrix<*> && origin is DMatrixSparseCSC -> this as EjmlDoubleMatrix else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } } @Suppress("UNCHECKED_CAST") - override fun Point.toEjml(): EjmlDoubleVector = when { + override fun Point.toEjml(): EjmlDoubleVector = when { this is EjmlDoubleVector<*> && origin is DMatrixSparseCSC -> this as EjmlDoubleVector else -> EjmlDoubleVector(DMatrixSparseCSC(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } @@ -569,21 +636,21 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace T.wrapMatrix() = EjmlDoubleMatrix(this) private fun T.wrapVector() = EjmlDoubleVector(this) - override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } + override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } - override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { + override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { val out = DMatrixSparseCSC(1, 1) CommonOps_DSCC.mult(toEjml().origin, other.toEjml().origin, out) return out.wrapMatrix() } - override fun Matrix.dot(vector: Point): EjmlDoubleVector { + override fun Matrix.dot(vector: Point): EjmlDoubleVector { val out = DMatrixSparseCSC(1, 1) CommonOps_DSCC.mult(toEjml().origin, vector.toEjml().origin, out) return out.wrapVector() } - override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { + override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { val out = DMatrixSparseCSC(1, 1) CommonOps_DSCC.add( @@ -599,19 +666,19 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.times(value: Double): EjmlDoubleMatrix { + override operator fun Matrix.times(value: Double): EjmlDoubleMatrix { val res = DMatrixSparseCSC(1, 1) CommonOps_DSCC.scale(value, toEjml().origin, res) return res.wrapMatrix() } - override fun Point.unaryMinus(): EjmlDoubleVector { + override fun Point.unaryMinus(): EjmlDoubleVector { val res = DMatrixSparseCSC(1, 1) CommonOps_DSCC.changeSign(toEjml().origin, res) return res.wrapVector() } - override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { + override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { val out = DMatrixSparseCSC(1, 1) CommonOps_DSCC.add( @@ -627,7 +694,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.plus(other: Point): EjmlDoubleVector { + override fun Point.plus(other: Point): EjmlDoubleVector { val out = DMatrixSparseCSC(1, 1) CommonOps_DSCC.add( @@ -643,7 +710,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace.minus(other: Point): EjmlDoubleVector { + override fun Point.minus(other: Point): EjmlDoubleVector { val out = DMatrixSparseCSC(1, 1) CommonOps_DSCC.add( @@ -659,17 +726,17 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace): EjmlDoubleMatrix = m * this + override fun Double.times(m: Matrix): EjmlDoubleMatrix = m * this - override fun Point.times(value: Double): EjmlDoubleVector { + override fun Point.times(value: Double): EjmlDoubleVector { val res = DMatrixSparseCSC(1, 1) CommonOps_DSCC.scale(value, toEjml().origin, res) return res.wrapVector() } - override fun Double.times(v: Point): EjmlDoubleVector = v * this + override fun Double.times(v: Point): EjmlDoubleVector = v * this - override fun > computeAttribute(structure: Structure2D, attribute: A): V? { + override fun > computeAttribute(structure: Structure2D, attribute: A): V? { val origin = structure.toEjml().origin val raw: Any? = when (attribute) { @@ -681,16 +748,16 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace CommonOps_DSCC.det(origin) - QR -> object : QRDecomposition { + 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() + 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 { + Cholesky -> object : CholeskyDecomposition { + override val l: Matrix by lazy { val cholesky = DecompositionFactory_DSCC.cholesky().apply { decompose(origin.copy()) } @@ -698,16 +765,16 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace object : LupDecomposition { + LUP -> object : LupDecomposition { private val lup by lazy { DecompositionFactory_DSCC.lu(FillReducing.NONE).apply { decompose(origin.copy()) } } - override val l: Matrix + override val l: Matrix get() = lup.getLower(null).wrapMatrix().withAttribute(LowerTriangular) - override val u: Matrix + override val u: Matrix get() = lup.getUpper(null).wrapMatrix().withAttribute(UpperTriangular) override val pivot: IntBuffer get() = lup.getRowPivotV(null).asBuffer() } @@ -726,7 +793,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace, b: Matrix): EjmlDoubleMatrix { + public fun solve(a: Matrix, b: Matrix): EjmlDoubleMatrix { val res = DMatrixSparseCSC(1, 1) CommonOps_DSCC.solve(DMatrixSparseCSC(a.toEjml().origin), DMatrixSparseCSC(b.toEjml().origin), res) return res.wrapMatrix() @@ -739,7 +806,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace, b: Point): EjmlDoubleVector { + public fun solve(a: Matrix, b: Point): EjmlDoubleVector { val res = DMatrixSparseCSC(1, 1) CommonOps_DSCC.solve(DMatrixSparseCSC(a.toEjml().origin), DMatrixSparseCSC(b.toEjml().origin), res) return EjmlDoubleVector(res) 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 8b1b28e7d..dda42d42c 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 @@ -17,6 +17,7 @@ import space.kscience.kmath.linear.* import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.toArray import space.kscience.kmath.operations.algebra +import space.kscience.kmath.structures.Float64 import kotlin.random.Random import kotlin.random.asJavaRandom import kotlin.test.* @@ -63,7 +64,7 @@ internal class EjmlMatrixTest { val w = EjmlDoubleMatrix(m) val det: Double = w.getOrComputeAttribute(Determinant) ?: fail() assertEquals(CommonOps_DDRM.det(m), det) - val lup: LupDecomposition = w.getOrComputeAttribute(LUP) ?: fail() + val lup: LupDecomposition = w.getOrComputeAttribute(LUP) ?: fail() val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows, m.numCols) .also { it.decompose(m.copy()) } @@ -104,4 +105,11 @@ internal class EjmlMatrixTest { assertTrue { StructureND.contentEquals(one(dim, dim), res, 1e-3) } } + + @Test + fun eigenValueDecomposition() = EjmlLinearSpaceDDRM { + val matrix = EjmlDoubleMatrix(randomMatrix) + val eigen = matrix.getOrComputeAttribute(EIG) ?: fail() + assertMatrixEquals(matrix, eigen.v dot eigen.d dot eigen.v.transposed()) + } } diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt index 493a299c0..7d13f82da 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt @@ -9,12 +9,13 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.Point import space.kscience.kmath.operations.Float64L2Norm import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.MutableBuffer.Companion.double import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.indices import kotlin.math.pow -public typealias DoubleVector = Point +public typealias DoubleVector = Point public fun DoubleVector(vararg doubles: Double): DoubleVector = doubles.asBuffer() @@ -41,7 +42,7 @@ public operator fun DoubleVector.plus(number: Number): DoubleVector = map { it + public operator fun Number.plus(vector: DoubleVector): DoubleVector = vector + this -public operator fun DoubleVector.unaryMinus(): Buffer = map { -it } +public operator fun DoubleVector.unaryMinus(): Buffer = map { -it } public operator fun DoubleVector.minus(other: DoubleVector): DoubleVector { require(size == other.size) { "Vector size $size expected but ${other.size} found" } diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt index 208c82c6d..39933076a 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt @@ -15,6 +15,7 @@ import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.asIterable import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import kotlin.math.pow @@ -30,7 +31,7 @@ import kotlin.math.pow * Functions that help create a real (Double) matrix */ -public typealias RealMatrix = Matrix +public typealias RealMatrix = Matrix public fun realMatrix(rowNum: Int, colNum: Int, initializer: Float64Field.(i: Int, j: Int) -> Double): RealMatrix = Double.algebra.linearSpace.buildMatrix(rowNum, colNum, initializer) @@ -114,7 +115,7 @@ public operator fun RealMatrix.minus(other: RealMatrix): RealMatrix = * Operations on columns */ -public inline fun RealMatrix.appendColumn(crossinline mapper: (Buffer) -> Double): RealMatrix = +public inline fun RealMatrix.appendColumn(crossinline mapper: (Buffer) -> Double): RealMatrix = Double.algebra.linearSpace.buildMatrix(rowNum, colNum + 1) { row, col -> if (col < colNum) get(row, col) diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/dot.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/dot.kt index e9fdfb506..d69333b0e 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/dot.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/dot.kt @@ -8,11 +8,12 @@ package space.kscience.kmath.real import space.kscience.kmath.linear.Matrix import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.operations.algebra +import space.kscience.kmath.structures.Float64 /** * Optimized dot product for real matrices */ -public infix fun Matrix.dot(other: Matrix): Matrix = Double.algebra.linearSpace.run { +public infix fun Matrix.dot(other: Matrix): Matrix = Double.algebra.linearSpace.run { this@dot dot other } \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt index 3a25739c3..282c19f42 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt @@ -7,16 +7,17 @@ package space.kscience.kmath.real import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import kotlin.math.floor -public val ClosedFloatingPointRange.length: Double get() = endInclusive - start +public val ClosedFloatingPointRange.length: Double get() = endInclusive - start /** * Create a Buffer-based grid with equally distributed [numberOfPoints] points. The range could be increasing or decreasing. * If range has a zero size, then the buffer consisting of [numberOfPoints] equal values is returned. */ -public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, numberOfPoints: Int): Float64Buffer { +public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, numberOfPoints: Int): Float64Buffer { require(numberOfPoints >= 2) { "Number of points in grid must be more than 1" } val normalizedRange = when { range.endInclusive > range.start -> range @@ -31,7 +32,7 @@ public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, n * Create a Buffer-based grid with equally distributed points with a fixed [step]. The range could be increasing or decreasing. * If the step is larger than the range size, single point is returned. */ -public fun Buffer.Companion.withFixedStep(range: ClosedFloatingPointRange, step: Double): Float64Buffer { +public fun Buffer.Companion.withFixedStep(range: ClosedFloatingPointRange, step: Double): Float64Buffer { require(step > 0) { "The grid step must be positive" } val normalizedRange = when { range.endInclusive > range.start -> range @@ -51,4 +52,4 @@ public fun Buffer.Companion.withFixedStep(range: ClosedFloatingPointRange.step(step: Double): Float64Buffer = Buffer.withFixedStep(this, step) \ No newline at end of file +public infix fun ClosedFloatingPointRange.step(step: Double): Float64Buffer = Buffer.withFixedStep(this, step) \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt index 223cdd25d..7fac74f64 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt @@ -7,12 +7,13 @@ package space.kscience.kmath.real import space.kscience.kmath.nd.BufferND import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer /** * Map one [BufferND] using function without indices. */ -public inline fun BufferND.mapInline(crossinline transform: Float64Field.(Double) -> Double): BufferND { +public inline fun BufferND.mapInline(crossinline transform: Float64Field.(Double) -> Double): BufferND { val array = DoubleArray(indices.linearSize) { offset -> Float64Field.transform(buffer[offset]) } return BufferND(indices, Float64Buffer(array)) } @@ -20,7 +21,7 @@ public inline fun BufferND.mapInline(crossinline transform: Float64Field /** * Element by element application of any operation on elements to the whole array. Just like in numpy. */ -public operator fun Function1.invoke(elementND: BufferND): BufferND = +public operator fun Function1.invoke(elementND: BufferND): BufferND = elementND.mapInline { this@invoke(it) } /* plus and minus */ @@ -28,9 +29,9 @@ public operator fun Function1.invoke(elementND: BufferND /** * Summation operation for [BufferND] and single element */ -public operator fun BufferND.plus(arg: Double): BufferND = mapInline { it + arg } +public operator fun BufferND.plus(arg: Double): BufferND = mapInline { it + arg } /** * Subtraction operation between [BufferND] and single element */ -public operator fun BufferND.minus(arg: Double): BufferND = mapInline { it - arg } \ No newline at end of file +public operator fun BufferND.minus(arg: Double): BufferND = mapInline { it - arg } \ No newline at end of file diff --git a/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleMatrixTest.kt b/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleMatrixTest.kt index f54ec6290..69ed9574d 100644 --- a/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleMatrixTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleMatrixTest.kt @@ -170,7 +170,7 @@ internal class DoubleMatrixTest { assertEquals(matrix1.average(), 1.375) } -// fun printMatrix(m: Matrix) { +// fun printMatrix(m: Matrix) { // for (row in 0 until m.shape[0]) { // for (col in 0 until m.shape[1]) { // print(m[row, col]) diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/polynomialUtil.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/polynomialUtil.kt index e85f698c4..094ab5b6f 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/polynomialUtil.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/polynomialUtil.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.functions import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.* +import space.kscience.kmath.structures.Float64 import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.math.max @@ -31,7 +32,7 @@ public inline fun A.polynomialSpace(block: PolynomialSpace.() -> /** * Evaluates value of [this] Double polynomial on provided Double argument. */ -public fun Polynomial.value(arg: Double): Double = +public fun Polynomial.value(arg: Double): Double = coefficients.reduceIndexedOrNull { index, acc, c -> acc + c * arg.pow(index) } ?: .0 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 666379c7a..7aa1b601a 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 @@ -8,6 +8,7 @@ import space.kscience.attributes.AttributesBuilder import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.Field import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.indices @@ -27,7 +28,7 @@ public class GaussIntegrator( public val algebra: Field, ) : UnivariateIntegrator { - private fun buildRule(integrand: UnivariateIntegrand): Pair, Buffer> { + private fun buildRule(integrand: UnivariateIntegrand): Pair, Buffer> { val factory = integrand[GaussIntegratorRuleFactory] ?: GaussLegendreRuleFactory val predefinedRanges = integrand[UnivariateIntegrandRanges] if (predefinedRanges == null || predefinedRanges.ranges.isEmpty()) { @@ -89,7 +90,7 @@ public val Field.gaussIntegrator: GaussIntegrator get() = GaussI */ @UnstableKMathAPI public inline fun GaussIntegrator.integrate( - range: ClosedRange, + range: ClosedRange, order: Int = 10, intervals: Int = 10, noinline attributesBuilder: AttributesBuilder>.() -> Unit = {}, diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt index 60777bfdb..cf067f602 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt @@ -8,16 +8,17 @@ package space.kscience.kmath.integration import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.mapToBuffer import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer import kotlin.math.ulp import kotlin.native.concurrent.ThreadLocal public interface GaussIntegratorRuleFactory { - public fun build(numPoints: Int): Pair, Buffer> + public fun build(numPoints: Int): Pair, Buffer> public companion object : IntegrandAttribute { - public fun double(numPoints: Int, range: ClosedRange): Pair, Buffer> = + public fun double(numPoints: Int, range: ClosedRange): Pair, Buffer> = GaussLegendreRuleFactory.build(numPoints, range) } } @@ -28,9 +29,9 @@ public interface GaussIntegratorRuleFactory { */ public fun GaussIntegratorRuleFactory.build( numPoints: Int, - range: ClosedRange, -): Pair, Buffer> { - val normalized: Pair, Buffer> = build(numPoints) + range: ClosedRange, +): Pair, Buffer> { + val normalized: Pair, Buffer> = build(numPoints) val length = range.endInclusive - range.start val points = normalized.first.mapToBuffer(Float64Field.bufferFactory) { @@ -55,13 +56,13 @@ public fun GaussIntegratorRuleFactory.build( @ThreadLocal public object GaussLegendreRuleFactory : GaussIntegratorRuleFactory { - private val cache = HashMap, Buffer>>() + private val cache = HashMap, Buffer>>() - private fun getOrBuildRule(numPoints: Int): Pair, Buffer> = + private fun getOrBuildRule(numPoints: Int): Pair, Buffer> = cache.getOrPut(numPoints) { buildRule(numPoints) } - private fun buildRule(numPoints: Int): Pair, Buffer> { + private fun buildRule(numPoints: Int): Pair, Buffer> { if (numPoints == 1) { // Break recursion. return Pair( @@ -73,7 +74,7 @@ public object GaussLegendreRuleFactory : GaussIntegratorRuleFactory { // Get previous rule. // If it has not been computed, yet it will trigger a recursive call // to this method. - val previousPoints: Buffer = getOrBuildRule(numPoints - 1).first + val previousPoints: Buffer = getOrBuildRule(numPoints - 1).first // Compute next rule. val points = DoubleArray(numPoints) @@ -162,7 +163,7 @@ public object GaussLegendreRuleFactory : GaussIntegratorRuleFactory { return Pair(points.asBuffer(), weights.asBuffer()) } - override fun build(numPoints: Int): Pair, Buffer> = getOrBuildRule(numPoints) + override fun build(numPoints: Int): Pair, Buffer> = getOrBuildRule(numPoints) override fun toString(): String = "GaussLegendreRule" } \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrand.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrand.kt index babc86c54..cbf694e41 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrand.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrand.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.integration import space.kscience.attributes.* +import space.kscience.kmath.structures.Float64 public interface IntegrandAttribute : Attribute @@ -44,9 +45,9 @@ public inline val Integrand.valueOrNull: T? get() = attribu */ public inline val Integrand.value: T get() = valueOrNull ?: error("No value in the integrand") -public object IntegrandRelativeAccuracy : IntegrandAttribute +public object IntegrandRelativeAccuracy : IntegrandAttribute -public object IntegrandAbsoluteAccuracy : IntegrandAttribute +public object IntegrandAbsoluteAccuracy : IntegrandAttribute public object IntegrandCallsPerformed : IntegrandAttribute diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SimpsonIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SimpsonIntegrator.kt index 60e53c54a..ac589e160 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SimpsonIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SimpsonIntegrator.kt @@ -10,6 +10,7 @@ import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.sum +import space.kscience.kmath.structures.Float64 /** * Use double pass Simpson rule integration with a fixed number of points. @@ -24,7 +25,7 @@ public class SimpsonIntegrator( ) : UnivariateIntegrator { private fun integrateRange( - integrand: UnivariateIntegrand, range: ClosedRange, numPoints: Int, + integrand: UnivariateIntegrand, range: ClosedRange, numPoints: Int, ): T = algebra { val h: Double = (range.endInclusive - range.start) / (numPoints - 1) val values: List = List(numPoints) { i -> @@ -75,9 +76,9 @@ public val Field.simpsonIntegrator: SimpsonIntegrator get() = Si * * [IntegrandMaxCalls]—the maximum number of function calls during integration. For non-iterative rules, always uses * the maximum number of points. By default, uses 10 points. */ -public object DoubleSimpsonIntegrator : UnivariateIntegrator { +public object DoubleSimpsonIntegrator : UnivariateIntegrator { private fun integrateRange( - integrand: UnivariateIntegrand, range: ClosedRange, numPoints: Int, + integrand: UnivariateIntegrand, range: ClosedRange, numPoints: Int, ): Double { val h: Double = (range.endInclusive - range.start) / (numPoints - 1) val values = DoubleArray(numPoints) { i -> @@ -96,7 +97,7 @@ public object DoubleSimpsonIntegrator : UnivariateIntegrator { return res } - override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { + override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { val ranges = integrand[UnivariateIntegrandRanges] return if (ranges != null) { val res = ranges.ranges.sumOf { integrateRange(integrand, it.first, it.second) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt index 24149959b..5faeed011 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt @@ -14,6 +14,7 @@ import space.kscience.kmath.interpolation.SplineInterpolator import space.kscience.kmath.interpolation.interpolatePolynomials import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.MutableBufferFactory @@ -59,7 +60,7 @@ public class SplineIntegrator>( val interpolator: PolynomialInterpolator = SplineInterpolator(algebra, bufferFactory) - val nodes: Buffer = integrand[UnivariateIntegrationNodes] ?: run { + val nodes: Buffer = integrand[UnivariateIntegrationNodes] ?: run { val numPoints = integrand[IntegrandMaxCalls] ?: 100 val step = (range.endInclusive - range.start) / (numPoints - 1) Float64Buffer(numPoints) { i -> range.start + i * step } @@ -85,12 +86,12 @@ public class SplineIntegrator>( * uses the maximum number of points. By default, uses 10 points. */ @UnstableKMathAPI -public object DoubleSplineIntegrator : UnivariateIntegrator { - override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { +public object DoubleSplineIntegrator : UnivariateIntegrator { + override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { val range = integrand[IntegrationRange] ?: 0.0..1.0 - val interpolator: PolynomialInterpolator = SplineInterpolator(Float64Field, Float64Field.bufferFactory) + val interpolator: PolynomialInterpolator = SplineInterpolator(Float64Field, Float64Field.bufferFactory) - val nodes: Buffer = integrand[UnivariateIntegrationNodes] ?: run { + val nodes: Buffer = integrand[UnivariateIntegrationNodes] ?: run { val numPoints = integrand[IntegrandMaxCalls] ?: 100 val step = (range.endInclusive - range.start) / (numPoints - 1) Float64Buffer(numPoints) { i -> range.start + i * step } @@ -108,5 +109,5 @@ public object DoubleSplineIntegrator : UnivariateIntegrator { @Suppress("unused") @UnstableKMathAPI -public inline val Float64Field.splineIntegrator: UnivariateIntegrator +public inline val Float64Field.splineIntegrator: UnivariateIntegrator get() = DoubleSplineIntegrator \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt index e5d220f0f..72b678e2a 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.integration import space.kscience.attributes.* import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer public class UnivariateIntegrand( @@ -36,15 +37,15 @@ public inline fun UnivariateIntegrand( public typealias UnivariateIntegrator = Integrator> -public object IntegrationRange : IntegrandAttribute> +public object IntegrationRange : IntegrandAttribute> /** * Set of univariate integration ranges. First components correspond to the ranges themselves, second components to * the number of integration nodes per range. */ -public class UnivariateIntegrandRanges(public val ranges: List, Int>>) { - public constructor(vararg pairs: Pair, Int>) : this(pairs.toList()) +public class UnivariateIntegrandRanges(public val ranges: List, Int>>) { + public constructor(vararg pairs: Pair, Int>) : this(pairs.toList()) override fun toString(): String { val rangesString = ranges.joinToString(separator = ",") { (range, points) -> @@ -56,7 +57,7 @@ public class UnivariateIntegrandRanges(public val ranges: List } -public object UnivariateIntegrationNodes : IntegrandAttribute> +public object UnivariateIntegrationNodes : IntegrandAttribute> public fun AttributesBuilder>.integrationNodes(vararg nodes: Double) { UnivariateIntegrationNodes(Float64Buffer(nodes)) @@ -78,7 +79,7 @@ public inline fun UnivariateIntegrator.integrate( */ @UnstableKMathAPI public inline fun UnivariateIntegrator.integrate( - range: ClosedRange, + range: ClosedRange, noinline attributeBuilder: AttributesBuilder>.() -> Unit = {}, noinline function: (Double) -> T, ): UnivariateIntegrand { 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 2b58f0b05..381c75b65 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt @@ -12,6 +12,7 @@ import space.kscience.kmath.functions.Polynomial import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.MutableBufferFactory /** @@ -78,5 +79,5 @@ public fun > Field.splineInterpolator( bufferFactory: MutableBufferFactory, ): SplineInterpolator = SplineInterpolator(this, bufferFactory) -public val Float64Field.splineInterpolator: SplineInterpolator +public val Float64Field.splineInterpolator: SplineInterpolator get() = SplineInterpolator(this, bufferFactory) \ No newline at end of file diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/LinearInterpolatorTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/LinearInterpolatorTest.kt index c62d2c8bc..bb5ddf9c6 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/LinearInterpolatorTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/LinearInterpolatorTest.kt @@ -19,7 +19,7 @@ internal class LinearInterpolatorTest { 3.0 to 4.0 ) - //val polynomial: PiecewisePolynomial = DoubleField.linearInterpolator.interpolatePolynomials(data) + //val polynomial: PiecewisePolynomial = DoubleField.linearInterpolator.interpolatePolynomials(data) val function = Float64Field.linearInterpolator.interpolate(data) assertEquals(null, function(-1.0)) assertEquals(0.5, function(0.5)) diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/SplineInterpolatorTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/SplineInterpolatorTest.kt index 8869a1f45..52efc5db6 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/SplineInterpolatorTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/SplineInterpolatorTest.kt @@ -19,7 +19,7 @@ internal class SplineInterpolatorTest { x to sin(x) } - //val polynomial: PiecewisePolynomial = DoubleField.splineInterpolator.interpolatePolynomials(data) + //val polynomial: PiecewisePolynomial = DoubleField.splineInterpolator.interpolatePolynomials(data) val function = Float64Field.splineInterpolator.interpolate(data, Double.NaN) diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt index ca9a37dcb..702a050e2 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt @@ -25,7 +25,7 @@ public val Circle2D<*>.circumference: Double get() = radius * 2 * PI public data class Float64Circle2D( @Serializable(Float64Space2D.VectorSerializer::class) override val center: Float64Vector2D, override val radius: Float64, -) : Circle2D +) : Circle2D public fun Circle2D(center: Vector2D, radius: Double): Float64Circle2D = Float64Circle2D( center as? Float64Vector2D ?: Float64Vector2D(center.x, center.y), 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 fa07190bb..03b6602ea 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 @@ -113,7 +113,7 @@ public object Float64Space3D : GeometrySpace, Double> { /** * Vector product with the right basis */ - public infix fun Vector3D.cross(other: Vector3D): Vector3D = vectorProduct(this, other) + public infix fun Vector3D.cross(other: Vector3D): Vector3D = vectorProduct(this, other) public val xAxis: Float64Vector3D = vector(1.0, 0.0, 0.0) public val yAxis: Float64Vector3D = vector(0.0, 1.0, 0.0) 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 cebad79a2..78fe01733 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 @@ -90,7 +90,7 @@ public fun Float64Space3D.rotate( /** * Rotate a [Float64] vector in 3D space with a rotation matrix */ -public fun Float64Space3D.rotate(vector: Float64Vector3D, matrix: Matrix): Vector3D { +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() } } @@ -101,7 +101,7 @@ public fun Float64Space3D.rotate(vector: Float64Vector3D, matrix: Matrix @OptIn(UnstableKMathAPI::class) public fun Quaternion.toRotationMatrix( linearSpace: LinearSpace = Float64Field.linearSpace, -): Matrix { +): Matrix { val s = QuaternionAlgebra.norm(this).pow(-2) return linearSpace.matrix(3, 3)( 1.0 - 2 * s * (y * y + z * z), 2 * s * (x * y - z * w), 2 * s * (x * z + y * w), @@ -115,7 +115,7 @@ public fun Quaternion.toRotationMatrix( * * taken from https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ */ -public fun Quaternion.Companion.fromRotationMatrix(matrix: Matrix): Quaternion { +public fun Quaternion.Companion.fromRotationMatrix(matrix: Matrix): Quaternion { require(matrix.colNum == 3 && matrix.rowNum == 3) { "Rotation matrix should be 3x3 but is ${matrix.rowNum}x${matrix.colNum}" } val trace = matrix[0, 0] + matrix[1, 1] + matrix[2, 2] @@ -256,7 +256,7 @@ public fun Quaternion.Companion.fromEuler( * Based on https://github.com/mrdoob/three.js/blob/master/src/math/Euler.js */ public fun AngleVector.Companion.fromRotationMatrix( - matrix: Matrix, + matrix: Matrix, rotationOrder: RotationOrder, gimbaldLockThreshold: Double = 0.9999999, ): AngleVector = when (rotationOrder) { 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 22b58d260..6deff6bc8 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 @@ -9,15 +9,15 @@ import space.kscience.kmath.structures.Float64 import kotlin.math.abs import kotlin.test.assertEquals -fun ClosedRange.generateList(step: Double): List = generateSequence(start) { previous -> +fun ClosedRange.generateList(step: Double): List = generateSequence(start) { previous -> if (previous == Double.POSITIVE_INFINITY) return@generateSequence null val next = previous + step if (next > endInclusive) null else next }.toList() fun grid( - xRange: ClosedRange, - yRange: ClosedRange, + xRange: ClosedRange, + yRange: ClosedRange, step: Double, ): List> { val xs = xRange.generateList(step) diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Counter.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Counter.kt index a931b957d..b99f0bf75 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Counter.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Counter.kt @@ -9,6 +9,7 @@ import kotlinx.atomicfu.atomic import kotlinx.atomicfu.getAndUpdate import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Group +import space.kscience.kmath.structures.Float64 /** * Common representation for atomic counters @@ -18,7 +19,7 @@ public interface Counter { public val value: T public companion object { - public fun ofDouble(): ObjectCounter = ObjectCounter(Float64Field) + public fun ofDouble(): ObjectCounter = ObjectCounter(Float64Field) public fun of(group: Group): ObjectCounter = ObjectCounter(group) } } diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram1D.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram1D.kt index 75f18a5a4..833ea6eb5 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram1D.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram1D.kt @@ -11,6 +11,7 @@ import space.kscience.kmath.domains.center import space.kscience.kmath.linear.Point import space.kscience.kmath.operations.asSequence import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 /** @@ -49,7 +50,7 @@ public interface Histogram1DBuilder : HistogramBuilder.fill(items: Iterable): Unit = +public fun Histogram1DBuilder.fill(items: Iterable): Unit = items.forEach(this::putValue) @UnstableKMathAPI 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 770fd70a0..d1863edb2 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.Float64 import space.kscience.kmath.structures.MutableBufferFactory import kotlin.math.floor @@ -137,7 +138,7 @@ public fun Histogram.Companion.uniform1D( @UnstableKMathAPI public fun UniformHistogram1DGroup.produce( - buffer: Buffer, + buffer: Buffer, ): UniformHistogram1D = produce { fill(buffer) } /** 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 4a53ea1a0..a6d817a67 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 @@ -24,8 +24,8 @@ public typealias HyperSquareBin = DomainBin */ public class UniformHistogramGroupND>( override val valueAlgebraND: FieldOpsND, - private val lower: Buffer, - private val upper: Buffer, + private val lower: Buffer, + private val upper: Buffer, private val binNums: IntArray = IntArray(lower.size) { 20 }, private val valueBufferFactory: BufferFactory = valueAlgebraND.elementAlgebra.bufferFactory, ) : HistogramGroupND { @@ -54,7 +54,7 @@ public class UniformHistogramGroupND>( else -> floor((value - lower[axis]) / binSize[axis]).toInt() } - override fun getIndexOrNull(point: Buffer): IntArray = IntArray(dimension) { + override fun getIndexOrNull(point: Buffer): IntArray = IntArray(dimension) { getIndex(it, point[it]) } @@ -93,7 +93,7 @@ public class UniformHistogramGroupND>( val hBuilder = object : HistogramBuilder { override val defaultValue: V get() = valueAlgebraND.elementAlgebra.one - override fun putValue(point: Point, value: V) = with(valueAlgebraND.elementAlgebra) { + override fun putValue(point: Point, value: V) = with(valueAlgebraND.elementAlgebra) { val index = getIndexOrNull(point) ndCounter[index].add(value) } @@ -120,17 +120,17 @@ public class UniformHistogramGroupND>( */ public fun > Histogram.Companion.uniformNDFromRanges( valueAlgebraND: FieldOpsND, - vararg ranges: ClosedFloatingPointRange, + vararg ranges: ClosedFloatingPointRange, bufferFactory: BufferFactory = valueAlgebraND.elementAlgebra.bufferFactory, ): UniformHistogramGroupND = UniformHistogramGroupND( valueAlgebraND, - ranges.map(ClosedFloatingPointRange::start).asBuffer(), - ranges.map(ClosedFloatingPointRange::endInclusive).asBuffer(), + ranges.map(ClosedFloatingPointRange::start).asBuffer(), + ranges.map(ClosedFloatingPointRange::endInclusive).asBuffer(), valueBufferFactory = bufferFactory ) public fun Histogram.Companion.uniformDoubleNDFromRanges( - vararg ranges: ClosedFloatingPointRange, + vararg ranges: ClosedFloatingPointRange, ): UniformHistogramGroupND = uniformNDFromRanges(Floa64FieldOpsND, *ranges) @@ -145,22 +145,22 @@ public fun Histogram.Companion.uniformDoubleNDFromRanges( */ public fun > Histogram.Companion.uniformNDFromRanges( valueAlgebraND: FieldOpsND, - vararg ranges: Pair, Int>, + vararg ranges: Pair, Int>, bufferFactory: BufferFactory = valueAlgebraND.elementAlgebra.bufferFactory, ): UniformHistogramGroupND = UniformHistogramGroupND( valueAlgebraND, ranges - .map(Pair, Int>::first) - .map(ClosedFloatingPointRange::start) + .map(Pair, Int>::first) + .map(ClosedFloatingPointRange::start) .asBuffer(), ranges - .map(Pair, Int>::first) - .map(ClosedFloatingPointRange::endInclusive) + .map(Pair, Int>::first) + .map(ClosedFloatingPointRange::endInclusive) .asBuffer(), - ranges.map(Pair, Int>::second).toIntArray(), + ranges.map(Pair, Int>::second).toIntArray(), valueBufferFactory = bufferFactory ) public fun Histogram.Companion.uniformDoubleNDFromRanges( - vararg ranges: Pair, Int>, + vararg ranges: Pair, Int>, ): 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 c8be4f846..dfe44fe05 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 @@ -17,7 +17,7 @@ import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.structures.* import java.util.* -private fun > TreeMap.getBin(value: Double): B? { +private fun > TreeMap.getBin(value: Double): B? { // check ceiling entry and return it if it is what needed val ceil = ceilingEntry(value)?.value if (ceil != null && value in ceil) return ceil @@ -53,7 +53,7 @@ public class TreeHistogramGroup( override val bufferFactory: MutableBufferFactory> = MutableBufferFactory() internal inner class DomainCounter(val domain: DoubleDomain1D, val counter: Counter = Counter.of(valueAlgebra)) : - ClosedRange by domain.range + ClosedRange by domain.range @PublishedApi internal inner class TreeHistogramBuilder : Histogram1DBuilder { @@ -153,7 +153,7 @@ public class TreeHistogramGroup( */ public fun Histogram.Companion.custom1D( valueAlgebra: A, - borders: Buffer, + borders: Buffer, ): TreeHistogramGroup where A : Ring, A : ScaleOperations { val sorted = borders.sorted() diff --git a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt index 1b7bbf3ed..87eef4331 100644 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt @@ -8,15 +8,16 @@ package space.kscience.kmath.jafama import net.jafama.FastMath import net.jafama.StrictFastMath import space.kscience.kmath.operations.* +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.MutableBufferFactory /** * A field for [Double] (using FastMath) without boxing. Does not produce appropriate field element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -public object JafamaDoubleField : ExtendedField, Norm, ScaleOperations { +public object JafamaDoubleField : ExtendedField, Norm, ScaleOperations { - override val bufferFactory: MutableBufferFactory get() = DoubleField.bufferFactory + override val bufferFactory: MutableBufferFactory get() = DoubleField.bufferFactory override inline val zero: Double get() = 0.0 override inline val one: Double get() = 1.0 @@ -68,9 +69,9 @@ public object JafamaDoubleField : ExtendedField, Norm, S * A field for [Double] (using StrictMath) without boxing. Does not produce appropriate field element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -public object StrictJafamaDoubleField : ExtendedField, Norm, ScaleOperations { +public object StrictJafamaDoubleField : ExtendedField, Norm, ScaleOperations { - override val bufferFactory: MutableBufferFactory get() = DoubleField.bufferFactory + override val bufferFactory: MutableBufferFactory get() = DoubleField.bufferFactory override inline val zero: Double get() = 0.0 override inline val one: Double get() = 1.0 diff --git a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikDoubleAlgebra.kt b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikDoubleAlgebra.kt index 6a8749946..eae53c12c 100644 --- a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikDoubleAlgebra.kt +++ b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikDoubleAlgebra.kt @@ -14,56 +14,57 @@ import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.ExponentialOperations import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.TrigonometricOperations +import space.kscience.kmath.structures.Float64 public class MultikDoubleAlgebra( multikEngine: Engine, ) : MultikDivisionTensorAlgebra(multikEngine), - TrigonometricOperations>, ExponentialOperations> { + TrigonometricOperations>, ExponentialOperations> { override val elementAlgebra: Float64Field get() = Float64Field override val dataType: DataType get() = DataType.DoubleDataType - override fun sin(arg: StructureND): MultikTensor = + override fun sin(arg: StructureND): MultikTensor = multikMath.mathEx.sin(arg.asMultik().array).wrap() - override fun cos(arg: StructureND): MultikTensor = + override fun cos(arg: StructureND): MultikTensor = multikMath.mathEx.cos(arg.asMultik().array).wrap() - override fun tan(arg: StructureND): MultikTensor = sin(arg) / cos(arg) + override fun tan(arg: StructureND): MultikTensor = sin(arg) / cos(arg) @PerformancePitfall - override fun asin(arg: StructureND): MultikTensor = arg.map { asin(it) } + override fun asin(arg: StructureND): MultikTensor = arg.map { asin(it) } @PerformancePitfall - override fun acos(arg: StructureND): MultikTensor = arg.map { acos(it) } + override fun acos(arg: StructureND): MultikTensor = arg.map { acos(it) } @PerformancePitfall - override fun atan(arg: StructureND): MultikTensor = arg.map { atan(it) } + override fun atan(arg: StructureND): MultikTensor = arg.map { atan(it) } - override fun exp(arg: StructureND): MultikTensor = + override fun exp(arg: StructureND): MultikTensor = multikMath.mathEx.exp(arg.asMultik().array).wrap() - override fun ln(arg: StructureND): MultikTensor = multikMath.mathEx.log(arg.asMultik().array).wrap() + override fun ln(arg: StructureND): MultikTensor = multikMath.mathEx.log(arg.asMultik().array).wrap() - override fun sinh(arg: StructureND): MultikTensor = (exp(arg) - exp(-arg)) / 2.0 + override fun sinh(arg: StructureND): MultikTensor = (exp(arg) - exp(-arg)) / 2.0 - override fun cosh(arg: StructureND): MultikTensor = (exp(arg) + exp(-arg)) / 2.0 + override fun cosh(arg: StructureND): MultikTensor = (exp(arg) + exp(-arg)) / 2.0 - override fun tanh(arg: StructureND): MultikTensor { + override fun tanh(arg: StructureND): MultikTensor { val expPlus = exp(arg) val expMinus = exp(-arg) return (expPlus - expMinus) / (expPlus + expMinus) } @PerformancePitfall - override fun asinh(arg: StructureND): MultikTensor = arg.map { asinh(it) } + override fun asinh(arg: StructureND): MultikTensor = arg.map { asinh(it) } @PerformancePitfall - override fun acosh(arg: StructureND): MultikTensor = arg.map { acosh(it) } + override fun acosh(arg: StructureND): MultikTensor = arg.map { acosh(it) } @PerformancePitfall - override fun atanh(arg: StructureND): MultikTensor = arg.map { atanh(it) } + override fun atanh(arg: StructureND): MultikTensor = arg.map { atanh(it) } - override fun scalar(value: Double): MultikTensor = Multik.ndarrayOf(value).wrap() + override fun scalar(value: Double): MultikTensor = Multik.ndarrayOf(value).wrap() } //public val Double.Companion.multikAlgebra: MultikTensorAlgebra get() = MultikDoubleAlgebra diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt index 51f59f731..01a182034 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt @@ -15,6 +15,7 @@ import space.kscience.kmath.UnsafeKMathAPI import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.* import space.kscience.kmath.operations.* +import space.kscience.kmath.structures.Float64 /** * Represents [AlgebraND] over [Nd4jArrayAlgebra]. @@ -193,31 +194,31 @@ public sealed interface Nd4jArrayExtendedFieldOps> : public open class DoubleNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps { override val elementAlgebra: Float64Field get() = Float64Field - override fun INDArray.wrap(): Nd4jArrayStructure = asDoubleStructure() + override fun INDArray.wrap(): Nd4jArrayStructure = asDoubleStructure() @OptIn(PerformancePitfall::class, UnsafeKMathAPI::class) - override val StructureND.ndArray: INDArray + override val StructureND.ndArray: INDArray get() = when (this) { - is Nd4jArrayStructure -> ndArray + is Nd4jArrayStructure -> ndArray else -> Nd4j.zeros(*shape.asArray()).also { elements().forEach { (idx, value) -> it.putScalar(idx, value) } } } - override fun scale(a: StructureND, value: Double): Nd4jArrayStructure = a.ndArray.mul(value).wrap() + override fun scale(a: StructureND, value: Double): Nd4jArrayStructure = a.ndArray.mul(value).wrap() - override operator fun StructureND.div(arg: Double): Nd4jArrayStructure = ndArray.div(arg).wrap() + override operator fun StructureND.div(arg: Double): Nd4jArrayStructure = ndArray.div(arg).wrap() - override operator fun StructureND.plus(arg: Double): Nd4jArrayStructure = ndArray.add(arg).wrap() + override operator fun StructureND.plus(arg: Double): Nd4jArrayStructure = ndArray.add(arg).wrap() - override operator fun StructureND.minus(arg: Double): Nd4jArrayStructure = ndArray.sub(arg).wrap() + override operator fun StructureND.minus(arg: Double): Nd4jArrayStructure = ndArray.sub(arg).wrap() - override operator fun StructureND.times(arg: Double): Nd4jArrayStructure = ndArray.mul(arg).wrap() + override operator fun StructureND.times(arg: Double): Nd4jArrayStructure = ndArray.mul(arg).wrap() - override operator fun Double.div(arg: StructureND): Nd4jArrayStructure = + override operator fun Double.div(arg: StructureND): Nd4jArrayStructure = arg.ndArray.rdiv(this).wrap() - override operator fun Double.minus(arg: StructureND): Nd4jArrayStructure = + override operator fun Double.minus(arg: StructureND): Nd4jArrayStructure = arg.ndArray.rsub(this).wrap() public companion object : DoubleNd4jArrayFieldOps() diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayIterator.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayIterator.kt index 0a2305e71..cf5d8988b 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayIterator.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayIterator.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray import org.nd4j.linalg.api.shape.Shape +import space.kscience.kmath.structures.Float64 private class Nd4jArrayIndicesIterator(private val iterateOver: INDArray) : Iterator { private var i: Int = 0 @@ -42,7 +43,7 @@ private sealed class Nd4jArrayIteratorBase(protected val iterateOver: IND } } -private class Nd4jArrayDoubleIterator(iterateOver: INDArray) : Nd4jArrayIteratorBase(iterateOver) { +private class Nd4jArrayDoubleIterator(iterateOver: INDArray) : Nd4jArrayIteratorBase(iterateOver) { override fun getSingle(indices: LongArray): Double = iterateOver.getDouble(*indices) } 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 ac4ef68d2..b8c04faa0 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 @@ -8,6 +8,7 @@ package space.kscience.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* +import space.kscience.kmath.structures.Float64 /** * Represents a [StructureND] wrapping an [INDArray] object. @@ -48,7 +49,7 @@ 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(), +public data class Nd4jArrayDoubleStructure(override val ndArray: INDArray) : Nd4jArrayStructure(), StructureNDOfDouble { override fun elementsIterator(): Iterator> = ndArray.realIterator() @@ -64,7 +65,7 @@ public data class Nd4jArrayDoubleStructure(override val ndArray: INDArray) : Nd4 /** * Wraps this [INDArray] to [Nd4jArrayStructure]. */ -public fun INDArray.asDoubleStructure(): Nd4jArrayStructure = Nd4jArrayDoubleStructure(this) +public fun INDArray.asDoubleStructure(): Nd4jArrayStructure = Nd4jArrayDoubleStructure(this) public data class Nd4jArrayFloatStructure(override val ndArray: INDArray) : Nd4jArrayStructure() { diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt index 954984a69..af69c8c44 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt @@ -14,9 +14,13 @@ import org.nd4j.linalg.factory.ops.NDBase import org.nd4j.linalg.ops.transforms.Transforms import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnsafeKMathAPI -import space.kscience.kmath.nd.* +import space.kscience.kmath.nd.ColumnStrides +import space.kscience.kmath.nd.ShapeND +import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.nd.asArray import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.api.TensorAlgebra @@ -177,13 +181,13 @@ public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra override val elementAlgebra: Float64Field get() = Float64Field - override fun INDArray.wrap(): Nd4jArrayStructure = asDoubleStructure() + override fun INDArray.wrap(): Nd4jArrayStructure = asDoubleStructure() @OptIn(UnsafeKMathAPI::class) override fun mutableStructureND( shape: ShapeND, initializer: Float64Field.(IntArray) -> Double, - ): Nd4jArrayStructure { + ): Nd4jArrayStructure { val array: INDArray = Nd4j.zeros(*shape.asArray()) val indices = ColumnStrides(shape) indices.asSequence().forEach { index -> @@ -194,29 +198,29 @@ public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra @OptIn(PerformancePitfall::class, UnsafeKMathAPI::class) - override val StructureND.ndArray: INDArray + override val StructureND.ndArray: INDArray get() = when (this) { - is Nd4jArrayStructure -> ndArray + is Nd4jArrayStructure -> ndArray else -> Nd4j.zeros(*shape.asArray()).also { elements().forEach { (idx, value) -> it.putScalar(idx, value) } } } - override fun StructureND.valueOrNull(): Double? = + override fun StructureND.valueOrNull(): Double? = if (shape == ShapeND(1)) ndArray.getDouble(0) else null // TODO rewrite override fun diagonalEmbedding( - diagonalEntries: StructureND, + diagonalEntries: StructureND, offset: Int, dim1: Int, dim2: Int, - ): Tensor = DoubleTensorAlgebra.diagonalEmbedding(diagonalEntries, offset, dim1, dim2) + ): Tensor = DoubleTensorAlgebra.diagonalEmbedding(diagonalEntries, offset, dim1, dim2) - override fun StructureND.sum(): Double = ndArray.sumNumber().toDouble() - override fun StructureND.min(): Double = ndArray.minNumber().toDouble() - override fun StructureND.max(): Double = ndArray.maxNumber().toDouble() - override fun mean(structureND: StructureND): Double = structureND.ndArray.meanNumber().toDouble() - override fun std(structureND: StructureND): Double = structureND.ndArray.stdNumber().toDouble() - override fun variance(structureND: StructureND): Double = structureND.ndArray.varNumber().toDouble() + override fun StructureND.sum(): Double = ndArray.sumNumber().toDouble() + override fun StructureND.min(): Double = ndArray.minNumber().toDouble() + override fun StructureND.max(): Double = ndArray.maxNumber().toDouble() + override fun mean(structureND: StructureND): Double = structureND.ndArray.meanNumber().toDouble() + override fun std(structureND: StructureND): Double = structureND.ndArray.stdNumber().toDouble() + override fun variance(structureND: StructureND): Double = structureND.ndArray.varNumber().toDouble() } 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 07fa97efb..25f1ec72e 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 @@ -12,6 +12,7 @@ import space.kscience.kmath.misc.log import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Float64L2Norm import space.kscience.kmath.operations.algebra +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import kotlin.math.abs @@ -27,7 +28,7 @@ public object QowRuns : OptimizationAttribute public object QowOptimizer : Optimizer { private val linearSpace: LinearSpace = Double.algebra.linearSpace - private val solver: LinearSolver = linearSpace.lupSolver() + private val solver: LinearSolver = linearSpace.lupSolver() @OptIn(UnstableKMathAPI::class) private class QoWeight( @@ -47,7 +48,7 @@ public object QowOptimizer : Optimizer { /** * Derivatives of the spectrum over parameters. First index in the point number, second one - index of parameter */ - val derivs: Matrix by lazy { + val derivs: Matrix by lazy { linearSpace.buildMatrix(problem.data.size, symbols.size) { d, s -> problem.distance(d).derivative(symbols[s]).invoke(allParameters) } @@ -56,14 +57,14 @@ public object QowOptimizer : Optimizer { /** * Array of dispersions in each point */ - val dispersion: Point by lazy { + val dispersion: Point by lazy { Float64Buffer(problem.data.size) { d -> 1.0 / problem.weight(d).invoke(allParameters) } } - val prior: DifferentiableExpression? - get() = problem.attributes[OptimizationPrior()]?.withDefaultArgs(allParameters) + val prior: DifferentiableExpression? + get() = problem.attributes[OptimizationPrior()]?.withDefaultArgs(allParameters) override fun toString(): String = freeParameters.toString() } @@ -86,7 +87,7 @@ public object QowOptimizer : Optimizer { * * D(\phi)=E(\phi_k(\theta_0) \phi_l(\theta_0))= disDeriv_k * disDeriv_l /sigma^2 */ - private fun QoWeight.covarF(): Matrix = + private fun QoWeight.covarF(): Matrix = linearSpace.matrix(size, size).symmetric { s1, s2 -> (0 until data.size).sumOf { d -> derivs[d, s1] * derivs[d, s2] / dispersion[d] } } @@ -95,7 +96,7 @@ public object QowOptimizer : Optimizer { * Experimental covariance Eq (22) from * http://arxiv.org/abs/physics/0604127 */ - private fun QoWeight.covarFExp(theta: Map): Matrix = + private fun QoWeight.covarFExp(theta: Map): Matrix = with(linearSpace) { /* * Важно! Если не делать предварителього вычисления этих производных, то @@ -116,7 +117,7 @@ public object QowOptimizer : Optimizer { */ private fun QoWeight.getEqDerivValues( theta: Map = freeParameters, - ): Matrix = with(linearSpace) { + ): Matrix = with(linearSpace) { //Derivative of k Eq over l parameter val sderiv = buildMatrix(data.size, size) { d, s -> distanceDerivative(symbols[s], d, theta) @@ -141,7 +142,7 @@ public object QowOptimizer : Optimizer { /** * Quasi optimal weights equations values */ - private fun QoWeight.getEqValues(theta: Map): Point { + private fun QoWeight.getEqValues(theta: Map): Point { val distances = Float64Buffer(data.size) { d -> distance(d, theta) } return Float64Buffer(size) { s -> val base = (0 until data.size).sumOf { d -> distances[d] * derivs[d, s] / dispersion[d] } @@ -155,7 +156,7 @@ public object QowOptimizer : Optimizer { private fun QoWeight.newtonianStep( theta: Map, - eqValues: Point, + eqValues: Point, ): QoWeight = linearSpace { val start = theta.toPoint() val invJacob = solver.inverse(getEqDerivValues(theta)) @@ -224,7 +225,7 @@ public object QowOptimizer : Optimizer { return QoWeight(problem, par) } - private fun QoWeight.covariance(): NamedMatrix { + private fun QoWeight.covariance(): NamedMatrix { val logger = problem.attributes[OptimizationLog] logger?.log { diff --git a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt index 44b7d17bd..d27783926 100644 --- a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt +++ b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt @@ -16,26 +16,27 @@ import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.bindSymbol +import space.kscience.kmath.structures.Float64 import kotlin.math.pow /** * Specify the way to compute distance from point to the curve as DifferentiableExpression */ public interface PointToCurveDistance { - public fun distance(problem: XYFit, index: Int): DifferentiableExpression + public fun distance(problem: XYFit, index: Int): DifferentiableExpression public companion object : OptimizationAttribute { public val byY: PointToCurveDistance = object : PointToCurveDistance { - override fun distance(problem: XYFit, index: Int): DifferentiableExpression { + override fun distance(problem: XYFit, index: Int): DifferentiableExpression { val x = problem.data.x[index] val y = problem.data.y[index] - return object : DifferentiableExpression { - override val type: SafeType get() = DoubleField.type + return object : DifferentiableExpression { + override val type: SafeType get() = DoubleField.type override fun derivativeOrNull( symbols: List, - ): Expression? = problem.model.derivativeOrNull(symbols)?.let { derivExpression -> + ): Expression? = problem.model.derivativeOrNull(symbols)?.let { derivExpression -> Expression(DoubleField.type) { arguments -> derivExpression.invoke(arguments + (Symbol.x to x)) } @@ -56,19 +57,19 @@ public interface PointToCurveDistance { * By default, uses Dispersion^-1 */ public interface PointWeight { - public fun weight(problem: XYFit, index: Int): DifferentiableExpression + public fun weight(problem: XYFit, index: Int): DifferentiableExpression public companion object : OptimizationAttribute { public fun bySigma(sigmaSymbol: Symbol): PointWeight = object : PointWeight { - override fun weight(problem: XYFit, index: Int): DifferentiableExpression = - object : DifferentiableExpression { - override val type: SafeType get() = DoubleField.type + override fun weight(problem: XYFit, index: Int): DifferentiableExpression = + object : DifferentiableExpression { + override val type: SafeType get() = DoubleField.type override fun invoke(arguments: Map): Double { return problem.data[sigmaSymbol]?.get(index)?.pow(-2) ?: 1.0 } - override fun derivativeOrNull(symbols: List): Expression = + override fun derivativeOrNull(symbols: List): Expression = Expression(DoubleField.type) { 0.0 } } @@ -85,24 +86,24 @@ public interface PointWeight { */ public class XYFit( public val data: XYColumnarData, - public val model: DifferentiableExpression, + public val model: DifferentiableExpression, override val attributes: Attributes, internal val pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY, internal val pointWeight: PointWeight = PointWeight.byYSigma, public val xSymbol: Symbol = Symbol.x, -) : OptimizationProblem { +) : OptimizationProblem { - override val type: SafeType get() = Float64Field.type + override val type: SafeType get() = Float64Field.type - public fun distance(index: Int): DifferentiableExpression = pointToCurveDistance.distance(this, index) + public fun distance(index: Int): DifferentiableExpression = pointToCurveDistance.distance(this, index) - public fun weight(index: Int): DifferentiableExpression = pointWeight.weight(this, index) + public fun weight(index: Int): DifferentiableExpression = pointWeight.weight(this, index) } public fun XYOptimization( data: XYColumnarData, - model: DifferentiableExpression, + model: DifferentiableExpression, builder: AttributesBuilder.() -> Unit, ): XYFit = XYFit(data, model, Attributes(builder)) @@ -112,7 +113,7 @@ public fun XYFit.withAttributes( public suspend fun XYColumnarData.fitWith( optimizer: Optimizer, - modelExpression: DifferentiableExpression, + modelExpression: DifferentiableExpression, startingPoint: Map, attributes: Attributes = Attributes.EMPTY, xSymbol: Symbol = Symbol.x, @@ -149,7 +150,7 @@ public suspend fun XYColumnarData.fitWith( pointWeight: PointWeight = PointWeight.byYSigma, model: A.(I) -> I, ): XYFit where A : ExtendedField, A : ExpressionAlgebra { - val modelExpression: DifferentiableExpression = processor.differentiate { + val modelExpression: DifferentiableExpression = processor.differentiate { val x = bindSymbol(xSymbol) model(x) } diff --git a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/logLikelihood.kt b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/logLikelihood.kt index a3ccbfab4..ef346278d 100644 --- a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/logLikelihood.kt +++ b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/logLikelihood.kt @@ -15,6 +15,7 @@ import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.derivative import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.structures.Float64 import kotlin.math.PI import kotlin.math.ln import kotlin.math.pow @@ -24,10 +25,10 @@ import kotlin.math.sqrt private val oneOver2Pi = 1.0 / sqrt(2 * PI) @UnstableKMathAPI -internal fun XYFit.logLikelihood(): DifferentiableExpression = object : DifferentiableExpression { - override val type: SafeType get() = Float64Field.type +internal fun XYFit.logLikelihood(): DifferentiableExpression = object : DifferentiableExpression { + override val type: SafeType get() = Float64Field.type - override fun derivativeOrNull(symbols: List): Expression = Expression(type) { arguments -> + override fun derivativeOrNull(symbols: List): Expression = Expression(type) { arguments -> data.indices.sumOf { index -> val d = distance(index)(arguments) val weight = weight(index)(arguments) @@ -57,7 +58,7 @@ internal fun XYFit.logLikelihood(): DifferentiableExpression = object : * possible weight dependency on the model and parameters. */ @UnstableKMathAPI -public suspend fun Optimizer>.maximumLogLikelihood(problem: XYFit): XYFit { +public suspend fun Optimizer>.maximumLogLikelihood(problem: XYFit): XYFit { val functionOptimization = FunctionOptimization(problem.logLikelihood(), problem.attributes) val result = optimize( functionOptimization.withAttributes { @@ -68,8 +69,8 @@ public suspend fun Optimizer>.maximumLogLik } @UnstableKMathAPI -public suspend fun Optimizer>.maximumLogLikelihood( +public suspend fun Optimizer>.maximumLogLikelihood( data: XYColumnarData, - model: DifferentiableExpression, + model: DifferentiableExpression, builder: AttributesBuilder.() -> Unit, ): XYFit = maximumLogLikelihood(XYOptimization(data, model, builder)) diff --git a/kmath-optimization/src/commonMain/tmp/QowFit.kt b/kmath-optimization/src/commonMain/tmp/QowFit.kt index 493ef8d12..1b204d9b3 100644 --- a/kmath-optimization/src/commonMain/tmp/QowFit.kt +++ b/kmath-optimization/src/commonMain/tmp/QowFit.kt @@ -27,28 +27,28 @@ private typealias ParamSet = Map public class QowFit( override val symbols: List, private val space: LinearSpace, - private val solver: LinearSolver, -) : XYOptimization, SymbolIndexer { + private val solver: LinearSolver, +) : XYOptimization, SymbolIndexer { private var logger: FitLogger? = null private var startingPoint: Map = TODO() - private var covariance: Matrix? = TODO() - private val prior: DifferentiableExpression>? = TODO() + private var covariance: Matrix? = TODO() + private val prior: DifferentiableExpression>? = TODO() private var data: XYErrorColumnarData = TODO() - private var model: DifferentiableExpression> = TODO() + private var model: DifferentiableExpression> = TODO() private val features = HashSet() - override fun update(result: OptimizationResult) { + override fun update(result: OptimizationResult) { TODO("Not yet implemented") } - override val algebra: Field + override val algebra: Field get() = TODO("Not yet implemented") override fun data( - dataSet: ColumnarData, + dataSet: ColumnarData, xSymbol: Symbol, ySymbol: Symbol, xErrSymbol: Symbol?, @@ -82,14 +82,14 @@ public class QowFit( */ private fun getDispersion(i: Int, parameters: Map): Double = data.yErr[i].pow(2) - private fun getCovariance(weight: QoWeight): Matrix = solver.inverse(getEqDerivValues(weight)) + private fun getCovariance(weight: QoWeight): Matrix = solver.inverse(getEqDerivValues(weight)) /** * Теоретическая ковариация весовых функций. * * D(\phi)=E(\phi_k(\theta_0) \phi_l(\theta_0))= disDeriv_k * disDeriv_l /sigma^2 */ - private fun covarF(weight: QoWeight): Matrix = space.buildSymmetricMatrix(symbols.size) { k, l -> + private fun covarF(weight: QoWeight): Matrix = space.buildSymmetricMatrix(symbols.size) { k, l -> (0 until data.size).sumOf { i -> weight.derivs[k, i] * weight.derivs[l, i] / weight.dispersion[i] } } @@ -103,7 +103,7 @@ public class QowFit( * @param weight * @return */ - private fun covarFExp(weight: QoWeight, theta: Map): Matrix = space.run { + private fun covarFExp(weight: QoWeight, theta: Map): Matrix = space.run { /* * Важно! Если не делать предварителього вычисления этих производных, то * количество вызывов функции будет dim^2 вместо dim Первый индекс - @@ -129,7 +129,7 @@ public class QowFit( */ private fun getEqDerivValues( weight: QoWeight, theta: Map = weight.theta, - ): Matrix = space.run { + ): Matrix = space.run { val fitDim = symbols.size //Возвращает производную k-того Eq по l-тому параметру val res = Array(fitDim) { DoubleArray(fitDim) } @@ -162,7 +162,7 @@ public class QowFit( * @param weight * @return */ - private fun getEqValues(weight: QoWeight, theta: Map = weight.theta): Point { + private fun getEqValues(weight: QoWeight, theta: Map = weight.theta): Point { val distances = DoubleBuffer(data.size) { i -> distance(i, theta) } return DoubleBuffer(symbols.size) { k -> @@ -190,7 +190,7 @@ public class QowFit( /** * Derivatives of the spectrum over parameters. First index in the point number, second one - index of parameter */ - val derivs: Matrix by lazy { + val derivs: Matrix by lazy { space.buildMatrix(data.size, symbols.size) { i, k -> distanceDerivative(symbols[k], i, theta) } @@ -199,7 +199,7 @@ public class QowFit( /** * Array of dispersions in each point */ - val dispersion: Point by lazy { + val dispersion: Point by lazy { DoubleBuffer(data.size) { i -> getDispersion(i, theta) } } @@ -208,7 +208,7 @@ public class QowFit( private fun newtonianStep( weight: QoWeight, par: Map, - eqvalues: Point, + eqvalues: Point, ): Map = space.run { val start = par.toPoint() val invJacob = solver.inverse(getEqDerivValues(weight, par)) @@ -318,7 +318,7 @@ public class QowFit( /** * generateErrors. */ - private fun generateErrors(): Matrix { + private fun generateErrors(): Matrix { logger?.log { """ Starting errors estimation using quasioptimal weights method. The starting weight is: @@ -340,7 +340,7 @@ public class QowFit( } - override suspend fun optimize(): OptimizationResult { + override suspend fun optimize(): OptimizationResult { val curWeight = QoWeight(startingPoint) logger?.log { """ diff --git a/kmath-optimization/src/commonMain/tmp/minuit/MnUserParameterState.kt b/kmath-optimization/src/commonMain/tmp/minuit/MnUserParameterState.kt index 8b2f8786e..145b278fe 100644 --- a/kmath-optimization/src/commonMain/tmp/minuit/MnUserParameterState.kt +++ b/kmath-optimization/src/commonMain/tmp/minuit/MnUserParameterState.kt @@ -33,7 +33,7 @@ class MnUserParameterState { private var theGCCValid = false private var theGlobalCC: MnGlobalCorrelationCoeff? = null private var theIntCovariance: MnUserCovariance - private var theIntParameters: MutableList + private var theIntParameters: MutableList private var theNFcn = 0 private var theParameters: MnUserParameters private var theValid: Boolean @@ -43,7 +43,7 @@ class MnUserParameterState { theCovarianceValid = false theParameters = MnUserParameters() theCovariance = MnUserCovariance() - theIntParameters = java.util.ArrayList() + theIntParameters = java.util.ArrayList() theIntCovariance = MnUserCovariance() } @@ -57,7 +57,7 @@ class MnUserParameterState { theParameters = other.theParameters.copy() theCovariance = other.theCovariance theGlobalCC = other.theGlobalCC - theIntParameters = java.util.ArrayList(other.theIntParameters) + theIntParameters = java.util.ArrayList(other.theIntParameters) theIntCovariance = other.theIntCovariance.copy() } @@ -71,7 +71,7 @@ class MnUserParameterState { theParameters = MnUserParameters(par, err) theCovariance = MnUserCovariance() theGlobalCC = MnGlobalCorrelationCoeff() - theIntParameters = java.util.ArrayList(par.size) + theIntParameters = java.util.ArrayList(par.size) for (i in par.indices) { theIntParameters.add(par[i]) } @@ -108,7 +108,7 @@ class MnUserParameterState { theCovarianceValid = true theCovariance = MnUserCovariance(cov, nrow) theGlobalCC = MnGlobalCorrelationCoeff() - theIntParameters = java.util.ArrayList(par.size) + theIntParameters = java.util.ArrayList(par.size) theIntCovariance = MnUserCovariance(cov, nrow) val err = DoubleArray(par.size) for (i in par.indices) { @@ -125,7 +125,7 @@ class MnUserParameterState { theCovarianceValid = true theCovariance = cov theGlobalCC = MnGlobalCorrelationCoeff() - theIntParameters = java.util.ArrayList(par.size) + theIntParameters = java.util.ArrayList(par.size) theIntCovariance = cov.copy() require(!(theCovariance.nrow() !== variableParameters())) { "Bad covariance size" } val err = DoubleArray(par.size) @@ -143,7 +143,7 @@ class MnUserParameterState { theParameters = par theCovariance = cov theGlobalCC = MnGlobalCorrelationCoeff() - theIntParameters = java.util.ArrayList() + theIntParameters = java.util.ArrayList() theIntCovariance = cov.copy() theIntCovariance.scale(0.5) val i = 0 @@ -175,7 +175,7 @@ class MnUserParameterState { theParameters = MnUserParameters() theCovariance = MnUserCovariance() theGlobalCC = MnGlobalCorrelationCoeff() - theIntParameters = java.util.ArrayList() + theIntParameters = java.util.ArrayList() theIntCovariance = MnUserCovariance() for (ipar in trafo.parameters()) { if (ipar.isConst()) { @@ -442,7 +442,7 @@ class MnUserParameterState { * Minuit internal representation * @return */ - fun intParameters(): List { + fun intParameters(): List { return theIntParameters } diff --git a/kmath-optimization/src/commonMain/tmp/minuit/MnUserTransformation.kt b/kmath-optimization/src/commonMain/tmp/minuit/MnUserTransformation.kt index ca17611b1..9209ae1c3 100644 --- a/kmath-optimization/src/commonMain/tmp/minuit/MnUserTransformation.kt +++ b/kmath-optimization/src/commonMain/tmp/minuit/MnUserTransformation.kt @@ -27,7 +27,7 @@ import org.apache.commons.math3.linear.ArrayRealVector */ class MnUserTransformation { private val nameMap: MutableMap = HashMap() - private var theCache: MutableList + private var theCache: MutableList private var theExtOfInt: MutableList private var theParameters: MutableList private var thePrecision: MnMachinePrecision @@ -36,7 +36,7 @@ class MnUserTransformation { thePrecision = MnMachinePrecision() theParameters = java.util.ArrayList() theExtOfInt = java.util.ArrayList() - theCache = java.util.ArrayList(0) + theCache = java.util.ArrayList(0) } private constructor(other: MnUserTransformation) { @@ -46,14 +46,14 @@ class MnUserTransformation { theParameters.add(par.copy()) } theExtOfInt = java.util.ArrayList(other.theExtOfInt) - theCache = java.util.ArrayList(other.theCache) + theCache = java.util.ArrayList(other.theCache) } constructor(par: DoubleArray, err: DoubleArray) { thePrecision = MnMachinePrecision() theParameters = java.util.ArrayList(par.size) theExtOfInt = java.util.ArrayList(par.size) - theCache = java.util.ArrayList(par.size) + theCache = java.util.ArrayList(par.size) for (i in par.indices) { add("p$i", par[i], err[i]) } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt index 89f1dd8d7..2d30e8b6c 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt @@ -12,6 +12,7 @@ import space.kscience.kmath.samplers.GaussianSampler import space.kscience.kmath.samplers.InternalGamma import space.kscience.kmath.samplers.NormalizedGaussianSampler import space.kscience.kmath.samplers.ZigguratNormalizedGaussianSampler +import space.kscience.kmath.structures.Float64 import kotlin.math.* @@ -30,7 +31,7 @@ internal object InternalErf { /** * Implements [Distribution1D] for the normal (gaussian) distribution. */ -public class NormalDistribution(public val sampler: GaussianSampler) : Distribution1D { +public class NormalDistribution(public val sampler: GaussianSampler) : Distribution1D { override fun probability(arg: Double): Double { val x1 = (arg - sampler.mean) / sampler.standardDeviation diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/UniformDistribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/UniformDistribution.kt index d0f13398c..7458c4965 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/UniformDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/UniformDistribution.kt @@ -8,13 +8,14 @@ package space.kscience.kmath.distributions import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.SimpleChain import space.kscience.kmath.random.RandomGenerator +import space.kscience.kmath.structures.Float64 -public class UniformDistribution(public val range: ClosedFloatingPointRange) : Distribution1D { +public class UniformDistribution(public val range: ClosedFloatingPointRange) : Distribution1D { private val length: Double = range.endInclusive - range.start override fun probability(arg: Double): Double = if (arg in range) 1.0 / length else 0.0 - override fun sample(generator: RandomGenerator): Chain = + override fun sample(generator: RandomGenerator): Chain = SimpleChain { range.start + generator.nextDouble() * length } override fun cumulative(arg: Double): Double = when { @@ -24,5 +25,5 @@ public class UniformDistribution(public val range: ClosedFloatingPointRange): UniformDistribution = +public fun Distribution.Companion.uniform(range: ClosedFloatingPointRange): UniformDistribution = UniformDistribution(range) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt index 0276cd654..232d17166 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.samplers import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import kotlin.math.ln import kotlin.math.pow @@ -18,7 +19,7 @@ import kotlin.math.pow * Based on Commons RNG implementation. * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.html]. */ -public class AhrensDieterExponentialSampler(public val mean: Double) : Sampler { +public class AhrensDieterExponentialSampler(public val mean: Double) : Sampler { init { require(mean > 0) { "mean is not strictly positive: $mean" } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt index 05163387d..7a5333dc2 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt @@ -10,6 +10,7 @@ import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.random.chain import space.kscience.kmath.stat.Sampler import space.kscience.kmath.stat.next +import space.kscience.kmath.structures.Float64 import kotlin.math.* /** @@ -26,14 +27,14 @@ import kotlin.math.* public class AhrensDieterMarsagliaTsangGammaSampler private constructor( alpha: Double, theta: Double, -) : Sampler { +) : Sampler { private val delegate: BaseGammaSampler = if (alpha < 1) AhrensDieterGammaSampler(alpha, theta) else MarsagliaTsangGammaSampler(alpha, theta) private abstract class BaseGammaSampler internal constructor( protected val alpha: Double, protected val theta: Double, - ) : Sampler { + ) : Sampler { init { require(alpha > 0) { "alpha is not strictly positive: $alpha" } require(theta > 0) { "theta is not strictly positive: $theta" } @@ -47,7 +48,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler private constructor( private val oneOverAlpha: Double = 1.0 / alpha private val bGSOptim: Double = 1.0 + alpha / E - override fun sample(generator: RandomGenerator): Chain = generator.chain { + override fun sample(generator: RandomGenerator): Chain = generator.chain { var x: Double // [1]: p. 228, Algorithm GS. @@ -90,7 +91,7 @@ public class AhrensDieterMarsagliaTsangGammaSampler private constructor( cOptim = ONE_THIRD / sqrt(dOptim) } - override fun sample(generator: RandomGenerator): Chain = generator.chain { + override fun sample(generator: RandomGenerator): Chain = generator.chain { var v: Double while (true) { @@ -113,13 +114,13 @@ public class AhrensDieterMarsagliaTsangGammaSampler private constructor( } } - override fun sample(generator: RandomGenerator): Chain = delegate.sample(generator) + override fun sample(generator: RandomGenerator): Chain = delegate.sample(generator) override fun toString(): String = delegate.toString() public companion object { public fun of( alpha: Double, theta: Double, - ): Sampler = AhrensDieterMarsagliaTsangGammaSampler(alpha, theta) + ): Sampler = AhrensDieterMarsagliaTsangGammaSampler(alpha, theta) } } \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt index a6bba910b..7dcf1dc06 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MetropolisHastingsSampler.kt @@ -57,7 +57,7 @@ public class MetropolisHastingsSampler( startPoint: Float64, stepSampler: Sampler, targetPdf: suspend (Float64) -> Float64, - ): MetropolisHastingsSampler = MetropolisHastingsSampler( + ): MetropolisHastingsSampler = MetropolisHastingsSampler( algebra = Float64.algebra, startPoint = {startPoint}, stepSampler = stepSampler, @@ -71,6 +71,6 @@ public class MetropolisHastingsSampler( startPoint: Float64, stepSigma: Float64, targetPdf: suspend (Float64) -> Float64, - ): MetropolisHastingsSampler = univariate(startPoint, GaussianSampler(0.0, stepSigma), targetPdf) + ): MetropolisHastingsSampler = univariate(startPoint, GaussianSampler(0.0, stepSigma), targetPdf) } } \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/NormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/NormalizedGaussianSampler.kt index 81d1edb47..b3f18e605 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/NormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/NormalizedGaussianSampler.kt @@ -8,8 +8,9 @@ package space.kscience.kmath.samplers import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.structures.Float64 -public interface BlockingDoubleSampler : Sampler { +public interface BlockingDoubleSampler : Sampler { override fun sample(generator: RandomGenerator): BlockingDoubleChain } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt index 9bc1a6bc2..b53a1d923 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt @@ -12,6 +12,7 @@ import space.kscience.kmath.chains.combine import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory +import space.kscience.kmath.structures.Float64 import kotlin.jvm.JvmName /** @@ -66,7 +67,7 @@ public inline fun Sampler.sampleBuffer(generator: RandomGen /** * Samples a [Buffer] of values from this [Sampler]. */ -public suspend fun Sampler.nextBuffer(generator: RandomGenerator, size: Int): Buffer = +public suspend fun Sampler.nextBuffer(generator: RandomGenerator, size: Int): Buffer = sampleBuffer(generator, size).first() //TODO add `context(RandomGenerator) Sampler.nextBuffer \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/VarianceRatioTest.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/VarianceRatioTest.kt index 337b13020..1abd50f47 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/VarianceRatioTest.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/VarianceRatioTest.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.series import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.operations.Float64Field.pow import space.kscience.kmath.operations.fold +import space.kscience.kmath.structures.Float64 import kotlin.math.absoluteValue @@ -31,7 +32,7 @@ public data class VarianceRatioTestResult( * @author https://github.com/mrFendel */ public fun SeriesAlgebra.varianceRatioTest( - series: Series, + series: Series, shift: Int, homoscedastic: Boolean = true, ): VarianceRatioTestResult { diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt index 846e95b18..031bb3298 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.stat import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.indices /** @@ -42,7 +43,7 @@ public class Mean( } public companion object { - public fun evaluate(buffer: Buffer): Double = Float64Field.mean.evaluateBlocking(buffer) + public fun evaluate(buffer: Buffer): Double = Float64Field.mean.evaluateBlocking(buffer) public fun evaluate(buffer: Buffer): Int = Int32Ring.mean.evaluateBlocking(buffer) public fun evaluate(buffer: Buffer): Long = Int64Ring.mean.evaluateBlocking(buffer) } @@ -50,7 +51,7 @@ public class Mean( //TODO replace with optimized version which respects overflow -public val Float64Field.mean: Mean get() = Mean(Float64Field) +public val Float64Field.mean: Mean get() = Mean(Float64Field) public val Int32Ring.mean: Mean get() = Mean(Int32Field) public val Int64Ring.mean: Mean get() = Mean(Int64Field) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt index f0328839f..fd829bffe 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.stat import space.kscience.kmath.misc.sortedWith import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 /** * Non-composable median @@ -27,12 +28,12 @@ public class Median(private val field: Field, private val comparator: Comp public companion object { - public fun evaluate(buffer: Buffer): Double = Float64Field.mean.evaluateBlocking(buffer) + public fun evaluate(buffer: Buffer): Double = Float64Field.mean.evaluateBlocking(buffer) public fun evaluate(buffer: Buffer): Int = Int32Ring.mean.evaluateBlocking(buffer) public fun evaluate(buffer: Buffer): Long = Int64Ring.mean.evaluateBlocking(buffer) } } -public val Float64Field.median: Median get() = Median(Float64Field) { a, b -> a.compareTo(b) } +public val Float64Field.median: Median get() = Median(Float64Field) { a, b -> a.compareTo(b) } public val Int32Ring.median: Median get() = Median(Int32Field) { a, b -> a.compareTo(b) } public val Int64Ring.median: Median get() = Median(Int64Field) { a, b -> a.compareTo(b) } \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/chiSquaredExpression.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/chiSquaredExpression.kt index c42f149f2..19ca518e5 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/chiSquaredExpression.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/chiSquaredExpression.kt @@ -11,6 +11,7 @@ import space.kscience.kmath.expressions.ExpressionAlgebra import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.asIterable import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.indices import kotlin.jvm.JvmName @@ -48,11 +49,11 @@ public fun , I : Any, A> AutoDiffProcessor.chiSquared } public fun AutoDiffProcessor.chiSquaredExpression( - x: Buffer, - y: Buffer, - yErr: Buffer, + x: Buffer, + y: Buffer, + yErr: Buffer, model: A.(I) -> I, -): DifferentiableExpression where A : ExtendedField, A : ExpressionAlgebra { +): DifferentiableExpression where A : ExtendedField, A : ExpressionAlgebra { require(yErr.asIterable().all { it > 0.0 }) { "All errors must be strictly positive" } return chiSquaredExpression(x, y, yErr, model) } \ No newline at end of file diff --git a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/series/TestSeries.kt b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/series/TestSeries.kt index 461edcaf5..275160b06 100644 --- a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/series/TestSeries.kt +++ b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/series/TestSeries.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.series import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.bufferAlgebra import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.slice import kotlin.math.PI import kotlin.test.Test @@ -21,7 +22,7 @@ class TestSeries { val s2 = s1.slice(20..50).moveTo(40) - val s3: Buffer = s1.zip(s2) { l, r -> l + r } //s1 + s2 + val s3: Buffer = s1.zip(s2) { l, r -> l + r } //s1 + s2 assertEquals(s3.getByOffset(40), s1.getByOffset(40) + s1.getByOffset(20)) } 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 516a3a2ce..ff7b9a190 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 @@ -20,13 +20,14 @@ import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.FieldOps import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.PowerOperations +import space.kscience.kmath.structures.Float64 public class DoubleTensorFlowOutput( graph: Graph, output: Output, ) : TensorFlowOutput(graph, output) { - override fun org.tensorflow.Tensor.actualizeTensor(): NdArray = this as TFloat64 + override fun org.tensorflow.Tensor.actualizeTensor(): NdArray = this as TFloat64 } internal fun ShapeND.toLongArray(): LongArray = LongArray(size) { get(it).toLong() } @@ -34,14 +35,14 @@ internal fun ShapeND.toLongArray(): LongArray = LongArray(size) { get(it).toLong public class DoubleTensorFlowAlgebra internal constructor( graph: Graph, ) : TensorFlowAlgebra(graph), FieldOps>, PowerOperations> { + Float64Field>(graph), FieldOps>, PowerOperations> { override val elementAlgebra: Float64Field get() = Float64Field override fun mutableStructureND( shape: ShapeND, initializer: Float64Field.(IntArray) -> Double, - ): MutableStructureND { + ): MutableStructureND { val res = TFloat64.tensorOf(org.tensorflow.ndarray.Shape.of(*shape.toLongArray())) { array -> ColumnStrides(shape).forEach { index -> array.setDouble(elementAlgebra.initializer(index), *index.toLongArray()) @@ -50,7 +51,7 @@ public class DoubleTensorFlowAlgebra internal constructor( return DoubleTensorFlowOutput(graph, ops.constant(res).asOutput()) } - override fun StructureND.asTensorFlow(): TensorFlowOutput = + override fun StructureND.asTensorFlow(): TensorFlowOutput = if (this is TensorFlowOutput && output.type() == TFloat64::class.java) { @Suppress("UNCHECKED_CAST") this as TensorFlowOutput @@ -69,13 +70,13 @@ public class DoubleTensorFlowAlgebra internal constructor( override fun const(value: Double): Constant = ops.constant(value) override fun divide( - left: StructureND, - right: StructureND, + left: StructureND, + right: StructureND, ): TensorFlowOutput = left.operate(right) { l, r -> ops.math.div(l, r) } - override fun power(arg: StructureND, pow: Number): TensorFlowOutput = + override fun power(arg: StructureND, pow: Number): TensorFlowOutput = arg.operate { ops.math.pow(it, const(pow.toDouble())) } } @@ -86,8 +87,8 @@ public class DoubleTensorFlowAlgebra internal constructor( */ @UnstableKMathAPI public fun Float64Field.produceWithTF( - block: DoubleTensorFlowAlgebra.() -> StructureND, -): StructureND = Graph().use { graph -> + block: DoubleTensorFlowAlgebra.() -> StructureND, +): StructureND = Graph().use { graph -> val scope = DoubleTensorFlowAlgebra(graph) scope.export(scope.block()) } @@ -99,8 +100,8 @@ public fun Float64Field.produceWithTF( */ @OptIn(UnstableKMathAPI::class) public fun Float64Field.produceMapWithTF( - block: DoubleTensorFlowAlgebra.() -> Map>, -): Map> = Graph().use { graph -> + block: DoubleTensorFlowAlgebra.() -> Map>, +): Map> = Graph().use { graph -> val scope = DoubleTensorFlowAlgebra(graph) scope.block().mapValues { scope.export(it.value) } } \ No newline at end of file diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt index f9f0ed832..09a3265f3 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.tensors.api import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.StructureND import space.kscience.kmath.operations.Field +import space.kscience.kmath.structures.Float64 /** * Common linear algebra operations. Operates on [Tensor]. @@ -110,5 +111,5 @@ public interface LinearOpsTensorAlgebra> : TensorPartialDivision * @receiver the `input1` and the `input2`. * @return the square matrix x which is the solution of the equation. */ - public fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D + public fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt index d02a9f99e..801a24529 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.internal.broadcastTensors @@ -18,7 +19,7 @@ import space.kscience.kmath.tensors.core.internal.broadcastTo */ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { - override fun StructureND.plus(arg: StructureND): DoubleTensor { + override fun StructureND.plus(arg: StructureND): DoubleTensor { val broadcast = broadcastTensors(asDoubleTensor(), arg.asDoubleTensor()) val newThis = broadcast[0] val newOther = broadcast[1] @@ -28,14 +29,14 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { return DoubleTensor(newThis.shape, resBuffer) } - override fun Tensor.plusAssign(arg: StructureND) { + override fun Tensor.plusAssign(arg: StructureND) { val newOther = broadcastTo(arg.asDoubleTensor(), asDoubleTensor().shape) for (i in 0 until asDoubleTensor().indices.linearSize) { asDoubleTensor().source[i] += newOther.source[i] } } - override fun StructureND.minus(arg: StructureND): DoubleTensor { + override fun StructureND.minus(arg: StructureND): DoubleTensor { val broadcast = broadcastTensors(asDoubleTensor(), arg.asDoubleTensor()) val newThis = broadcast[0] val newOther = broadcast[1] @@ -45,14 +46,14 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { return DoubleTensor(newThis.shape, resBuffer) } - override fun Tensor.minusAssign(arg: StructureND) { + override fun Tensor.minusAssign(arg: StructureND) { val newOther = broadcastTo(arg.asDoubleTensor(), asDoubleTensor().shape) for (i in 0 until indices.linearSize) { asDoubleTensor().source[i] -= newOther.source[i] } } - override fun StructureND.times(arg: StructureND): DoubleTensor { + override fun StructureND.times(arg: StructureND): DoubleTensor { val broadcast = broadcastTensors(asDoubleTensor(), arg.asDoubleTensor()) val newThis = broadcast[0] val newOther = broadcast[1] @@ -62,14 +63,14 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { return DoubleTensor(newThis.shape, resBuffer) } - override fun Tensor.timesAssign(arg: StructureND) { + override fun Tensor.timesAssign(arg: StructureND) { val newOther = broadcastTo(arg.asDoubleTensor(), asDoubleTensor().shape) for (i in 0 until indices.linearSize) { asDoubleTensor().source[+i] *= newOther.source[i] } } - override fun StructureND.div(arg: StructureND): DoubleTensor { + override fun StructureND.div(arg: StructureND): DoubleTensor { val broadcast = broadcastTensors(asDoubleTensor(), arg.asDoubleTensor()) val newThis = broadcast[0] val newOther = broadcast[1] @@ -79,7 +80,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { return DoubleTensor(newThis.shape, resBuffer) } - override fun Tensor.divAssign(arg: StructureND) { + override fun Tensor.divAssign(arg: StructureND) { val newOther = broadcastTo(arg.asDoubleTensor(), asDoubleTensor().shape) for (i in 0 until indices.linearSize) { asDoubleTensor().source[i] /= newOther.source[i] diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt index b3c2eb1b4..132a1f1e6 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt @@ -16,7 +16,7 @@ public class OffsetDoubleBuffer( override val origin: Float64Buffer, private val offset: Int, override val size: Int, -) : MutableBuffer, BufferView { +) : MutableBuffer, BufferView { init { require(offset >= 0) { "Offset must be non-negative" } @@ -36,7 +36,7 @@ public class OffsetDoubleBuffer( */ public fun copy(): Float64Buffer = origin.array.copyOfRange(offset, offset + size).asBuffer() - override fun iterator(): Iterator = iterator { + override fun iterator(): Iterator = iterator { for (i in indices) { yield(get(i)) } @@ -86,7 +86,7 @@ public inline fun OffsetDoubleBuffer.mapInPlace(operation: (Double) -> Double) { public open class DoubleTensor( shape: ShapeND, final override val source: OffsetDoubleBuffer, -) : BufferedTensor(shape), MutableStructureNDOfDouble { +) : BufferedTensor(shape), MutableStructureNDOfDouble { init { require(linearSize == source.size) { "Source buffer size must be equal tensor size" } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor1D.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor1D.kt index 05f1b2f20..aefd87bf0 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor1D.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor1D.kt @@ -8,10 +8,11 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.MutableStructure1D import space.kscience.kmath.nd.ShapeND +import space.kscience.kmath.structures.Float64 public class DoubleTensor1D( source: OffsetDoubleBuffer, -) : DoubleTensor(ShapeND(source.size), source), MutableStructure1D { +) : DoubleTensor(ShapeND(source.size), source), MutableStructure1D { @PerformancePitfall override fun get(index: IntArray): Double = super.get(index) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor2D.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor2D.kt index 013dfe791..bee8832ea 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor2D.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor2D.kt @@ -9,6 +9,7 @@ import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.linearSize +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.PermutedMutableBuffer import space.kscience.kmath.structures.permute @@ -16,7 +17,7 @@ public class DoubleTensor2D( override val rowNum: Int, override val colNum: Int, source: OffsetDoubleBuffer, -) : DoubleTensor(ShapeND(rowNum, colNum), source), MutableStructure2D { +) : DoubleTensor(ShapeND(rowNum, colNum), source), MutableStructure2D { override fun get(i: Int, j: Int): Double = source[i * colNum + j] @@ -35,7 +36,7 @@ public class DoubleTensor2D( @OptIn(PerformancePitfall::class) - override val columns: List> + override val columns: List> get() = List(colNum) { j -> val indices = IntArray(rowNum) { i -> j + i * colNum } source.permute(indices) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index d9e107d86..7bf0dcb8b 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -44,7 +44,7 @@ public open class DoubleTensorAlgebra : * @return the resulting tensor after applying the function. */ @Suppress("OVERRIDE_BY_INLINE") - final override inline fun StructureND.map(transform: Float64Field.(Double) -> Double): DoubleTensor { + final override inline fun StructureND.map(transform: Float64Field.(Double) -> Double): DoubleTensor { val tensor = asDoubleTensor() //TODO remove additional copy val array = Float64Buffer(tensor.source.size) { Float64Field.transform(tensor.source[it]) } @@ -54,7 +54,7 @@ public open class DoubleTensorAlgebra : ) } - public inline fun Tensor.mapInPlace(operation: (Double) -> Double) { + public inline fun Tensor.mapInPlace(operation: (Double) -> Double) { if (this is DoubleTensor) { source.mapInPlace(operation) } else { @@ -62,20 +62,20 @@ public open class DoubleTensorAlgebra : } } - public inline fun Tensor.mapIndexedInPlace(operation: Float64Field.(IntArray, Double) -> Double) { + public inline fun Tensor.mapIndexedInPlace(operation: Float64Field.(IntArray, Double) -> Double) { indices.forEach { set(it, Float64Field.operation(it, get(it))) } } @Suppress("OVERRIDE_BY_INLINE") - final override inline fun StructureND.mapIndexed(transform: Float64Field.(index: IntArray, Double) -> Double): DoubleTensor { + final override inline fun StructureND.mapIndexed(transform: Float64Field.(index: IntArray, Double) -> Double): DoubleTensor { return copyToTensor().apply { mapIndexedInPlace(transform) } } @Suppress("OVERRIDE_BY_INLINE") final override inline fun zip( - left: StructureND, - right: StructureND, + left: StructureND, + right: StructureND, transform: Float64Field.(Double, Double) -> Double, ): DoubleTensor { checkShapesCompatible(left, right) @@ -89,19 +89,19 @@ public open class DoubleTensorAlgebra : } - public inline fun StructureND.reduceElements(transform: (Float64Buffer) -> Double): Double = + public inline fun StructureND.reduceElements(transform: (Float64Buffer) -> Double): Double = transform(asDoubleTensor().source.copy()) //TODO Add read-only DoubleBuffer wrapper. To avoid protective copy - override fun StructureND.valueOrNull(): Double? { + override fun StructureND.valueOrNull(): Double? { val dt = asDoubleTensor() return if (dt.shape == ShapeND(1)) dt.source[0] else null } - override fun StructureND.value(): Double = valueOrNull() + override fun StructureND.value(): Double = valueOrNull() ?: throw IllegalArgumentException("The tensor shape is $shape, but value method is allowed only for shape [1]") - public fun fromBuffer(shape: ShapeND, buffer: Buffer): DoubleTensor { + public fun fromBuffer(shape: ShapeND, buffer: Buffer): DoubleTensor { checkNotEmptyShape(shape) check(buffer.size > 0) { "Illegal empty buffer provided" } check(buffer.size == shape.linearSize) { @@ -133,7 +133,7 @@ public open class DoubleTensorAlgebra : RowStrides(shape).asSequence().map { Float64Field.initializer(it) }.toMutableList().toDoubleArray() ) - override fun Tensor.getTensor(i: Int): DoubleTensor { + override fun Tensor.getTensor(i: Int): DoubleTensor { val dt = asDoubleTensor() val lastShape = shape.last(shape.size - 1) val newShape: ShapeND = if (lastShape.isNotEmpty()) lastShape else ShapeND(1) @@ -214,71 +214,71 @@ public open class DoubleTensorAlgebra : return res } - override fun Double.plus(arg: StructureND): DoubleTensor = arg.map { this@plus + it } + override fun Double.plus(arg: StructureND): DoubleTensor = arg.map { this@plus + it } - override fun StructureND.plus(arg: Double): DoubleTensor = map { it + arg } + override fun StructureND.plus(arg: Double): DoubleTensor = map { it + arg } - override fun StructureND.plus(arg: StructureND): DoubleTensor = zip(this, arg) { l, r -> l + r } + override fun StructureND.plus(arg: StructureND): DoubleTensor = zip(this, arg) { l, r -> l + r } - override fun Tensor.plusAssign(value: Double) { + override fun Tensor.plusAssign(value: Double) { mapInPlace { it + value } } - override fun Tensor.plusAssign(arg: StructureND) { + override fun Tensor.plusAssign(arg: StructureND) { checkShapesCompatible(asDoubleTensor(), arg.asDoubleTensor()) mapIndexedInPlace { index, value -> value + arg[index] } } - override fun Double.minus(arg: StructureND): DoubleTensor = arg.map { this@minus - it } + override fun Double.minus(arg: StructureND): DoubleTensor = arg.map { this@minus - it } - override fun StructureND.minus(arg: Double): DoubleTensor = map { it - arg } + override fun StructureND.minus(arg: Double): DoubleTensor = map { it - arg } - override fun StructureND.minus(arg: StructureND): DoubleTensor = zip(this, arg) { l, r -> l - r } + override fun StructureND.minus(arg: StructureND): DoubleTensor = zip(this, arg) { l, r -> l - r } - override fun Tensor.minusAssign(value: Double) { + override fun Tensor.minusAssign(value: Double) { mapInPlace { it - value } } - override fun Tensor.minusAssign(arg: StructureND) { + override fun Tensor.minusAssign(arg: StructureND) { checkShapesCompatible(this, arg) mapIndexedInPlace { index, value -> value - arg.getDouble(index) } } - override fun Double.times(arg: StructureND): DoubleTensor = arg.map { this@times * it } + override fun Double.times(arg: StructureND): DoubleTensor = arg.map { this@times * it } - override fun StructureND.times(arg: Double): DoubleTensor = arg * asDoubleTensor() + override fun StructureND.times(arg: Double): DoubleTensor = arg * asDoubleTensor() - override fun StructureND.times(arg: StructureND): DoubleTensor = zip(this, arg) { l, r -> l * r } + override fun StructureND.times(arg: StructureND): DoubleTensor = zip(this, arg) { l, r -> l * r } - override fun Tensor.timesAssign(value: Double) { + override fun Tensor.timesAssign(value: Double) { mapInPlace { it * value } } - override fun Tensor.timesAssign(arg: StructureND) { + override fun Tensor.timesAssign(arg: StructureND) { checkShapesCompatible(this, arg) mapIndexedInPlace { index, value -> value * arg[index] } } - override fun Double.div(arg: StructureND): DoubleTensor = arg.map { this@div / it } + override fun Double.div(arg: StructureND): DoubleTensor = arg.map { this@div / it } - override fun StructureND.div(arg: Double): DoubleTensor = map { it / arg } + override fun StructureND.div(arg: Double): DoubleTensor = map { it / arg } - override fun StructureND.div(arg: StructureND): DoubleTensor = zip(this, arg) { l, r -> l / r } + override fun StructureND.div(arg: StructureND): DoubleTensor = zip(this, arg) { l, r -> l / r } - override fun Tensor.divAssign(value: Double) { + override fun Tensor.divAssign(value: Double) { mapInPlace { it / value } } - override fun Tensor.divAssign(arg: StructureND) { + override fun Tensor.divAssign(arg: StructureND) { checkShapesCompatible(asDoubleTensor(), arg) mapIndexedInPlace { index, value -> value / arg[index] } } - override fun StructureND.unaryMinus(): DoubleTensor = map { -it } + override fun StructureND.unaryMinus(): DoubleTensor = map { -it } - override fun StructureND.transposed(i: Int, j: Int): Tensor { + override fun StructureND.transposed(i: Int, j: Int): Tensor { val actualI = if (i >= 0) i else shape.size + i val actualJ = if (j >= 0) j else shape.size + j return asDoubleTensor().permute( @@ -315,12 +315,12 @@ public open class DoubleTensorAlgebra : // return resTensor } - override fun Tensor.view(shape: ShapeND): DoubleTensor { + override fun Tensor.view(shape: ShapeND): DoubleTensor { checkView(asDoubleTensor(), shape) return DoubleTensor(shape, asDoubleTensor().source) } - override fun Tensor.viewAs(other: StructureND): DoubleTensor = + override fun Tensor.viewAs(other: StructureND): DoubleTensor = view(other.shape) /** @@ -352,7 +352,7 @@ public open class DoubleTensorAlgebra : * @param other tensor to be multiplied. * @return a mathematical product of two tensors. */ - public infix fun StructureND.matmul(other: StructureND): DoubleTensor { + public infix fun StructureND.matmul(other: StructureND): DoubleTensor { if (shape.size == 1 && other.shape.size == 1) { return DoubleTensor(ShapeND(1), Float64Buffer(times(other).sum())) } @@ -412,13 +412,13 @@ public open class DoubleTensorAlgebra : } } - override fun StructureND.dot(other: StructureND): DoubleTensor { + override fun StructureND.dot(other: StructureND): DoubleTensor { return if (dimension in 0..2 && other.dimension in 0..2) this.matmul(other) else error("Only vectors and matrices are allowed in non-broadcasting dot operation") } override fun diagonalEmbedding( - diagonalEntries: StructureND, + diagonalEntries: StructureND, offset: Int, dim1: Int, dim2: Int, @@ -476,7 +476,7 @@ public open class DoubleTensorAlgebra : * @param epsilon permissible error when comparing two Double values. * @return true if two tensors have the same shape and elements, false otherwise. */ - public fun Tensor.eq(other: Tensor, epsilon: Double): Boolean = + public fun Tensor.eq(other: Tensor, epsilon: Double): Boolean = asDoubleTensor().eq(other) { x, y -> abs(x - y) < epsilon } /** @@ -486,10 +486,10 @@ public open class DoubleTensorAlgebra : * @param other the tensor to compare with `input` tensor. * @return true if two tensors have the same shape and elements, false otherwise. */ - public infix fun Tensor.eq(other: Tensor): Boolean = eq(other, 1e-5) + public infix fun Tensor.eq(other: Tensor): Boolean = eq(other, 1e-5) - private fun Tensor.eq( - other: Tensor, + private fun Tensor.eq( + other: Tensor, eqFunction: (Double, Double) -> Boolean, ): Boolean { //TODO optimize tensor conversion @@ -512,10 +512,10 @@ public open class DoubleTensorAlgebra : * @param indices the [IntArray] of 1-dimensional indices * @return tensor with rows corresponding to row by [indices] */ - public fun Tensor.rowsByIndices(indices: IntArray): DoubleTensor = stack(indices.map { getTensor(it) }) + public fun Tensor.rowsByIndices(indices: IntArray): DoubleTensor = stack(indices.map { getTensor(it) }) - private inline fun StructureND.foldDimToDouble( + private inline fun StructureND.foldDimToDouble( dim: Int, keepDim: Boolean, foldFunction: (DoubleArray) -> Double, @@ -543,7 +543,7 @@ public open class DoubleTensorAlgebra : return resTensor } - private inline fun StructureND.foldDimToInt( + private inline fun StructureND.foldDimToInt( dim: Int, keepDim: Boolean, foldFunction: (DoubleArray) -> Int, @@ -571,46 +571,46 @@ public open class DoubleTensorAlgebra : } - override fun StructureND.sum(): Double = reduceElements { it.array.sum() } + override fun StructureND.sum(): Double = reduceElements { it.array.sum() } - override fun StructureND.sum(dim: Int, keepDim: Boolean): DoubleTensor = + override fun StructureND.sum(dim: Int, keepDim: Boolean): DoubleTensor = foldDimToDouble(dim, keepDim) { x -> x.sum() } - override fun StructureND.min(): Double = reduceElements { it.array.min() } + override fun StructureND.min(): Double = reduceElements { it.array.min() } - override fun StructureND.min(dim: Int, keepDim: Boolean): DoubleTensor = + override fun StructureND.min(dim: Int, keepDim: Boolean): DoubleTensor = foldDimToDouble(dim, keepDim) { x -> x.minOrNull()!! } - override fun StructureND.argMin(dim: Int, keepDim: Boolean): Tensor = foldDimToInt(dim, keepDim) { x -> + override fun StructureND.argMin(dim: Int, keepDim: Boolean): Tensor = foldDimToInt(dim, keepDim) { x -> x.withIndex().minBy { it.value }.index } - override fun StructureND.max(): Double = reduceElements { it.array.max() } + override fun StructureND.max(): Double = reduceElements { it.array.max() } - override fun StructureND.max(dim: Int, keepDim: Boolean): DoubleTensor = + override fun StructureND.max(dim: Int, keepDim: Boolean): DoubleTensor = foldDimToDouble(dim, keepDim) { x -> x.maxOrNull()!! } - override fun StructureND.argMax(dim: Int, keepDim: Boolean): IntTensor = + override fun StructureND.argMax(dim: Int, keepDim: Boolean): IntTensor = foldDimToInt(dim, keepDim) { x -> x.withIndex().maxBy { it.value }.index } - override fun mean(structureND: StructureND): Double = structureND.sum() / structureND.indices.linearSize + override fun mean(structureND: StructureND): Double = structureND.sum() / structureND.indices.linearSize - override fun mean(structureND: StructureND, dim: Int, keepDim: Boolean): Tensor = + override fun mean(structureND: StructureND, dim: Int, keepDim: Boolean): Tensor = structureND.foldDimToDouble(dim, keepDim) { arr -> check(dim < structureND.dimension) { "Dimension $dim out of range ${structureND.dimension}" } arr.sum() / structureND.shape[dim] } - override fun std(structureND: StructureND): Double = structureND.reduceElements { arr -> + override fun std(structureND: StructureND): Double = structureND.reduceElements { arr -> val mean = arr.array.sum() / structureND.indices.linearSize sqrt(arr.array.sumOf { (it - mean) * (it - mean) } / (structureND.indices.linearSize - 1)) } - override fun std(structureND: StructureND, dim: Int, keepDim: Boolean): Tensor = + override fun std(structureND: StructureND, dim: Int, keepDim: Boolean): Tensor = structureND.foldDimToDouble( dim, keepDim @@ -620,13 +620,13 @@ public open class DoubleTensorAlgebra : sqrt(arr.sumOf { (it - mean) * (it - mean) } / (structureND.shape[dim] - 1)) } - override fun variance(structureND: StructureND): Double = structureND.reduceElements { arr -> + override fun variance(structureND: StructureND): Double = structureND.reduceElements { arr -> val linearSize = structureND.indices.linearSize val mean = arr.array.sum() / linearSize arr.array.sumOf { (it - mean) * (it - mean) } / (linearSize - 1) } - override fun variance(structureND: StructureND, dim: Int, keepDim: Boolean): Tensor = + override fun variance(structureND: StructureND, dim: Int, keepDim: Boolean): Tensor = structureND.foldDimToDouble( dim, keepDim @@ -637,56 +637,56 @@ public open class DoubleTensorAlgebra : } - override fun exp(arg: StructureND): DoubleTensor = arg.map { this.exp(it) } + override fun exp(arg: StructureND): DoubleTensor = arg.map { this.exp(it) } - override fun ln(arg: StructureND): DoubleTensor = arg.map { this.ln(it) } + override fun ln(arg: StructureND): DoubleTensor = arg.map { this.ln(it) } - override fun sqrt(arg: StructureND): DoubleTensor = arg.map { this.sqrt(it) } + override fun sqrt(arg: StructureND): DoubleTensor = arg.map { this.sqrt(it) } - override fun cos(arg: StructureND): DoubleTensor = arg.map { this.cos(it) } + override fun cos(arg: StructureND): DoubleTensor = arg.map { this.cos(it) } - override fun acos(arg: StructureND): DoubleTensor = arg.map { this.acos(it) } + override fun acos(arg: StructureND): DoubleTensor = arg.map { this.acos(it) } - override fun cosh(arg: StructureND): DoubleTensor = arg.map { this.cosh(it) } + override fun cosh(arg: StructureND): DoubleTensor = arg.map { this.cosh(it) } - override fun acosh(arg: StructureND): DoubleTensor = arg.map { this.acosh(it) } + override fun acosh(arg: StructureND): DoubleTensor = arg.map { this.acosh(it) } - override fun sin(arg: StructureND): DoubleTensor = arg.map { this.sin(it) } + override fun sin(arg: StructureND): DoubleTensor = arg.map { this.sin(it) } - override fun asin(arg: StructureND): DoubleTensor = arg.map { this.asin(it) } + override fun asin(arg: StructureND): DoubleTensor = arg.map { this.asin(it) } - override fun sinh(arg: StructureND): DoubleTensor = arg.map { this.sinh(it) } + override fun sinh(arg: StructureND): DoubleTensor = arg.map { this.sinh(it) } - override fun asinh(arg: StructureND): DoubleTensor = arg.map { this.asinh(it) } + override fun asinh(arg: StructureND): DoubleTensor = arg.map { this.asinh(it) } - override fun tan(arg: StructureND): DoubleTensor = arg.map { this.tan(it) } + override fun tan(arg: StructureND): DoubleTensor = arg.map { this.tan(it) } - override fun atan(arg: StructureND): DoubleTensor = arg.map { this.atan(it) } + override fun atan(arg: StructureND): DoubleTensor = arg.map { this.atan(it) } - override fun tanh(arg: StructureND): DoubleTensor = arg.map { this.tanh(it) } + override fun tanh(arg: StructureND): DoubleTensor = arg.map { this.tanh(it) } - override fun atanh(arg: StructureND): DoubleTensor = arg.map { this.atanh(it) } + override fun atanh(arg: StructureND): DoubleTensor = arg.map { this.atanh(it) } - override fun power(arg: StructureND, pow: Number): StructureND = if (pow is Int) { + override fun power(arg: StructureND, pow: Number): StructureND = if (pow is Int) { arg.map { it.pow(pow) } } else { arg.map { it.pow(pow.toDouble()) } } - override fun ceil(arg: StructureND): DoubleTensor = arg.map { ceil(it) } + override fun ceil(arg: StructureND): DoubleTensor = arg.map { ceil(it) } - override fun floor(structureND: StructureND): DoubleTensor = structureND.map { floor(it) } + override fun floor(structureND: StructureND): DoubleTensor = structureND.map { floor(it) } - override fun StructureND.inv(): DoubleTensor = invLU(this, 1e-9) + override fun StructureND.inv(): DoubleTensor = invLU(this, 1e-9) - override fun StructureND.det(): DoubleTensor = detLU(this, 1e-9) + override fun StructureND.det(): DoubleTensor = detLU(this, 1e-9) - override fun lu(structureND: StructureND): Triple = + override fun lu(structureND: StructureND): Triple = lu(structureND, 1e-9) - override fun cholesky(structureND: StructureND): DoubleTensor = cholesky(structureND, 1e-6) + override fun cholesky(structureND: StructureND): DoubleTensor = cholesky(structureND, 1e-6) - override fun qr(structureND: StructureND): Pair { + override fun qr(structureND: StructureND): Pair { checkSquareMatrix(structureND.shape) val qTensor = zeroesLike(structureND) val rTensor = zeroesLike(structureND) @@ -705,14 +705,14 @@ public open class DoubleTensorAlgebra : } override fun svd( - structureND: StructureND, - ): Triple, StructureND, StructureND> = + structureND: StructureND, + ): Triple, StructureND, StructureND> = svdGolubKahan(structureND = structureND, epsilon = 1e-10) - override fun symEig(structureND: StructureND): Pair = + override fun symEig(structureND: StructureND): Pair = symEigJacobi(structureND = structureND, maxIteration = 50, epsilon = 1e-15) - override fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { + override fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { val aSvd = DoubleTensorAlgebra.svd(a) val s = BroadcastDoubleTensorAlgebra.diagonalEmbedding(aSvd.second.map { 1.0 / it }) val aInverse = aSvd.third.dot(s).dot(aSvd.first.transposed()) 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 b2106200e..60a7229e6 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 @@ -7,6 +7,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* +import space.kscience.kmath.structures.Float64 import kotlin.math.abs import kotlin.math.max import kotlin.math.min @@ -49,7 +50,7 @@ public data class LMResultInfo( var funcCalls: Int, var resultChiSq: Double, var resultLambda: Double, - var resultParameters: MutableStructure2D, + var resultParameters: MutableStructure2D, var typeOfConvergence: TypeOfConvergence, ) @@ -82,14 +83,14 @@ public data class LMResultInfo( * exampleNumber: a parameter for a function with which you can choose its behavior. */ public data class LMInput( - var func: (MutableStructure2D, MutableStructure2D, Int) -> (MutableStructure2D), - var startParameters: MutableStructure2D, - var independentVariables: MutableStructure2D, - var realValues: MutableStructure2D, + var func: (MutableStructure2D, MutableStructure2D, Int) -> (MutableStructure2D), + var startParameters: MutableStructure2D, + var independentVariables: MutableStructure2D, + var realValues: MutableStructure2D, var weight: Double, - var pDelta: MutableStructure2D, - var minParameters: MutableStructure2D, - var maxParameters: MutableStructure2D, + var pDelta: MutableStructure2D, + var minParameters: MutableStructure2D, + var maxParameters: MutableStructure2D, var maxIterations: Int, var epsilons: DoubleArray, var lambdas: DoubleArray, @@ -408,7 +409,7 @@ private data class LMSettings( ) /* matrix -> column of all elements */ -private fun makeColumn(tensor: MutableStructure2D): MutableStructure2D { +private fun makeColumn(tensor: MutableStructure2D): MutableStructure2D { val shape = intArrayOf(tensor.shape.component1() * tensor.shape.component2(), 1) val buffer = DoubleArray(tensor.shape.component1() * tensor.shape.component2()) for (i in 0 until tensor.shape.component1()) { @@ -420,11 +421,11 @@ private fun makeColumn(tensor: MutableStructure2D): MutableStructure2D): Int { +private fun length(column: MutableStructure2D): Int { return column.shape.component1() } -private fun MutableStructure2D.abs() { +private fun MutableStructure2D.abs() { for (i in 0 until this.shape[0]) { for (j in 0 until this.shape[1]) { this[i, j] = abs(this[i, j]) @@ -432,7 +433,7 @@ private fun MutableStructure2D.abs() { } } -private fun abs(input: MutableStructure2D): MutableStructure2D { +private fun abs(input: MutableStructure2D): MutableStructure2D { val tensor = BroadcastDoubleTensorAlgebra.ones( ShapeND( intArrayOf( @@ -449,7 +450,7 @@ private fun abs(input: MutableStructure2D): MutableStructure2D { return tensor } -private fun makeColumnFromDiagonal(input: MutableStructure2D): MutableStructure2D { +private fun makeColumnFromDiagonal(input: MutableStructure2D): MutableStructure2D { val tensor = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(input.shape.component1(), 1))).as2D() for (i in 0 until tensor.shape.component1()) { tensor[i, 0] = input[i, i] @@ -457,7 +458,7 @@ private fun makeColumnFromDiagonal(input: MutableStructure2D): MutableSt return tensor } -private fun makeMatrixWithDiagonal(column: MutableStructure2D): MutableStructure2D { +private fun makeMatrixWithDiagonal(column: MutableStructure2D): MutableStructure2D { val size = column.shape.component1() val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(size, size))).as2D() for (i in 0 until size) { @@ -466,15 +467,15 @@ private fun makeMatrixWithDiagonal(column: MutableStructure2D): MutableS return tensor } -private fun lmEye(size: Int): MutableStructure2D { +private fun lmEye(size: Int): MutableStructure2D { val column = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(size, 1))).as2D() return makeMatrixWithDiagonal(column) } private fun largestElementComparison( - a: MutableStructure2D, - b: MutableStructure2D, -): MutableStructure2D { + a: MutableStructure2D, + b: MutableStructure2D, +): MutableStructure2D { val aSizeX = a.shape.component1() val aSizeY = a.shape.component2() val bSizeX = b.shape.component1() @@ -496,9 +497,9 @@ private fun largestElementComparison( } private fun smallestElementComparison( - a: MutableStructure2D, - b: MutableStructure2D, -): MutableStructure2D { + a: MutableStructure2D, + b: MutableStructure2D, +): MutableStructure2D { val aSizeX = a.shape.component1() val aSizeY = a.shape.component2() val bSizeX = b.shape.component1() @@ -520,10 +521,10 @@ private fun smallestElementComparison( } private fun getZeroIndices( - column: MutableStructure2D, + column: MutableStructure2D, epsilon: Double = 0.000001, -): MutableStructure2D? { - var idx = emptyArray() +): MutableStructure2D? { + var idx = emptyArray() for (i in 0 until column.shape.component1()) { if (abs(column[i, 0]) > epsilon) { idx += (i + 1.0) @@ -536,26 +537,26 @@ private fun getZeroIndices( } private fun evaluateFunction( - func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, - t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int, + func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, + t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int, ) - : MutableStructure2D { + : MutableStructure2D { return func(t, p, exampleNumber) } private fun lmMatx( - func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, - t: MutableStructure2D, - pOld: MutableStructure2D, - yOld: MutableStructure2D, + func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, + t: MutableStructure2D, + pOld: MutableStructure2D, + yOld: MutableStructure2D, dX2: Int, - JInput: MutableStructure2D, - p: MutableStructure2D, - yDat: MutableStructure2D, - weight: MutableStructure2D, - dp: MutableStructure2D, + JInput: MutableStructure2D, + p: MutableStructure2D, + yDat: MutableStructure2D, + weight: MutableStructure2D, + dp: MutableStructure2D, settings: LMSettings, -): Array> = with(DoubleTensorAlgebra) { +): Array> = with(DoubleTensorAlgebra) { // default: dp = 0.001 val Npar = length(p) // number of parameters @@ -581,9 +582,9 @@ private fun lmMatx( } private fun lmBroydenJ( - pOld: MutableStructure2D, yOld: MutableStructure2D, JInput: MutableStructure2D, - p: MutableStructure2D, y: MutableStructure2D, -): MutableStructure2D = with(DoubleTensorAlgebra) { + pOld: MutableStructure2D, yOld: MutableStructure2D, JInput: MutableStructure2D, + p: MutableStructure2D, y: MutableStructure2D, +): MutableStructure2D = with(DoubleTensorAlgebra) { var J = JInput.copyToTensor() val h = p.minus(pOld) @@ -595,13 +596,13 @@ private fun lmBroydenJ( @OptIn(PerformancePitfall::class) private fun lmFdJ( - func: (MutableStructure2D, MutableStructure2D, exampleNumber: Int) -> MutableStructure2D, - t: MutableStructure2D, - p: MutableStructure2D, - y: MutableStructure2D, - dp: MutableStructure2D, + func: (MutableStructure2D, MutableStructure2D, exampleNumber: Int) -> MutableStructure2D, + t: MutableStructure2D, + p: MutableStructure2D, + y: MutableStructure2D, + dp: MutableStructure2D, settings: LMSettings, -): MutableStructure2D = with(DoubleTensorAlgebra) { +): MutableStructure2D = with(DoubleTensorAlgebra) { // default: dp = 0.001 * ones(1,n) val m = length(y) // number of data points diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/checks.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/checks.kt index 736f9c33d..f526a8c98 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/checks.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/checks.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.tensors.core.internal import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.linearSize +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.DoubleTensor import space.kscience.kmath.tensors.core.DoubleTensorAlgebra @@ -54,7 +55,7 @@ internal fun checkSquareMatrix(shape: ShapeND) { } internal fun DoubleTensorAlgebra.checkSymmetric( - tensor: Tensor, epsilon: Double = 1e-6, + tensor: Tensor, epsilon: Double = 1e-6, ) = check(tensor.eq(tensor.transposed(), epsilon)) { "Tensor is not symmetric about the last 2 dimensions at precision $epsilon" } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt index 4750dbea2..3e41a19c6 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.tensors.core.internal import space.kscience.kmath.nd.* +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.indices @@ -19,10 +20,10 @@ import kotlin.math.max import kotlin.math.sqrt -internal fun MutableStructure2D.jacobiHelper( +internal fun MutableStructure2D.jacobiHelper( maxIteration: Int, epsilon: Double, -): Pair> { +): Pair> { val n = rowNum val A_ = copyToTensor() val V = eye(n) @@ -35,7 +36,7 @@ internal fun MutableStructure2D.jacobiHelper( return source[i * shape[0] + j] } - operator fun BufferedTensor.set(i: Int, j: Int, value: Double) { + operator fun BufferedTensor.set(i: Int, j: Int, value: Double) { source[i * shape[0] + j] = value } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt index 239d1dd18..eaf64c8a2 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt @@ -7,10 +7,7 @@ package space.kscience.kmath.tensors.core.internal import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.Float64Buffer -import space.kscience.kmath.structures.Int32Buffer -import space.kscience.kmath.structures.asBuffer -import space.kscience.kmath.structures.indices +import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.core.* import kotlin.math.abs import kotlin.math.max @@ -18,9 +15,9 @@ import kotlin.math.min import kotlin.math.sqrt internal fun dotTo( - a: BufferedTensor, - b: BufferedTensor, - res: BufferedTensor, + a: BufferedTensor, + b: BufferedTensor, + res: BufferedTensor, l: Int, m: Int, n: Int, ) { val aBuffer = a.source @@ -39,7 +36,7 @@ internal fun dotTo( } internal fun luHelper( - lu: MutableStructure2D, + lu: MutableStructure2D, pivots: MutableStructure1D, epsilon: Double, ): Boolean { @@ -102,7 +99,7 @@ internal fun StructureND.setUpPivots(): IntTensor { } internal fun DoubleTensorAlgebra.computeLU( - tensor: StructureND, + tensor: StructureND, epsilon: Double, ): Pair? { @@ -118,7 +115,7 @@ internal fun DoubleTensorAlgebra.computeLU( } internal fun pivInit( - p: MutableStructure2D, + p: MutableStructure2D, pivot: MutableStructure1D, n: Int, ) { @@ -128,9 +125,9 @@ internal fun pivInit( } internal fun luPivotHelper( - l: MutableStructure2D, - u: MutableStructure2D, - lu: MutableStructure2D, + l: MutableStructure2D, + u: MutableStructure2D, + lu: MutableStructure2D, n: Int, ) { for (i in 0 until n) { @@ -149,8 +146,8 @@ internal fun luPivotHelper( } internal fun choleskyHelper( - a: MutableStructure2D, - l: MutableStructure2D, + a: MutableStructure2D, + l: MutableStructure2D, n: Int, ) { for (i in 0 until n) { @@ -169,7 +166,7 @@ internal fun choleskyHelper( } } -internal fun luMatrixDet(lu: MutableStructure2D, pivots: MutableStructure1D): Double { +internal fun luMatrixDet(lu: MutableStructure2D, pivots: MutableStructure1D): Double { if (lu[0, 0] == 0.0) { return 0.0 } @@ -179,9 +176,9 @@ internal fun luMatrixDet(lu: MutableStructure2D, pivots: MutableStructur } internal fun luMatrixInv( - lu: MutableStructure2D, + lu: MutableStructure2D, pivots: MutableStructure1D, - invMatrix: MutableStructure2D, + invMatrix: MutableStructure2D, ) { val m = lu.shape[0] @@ -208,7 +205,7 @@ internal fun luMatrixInv( internal fun DoubleTensorAlgebra.qrHelper( matrix: DoubleTensor, q: DoubleTensor, - r: MutableStructure2D, + r: MutableStructure2D, ) { checkSquareMatrix(matrix.shape) val n = matrix.shape[0] @@ -261,7 +258,7 @@ internal fun DoubleTensorAlgebra.svd1d(a: DoubleTensor, epsilon: Double = 1e-10) internal fun DoubleTensorAlgebra.svdHelper( matrix: DoubleTensor, - USV: Triple, BufferedTensor, BufferedTensor>, + USV: Triple, BufferedTensor, BufferedTensor>, m: Int, n: Int, epsilon: Double, ) { val res = ArrayList>(0) @@ -310,9 +307,9 @@ internal fun DoubleTensorAlgebra.svdHelper( } } -internal fun MutableStructure2D.svdGolubKahanHelper( - u: MutableStructure2D, w: BufferedTensor, - v: MutableStructure2D, iterations: Int, epsilon: Double, +internal fun MutableStructure2D.svdGolubKahanHelper( + u: MutableStructure2D, w: BufferedTensor, + v: MutableStructure2D, iterations: Int, epsilon: Double, ) { fun pythag(a: Double, b: Double): Double { val at: Double = abs(a) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt index bd95cee5f..1eb25477b 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.nd.* import space.kscience.kmath.operations.covariance import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.internal.* @@ -44,12 +45,12 @@ public fun DoubleTensorAlgebra.randomNormalLike(structure: WithShape, seed: Long * @param tensors the [List] of tensors with same shapes to concatenate * @return tensor with concatenation result */ -public fun stack(tensors: List>): DoubleTensor { +public fun stack(tensors: List>): DoubleTensor { check(tensors.isNotEmpty()) { "List must have at least 1 element" } val shape = tensors[0].shape check(tensors.all { it.shape == shape }) { "Tensors must have same shapes" } val resShape = ShapeND(tensors.size) + shape -// val resBuffer: List = tensors.flatMap { +// val resBuffer: List = tensors.flatMap { // it.asDoubleTensor().source.array.drop(it.asDoubleTensor().bufferStart) // .take(it.asDoubleTensor().linearSize) // } @@ -67,7 +68,7 @@ public fun stack(tensors: List>): DoubleTensor { * The `pivots` has the shape ``(∗, min(m, n))``. `pivots` stores all the intermediate transpositions of rows. */ public fun DoubleTensorAlgebra.luFactor( - structureND: StructureND, + structureND: StructureND, epsilon: Double = 1e-9, ): Pair = computeLU(structureND, epsilon) @@ -86,7 +87,7 @@ public fun DoubleTensorAlgebra.luFactor( * @return triple of `P`, `L` and `U` tensors */ public fun DoubleTensorAlgebra.luPivot( - luTensor: StructureND, + luTensor: StructureND, pivotsTensor: Tensor, ): Triple { checkSquareMatrix(luTensor.shape) @@ -130,7 +131,7 @@ public fun DoubleTensorAlgebra.luPivot( * @return triple of `P`, `L` and `U` tensors. */ public fun DoubleTensorAlgebra.lu( - structureND: StructureND, + structureND: StructureND, epsilon: Double = 1e-9, ): Triple { val (lu, pivots) = luFactor(structureND, epsilon) @@ -151,7 +152,7 @@ public fun DoubleTensorAlgebra.lu( * Used when checking the positive definiteness of the input matrix or matrices. * @return a pair of `Q` and `R` tensors. */ -public fun DoubleTensorAlgebra.cholesky(structureND: StructureND, epsilon: Double = 1e-6): DoubleTensor { +public fun DoubleTensorAlgebra.cholesky(structureND: StructureND, epsilon: Double = 1e-6): DoubleTensor { checkSquareMatrix(structureND.shape) checkPositiveDefinite(structureND.asDoubleTensor(), epsilon) @@ -179,9 +180,9 @@ public fun DoubleTensorAlgebra.cholesky(structureND: StructureND, epsilo * @return a triple `Triple(U, S, V)`. */ public fun DoubleTensorAlgebra.svd( - structureND: StructureND, + structureND: StructureND, epsilon: Double, -): Triple, StructureND, StructureND> { +): Triple, StructureND, StructureND> { val size = structureND.dimension val commonShape = structureND.shape.slice(0 until size - 2) val (n, m) = structureND.shape.slice(size - 2 until size) @@ -213,7 +214,7 @@ public fun DoubleTensorAlgebra.svd( } public fun DoubleTensorAlgebra.svdGolubKahan( - structureND: StructureND, + structureND: StructureND, iterations: Int = 30, epsilon: Double = 1e-10, ): Triple { val size = structureND.dimension @@ -253,13 +254,13 @@ public fun DoubleTensorAlgebra.svdGolubKahan( * @return a pair `eigenvalues to eigenvectors`. */ public fun DoubleTensorAlgebra.symEigSvd( - structureND: StructureND, + structureND: StructureND, epsilon: Double, -): Pair> { +): Pair> { //TODO optimize conversion checkSymmetric(structureND.asDoubleTensor(), epsilon) - fun MutableStructure2D.cleanSym(n: Int) { + fun MutableStructure2D.cleanSym(n: Int) { for (i in 0 until n) { for (j in 0 until n) { if (i == j) { @@ -284,7 +285,7 @@ public fun DoubleTensorAlgebra.symEigSvd( } public fun DoubleTensorAlgebra.symEigJacobi( - structureND: StructureND, + structureND: StructureND, maxIteration: Int, epsilon: Double, ): Pair { @@ -326,7 +327,7 @@ public fun DoubleTensorAlgebra.symEigJacobi( * with zero. * @return the determinant. */ -public fun DoubleTensorAlgebra.detLU(structureND: StructureND, epsilon: Double = 1e-9): DoubleTensor { +public fun DoubleTensorAlgebra.detLU(structureND: StructureND, epsilon: Double = 1e-9): DoubleTensor { checkSquareMatrix(structureND.shape) //TODO check for unnecessary copies val luTensor = structureND.copyToTensor() @@ -362,7 +363,7 @@ public fun DoubleTensorAlgebra.detLU(structureND: StructureND, epsilon: * @param epsilon error in the LU algorithm—permissible error when comparing the determinant of a matrix with zero * @return the multiplicative inverse of a matrix. */ -public fun DoubleTensorAlgebra.invLU(structureND: StructureND, epsilon: Double = 1e-9): DoubleTensor { +public fun DoubleTensorAlgebra.invLU(structureND: StructureND, epsilon: Double = 1e-9): DoubleTensor { val (luTensor, pivotsTensor) = luFactor(structureND, epsilon) val invTensor = zeroesLike(luTensor) @@ -384,7 +385,7 @@ public fun DoubleTensorAlgebra.invLU(structureND: StructureND, epsilon: * @param vectors the [List] of 1-dimensional tensors with same shape * @return `M`. */ -public fun DoubleTensorAlgebra.covariance(vectors: List>): DoubleTensor { +public fun DoubleTensorAlgebra.covariance(vectors: List>): DoubleTensor { check(vectors.isNotEmpty()) { "List must have at least 1 element" } val n = vectors.size val m = vectors[0].size diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt index 6490ecdb9..07891cd5b 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.nd.* +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.tensors.api.Tensor @@ -14,7 +15,7 @@ import space.kscience.kmath.tensors.api.Tensor /** * Create a mutable copy of given [StructureND]. */ -public fun StructureND.copyToTensor(): DoubleTensor = if (this is DoubleTensor) { +public fun StructureND.copyToTensor(): DoubleTensor = if (this is DoubleTensor) { DoubleTensor(shape, source.copy()) } else if (this is Float64BufferND && indices is RowStrides) { DoubleTensor(shape, buffer.array.copyOf().asBuffer()) @@ -43,7 +44,7 @@ public fun StructureND.toDoubleTensor(): DoubleTensor { /** * Transforms [StructureND] of [Double] to [DoubleTensor]. Zero copy if possible, but is not guaranteed */ -public fun StructureND.asDoubleTensor(): DoubleTensor = if (this is DoubleTensor) { +public fun StructureND.asDoubleTensor(): DoubleTensor = if (this is DoubleTensor) { this } else if (this is Float64BufferND && indices is RowStrides) { DoubleTensor(shape, buffer) diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt index 92d997468..25db17f4f 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.toDoubleArray import space.kscience.kmath.tensors.core.internal.matrixSequence @@ -68,7 +69,7 @@ internal class TestDoubleTensor { val doubleArray = Float64Buffer(1.0, 2.0, 3.0) // create ND buffers, no data is copied - val ndArray: MutableBufferND = Float64BufferND(ColumnStrides(ShapeND(3)), doubleArray) + val ndArray: MutableBufferND = Float64BufferND(ColumnStrides(ShapeND(3)), doubleArray) // map to tensors val tensorArray = ndArray.asDoubleTensor() // Data is copied because of strides change. diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/offsetBufferEquality.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/offsetBufferEquality.kt index 38c51d66e..245db5c2a 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/offsetBufferEquality.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/offsetBufferEquality.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.indices import kotlin.jvm.JvmName @@ -20,5 +21,5 @@ public fun OffsetDoubleBuffer.contentEquals(vararg doubles: Double): Boolean = i public infix fun OffsetDoubleBuffer.contentEquals(otherArray: DoubleArray): Boolean = contentEquals(*otherArray) @JvmName("contentEqualsBuffer") -public infix fun OffsetDoubleBuffer.contentEquals(otherBuffer: Buffer): Boolean = +public infix fun OffsetDoubleBuffer.contentEquals(otherBuffer: Buffer): Boolean = indices.all { get(it) == otherBuffer[it] } \ No newline at end of file diff --git a/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index 6c072d52b..a6176dcf6 100644 --- a/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64Buffer import kotlin.test.Test import kotlin.test.assertEquals @@ -16,10 +17,10 @@ import kotlin.test.assertEquals class TestLmAlgorithm { companion object { fun funcEasyForLm( - t: MutableStructure2D, - p: MutableStructure2D, + t: MutableStructure2D, + p: MutableStructure2D, exampleNumber: Int, - ): MutableStructure2D = with(DoubleTensorAlgebra) { + ): MutableStructure2D = with(DoubleTensorAlgebra) { val m = t.shape.component1() val yHat = when (exampleNumber) { 1 -> exp((t * (-1.0 / p[1, 0]))) * p[0, 0] + (t * p[2, 0]) * exp((t * (-1.0 / p[3, 0]))) @@ -42,10 +43,10 @@ class TestLmAlgorithm { } fun funcMiddleForLm( - t: MutableStructure2D, - p: MutableStructure2D, + t: MutableStructure2D, + p: MutableStructure2D, exampleNumber: Int, - ): MutableStructure2D = with(DoubleTensorAlgebra) { + ): MutableStructure2D = with(DoubleTensorAlgebra) { val m = t.shape.component1() var yHat = zeros(ShapeND(intArrayOf(m, 1))) @@ -62,10 +63,10 @@ class TestLmAlgorithm { } fun funcDifficultForLm( - t: MutableStructure2D, - p: MutableStructure2D, + t: MutableStructure2D, + p: MutableStructure2D, exampleNumber: Int, - ): MutableStructure2D = with(DoubleTensorAlgebra) { + ): MutableStructure2D = with(DoubleTensorAlgebra) { val m = t.shape.component1() var yHat = zeros(ShapeND(intArrayOf(m, 1))) 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 89b51f269..86099226a 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 @@ -7,11 +7,12 @@ package space.kscience.kmath.viktor import org.jetbrains.bio.viktor.F64FlatArray import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.MutableBuffer @Suppress("NOTHING_TO_INLINE", "OVERRIDE_BY_INLINE") @JvmInline -public value class ViktorBuffer(public val flatArray: F64FlatArray) : MutableBuffer { +public value class ViktorBuffer(public val flatArray: F64FlatArray) : MutableBuffer { override val size: Int get() = flatArray.length @@ -22,7 +23,7 @@ public value class ViktorBuffer(public val flatArray: F64FlatArray) : MutableBuf flatArray[index] = value } - override operator fun iterator(): Iterator = flatArray.data.iterator() + override operator fun iterator(): Iterator = flatArray.data.iterator() override fun toString(): String = Buffer.toString(this) } diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt index 61ef00c6f..22ef49db2 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt @@ -16,14 +16,15 @@ import space.kscience.kmath.operations.ExtendedFieldOps import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.NumbersAddOps import space.kscience.kmath.operations.PowerOperations +import space.kscience.kmath.structures.Float64 @OptIn(PerformancePitfall::class) public open class ViktorFieldOpsND : FieldOpsND, - ExtendedFieldOps>, - PowerOperations> { + ExtendedFieldOps>, + PowerOperations> { - public val StructureND.f64Buffer: F64Array + public val StructureND.f64Buffer: F64Array get() = when (this) { is ViktorStructureND -> this.f64Buffer else -> mutableStructureND(shape) { this@f64Buffer[it] }.f64Buffer @@ -39,11 +40,11 @@ public open class ViktorFieldOpsND : } }.asStructure() - override fun StructureND.unaryMinus(): StructureND = -1 * this + override fun StructureND.unaryMinus(): StructureND = -1 * this @OptIn(UnsafeKMathAPI::class) @PerformancePitfall - override fun StructureND.map(transform: Float64Field.(Double) -> Double): ViktorStructureND = + override fun StructureND.map(transform: Float64Field.(Double) -> Double): ViktorStructureND = F64Array(*shape.asArray()).apply { ColumnStrides(ShapeND(shape)).asSequence().forEach { index -> set(value = Float64Field.transform(this@map[index]), indices = index) @@ -52,7 +53,7 @@ public open class ViktorFieldOpsND : @OptIn(UnsafeKMathAPI::class) @PerformancePitfall - override fun StructureND.mapIndexed( + override fun StructureND.mapIndexed( transform: Float64Field.(index: IntArray, Double) -> Double, ): ViktorStructureND = F64Array(*shape.asArray()).apply { ColumnStrides(ShapeND(shape)).asSequence().forEach { index -> @@ -63,8 +64,8 @@ public open class ViktorFieldOpsND : @OptIn(UnsafeKMathAPI::class) @PerformancePitfall override fun zip( - left: StructureND, - right: StructureND, + left: StructureND, + right: StructureND, transform: Float64Field.(Double, Double) -> Double, ): ViktorStructureND { require(left.shape == right.shape) @@ -75,46 +76,46 @@ public open class ViktorFieldOpsND : }.asStructure() } - override fun add(left: StructureND, right: StructureND): ViktorStructureND = + override fun add(left: StructureND, right: StructureND): ViktorStructureND = (left.f64Buffer + right.f64Buffer).asStructure() - override fun scale(a: StructureND, value: Double): ViktorStructureND = + override fun scale(a: StructureND, value: Double): ViktorStructureND = (a.f64Buffer * value).asStructure() - override fun StructureND.plus(arg: StructureND): ViktorStructureND = + override fun StructureND.plus(arg: StructureND): ViktorStructureND = (f64Buffer + arg.f64Buffer).asStructure() - override fun StructureND.minus(arg: StructureND): ViktorStructureND = + override fun StructureND.minus(arg: StructureND): ViktorStructureND = (f64Buffer - arg.f64Buffer).asStructure() - override fun StructureND.times(k: Number): ViktorStructureND = + override fun StructureND.times(k: Number): ViktorStructureND = (f64Buffer * k.toDouble()).asStructure() - override fun StructureND.plus(arg: Double): ViktorStructureND = + override fun StructureND.plus(arg: Double): ViktorStructureND = (f64Buffer.plus(arg)).asStructure() - override fun sin(arg: StructureND): ViktorStructureND = arg.map { sin(it) } - override fun cos(arg: StructureND): ViktorStructureND = arg.map { cos(it) } - override fun tan(arg: StructureND): ViktorStructureND = arg.map { tan(it) } - override fun asin(arg: StructureND): ViktorStructureND = arg.map { asin(it) } - override fun acos(arg: StructureND): ViktorStructureND = arg.map { acos(it) } - override fun atan(arg: StructureND): ViktorStructureND = arg.map { atan(it) } + override fun sin(arg: StructureND): ViktorStructureND = arg.map { sin(it) } + override fun cos(arg: StructureND): ViktorStructureND = arg.map { cos(it) } + override fun tan(arg: StructureND): ViktorStructureND = arg.map { tan(it) } + override fun asin(arg: StructureND): ViktorStructureND = arg.map { asin(it) } + override fun acos(arg: StructureND): ViktorStructureND = arg.map { acos(it) } + override fun atan(arg: StructureND): ViktorStructureND = arg.map { atan(it) } - override fun power(arg: StructureND, pow: Number): ViktorStructureND = arg.map { it.pow(pow) } + override fun power(arg: StructureND, pow: Number): ViktorStructureND = arg.map { it.pow(pow) } - override fun exp(arg: StructureND): ViktorStructureND = arg.f64Buffer.exp().asStructure() + override fun exp(arg: StructureND): ViktorStructureND = arg.f64Buffer.exp().asStructure() - override fun ln(arg: StructureND): ViktorStructureND = arg.f64Buffer.log().asStructure() + override fun ln(arg: StructureND): ViktorStructureND = arg.f64Buffer.log().asStructure() - override fun sinh(arg: StructureND): ViktorStructureND = arg.map { sinh(it) } + override fun sinh(arg: StructureND): ViktorStructureND = arg.map { sinh(it) } - override fun cosh(arg: StructureND): ViktorStructureND = arg.map { cosh(it) } + override fun cosh(arg: StructureND): ViktorStructureND = arg.map { cosh(it) } - override fun asinh(arg: StructureND): ViktorStructureND = arg.map { asinh(it) } + override fun asinh(arg: StructureND): ViktorStructureND = arg.map { asinh(it) } - override fun acosh(arg: StructureND): ViktorStructureND = arg.map { acosh(it) } + override fun acosh(arg: StructureND): ViktorStructureND = arg.map { acosh(it) } - override fun atanh(arg: StructureND): ViktorStructureND = arg.map { atanh(it) } + override fun atanh(arg: StructureND): ViktorStructureND = arg.map { atanh(it) } public companion object : ViktorFieldOpsND() } @@ -124,7 +125,7 @@ public val Float64Field.viktorAlgebra: ViktorFieldOpsND get() = ViktorFieldOpsND @OptIn(UnstableKMathAPI::class) public open class ViktorFieldND( private val shapeAsArray: IntArray, -) : ViktorFieldOpsND(), FieldND, NumbersAddOps> { +) : ViktorFieldOpsND(), FieldND, NumbersAddOps> { override val shape: ShapeND = ShapeND(shapeAsArray) 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..5a9e80fe6 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 @@ -10,9 +10,10 @@ 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.structures.Float64 @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -public class ViktorStructureND(public val f64Buffer: F64Array) : MutableStructureND { +public class ViktorStructureND(public val f64Buffer: F64Array) : MutableStructureND { override val shape: ShapeND get() = ShapeND(f64Buffer.shape) @OptIn(PerformancePitfall::class) diff --git a/test-utils/src/commonMain/kotlin/asserts.kt b/test-utils/src/commonMain/kotlin/asserts.kt index 54c6301b6..f4716f9f6 100644 --- a/test-utils/src/commonMain/kotlin/asserts.kt +++ b/test-utils/src/commonMain/kotlin/asserts.kt @@ -6,11 +6,12 @@ package space.kscience.kmath.testutils import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.indices import kotlin.test.assertEquals import kotlin.test.fail -public fun assertBufferEquals(expected: Buffer, result: Buffer, tolerance: Double = 1e-4) { +public fun assertBufferEquals(expected: Buffer, result: Buffer, tolerance: Double = 1e-4) { if (expected.size != result.size) { fail("Expected size is ${expected.size}, but the result size is ${result.size}") } From 1619a490170b4e84aa1f32fb604ef0700cfa536e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 18 Aug 2024 22:45:33 +0300 Subject: [PATCH 17/20] Add proper test for symmetric matrices eigenValueDecomposition --- .../kmath/linear/eigenValueDecomposition.kt | 40 +++++++++++++++++++ .../kmath/linear/EigenDecomposition.kt | 2 + kmath-ejml/build.gradle.kts | 27 ++++++------- .../kscience/kmath/ejml/EjmlLinearSpace.kt | 0 .../space/kscience/kmath/ejml/EjmlMatrix.kt | 0 .../space/kscience/kmath/ejml/EjmlVector.kt | 0 .../kscience/kmath/ejml/implementations.kt | 4 +- .../kscience/kmath/ejml/EjmlMatrixTest.kt | 13 ++++-- .../kscience/kmath/ejml/EjmlVectorTest.kt | 0 test-utils/src/commonMain/kotlin/asserts.kt | 10 +++++ 10 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 examples/src/main/kotlin/space/kscience/kmath/linear/eigenValueDecomposition.kt rename kmath-ejml/src/{main => jvmMain}/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt (100%) rename kmath-ejml/src/{main => jvmMain}/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt (100%) rename kmath-ejml/src/{main => jvmMain}/kotlin/space/kscience/kmath/ejml/EjmlVector.kt (100%) rename kmath-ejml/src/{main => jvmMain}/kotlin/space/kscience/kmath/ejml/implementations.kt (99%) rename kmath-ejml/src/{test => jvmTest}/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt (85%) rename kmath-ejml/src/{test => jvmTest}/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt (100%) diff --git a/examples/src/main/kotlin/space/kscience/kmath/linear/eigenValueDecomposition.kt b/examples/src/main/kotlin/space/kscience/kmath/linear/eigenValueDecomposition.kt new file mode 100644 index 000000000..1e7211757 --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/linear/eigenValueDecomposition.kt @@ -0,0 +1,40 @@ +/* + * 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.linear + +import space.kscience.kmath.commons.linear.CMLinearSpace +import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM +import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.operations.algebra +import space.kscience.kmath.structures.Float64 +import kotlin.random.Random + +fun main() { + val dim = 46 + + val random = Random(123) + + val u = Float64.algebra.linearSpace.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } + + listOf(CMLinearSpace, EjmlLinearSpaceDDRM).forEach { algebra -> + with(algebra) { + //create a simmetric matrix + val matrix = buildMatrix(dim, dim) { row, col -> + if (row >= col) u[row, col] else u[col, row] + } + val eigen = matrix.getOrComputeAttribute(EIG) ?: error("Failed to compute eigenvalue decomposition") + check( + StructureND.contentEquals( + matrix, + eigen.v dot eigen.d dot eigen.v.transposed(), + 1e-4 + ) + ) { "$algebra decomposition failed" } + println("$algebra eigenvalue decomposition complete and checked" ) + } + } + +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt index 4675509bd..dcb97d24a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.linear import space.kscience.attributes.PolymorphicAttribute import space.kscience.attributes.safeTypeOf +import space.kscience.kmath.UnstableKMathAPI public interface EigenDecomposition { /** @@ -24,5 +25,6 @@ public class EigenDecompositionAttribute : PolymorphicAttribute>(safeTypeOf()), MatrixAttribute> +@UnstableKMathAPI public val MatrixScope.EIG: EigenDecompositionAttribute get() = EigenDecompositionAttribute() diff --git a/kmath-ejml/build.gradle.kts b/kmath-ejml/build.gradle.kts index de0399244..003d5d56e 100644 --- a/kmath-ejml/build.gradle.kts +++ b/kmath-ejml/build.gradle.kts @@ -1,13 +1,20 @@ plugins { - id("space.kscience.gradle.jvm") + id("space.kscience.gradle.mpp") } val ejmlVerision = "0.43.1" -dependencies { - api(projects.kmathCore) - api(projects.kmathComplex) - api("org.ejml:ejml-all:$ejmlVerision") +kscience { + jvm() + jvmMain { + api(projects.kmathCore) + api(projects.kmathComplex) + api("org.ejml:ejml-all:$ejmlVerision") + } + + jvmTest { + implementation(projects.testUtils) + } } readme { @@ -28,12 +35,4 @@ readme { id = "ejml-linear-space", ref = "src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt" ) { "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)) -//} +} \ No newline at end of file diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt similarity index 100% rename from kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt rename to kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt similarity index 100% rename from kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt rename to kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlVector.kt similarity index 100% rename from kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt rename to kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/EjmlVector.kt diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt similarity index 99% rename from kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt rename to kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt index d84eeb589..5657f94e3 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/implementations.kt +++ b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt @@ -278,8 +278,8 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace by lazy { - val eigenvectors = List(origin.numRows) { cmEigen.getEigenVector(it) } - buildMatrix(origin.numRows, origin.numCols) { row, column -> + val eigenvectors = List(origin.numRows) { cmEigen.getEigenVector(it) }.filterNotNull() + buildMatrix(eigenvectors.size, origin.numCols) { row, column -> eigenvectors[row][column] } } diff --git a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt b/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt similarity index 85% rename from kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt rename to kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt index dda42d42c..239889e04 100644 --- a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt +++ b/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt @@ -18,12 +18,15 @@ import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.toArray import space.kscience.kmath.operations.algebra import space.kscience.kmath.structures.Float64 +import space.kscience.kmath.testutils.assertStructureEquals import kotlin.random.Random import kotlin.random.asJavaRandom import kotlin.test.* internal fun assertMatrixEquals(expected: StructureND, actual: StructureND) { - assertTrue { StructureND.contentEquals(expected, actual) } + expected.elements().forEach { (index, value) -> + assertEquals(value, actual[index], "Structure element with index ${index.toList()} should be equal to $value but is ${actual[index]}") + } } @OptIn(UnstableKMathAPI::class) @@ -108,8 +111,12 @@ internal class EjmlMatrixTest { @Test fun eigenValueDecomposition() = EjmlLinearSpaceDDRM { - val matrix = EjmlDoubleMatrix(randomMatrix) + val dim = 46 + val u = buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } + val matrix = buildMatrix(dim, dim) { row, col -> + if (row >= col) u[row, col] else u[col, row] + } val eigen = matrix.getOrComputeAttribute(EIG) ?: fail() - assertMatrixEquals(matrix, eigen.v dot eigen.d dot eigen.v.transposed()) + assertStructureEquals(matrix, eigen.v dot eigen.d dot eigen.v.transposed()) } } diff --git a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt b/kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt similarity index 100% rename from kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt rename to kmath-ejml/src/jvmTest/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt diff --git a/test-utils/src/commonMain/kotlin/asserts.kt b/test-utils/src/commonMain/kotlin/asserts.kt index f4716f9f6..a3f8a28fd 100644 --- a/test-utils/src/commonMain/kotlin/asserts.kt +++ b/test-utils/src/commonMain/kotlin/asserts.kt @@ -5,6 +5,8 @@ package space.kscience.kmath.testutils +import space.kscience.kmath.PerformancePitfall +import space.kscience.kmath.nd.StructureND import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.indices @@ -18,4 +20,12 @@ public fun assertBufferEquals(expected: Buffer, result: Buffer expected.indices.forEach { assertEquals(expected[it], result[it], tolerance) } +} + +@OptIn(PerformancePitfall::class) +public fun assertStructureEquals(expected: StructureND, result: StructureND, tolerance: Double = 1e-4) { + assertEquals(expected.shape, result.shape, "Structure shape mismatch") + expected.indices.forEach { + assertEquals(expected[it], result[it], tolerance) + } } \ No newline at end of file From 6c1a5e62bff541cca8065e4ab4c97d02a29d685a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 21 Aug 2024 11:56:05 +0300 Subject: [PATCH 18/20] Remove buildSrc --- benchmarks/build.gradle.kts | 154 +++++++++++++++++- build.gradle.kts | 2 +- buildSrc/build.gradle.kts | 33 ---- buildSrc/settings.gradle.kts | 38 ----- .../kscience/kmath/benchmarks/JmhReport.kt | 55 ------- .../benchmarks/addBenchmarkProperties.kt | 110 ------------- gradle.properties | 4 +- gradle/libs.versions.toml | 4 +- .../kscience/kmath/commons/linear/CMMatrix.kt | 1 + .../kscience/kmath/ejml/implementations.kt | 2 + .../samplers/TestMetropolisHastingsSampler.kt | 8 +- .../kmath/stat/RandomSourceGenerator.kt | 2 +- settings.gradle.kts | 21 ++- 13 files changed, 179 insertions(+), 255 deletions(-) delete mode 100644 buildSrc/build.gradle.kts delete mode 100644 buildSrc/settings.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/space/kscience/kmath/benchmarks/JmhReport.kt delete mode 100644 buildSrc/src/main/kotlin/space/kscience/kmath/benchmarks/addBenchmarkProperties.kt diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index b0446946a..ccbb99927 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -1,11 +1,18 @@ -@file:Suppress("UNUSED_VARIABLE") - -import space.kscience.kmath.benchmarks.addBenchmarkProperties +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import kotlinx.benchmark.gradle.BenchmarksExtension +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder +import java.time.format.SignStyle +import java.time.temporal.ChronoField.* +import java.util.* plugins { kotlin("multiplatform") alias(spclibs.plugins.kotlin.plugin.allopen) - id("org.jetbrains.kotlinx.benchmark") + alias(spclibs.plugins.kotlinx.benchmark) } allOpen.annotation("org.openjdk.jmh.annotations.State") @@ -158,8 +165,141 @@ kotlin { } } -readme { - maturity = space.kscience.gradle.Maturity.EXPERIMENTAL + +private data class JmhReport( + val jmhVersion: String, + val benchmark: String, + val mode: String, + val threads: Int, + val forks: Int, + val jvm: String, + val jvmArgs: List, + val jdkVersion: String, + val vmName: String, + val vmVersion: String, + val warmupIterations: Int, + val warmupTime: String, + val warmupBatchSize: Int, + val measurementIterations: Int, + val measurementTime: String, + val measurementBatchSize: Int, + val params: Map = emptyMap(), + val primaryMetric: PrimaryMetric, + val secondaryMetrics: Map, +) { + interface Metric { + val score: Double + val scoreError: Double + val scoreConfidence: List + val scorePercentiles: Map + val scoreUnit: String + } + + data class PrimaryMetric( + override val score: Double, + override val scoreError: Double, + override val scoreConfidence: List, + override val scorePercentiles: Map, + override val scoreUnit: String, + val rawDataHistogram: List>>>? = null, + val rawData: List>? = null, + ) : Metric + + data class SecondaryMetric( + override val score: Double, + override val scoreError: Double, + override val scoreConfidence: List, + override val scorePercentiles: Map, + override val scoreUnit: String, + val rawData: List>, + ) : Metric } -addBenchmarkProperties() +readme { + maturity = space.kscience.gradle.Maturity.EXPERIMENTAL + + val jsonMapper = jacksonObjectMapper() + + + val ISO_DATE_TIME: DateTimeFormatter = DateTimeFormatterBuilder().run { + parseCaseInsensitive() + appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + appendLiteral('-') + appendValue(MONTH_OF_YEAR, 2) + appendLiteral('-') + appendValue(DAY_OF_MONTH, 2) + appendLiteral('T') + appendValue(HOUR_OF_DAY, 2) + appendLiteral('.') + appendValue(MINUTE_OF_HOUR, 2) + optionalStart() + appendLiteral('.') + appendValue(SECOND_OF_MINUTE, 2) + optionalStart() + appendFraction(NANO_OF_SECOND, 0, 9, true) + optionalStart() + appendOffsetId() + optionalStart() + appendLiteral('[') + parseCaseSensitive() + appendZoneRegionId() + appendLiteral(']') + toFormatter() + } + + fun noun(number: Number, singular: String, plural: String) = if (number.toLong() == 1L) singular else plural + + extensions.findByType(BenchmarksExtension::class.java)?.configurations?.forEach { cfg -> + property("benchmark${cfg.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}") { + val launches = layout.buildDirectory.dir("reports/benchmarks/${cfg.name}").get() + + val resDirectory = launches.files().maxByOrNull { + LocalDateTime.parse(it.name, ISO_DATE_TIME).atZone(ZoneId.systemDefault()).toInstant() + } + + if (resDirectory == null || !(resDirectory.resolve("jvm.json")).exists()) { + "> **Can't find appropriate benchmark data. Try generating readme files after running benchmarks**." + } else { + val reports: List = + jsonMapper.readValue>(resDirectory.resolve("jvm.json")) + + buildString { + appendLine("
") + appendLine("") + appendLine("Report for benchmark configuration ${cfg.name}") + appendLine("") + appendLine() + val first = reports.first() + + appendLine("* Run on ${first.vmName} (build ${first.vmVersion}) with Java process:") + appendLine() + appendLine("```") + appendLine( + "${first.jvm} ${ + first.jvmArgs.joinToString(" ") + }" + ) + appendLine("```") + + appendLine( + "* JMH ${first.jmhVersion} was used in `${first.mode}` mode with ${first.warmupIterations} warmup ${ + noun(first.warmupIterations, "iteration", "iterations") + } by ${first.warmupTime} and ${first.measurementIterations} measurement ${ + noun(first.measurementIterations, "iteration", "iterations") + } by ${first.measurementTime}." + ) + + appendLine() + appendLine("| Benchmark | Score |") + appendLine("|:---------:|:-----:|") + + reports.forEach { report -> + appendLine("|`${report.benchmark}`|${report.primaryMetric.score} ± ${report.primaryMetric.scoreError} ${report.primaryMetric.scoreUnit}|") + } + + appendLine("
") + } + } + } + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 1b4b4b3ab..185df9448 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ import space.kscience.gradle.useApache2Licence import space.kscience.gradle.useSPCTeam plugins { - id("space.kscience.gradle.project") + alias(spclibs.plugins.kscience.project) alias(spclibs.plugins.kotlinx.kover) } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100644 index beb2af7f6..000000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,33 +0,0 @@ -plugins { - kotlin("jvm") version "1.9.23" - `kotlin-dsl` - `version-catalog` -} - -repositories { - mavenLocal() - maven("https://repo.kotlin.link") - mavenCentral() - gradlePluginPortal() -} - -val toolsVersion = spclibs.versions.tools.get() -val kotlinVersion = spclibs.versions.kotlin.asProvider().get() -val benchmarksVersion = spclibs.versions.kotlinx.benchmark.get() - -dependencies { - api("space.kscience:gradle-tools:$toolsVersion") - //plugins form benchmarks - api("org.jetbrains.kotlinx:kotlinx-benchmark-plugin:$benchmarksVersion") - //api("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion") - //to be used inside build-script only - //implementation(spclibs.kotlinx.serialization.json) - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.+") -} - -kotlin { - jvmToolchain(11) - compilerOptions { - optIn.add("kotlin.OptIn") - } -} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts deleted file mode 100644 index 09633711e..000000000 --- a/buildSrc/settings.gradle.kts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2018-2021 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. - */ - -enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") - -plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" -} - -dependencyResolutionManagement { - val projectProperties = java.util.Properties() - file("../gradle.properties").inputStream().use { - projectProperties.load(it) - } - - projectProperties.forEach { key, value -> - extra.set(key.toString(), value) - } - - - val toolsVersion: String = projectProperties["toolsVersion"].toString() - - @Suppress("UnstableApiUsage") - repositories { - mavenLocal() - maven("https://repo.kotlin.link") - mavenCentral() - gradlePluginPortal() - } - - versionCatalogs { - create("spclibs") { - from("space.kscience:version-catalog:$toolsVersion") - } - } -} diff --git a/buildSrc/src/main/kotlin/space/kscience/kmath/benchmarks/JmhReport.kt b/buildSrc/src/main/kotlin/space/kscience/kmath/benchmarks/JmhReport.kt deleted file mode 100644 index 98778e009..000000000 --- a/buildSrc/src/main/kotlin/space/kscience/kmath/benchmarks/JmhReport.kt +++ /dev/null @@ -1,55 +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.benchmarks - -data class JmhReport( - val jmhVersion: String, - val benchmark: String, - val mode: String, - val threads: Int, - val forks: Int, - val jvm: String, - val jvmArgs: List, - val jdkVersion: String, - val vmName: String, - val vmVersion: String, - val warmupIterations: Int, - val warmupTime: String, - val warmupBatchSize: Int, - val measurementIterations: Int, - val measurementTime: String, - val measurementBatchSize: Int, - val params: Map = emptyMap(), - val primaryMetric: PrimaryMetric, - val secondaryMetrics: Map, -) { - interface Metric { - val score: Double - val scoreError: Double - val scoreConfidence: List - val scorePercentiles: Map - val scoreUnit: String - } - - data class PrimaryMetric( - override val score: Double, - override val scoreError: Double, - override val scoreConfidence: List, - override val scorePercentiles: Map, - override val scoreUnit: String, - val rawDataHistogram: List>>>? = null, - val rawData: List>? = null, - ) : Metric - - data class SecondaryMetric( - override val score: Double, - override val scoreError: Double, - override val scoreConfidence: List, - override val scorePercentiles: Map, - override val scoreUnit: String, - val rawData: List>, - ) : Metric -} diff --git a/buildSrc/src/main/kotlin/space/kscience/kmath/benchmarks/addBenchmarkProperties.kt b/buildSrc/src/main/kotlin/space/kscience/kmath/benchmarks/addBenchmarkProperties.kt deleted file mode 100644 index ae2083c5f..000000000 --- a/buildSrc/src/main/kotlin/space/kscience/kmath/benchmarks/addBenchmarkProperties.kt +++ /dev/null @@ -1,110 +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.benchmarks - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import kotlinx.benchmark.gradle.BenchmarksExtension -import org.gradle.api.Project -import space.kscience.gradle.KScienceReadmeExtension -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.format.DateTimeFormatter -import java.time.format.DateTimeFormatterBuilder -import java.time.format.SignStyle -import java.time.temporal.ChronoField.* -import java.util.* - -private val ISO_DATE_TIME: DateTimeFormatter = DateTimeFormatterBuilder().run { - parseCaseInsensitive() - appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) - appendLiteral('-') - appendValue(MONTH_OF_YEAR, 2) - appendLiteral('-') - appendValue(DAY_OF_MONTH, 2) - appendLiteral('T') - appendValue(HOUR_OF_DAY, 2) - appendLiteral('.') - appendValue(MINUTE_OF_HOUR, 2) - optionalStart() - appendLiteral('.') - appendValue(SECOND_OF_MINUTE, 2) - optionalStart() - appendFraction(NANO_OF_SECOND, 0, 9, true) - optionalStart() - appendOffsetId() - optionalStart() - appendLiteral('[') - parseCaseSensitive() - appendZoneRegionId() - appendLiteral(']') - toFormatter() -} - -private fun noun(number: Number, singular: String, plural: String) = if (number.toLong() == 1L) singular else plural - -private val jsonMapper = jacksonObjectMapper() - -fun Project.addBenchmarkProperties() { - val benchmarksProject = this - rootProject.subprojects.forEach { p -> - p.extensions.findByType(KScienceReadmeExtension::class.java)?.run { - benchmarksProject.extensions.findByType(BenchmarksExtension::class.java)?.configurations?.forEach { cfg -> - property("benchmark${cfg.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}") { - val launches = benchmarksProject.layout.buildDirectory.dir("reports/benchmarks/${cfg.name}").get() - - val resDirectory = launches.files().maxByOrNull { - LocalDateTime.parse(it.name, ISO_DATE_TIME).atZone(ZoneId.systemDefault()).toInstant() - } - - if (resDirectory == null || !(resDirectory.resolve("jvm.json")).exists()) { - "> **Can't find appropriate benchmark data. Try generating readme files after running benchmarks**." - } else { - val reports: List = - jsonMapper.readValue>(resDirectory.resolve("jvm.json")) - - buildString { - appendLine("
") - appendLine("") - appendLine("Report for benchmark configuration ${cfg.name}") - appendLine("") - appendLine() - val first = reports.first() - - appendLine("* Run on ${first.vmName} (build ${first.vmVersion}) with Java process:") - appendLine() - appendLine("```") - appendLine( - "${first.jvm} ${ - first.jvmArgs.joinToString(" ") - }" - ) - appendLine("```") - - appendLine( - "* JMH ${first.jmhVersion} was used in `${first.mode}` mode with ${first.warmupIterations} warmup ${ - noun(first.warmupIterations, "iteration", "iterations") - } by ${first.warmupTime} and ${first.measurementIterations} measurement ${ - noun(first.measurementIterations, "iteration", "iterations") - } by ${first.measurementTime}." - ) - - appendLine() - appendLine("| Benchmark | Score |") - appendLine("|:---------:|:-----:|") - - reports.forEach { report -> - appendLine("|`${report.benchmark}`|${report.primaryMetric.score} ± ${report.primaryMetric.scoreError} ${report.primaryMetric.scoreUnit}|") - } - - appendLine("
") - } - } - } - } - } - } -} diff --git a/gradle.properties b/gradle.properties index 2b3b0c9ce..724a19ce8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,6 +10,4 @@ org.gradle.jvmargs=-Xmx4096m org.gradle.parallel=true org.gradle.workers.max=4 -toolsVersion=0.15.4-kotlin-2.0.0 -#kotlin.experimental.tryK2=true -#kscience.wasm.disabled=true \ No newline at end of file +toolsVersion=0.15.4-kotlin-2.0.0 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e1add1777..180778767 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,4 +9,6 @@ commons-rng-simple = { module = "org.apache.commons:commons-rng-simple", version commons-rng-sampling = { module = "org.apache.commons:commons-rng-sampling", version.ref = "commons-rng" } multik-core = { module = "org.jetbrains.kotlinx:multik-core", version.ref = "multik" } -multik-default = { module = "org.jetbrains.kotlinx:multik-default", version.ref = "multik" } \ No newline at end of file +multik-default = { module = "org.jetbrains.kotlinx:multik-default", version.ref = "multik" } + +[plugins] \ No newline at end of file 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 d7470be56..416ad0838 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 @@ -109,6 +109,7 @@ public object CMLinearSpace : LinearSpace { override fun Double.times(v: Point): CMVector = v * this + @OptIn(UnstableKMathAPI::class) override fun > computeAttribute(structure: Structure2D, attribute: A): V? { val origin = structure.toCM().origin diff --git a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt index 5657f94e3..8588a7cda 100644 --- a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt +++ b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt @@ -21,6 +21,7 @@ import org.ejml.sparse.csc.factory.DecompositionFactory_DSCC import org.ejml.sparse.csc.factory.DecompositionFactory_FSCC import space.kscience.attributes.SafeType import space.kscience.attributes.safeTypeOf +import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.complex.Complex import space.kscience.kmath.linear.* import space.kscience.kmath.linear.Matrix @@ -216,6 +217,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace): EjmlDoubleVector = v * this + @OptIn(UnstableKMathAPI::class) override fun > computeAttribute(structure: Structure2D, attribute: A): V? { val origin: DMatrixRMaj = structure.toEjml().origin diff --git a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt index 1261c75e5..72bf2082f 100644 --- a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt +++ b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/samplers/TestMetropolisHastingsSampler.kt @@ -24,7 +24,7 @@ class TestMetropolisHastingsSampler { data class TestSetup(val mean: Double, val startPoint: Double, val sigma: Double = 0.5) - private val sample = 1e6.toInt() + private val sample = 1e5.toInt() private val burnIn = sample / 5 @Test @@ -40,7 +40,7 @@ class TestMetropolisHastingsSampler { .univariateNormal(it.startPoint, it.sigma, distribution::probability) val sampledValues = sampler.sample(generator).discard(burnIn).nextBuffer(sample) - assertEquals(it.mean, Float64Field.mean(sampledValues), 1e-2) + assertEquals(it.mean, Float64Field.mean(sampledValues), 0.05) } } @@ -59,7 +59,7 @@ class TestMetropolisHastingsSampler { } val sampledValues = sampler.sample(generator).discard(burnIn).nextBuffer(sample) - assertEquals(1.0 / setup.mean, Float64Field.mean(sampledValues), 1e-2) + assertEquals(1.0 / setup.mean, Float64Field.mean(sampledValues), 0.1) } } @@ -82,7 +82,7 @@ class TestMetropolisHastingsSampler { } val sampledValues = sampler.sample(generator).discard(burnIn).nextBuffer(sample) - assertEquals(setup.mean * sqrt(PI / 2), Float64Field.mean(sampledValues), 1e-2) + assertEquals(setup.mean * sqrt(PI / 2), Float64Field.mean(sampledValues), 0.05) } } diff --git a/kmath-stat/src/jvmMain/kotlin/space/kscience/kmath/stat/RandomSourceGenerator.kt b/kmath-stat/src/jvmMain/kotlin/space/kscience/kmath/stat/RandomSourceGenerator.kt index 8badbce64..9598109bc 100644 --- a/kmath-stat/src/jvmMain/kotlin/space/kscience/kmath/stat/RandomSourceGenerator.kt +++ b/kmath-stat/src/jvmMain/kotlin/space/kscience/kmath/stat/RandomSourceGenerator.kt @@ -18,7 +18,7 @@ public class RandomSourceGenerator internal constructor( public val source: RandomSource, seed: Long?, ) : RandomGenerator { - internal val random: UniformRandomProvider = seed?.let { RandomSource.create(source, seed) } + internal val random: UniformRandomProvider = seed?.let { source.create(seed) } ?: RandomSource.create(source) override fun nextBoolean(): Boolean = random.nextBoolean() diff --git a/settings.gradle.kts b/settings.gradle.kts index e660bef85..5e610ca85 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,14 +2,31 @@ rootProject.name = "kmath" enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +pluginManagement { + val toolsVersion: String by extra + + repositories { + mavenLocal() + gradlePluginPortal() + mavenCentral() + maven("https://repo.kotlin.link") + } + + plugins { + id("space.kscience.gradle.project") version toolsVersion + id("space.kscience.gradle.mpp") version toolsVersion + id("space.kscience.gradle.jvm") version toolsVersion + } +} + dependencyResolutionManagement { val toolsVersion: String by extra repositories { mavenLocal() - maven("https://repo.kotlin.link") - mavenCentral() gradlePluginPortal() + mavenCentral() + maven("https://repo.kotlin.link") } versionCatalogs { From 222cdc2c141f0dd92fd97fdadff66444d654f6f2 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 21 Aug 2024 12:06:00 +0300 Subject: [PATCH 19/20] Add unstable marker to eigenvalue decomposition --- .../kotlin/space/kscience/kmath/linear/EigenDecomposition.kt | 2 ++ .../jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt | 1 + 2 files changed, 3 insertions(+) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt index dcb97d24a..ee91c7d0f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/EigenDecomposition.kt @@ -9,6 +9,7 @@ import space.kscience.attributes.PolymorphicAttribute import space.kscience.attributes.safeTypeOf import space.kscience.kmath.UnstableKMathAPI +@UnstableKMathAPI public interface EigenDecomposition { /** * Eigenvector matrix. @@ -21,6 +22,7 @@ public interface EigenDecomposition { public val d: Matrix } +@UnstableKMathAPI public class EigenDecompositionAttribute : PolymorphicAttribute>(safeTypeOf()), MatrixAttribute> diff --git a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt index 8588a7cda..f831f495f 100644 --- a/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt +++ b/kmath-ejml/src/jvmMain/kotlin/space/kscience/kmath/ejml/implementations.kt @@ -474,6 +474,7 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace): EjmlFloatVector = v * this + @OptIn(UnstableKMathAPI::class) override fun > computeAttribute(structure: Structure2D, attribute: A): V? { val origin = structure.toEjml().origin From ab16bd16acf2569728a223d934b4785c77cc88cc Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 26 Aug 2024 13:37:49 +0300 Subject: [PATCH 20/20] Fix benchmark results script --- benchmarks/README.md | 117 +++++++++++++++++- benchmarks/build.gradle.kts | 68 ++++------ benchmarks/docs/README-TEMPLATE.md | 5 + .../kmath/stat/DistributionBenchmark.kt | 22 ++++ .../kscience/kmath/linear/MatrixBuilder.kt | 9 +- ...orkingEvent.kt => RandomForkingSampler.kt} | 0 6 files changed, 170 insertions(+), 51 deletions(-) create mode 100644 benchmarks/docs/README-TEMPLATE.md rename kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/{RandomForkingEvent.kt => RandomForkingSampler.kt} (100%) diff --git a/benchmarks/README.md b/benchmarks/README.md index cd8fbafd3..1535d55e6 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -1,4 +1,119 @@ -# Module benchmarks +# BenchmarksResult + +## Report for benchmark configuration main + +* Run on Java HotSpot(TM) 64-Bit Server VM (build 21.0.4+8-LTS-jvmci-23.1-b41) with Java process: + +``` +C:\Users\altavir\scoop\apps\graalvm-oracle-21jdk\current\bin\java.exe -XX:ThreadPriorityPolicy=1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCIProduct -XX:-UnlockExperimentalVMOptions -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant +``` +* JMH 1.21 was used in `thrpt` mode with 5 warmup iterations by 10 s and 5 measurement iterations by 10 s. +### [ArrayBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ArrayBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`benchmarkArrayRead`|1.9E+07 ± 2.3E+05 ops/s| +|`benchmarkBufferRead`|1.4E+07 ± 8.7E+05 ops/s| +|`nativeBufferRead`|1.4E+07 ± 1.3E+06 ops/s| +### [BigIntBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`jvmAdd`|5.1E+07 ± 1.3E+06 ops/s| +|`jvmAddLarge`|5.1E+04 ± 8.2E+02 ops/s| +|`jvmMultiply`|8.5E+07 ± 9.7E+06 ops/s| +|`jvmMultiplyLarge`|2.5E+02 ± 15 ops/s| +|`jvmParsing10`|8.7E+06 ± 5.1E+05 ops/s| +|`jvmParsing16`|6.4E+06 ± 1.8E+05 ops/s| +|`jvmPower`|28 ± 0.79 ops/s| +|`jvmSmallAdd`|7.0E+07 ± 4.3E+06 ops/s| +|`kmAdd`|4.8E+07 ± 2.2E+06 ops/s| +|`kmAddLarge`|3.5E+04 ± 3.7E+03 ops/s| +|`kmMultiply`|6.7E+07 ± 1.5E+07 ops/s| +|`kmMultiplyLarge`|54 ± 4.2 ops/s| +|`kmParsing10`|4.5E+06 ± 8.3E+04 ops/s| +|`kmParsing16`|4.9E+06 ± 1.1E+05 ops/s| +|`kmPower`|10 ± 0.96 ops/s| +|`kmSmallAdd`|4.1E+07 ± 5.9E+05 ops/s| +### [BufferBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`bufferViewReadWrite`|5.8E+06 ± 1.6E+05 ops/s| +|`bufferViewReadWriteSpecialized`|5.6E+06 ± 2.6E+05 ops/s| +|`complexBufferReadWrite`|6.6E+06 ± 2.7E+05 ops/s| +|`doubleArrayReadWrite`|7.5E+06 ± 1.0E+06 ops/s| +|`doubleBufferReadWrite`|8.0E+06 ± 6.7E+05 ops/s| +### [DotBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`bufferedDot`|1.3 ± 0.020 ops/s| +|`cmDot`|0.47 ± 0.42 ops/s| +|`cmDotWithConversion`|0.76 ± 0.13 ops/s| +|`ejmlDot`|6.7 ± 0.091 ops/s| +|`ejmlDotWithConversion`|6.4 ± 0.82 ops/s| +|`multikDot`|40 ± 6.7 ops/s| +|`parallelDot`|12 ± 1.8 ops/s| +|`tensorDot`|1.2 ± 0.041 ops/s| +|`tfDot`|5.9 ± 0.49 ops/s| +### [ExpressionsInterpretersBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`asmGenericExpression`|29 ± 1.2 ops/s| +|`asmPrimitiveExpression`|43 ± 1.3 ops/s| +|`asmPrimitiveExpressionArray`|71 ± 0.38 ops/s| +|`functionalExpression`|5.6 ± 0.11 ops/s| +|`justCalculate`|69 ± 9.0 ops/s| +|`mstExpression`|7.1 ± 0.020 ops/s| +|`rawExpression`|41 ± 1.5 ops/s| +### [IntegrationBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/IntegrationBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`complexIntegration`|3.6E+03 ± 1.9E+02 ops/s| +|`doubleIntegration`|3.7E+03 ± 12 ops/s| +### [JafamaBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`core`|38 ± 0.64 ops/s| +|`jafama`|52 ± 0.36 ops/s| +|`strictJafama`|52 ± 4.0 ops/s| +### [MatrixInverseBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`cmLUPInversion`|2.2E+03 ± 76 ops/s| +|`ejmlInverse`|1.3E+03 ± 5.7 ops/s| +|`kmathLupInversion`|9.5E+02 ± 1.8E+02 ops/s| +|`kmathParallelLupInversion`|9.1E+02 ± 1.4E+02 ops/s| +### [NDFieldBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`boxingFieldAdd`|7.7 ± 0.79 ops/s| +|`multikAdd`|6.5 ± 0.33 ops/s| +|`multikInPlaceAdd`|64 ± 0.79 ops/s| +|`specializedFieldAdd`|8.0 ± 0.090 ops/s| +|`tensorAdd`|9.2 ± 0.053 ops/s| +|`tensorInPlaceAdd`|17 ± 10 ops/s| +|`viktorAdd`|7.6 ± 1.2 ops/s| +### [ViktorBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`doubleFieldAddition`|7.7 ± 0.34 ops/s| +|`rawViktor`|5.9 ± 1.1 ops/s| +|`viktorFieldAddition`|7.3 ± 1.1 ops/s| +### [ViktorLogBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt) + +| Benchmark | Score | +|:---------:|:-----:| +|`rawViktorLog`|1.4 ± 0.076 ops/s| +|`realFieldLog`|1.3 ± 0.069 ops/s| +|`viktorFieldLog`|1.3 ± 0.032 ops/s| diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index ccbb99927..2bdab89fb 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -3,10 +3,6 @@ import com.fasterxml.jackson.module.kotlin.readValue import kotlinx.benchmark.gradle.BenchmarksExtension import java.time.LocalDateTime import java.time.ZoneId -import java.time.format.DateTimeFormatter -import java.time.format.DateTimeFormatterBuilder -import java.time.format.SignStyle -import java.time.temporal.ChronoField.* import java.util.* plugins { @@ -220,41 +216,20 @@ readme { val jsonMapper = jacksonObjectMapper() - - val ISO_DATE_TIME: DateTimeFormatter = DateTimeFormatterBuilder().run { - parseCaseInsensitive() - appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) - appendLiteral('-') - appendValue(MONTH_OF_YEAR, 2) - appendLiteral('-') - appendValue(DAY_OF_MONTH, 2) - appendLiteral('T') - appendValue(HOUR_OF_DAY, 2) - appendLiteral('.') - appendValue(MINUTE_OF_HOUR, 2) - optionalStart() - appendLiteral('.') - appendValue(SECOND_OF_MINUTE, 2) - optionalStart() - appendFraction(NANO_OF_SECOND, 0, 9, true) - optionalStart() - appendOffsetId() - optionalStart() - appendLiteral('[') - parseCaseSensitive() - appendZoneRegionId() - appendLiteral(']') - toFormatter() - } - fun noun(number: Number, singular: String, plural: String) = if (number.toLong() == 1L) singular else plural extensions.findByType(BenchmarksExtension::class.java)?.configurations?.forEach { cfg -> - property("benchmark${cfg.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}") { - val launches = layout.buildDirectory.dir("reports/benchmarks/${cfg.name}").get() + val propertyName = + "benchmark${cfg.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}" - val resDirectory = launches.files().maxByOrNull { - LocalDateTime.parse(it.name, ISO_DATE_TIME).atZone(ZoneId.systemDefault()).toInstant() + logger.info("Processing benchmark data from benchmark ${cfg.name} into readme property $propertyName") + + val launches = layout.buildDirectory.dir("reports/benchmarks/${cfg.name}").get().asFile + if (!launches.exists()) return@forEach + + property(propertyName) { + val resDirectory = launches.listFiles()?.maxByOrNull { + LocalDateTime.parse(it.name).atZone(ZoneId.systemDefault()).toInstant() } if (resDirectory == null || !(resDirectory.resolve("jvm.json")).exists()) { @@ -264,10 +239,7 @@ readme { jsonMapper.readValue>(resDirectory.resolve("jvm.json")) buildString { - appendLine("
") - appendLine("") - appendLine("Report for benchmark configuration ${cfg.name}") - appendLine("") + appendLine("## Report for benchmark configuration ${cfg.name}") appendLine() val first = reports.first() @@ -289,15 +261,19 @@ readme { } by ${first.measurementTime}." ) - appendLine() - appendLine("| Benchmark | Score |") - appendLine("|:---------:|:-----:|") + reports.groupBy { it.benchmark.substringBeforeLast(".") }.forEach { (cl, compare) -> + appendLine("### [${cl.substringAfterLast(".")}](src/jvmMain/kotlin/${cl.replace(".","/")}.kt)") + appendLine() + appendLine("| Benchmark | Score |") + appendLine("|:---------:|:-----:|") + compare.forEach { report -> + val benchmarkName = report.benchmark.substringAfterLast(".") + val score = String.format("%.2G", report.primaryMetric.score) + val error = String.format("%.2G", report.primaryMetric.scoreError) - reports.forEach { report -> - appendLine("|`${report.benchmark}`|${report.primaryMetric.score} ± ${report.primaryMetric.scoreError} ${report.primaryMetric.scoreUnit}|") + appendLine("|`$benchmarkName`|$score ± $error ${report.primaryMetric.scoreUnit}|") + } } - - appendLine("
") } } } diff --git a/benchmarks/docs/README-TEMPLATE.md b/benchmarks/docs/README-TEMPLATE.md new file mode 100644 index 000000000..eaac18cd8 --- /dev/null +++ b/benchmarks/docs/README-TEMPLATE.md @@ -0,0 +1,5 @@ +# BenchmarksResult + +${benchmarkMain} + + diff --git a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt index 89a1cdc28..062057ae7 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt @@ -36,6 +36,26 @@ private suspend fun runKMathChained(): Duration { return Duration.between(startTime, Instant.now()) } +private fun runKMathBlocking(): Duration { + val generator = RandomGenerator.fromSource(RandomSource.MT, 123L) + val normal = GaussianSampler(7.0, 2.0) + val chain = normal.sample(generator) + val startTime = Instant.now() + var sum = 0.0 + + repeat(10000001) { counter -> + sum += chain.nextBlocking() + + if (counter % 100000 == 0) { + val duration = Duration.between(startTime, Instant.now()) + val meanValue = sum / counter + println("Chain sampler completed $counter elements in $duration: $meanValue") + } + } + + return Duration.between(startTime, Instant.now()) +} + private fun runCMDirect(): Duration { val rng = RandomSource.MT.create(123L) @@ -67,6 +87,8 @@ private fun runCMDirect(): Duration { fun main(): Unit = runBlocking(Dispatchers.Default) { val directJob = async { runCMDirect() } val chainJob = async { runKMathChained() } + val blockingJob = async { runKMathBlocking() } println("KMath Chained: ${chainJob.await()}") + println("KMath Blocking: ${blockingJob.await()}") println("Apache Direct: ${directJob.await()}") } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt index 18e1edb56..0b3ac56fd 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt @@ -67,13 +67,14 @@ public fun > MatrixBuilder.symmetric( ): Matrix { require(columns == rows) { "In order to build symmetric matrix, number of rows $rows should be equal to number of columns $columns" } return with(BufferAccessor2D(rows, rows, MutableBufferFactory(type))) { - val cache = factory(rows * rows) { null } + val cache = HashMap() linearSpace.buildMatrix(rows, rows) { i, j -> - val cached = cache[i, j] + val index = intArrayOf(i, j) + val cached = cache[index] if (cached == null) { val value = if (i <= j) builder(i, j) else builder(j, i) - cache[i, j] = value - cache[j, i] = value + cache[index] = value + cache[index] = value value } else { cached diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingSampler.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingSampler.kt