diff --git a/.space.kts b/.space.kts new file mode 100644 index 000000000..9dda0cbf7 --- /dev/null +++ b/.space.kts @@ -0,0 +1 @@ +job("Build") { gradlew("openjdk:11", "build") } diff --git a/CHANGELOG.md b/CHANGELOG.md index ba271bb01..aa314fcb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,18 +2,27 @@ ## [Unreleased] ### Added +- `fun` annotation for SAM interfaces in library +- Explicit `public` visibility for all public APIs +- Better trigonometric and hyperbolic functions for `AutoDiffField` (https://github.com/mipt-npm/kmath/pull/140). - `kmath-ejml` to supply EJML SimpleMatrix wrapper. ### Changed +- Package changed from `scientifik` to `kscience.kmath`. +- Gradle version: 6.6 -> 6.6.1 +- Minor exceptions refactor (throwing `IllegalArgumentException` by argument checks instead of `IllegalStateException`) +- `Polynomial` secondary constructor made function. ### Deprecated ### Removed -- `kmath-koma` module. +- `kmath-koma` module because it doesn't support Kotlin 1.4. ### Fixed +- `symbol` method in `MstExtendedField` (https://github.com/mipt-npm/kmath/pull/140) ### Security + ## [0.1.4] ### Added diff --git a/README.md b/README.md index 6bfbc717a..c2e67e815 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@  -Bintray: [  ](https://bintray.com/mipt-npm/scientifik/kmath-core/_latestVersion) +Bintray: [  ](https://bintray.com/mipt-npm/kscience/kmath-core/_latestVersion) Bintray-dev: [  ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion) @@ -53,7 +53,7 @@ can be used for a wide variety of purposes from high performance calculations to * **Commons-math wrapper** It is planned to gradually wrap most parts of [Apache commons-math](http://commons.apache.org/proper/commons-math/) library in Kotlin code and maybe rewrite some parts to better suit the Kotlin programming paradigm, however there is no fixed roadmap for that. Feel free to submit a feature request if you want something to be done first. - + ## Planned features * **Messaging** A mathematical notation to support multi-language and multi-node communication for mathematical tasks. @@ -80,12 +80,12 @@ Release artifacts are accessible from bintray with following configuration (see ```kotlin repositories{ - maven("https://dl.bintray.com/mipt-npm/scientifik") + maven("https://dl.bintray.com/mipt-npm/kscience") } dependencies{ - api("scientifik:kmath-core:${kmathVersion}") - //api("scientifik:kmath-core-jvm:${kmathVersion}") for jvm-specific version + api("kscience.kmath:kmath-core:${kmathVersion}") + //api("kscience.kmath:kmath-core-jvm:${kmathVersion}") for jvm-specific version } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 96434d01f..838a05771 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,13 +1,10 @@ -import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension -import scientifik.ScientifikPublishPlugin - plugins { - id("scientifik.publish") apply false + id("ru.mipt.npm.base") id("org.jetbrains.changelog") version "0.4.0" } -val kmathVersion by extra("0.1.4") -val bintrayRepo by extra("scientifik") +val kmathVersion by extra("0.2.0-dev-1") +val bintrayRepo by extra("kscience") val githubProject by extra("kmath") allprojects { @@ -19,17 +16,6 @@ allprojects { group = "kscience.kmath" version = kmathVersion - - afterEvaluate { - extensions.findByType<KotlinProjectExtension>()?.run { - sourceSets.all { - languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") - } - } - } } -subprojects { - if (name.startsWith("kmath")) - apply<ScientifikPublishPlugin>() -} \ No newline at end of file +subprojects { if (name.startsWith("kmath")) apply(plugin = "ru.mipt.npm.publish") } diff --git a/doc/algebra.md b/doc/algebra.md index b1b77a31f..c3227517f 100644 --- a/doc/algebra.md +++ b/doc/algebra.md @@ -5,7 +5,7 @@ operation, say `+`, one needs two objects of a type `T` and an algebra context, say `Space<T>`. Next one needs to run the actual operation in the context: ```kotlin -import scientifik.kmath.operations.* +import kscience.kmath.operations.* val a: T = ... val b: T = ... @@ -47,7 +47,7 @@ but it also holds reference to the `ComplexField` singleton, which allows perfor numbers without explicit involving the context like: ```kotlin -import scientifik.kmath.operations.* +import kscience.kmath.operations.* // Using elements val c1 = Complex(1.0, 1.0) @@ -82,7 +82,7 @@ operations in all performance-critical places. The performance of element operat KMath submits both contexts and elements for builtin algebraic structures: ```kotlin -import scientifik.kmath.operations.* +import kscience.kmath.operations.* val c1 = Complex(1.0, 2.0) val c2 = ComplexField.i @@ -95,7 +95,7 @@ val c3 = ComplexField { c1 + c2 } Also, `ComplexField` features special operations to mix complex and real numbers, for example: ```kotlin -import scientifik.kmath.operations.* +import kscience.kmath.operations.* val c1 = Complex(1.0, 2.0) val c2 = ComplexField { c1 - 1.0 } // Returns: Complex(re=0.0, im=2.0) diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 519c72615..1c90db101 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -1,30 +1,25 @@ -import org.jetbrains.kotlin.allopen.gradle.AllOpenExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { java kotlin("jvm") - kotlin("plugin.allopen") version "1.3.72" - id("kotlinx.benchmark") version "0.2.0-dev-8" + kotlin("plugin.allopen") version "1.4.20-dev-3898-14" + id("kotlinx.benchmark") version "0.2.0-dev-20" } -configure<AllOpenExtension> { - annotation("org.openjdk.jmh.annotations.State") -} +allOpen.annotation("org.openjdk.jmh.annotations.State") repositories { - maven("http://dl.bintray.com/kyonifer/maven") - maven("https://dl.bintray.com/mipt-npm/scientifik") + maven("https://dl.bintray.com/mipt-npm/kscience") maven("https://dl.bintray.com/mipt-npm/dev") + maven("https://dl.bintray.com/kotlin/kotlin-dev/") mavenCentral() } -sourceSets { - register("benchmarks") -} +sourceSets.register("benchmarks") dependencies { - implementation(project(":kmath-ast")) +// implementation(project(":kmath-ast")) implementation(project(":kmath-core")) implementation(project(":kmath-coroutines")) implementation(project(":kmath-commons")) @@ -33,25 +28,22 @@ dependencies { implementation(project(":kmath-dimensions")) implementation(project(":kmath-ejml")) implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6") - implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-8") + implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20") "benchmarksCompile"(sourceSets.main.get().output + sourceSets.main.get().compileClasspath) //sourceSets.main.output + sourceSets.main.runtimeClasspath } // Configure benchmark benchmark { // Setup configurations - targets { + targets // This one matches sourceSet name above - register("benchmarks") - } + .register("benchmarks") - configurations { - register("fast") { - warmups = 5 // number of warmup iterations - iterations = 3 // number of iterations - iterationTime = 500 // time in seconds per iteration - iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds - } + configurations.register("fast") { + warmups = 5 // number of warmup iterations + iterations = 3 // number of iterations + iterationTime = 500 // time in seconds per iteration + iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds } } @@ -62,9 +54,4 @@ kotlin.sourceSets.all { } } -tasks.withType<KotlinCompile> { - kotlinOptions { - jvmTarget = Scientifik.JVM_TARGET.toString() - freeCompilerArgs = freeCompilerArgs + "-Xopt-in=kotlin.RequiresOptIn" - } -} +tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "11" } diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/structures/ArrayBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ArrayBenchmark.kt new file mode 100644 index 000000000..2673552f5 --- /dev/null +++ b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ArrayBenchmark.kt @@ -0,0 +1,39 @@ +package kscience.kmath.structures + +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State +import java.nio.IntBuffer + +@State(Scope.Benchmark) +class ArrayBenchmark { + @Benchmark + fun benchmarkArrayRead() { + var res = 0 + for (i in 1.._root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) res += _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.array[_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size - i] + } + + @Benchmark + fun benchmarkBufferRead() { + var res = 0 + for (i in 1.._root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) res += _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.arrayBuffer.get( + _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size - i) + } + + @Benchmark + fun nativeBufferRead() { + var res = 0 + for (i in 1.._root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) res += _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.nativeBuffer.get( + _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size - i) + } + + companion object { + const val size: Int = 1000 + val array: IntArray = IntArray(_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) { it } + val arrayBuffer: IntBuffer = IntBuffer.wrap(_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.array) + + val nativeBuffer: IntBuffer = IntBuffer.allocate(_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size).also { + for (i in 0 until _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) it.put(i, i) + } + } +} diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/structures/BufferBenchmark.kt similarity index 83% rename from examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt rename to examples/src/benchmarks/kotlin/kscience/kmath/structures/BufferBenchmark.kt index e40b0c4b7..009d51001 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/structures/BufferBenchmark.kt @@ -1,10 +1,10 @@ -package scientifik.kmath.structures +package kscience.kmath.structures +import kscience.kmath.operations.Complex +import kscience.kmath.operations.complex import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State -import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.complex @State(Scope.Benchmark) class BufferBenchmark { diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/structures/NDFieldBenchmark.kt similarity index 89% rename from examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt rename to examples/src/benchmarks/kotlin/kscience/kmath/structures/NDFieldBenchmark.kt index 46da6c6d8..64f279c39 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/structures/NDFieldBenchmark.kt @@ -1,10 +1,10 @@ -package scientifik.kmath.structures +package kscience.kmath.structures +import kscience.kmath.operations.RealField +import kscience.kmath.operations.invoke import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.invoke @State(Scope.Benchmark) class NDFieldBenchmark { diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt similarity index 89% rename from examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt rename to examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt index 9627743c9..a4b831f7c 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt @@ -1,12 +1,12 @@ -package scientifik.kmath.structures +package kscience.kmath.structures +import kscience.kmath.operations.RealField +import kscience.kmath.operations.invoke +import kscience.kmath.viktor.ViktorNDField import org.jetbrains.bio.viktor.F64Array import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.invoke -import scientifik.kmath.viktor.ViktorNDField @State(Scope.Benchmark) class ViktorBenchmark { diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt b/examples/src/benchmarks/kotlin/kscience/kmath/utils/utils.kt similarity index 91% rename from examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt rename to examples/src/benchmarks/kotlin/kscience/kmath/utils/utils.kt index 3b0d56291..329b7b17b 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/utils/utils.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.utils +package kscience.kmath.utils import kotlin.contracts.InvocationKind import kotlin.contracts.contract diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt deleted file mode 100644 index d605e1b9c..000000000 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt +++ /dev/null @@ -1,48 +0,0 @@ -package scientifik.kmath.structures - -import org.openjdk.jmh.annotations.Benchmark -import org.openjdk.jmh.annotations.Scope -import org.openjdk.jmh.annotations.State -import java.nio.IntBuffer - - -@State(Scope.Benchmark) -class ArrayBenchmark { - - @Benchmark - fun benchmarkArrayRead() { - var res = 0 - for (i in 1..size) { - res += array[size - i] - } - } - - @Benchmark - fun benchmarkBufferRead() { - var res = 0 - for (i in 1..size) { - res += arrayBuffer.get(size - i) - } - } - - @Benchmark - fun nativeBufferRead() { - var res = 0 - for (i in 1..size) { - res += nativeBuffer.get(size - i) - } - } - - companion object { - val size = 1000 - - val array = IntArray(size) { it } - val arrayBuffer = IntBuffer.wrap(array) - val nativeBuffer = IntBuffer.allocate(size).also { - for (i in 0 until size) { - it.put(i, i) - } - - } - } -} \ No newline at end of file diff --git a/examples/src/main/kotlin/kscience/kmath/ast/ExpressionsInterpretersBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/ast/ExpressionsInterpretersBenchmark.kt new file mode 100644 index 000000000..a5768f1f5 --- /dev/null +++ b/examples/src/main/kotlin/kscience/kmath/ast/ExpressionsInterpretersBenchmark.kt @@ -0,0 +1,70 @@ +//package kscience.kmath.ast +// +//import kscience.kmath.asm.compile +//import kscience.kmath.expressions.Expression +//import kscience.kmath.expressions.expressionInField +//import kscience.kmath.expressions.invoke +//import kscience.kmath.operations.Field +//import kscience.kmath.operations.RealField +//import kotlin.random.Random +//import kotlin.system.measureTimeMillis +// +//class ExpressionsInterpretersBenchmark { +// private val algebra: Field<Double> = RealField +// fun functionalExpression() { +// val expr = algebra.expressionInField { +// variable("x") * const(2.0) + const(2.0) / variable("x") - const(16.0) +// } +// +// invokeAndSum(expr) +// } +// +// fun mstExpression() { +// val expr = algebra.mstInField { +// symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) +// } +// +// invokeAndSum(expr) +// } +// +// fun asmExpression() { +// val expr = algebra.mstInField { +// symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) +// }.compile() +// +// invokeAndSum(expr) +// } +// +// private fun invokeAndSum(expr: Expression<Double>) { +// val random = Random(0) +// var sum = 0.0 +// +// repeat(1000000) { +// sum += expr("x" to random.nextDouble()) +// } +// +// println(sum) +// } +//} +// +//fun main() { +// val benchmark = ExpressionsInterpretersBenchmark() +// +// val fe = measureTimeMillis { +// benchmark.functionalExpression() +// } +// +// println("fe=$fe") +// +// val mst = measureTimeMillis { +// benchmark.mstExpression() +// } +// +// println("mst=$mst") +// +// val asm = measureTimeMillis { +// benchmark.asmExpression() +// } +// +// println("asm=$asm") +//} diff --git a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt similarity index 94% rename from examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt rename to examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt index b060cddb6..57a9c55c5 100644 --- a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt @@ -1,12 +1,12 @@ -package scientifik.kmath.commons.prob +package kscience.kmath.commons.prob import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking +import kscience.kmath.chains.BlockingRealChain +import kscience.kmath.prob.* import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler import org.apache.commons.rng.simple.RandomSource -import scientifik.kmath.chains.BlockingRealChain -import scientifik.kmath.prob.* import java.time.Duration import java.time.Instant diff --git a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt similarity index 72% rename from examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt rename to examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt index e059415dc..dbb51267b 100644 --- a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt @@ -1,11 +1,11 @@ -package scientifik.kmath.commons.prob +package kscience.kmath.commons.prob import kotlinx.coroutines.runBlocking -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.collectWithState -import scientifik.kmath.prob.Distribution -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.normal +import kscience.kmath.chains.Chain +import kscience.kmath.chains.collectWithState +import kscience.kmath.prob.Distribution +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.normal data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) diff --git a/examples/src/main/kotlin/scientifik/kmath/operations/BigIntDemo.kt b/examples/src/main/kotlin/kscience/kmath/operations/BigIntDemo.kt similarity index 72% rename from examples/src/main/kotlin/scientifik/kmath/operations/BigIntDemo.kt rename to examples/src/main/kotlin/kscience/kmath/operations/BigIntDemo.kt index 10b038943..692ea6d8c 100644 --- a/examples/src/main/kotlin/scientifik/kmath/operations/BigIntDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/operations/BigIntDemo.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.operations +package kscience.kmath.operations fun main() { val res = BigIntField { diff --git a/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt b/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt similarity index 68% rename from examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt rename to examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt index 6dbfebce1..3c97940a8 100644 --- a/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/operations/ComplexDemo.kt @@ -1,8 +1,8 @@ -package scientifik.kmath.operations +package kscience.kmath.operations -import scientifik.kmath.structures.NDElement -import scientifik.kmath.structures.NDField -import scientifik.kmath.structures.complex +import kscience.kmath.structures.NDElement +import kscience.kmath.structures.NDField +import kscience.kmath.structures.complex fun main() { val element = NDElement.complex(2, 2) { index: IntArray -> diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt similarity index 85% rename from examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt rename to examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt index 2329f3fc3..aa4b10ef2 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.linear.transpose -import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.ComplexField -import scientifik.kmath.operations.invoke +import kscience.kmath.linear.transpose +import kscience.kmath.operations.Complex +import kscience.kmath.operations.ComplexField +import kscience.kmath.operations.invoke import kotlin.system.measureTimeMillis fun main() { diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt similarity index 93% rename from examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt rename to examples/src/main/kotlin/kscience/kmath/structures/NDField.kt index 54ffcb67c..28bfab779 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt @@ -1,8 +1,8 @@ -package scientifik.kmath.structures +package kscience.kmath.structures import kotlinx.coroutines.GlobalScope -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.invoke +import kscience.kmath.operations.RealField +import kscience.kmath.operations.invoke import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.system.measureTimeMillis diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt similarity index 93% rename from examples/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt rename to examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt index a33fdb2c4..a2bfea2f9 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt @@ -1,8 +1,8 @@ -package scientifik.kmath.structures +package kscience.kmath.structures import kotlin.system.measureTimeMillis -fun main(args: Array<String>) { +fun main() { val n = 6000 val array = DoubleArray(n * n) { 1.0 } diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt similarity index 93% rename from examples/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt rename to examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt index 0241f12ad..b2975393f 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt @@ -1,10 +1,8 @@ -package scientifik.kmath.structures +package kscience.kmath.structures import kotlin.system.measureTimeMillis - -fun main(args: Array<String>) { - +fun main() { val n = 6000 val structure = NDStructure.build(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 } diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt b/examples/src/main/kotlin/kscience/kmath/structures/typeSafeDimensions.kt similarity index 71% rename from examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt rename to examples/src/main/kotlin/kscience/kmath/structures/typeSafeDimensions.kt index 5d323823a..bf83a9f05 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/typeSafeDimensions.kt @@ -1,10 +1,10 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.dimensions.D2 -import scientifik.kmath.dimensions.D3 -import scientifik.kmath.dimensions.DMatrixContext -import scientifik.kmath.dimensions.Dimension -import scientifik.kmath.operations.RealField +import kscience.kmath.dimensions.D2 +import kscience.kmath.dimensions.D3 +import kscience.kmath.dimensions.DMatrixContext +import kscience.kmath.dimensions.Dimension +import kscience.kmath.operations.RealField fun DMatrixContext<Double, RealField>.simple() { val m1 = produce<D2, D3> { i, j -> (i + j).toDouble() } diff --git a/examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt deleted file mode 100644 index 17a70a4aa..000000000 --- a/examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt +++ /dev/null @@ -1,70 +0,0 @@ -package scientifik.kmath.ast - -import scientifik.kmath.asm.compile -import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.expressionInField -import scientifik.kmath.expressions.invoke -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.RealField -import kotlin.random.Random -import kotlin.system.measureTimeMillis - -class ExpressionsInterpretersBenchmark { - private val algebra: Field<Double> = RealField - fun functionalExpression() { - val expr = algebra.expressionInField { - variable("x") * const(2.0) + const(2.0) / variable("x") - const(16.0) - } - - invokeAndSum(expr) - } - - fun mstExpression() { - val expr = algebra.mstInField { - symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) - } - - invokeAndSum(expr) - } - - fun asmExpression() { - val expr = algebra.mstInField { - symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) - }.compile() - - invokeAndSum(expr) - } - - private fun invokeAndSum(expr: Expression<Double>) { - val random = Random(0) - var sum = 0.0 - - repeat(1000000) { - sum += expr("x" to random.nextDouble()) - } - - println(sum) - } -} - -fun main() { - val benchmark = ExpressionsInterpretersBenchmark() - - val fe = measureTimeMillis { - benchmark.functionalExpression() - } - - println("fe=$fe") - - val mst = measureTimeMillis { - benchmark.mstExpression() - } - - println("mst=$mst") - - val asm = measureTimeMillis { - benchmark.asmExpression() - } - - println("asm=$asm") -} diff --git a/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt index 58ad8a77c..3316f3236 100644 --- a/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt @@ -1,13 +1,13 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.commons.linear.CMMatrixContext -import scientifik.kmath.commons.linear.inverse -import scientifik.kmath.commons.linear.toCM -import scientifik.kmath.ejml.EjmlMatrixContext -import scientifik.kmath.ejml.inverse -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.invoke -import scientifik.kmath.structures.Matrix +import kscience.kmath.commons.linear.CMMatrixContext +import kscience.kmath.commons.linear.inverse +import kscience.kmath.commons.linear.toCM +import kscience.kmath.ejml.EjmlMatrixContext +import kscience.kmath.ejml.inverse +import kscience.kmath.operations.RealField +import kscience.kmath.operations.invoke +import kscience.kmath.structures.Matrix import kotlin.random.Random import kotlin.system.measureTimeMillis diff --git a/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt index 6e3f786ea..b82c8aac4 100644 --- a/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt @@ -1,11 +1,11 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.commons.linear.CMMatrixContext -import scientifik.kmath.commons.linear.toCM -import scientifik.kmath.ejml.EjmlMatrixContext -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.invoke -import scientifik.kmath.structures.Matrix +import kscience.kmath.commons.linear.CMMatrixContext +import kscience.kmath.commons.linear.toCM +import kscience.kmath.ejml.EjmlMatrixContext +import kscience.kmath.operations.RealField +import kscience.kmath.operations.invoke +import kscience.kmath.structures.Matrix import kotlin.random.Random import kotlin.system.measureTimeMillis @@ -36,3 +36,14 @@ fun main() { val genericTime = measureTimeMillis { val res = matrix1 dot matrix2 } println("Generic implementation time: $genericTime") } + + (EjmlMatrixContext(RealField)) { + val ejmlMatrix1 = matrix1.toEjml() + val ejmlMatrix2 = matrix2.toEjml() + val ejmlTime = measureTimeMillis { ejmlMatrix1 dot ejmlMatrix2 } + println("EJML implementation time: $ejmlTime") + } + + val genericTime = measureTimeMillis { val res = matrix1 dot matrix2 } + println("Generic implementation time: $genericTime") +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 62d4c0535..e708b1c02 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bb8b2fc26..12d38de6a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index fbd7c5158..4f906e0c8 100755 --- a/gradlew +++ b/gradlew @@ -130,7 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 5093609d5..107acd32c 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -54,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -64,21 +64,6 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line @@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/kmath-ast/README.md b/kmath-ast/README.md index 2339d0426..043224800 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -8,32 +8,32 @@ This subproject implements the following features: - Evaluating expressions by traversing MST. > #### Artifact: -> This module is distributed in the artifact `scientifik:kmath-ast:0.1.4-dev-8`. +> This module is distributed in the artifact `kscience.kmath:kmath-ast:0.1.4-dev-8`. > > **Gradle:** > > ```gradle > repositories { -> maven { url 'https://dl.bintray.com/mipt-npm/scientifik' } +> maven { url 'https://dl.bintray.com/mipt-npm/kscience' } > maven { url 'https://dl.bintray.com/mipt-npm/dev' } > maven { url https://dl.bintray.com/hotkeytlt/maven' } > } > > dependencies { -> implementation 'scientifik:kmath-ast:0.1.4-dev-8' +> implementation 'kscience.kmath:kmath-ast:0.1.4-dev-8' > } > ``` > **Gradle Kotlin DSL:** > > ```kotlin > repositories { -> maven("https://dl.bintray.com/mipt-npm/scientifik") +> maven("https://dl.bintray.com/mipt-npm/kscience") > maven("https://dl.bintray.com/mipt-npm/dev") > maven("https://dl.bintray.com/hotkeytlt/maven") > } > > dependencies { -> implementation("scientifik:kmath-ast:0.1.4-dev-8") +> implementation("kscience.kmath:kmath-ast:0.1.4-dev-8") > } > ``` > @@ -52,12 +52,12 @@ RealField.mstInField { symbol("x") + 2 }.compile() … leads to generation of bytecode, which can be decompiled to the following Java class: ```java -package scientifik.kmath.asm.generated; +package kscience.kmath.asm.generated; import java.util.Map; -import scientifik.kmath.asm.internal.MapIntrinsics; -import scientifik.kmath.expressions.Expression; -import scientifik.kmath.operations.RealField; +import kscience.kmath.asm.internal.MapIntrinsics; +import kscience.kmath.expressions.Expression; +import kscience.kmath.operations.RealField; public final class AsmCompiledExpression_1073786867_0 implements Expression<Double> { private final RealField algebra; diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index ace121a09..df876df10 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -1,12 +1,11 @@ plugins { - id("scientifik.mpp") + id("ru.mipt.npm.mpp") } kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-core")) - implementation("com.github.h0tk3y.betterParse:better-parse:0.4.0") } } @@ -14,7 +13,8 @@ kotlin.sourceSets { dependencies { implementation("org.ow2.asm:asm:8.0.1") implementation("org.ow2.asm:asm-commons:8.0.1") + implementation("com.github.h0tk3y.betterParse:better-parse:0.4.0") implementation(kotlin("reflect")) } } -} \ No newline at end of file +} diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MST.kt similarity index 76% rename from kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt rename to kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MST.kt index 0e8151c04..f312323b9 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MST.kt @@ -1,26 +1,28 @@ -package scientifik.kmath.ast +package kscience.kmath.ast -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.NumericAlgebra -import scientifik.kmath.operations.RealField +import kscience.kmath.operations.Algebra +import kscience.kmath.operations.NumericAlgebra +import kscience.kmath.operations.RealField /** * A Mathematical Syntax Tree node for mathematical expressions. + * + * @author Alexander Nozik */ -sealed class MST { +public sealed class MST { /** * A node containing raw string. * * @property value the value of this node. */ - data class Symbolic(val value: String) : MST() + public data class Symbolic(val value: String) : MST() /** * A node containing a numeric value or scalar. * * @property value the value of this number. */ - data class Numeric(val value: Number) : MST() + public data class Numeric(val value: Number) : MST() /** * A node containing an unary operation. @@ -28,9 +30,7 @@ sealed class MST { * @property operation the identifier of operation. * @property value the argument of this operation. */ - data class Unary(val operation: String, val value: MST) : MST() { - companion object - } + public data class Unary(val operation: String, val value: MST) : MST() /** * A node containing binary operation. @@ -39,9 +39,7 @@ sealed class MST { * @property left the left operand. * @property right the right operand. */ - data class Binary(val operation: String, val left: MST, val right: MST) : MST() { - companion object - } + public data class Binary(val operation: String, val left: MST, val right: MST) : MST() } // TODO add a function with named arguments @@ -52,8 +50,9 @@ sealed class MST { * @receiver the algebra that provides operations. * @param node the node to evaluate. * @return the value of expression. + * @author Alexander Nozik */ -fun <T> Algebra<T>.evaluate(node: MST): T = when (node) { +public fun <T> Algebra<T>.evaluate(node: MST): T = when (node) { is MST.Numeric -> (this as? NumericAlgebra<T>)?.number(node.value) ?: error("Numeric nodes are not supported by $this") is MST.Symbolic -> symbol(node.value) @@ -84,4 +83,4 @@ fun <T> Algebra<T>.evaluate(node: MST): T = when (node) { * @param algebra the algebra that provides operations. * @return the value of expression. */ -fun <T> MST.interpret(algebra: Algebra<T>): T = algebra.evaluate(this) +public fun <T> MST.interpret(algebra: Algebra<T>): T = algebra.evaluate(this) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt similarity index 63% rename from kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt rename to kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt index 23deae24b..64a820b20 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt @@ -1,11 +1,11 @@ -package scientifik.kmath.ast +package kscience.kmath.ast -import scientifik.kmath.operations.* +import kscience.kmath.operations.* /** * [Algebra] over [MST] nodes. */ -object MstAlgebra : NumericAlgebra<MST> { +public object MstAlgebra : NumericAlgebra<MST> { override fun number(value: Number): MST = MST.Numeric(value) override fun symbol(value: String): MST = MST.Symbolic(value) @@ -20,7 +20,7 @@ object MstAlgebra : NumericAlgebra<MST> { /** * [Space] over [MST] nodes. */ -object MstSpace : Space<MST>, NumericAlgebra<MST> { +public object MstSpace : Space<MST>, NumericAlgebra<MST> { override val zero: MST = number(0.0) override fun number(value: Number): MST = MstAlgebra.number(value) @@ -37,8 +37,9 @@ object MstSpace : Space<MST>, NumericAlgebra<MST> { /** * [Ring] over [MST] nodes. */ -object MstRing : Ring<MST>, NumericAlgebra<MST> { - override val zero: MST = number(0.0) +public object MstRing : Ring<MST>, NumericAlgebra<MST> { + override val zero: MST + get() = MstSpace.zero override val one: MST = number(1.0) override fun number(value: Number): MST = MstSpace.number(value) @@ -58,18 +59,21 @@ object MstRing : Ring<MST>, NumericAlgebra<MST> { /** * [Field] over [MST] nodes. */ -object MstField : Field<MST> { - override val zero: MST = number(0.0) - override val one: MST = number(1.0) +public object MstField : Field<MST> { + public override val zero: MST + get() = MstRing.zero - override fun symbol(value: String): MST = MstRing.symbol(value) - override fun number(value: Number): MST = MstRing.number(value) - override fun add(a: MST, b: MST): MST = MstRing.add(a, b) - override fun multiply(a: MST, k: Number): MST = MstRing.multiply(a, k) - override fun multiply(a: MST, b: MST): MST = MstRing.multiply(a, b) - override fun divide(a: MST, b: MST): MST = binaryOperation(FieldOperations.DIV_OPERATION, a, b) + public override val one: MST + get() = MstRing.one - override fun binaryOperation(operation: String, left: MST, right: MST): MST = + public override fun symbol(value: String): MST = MstRing.symbol(value) + public override fun number(value: Number): MST = MstRing.number(value) + public override fun add(a: MST, b: MST): MST = MstRing.add(a, b) + public override fun multiply(a: MST, k: Number): MST = MstRing.multiply(a, k) + public override fun multiply(a: MST, b: MST): MST = MstRing.multiply(a, b) + public override fun divide(a: MST, b: MST): MST = binaryOperation(FieldOperations.DIV_OPERATION, a, b) + + public override fun binaryOperation(operation: String, left: MST, right: MST): MST = MstRing.binaryOperation(operation, left, right) override fun unaryOperation(operation: String, arg: MST): MST = MstRing.unaryOperation(operation, arg) @@ -78,15 +82,26 @@ object MstField : Field<MST> { /** * [ExtendedField] over [MST] nodes. */ -object MstExtendedField : ExtendedField<MST> { - override val zero: MST = number(0.0) - override val one: MST = number(1.0) +public object MstExtendedField : ExtendedField<MST> { + override val zero: MST + get() = MstField.zero + override val one: MST + get() = MstField.one + + override fun symbol(value: String): MST = MstField.symbol(value) override fun sin(arg: MST): MST = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg) override fun cos(arg: MST): MST = unaryOperation(TrigonometricOperations.COS_OPERATION, arg) + override fun tan(arg: MST): MST = unaryOperation(TrigonometricOperations.TAN_OPERATION, arg) override fun asin(arg: MST): MST = unaryOperation(TrigonometricOperations.ASIN_OPERATION, arg) override fun acos(arg: MST): MST = unaryOperation(TrigonometricOperations.ACOS_OPERATION, arg) override fun atan(arg: MST): MST = unaryOperation(TrigonometricOperations.ATAN_OPERATION, arg) + override fun sinh(arg: MST): MST = unaryOperation(HyperbolicOperations.SINH_OPERATION, arg) + override fun cosh(arg: MST): MST = unaryOperation(HyperbolicOperations.COSH_OPERATION, arg) + override fun tanh(arg: MST): MST = unaryOperation(HyperbolicOperations.TANH_OPERATION, arg) + override fun asinh(arg: MST): MST = unaryOperation(HyperbolicOperations.ASINH_OPERATION, arg) + override fun acosh(arg: MST): MST = unaryOperation(HyperbolicOperations.ACOSH_OPERATION, arg) + override fun atanh(arg: MST): MST = unaryOperation(HyperbolicOperations.ATANH_OPERATION, arg) override fun add(a: MST, b: MST): MST = MstField.add(a, b) override fun multiply(a: MST, k: Number): MST = MstField.multiply(a, k) override fun multiply(a: MST, b: MST): MST = MstField.multiply(a, b) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstExpression.kt similarity index 61% rename from kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt rename to kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstExpression.kt index 293152cdd..483bc530c 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstExpression.kt @@ -1,7 +1,7 @@ -package scientifik.kmath.ast +package kscience.kmath.ast -import scientifik.kmath.expressions.* -import scientifik.kmath.operations.* +import kscience.kmath.expressions.* +import kscience.kmath.operations.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -11,8 +11,9 @@ import kotlin.contracts.contract * * @property algebra the algebra that provides operations. * @property mst the [MST] node. + * @author Alexander Nozik */ -class MstExpression<T>(val algebra: Algebra<T>, val mst: MST) : Expression<T> { +public class MstExpression<T>(public val algebra: Algebra<T>, public val mst: MST) : Expression<T> { private inner class InnerAlgebra(val arguments: Map<String, T>) : NumericAlgebra<T> { override fun symbol(value: String): T = arguments[value] ?: algebra.symbol(value) override fun unaryOperation(operation: String, arg: T): T = algebra.unaryOperation(operation, arg) @@ -31,72 +32,92 @@ class MstExpression<T>(val algebra: Algebra<T>, val mst: MST) : Expression<T> { /** * Builds [MstExpression] over [Algebra]. + * + * @author Alexander Nozik */ -inline fun <reified T : Any, A : Algebra<T>, E : Algebra<MST>> A.mst( +public inline fun <reified T : Any, A : Algebra<T>, E : Algebra<MST>> A.mst( mstAlgebra: E, block: E.() -> MST ): MstExpression<T> = MstExpression(this, mstAlgebra.block()) /** * Builds [MstExpression] over [Space]. + * + * @author Alexander Nozik */ -inline fun <reified T : Any> Space<T>.mstInSpace(block: MstSpace.() -> MST): MstExpression<T> { +public inline fun <reified T : Any> Space<T>.mstInSpace(block: MstSpace.() -> MST): MstExpression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return MstExpression(this, MstSpace.block()) } /** * Builds [MstExpression] over [Ring]. + * + * @author Alexander Nozik */ -inline fun <reified T : Any> Ring<T>.mstInRing(block: MstRing.() -> MST): MstExpression<T> { +public inline fun <reified T : Any> Ring<T>.mstInRing(block: MstRing.() -> MST): MstExpression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return MstExpression(this, MstRing.block()) } /** * Builds [MstExpression] over [Field]. + * + * @author Alexander Nozik */ -inline fun <reified T : Any> Field<T>.mstInField(block: MstField.() -> MST): MstExpression<T> { +public inline fun <reified T : Any> Field<T>.mstInField(block: MstField.() -> MST): MstExpression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return MstExpression(this, MstField.block()) } /** * Builds [MstExpression] over [ExtendedField]. + * + * @author Iaroslav Postovalov */ -inline fun <reified T : Any> Field<T>.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression<T> { +public inline fun <reified T : Any> Field<T>.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return MstExpression(this, MstExtendedField.block()) } /** * Builds [MstExpression] over [FunctionalExpressionSpace]. + * + * @author Alexander Nozik */ -inline fun <reified T : Any, A : Space<T>> FunctionalExpressionSpace<T, A>.mstInSpace(block: MstSpace.() -> MST): MstExpression<T> { +public inline fun <reified T : Any, A : Space<T>> FunctionalExpressionSpace<T, A>.mstInSpace(block: MstSpace.() -> MST): MstExpression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return algebra.mstInSpace(block) } /** * Builds [MstExpression] over [FunctionalExpressionRing]. + * + * @author Alexander Nozik */ -inline fun <reified T : Any, A : Ring<T>> FunctionalExpressionRing<T, A>.mstInRing(block: MstRing.() -> MST): MstExpression<T> { +public inline fun <reified T : Any, A : Ring<T>> FunctionalExpressionRing<T, A>.mstInRing(block: MstRing.() -> MST): MstExpression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return algebra.mstInRing(block) } /** * Builds [MstExpression] over [FunctionalExpressionField]. + * + * @author Alexander Nozik */ -inline fun <reified T : Any, A : Field<T>> FunctionalExpressionField<T, A>.mstInField(block: MstField.() -> MST): MstExpression<T> { +public inline fun <reified T : Any, A : Field<T>> FunctionalExpressionField<T, A>.mstInField(block: MstField.() -> MST): MstExpression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return algebra.mstInField(block) } /** * Builds [MstExpression] over [FunctionalExpressionExtendedField]. + * + * @author Iaroslav Postovalov */ -inline fun <reified T : Any, A : ExtendedField<T>> FunctionalExpressionExtendedField<T, A>.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression<T> { +public inline fun <reified T : Any, A : ExtendedField<T>> FunctionalExpressionExtendedField<T, A>.mstInExtendedField( + block: MstExtendedField.() -> MST +): MstExpression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return algebra.mstInExtendedField(block) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/asm.kt similarity index 55% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt rename to kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/asm.kt index ee0ea15ff..edfe7f01d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/asm.kt @@ -1,19 +1,24 @@ -package scientifik.kmath.asm +package kscience.kmath.asm -import scientifik.kmath.asm.internal.AsmBuilder -import scientifik.kmath.asm.internal.MstType -import scientifik.kmath.asm.internal.buildAlgebraOperationCall -import scientifik.kmath.asm.internal.buildName -import scientifik.kmath.ast.MST -import scientifik.kmath.ast.MstExpression -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra +import kscience.kmath.asm.internal.AsmBuilder +import kscience.kmath.asm.internal.MstType +import kscience.kmath.asm.internal.buildAlgebraOperationCall +import kscience.kmath.asm.internal.buildName +import kscience.kmath.ast.MST +import kscience.kmath.ast.MstExpression +import kscience.kmath.expressions.Expression +import kscience.kmath.operations.Algebra import kotlin.reflect.KClass /** - * Compile given MST to an Expression using AST compiler + * Compiles given MST to an Expression using AST compiler. + * + * @param type the target type. + * @param algebra the target algebra. + * @return the compiled expression. + * @author Alexander Nozik */ -fun <T : Any> MST.compileWith(type: KClass<T>, algebra: Algebra<T>): Expression<T> { +public fun <T : Any> MST.compileWith(type: KClass<T>, algebra: Algebra<T>): Expression<T> { fun AsmBuilder<T>.visit(node: MST) { when (node) { is MST.Symbolic -> { @@ -54,11 +59,15 @@ fun <T : Any> MST.compileWith(type: KClass<T>, algebra: Algebra<T>): Expression< } /** - * Compile an [MST] to ASM using given algebra + * Compiles an [MST] to ASM using given algebra. + * + * @author Alexander Nozik. */ -inline fun <reified T : Any> Algebra<T>.expression(mst: MST): Expression<T> = mst.compileWith(T::class, this) +public inline fun <reified T : Any> Algebra<T>.expression(mst: MST): Expression<T> = mst.compileWith(T::class, this) /** - * Optimize performance of an [MstExpression] using ASM codegen + * Optimizes performance of an [MstExpression] using ASM codegen. + * + * @author Alexander Nozik. */ -inline fun <reified T : Any> MstExpression<T>.compile(): Expression<T> = mst.compileWith(T::class, algebra) +public inline fun <reified T : Any> MstExpression<T>.compile(): Expression<T> = mst.compileWith(T::class, algebra) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/AsmBuilder.kt similarity index 98% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt rename to kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/AsmBuilder.kt index f8c159baf..ab2de97aa 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/AsmBuilder.kt @@ -1,13 +1,13 @@ -package scientifik.kmath.asm.internal +package kscience.kmath.asm.internal +import kscience.kmath.asm.internal.AsmBuilder.ClassLoader +import kscience.kmath.ast.MST +import kscience.kmath.expressions.Expression +import kscience.kmath.operations.Algebra +import kscience.kmath.operations.NumericAlgebra import org.objectweb.asm.* import org.objectweb.asm.Opcodes.* import org.objectweb.asm.commons.InstructionAdapter -import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader -import scientifik.kmath.ast.MST -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.NumericAlgebra import java.util.* import java.util.stream.Collectors import kotlin.reflect.KClass @@ -20,6 +20,7 @@ import kotlin.reflect.KClass * @property algebra the algebra the applied AsmExpressions use. * @property className the unique class name of new loaded class. * @property invokeLabel0Visitor the function to apply to this object when generating invoke method, label 0. + * @author Iaroslav Postovalov */ internal class AsmBuilder<T> internal constructor( private val classOfT: KClass<*>, @@ -563,6 +564,6 @@ internal class AsmBuilder<T> internal constructor( /** * ASM type for MapIntrinsics. */ - internal val MAP_INTRINSICS_TYPE: Type by lazy { Type.getObjectType("scientifik/kmath/asm/internal/MapIntrinsics") } + internal val MAP_INTRINSICS_TYPE: Type by lazy { Type.getObjectType("kscience/kmath/asm/internal/MapIntrinsics") } } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MstType.kt b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/MstType.kt similarity index 62% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MstType.kt rename to kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/MstType.kt index bf73d304b..526c27305 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MstType.kt +++ b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/MstType.kt @@ -1,7 +1,10 @@ -package scientifik.kmath.asm.internal +package kscience.kmath.asm.internal -import scientifik.kmath.ast.MST +import kscience.kmath.ast.MST +/** + * Represents types known in [MST], numbers and general values. + */ internal enum class MstType { GENERAL, NUMBER; diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/codegenUtils.kt similarity index 81% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt rename to kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/codegenUtils.kt index 021c8ae02..67fce40ac 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt +++ b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/codegenUtils.kt @@ -1,11 +1,14 @@ -package scientifik.kmath.asm.internal +package kscience.kmath.asm.internal +import kscience.kmath.ast.MST +import kscience.kmath.expressions.Expression +import kscience.kmath.operations.Algebra +import kscience.kmath.operations.FieldOperations +import kscience.kmath.operations.RingOperations +import kscience.kmath.operations.SpaceOperations import org.objectweb.asm.* import org.objectweb.asm.Opcodes.INVOKEVIRTUAL import org.objectweb.asm.commons.InstructionAdapter -import scientifik.kmath.ast.MST -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra import java.lang.reflect.Method import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -13,20 +16,27 @@ import kotlin.reflect.KClass private val methodNameAdapters: Map<Pair<String, Int>, String> by lazy { hashMapOf( - "+" to 2 to "add", - "*" to 2 to "multiply", - "/" to 2 to "divide", - "+" to 1 to "unaryPlus", - "-" to 1 to "unaryMinus", - "-" to 2 to "minus" + SpaceOperations.PLUS_OPERATION to 2 to "add", + RingOperations.TIMES_OPERATION to 2 to "multiply", + FieldOperations.DIV_OPERATION to 2 to "divide", + SpaceOperations.PLUS_OPERATION to 1 to "unaryPlus", + SpaceOperations.MINUS_OPERATION to 1 to "unaryMinus", + SpaceOperations.MINUS_OPERATION to 2 to "minus" ) } +/** + * Returns ASM [Type] for given [KClass]. + * + * @author Iaroslav Postovalov + */ internal val KClass<*>.asm: Type get() = Type.getType(java) /** * Returns singleton array with this value if the [predicate] is true, returns empty array otherwise. + * + * @author Iaroslav Postovalov */ internal inline fun <reified T> T.wrapToArrayIf(predicate: (T) -> Boolean): Array<T> { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } @@ -35,11 +45,15 @@ internal inline fun <reified T> T.wrapToArrayIf(predicate: (T) -> Boolean): Arra /** * Creates an [InstructionAdapter] from this [MethodVisitor]. + * + * @author Iaroslav Postovalov */ private fun MethodVisitor.instructionAdapter(): InstructionAdapter = InstructionAdapter(this) /** * Creates an [InstructionAdapter] from this [MethodVisitor] and applies [block] to it. + * + * @author Iaroslav Postovalov */ internal inline fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } @@ -48,6 +62,8 @@ internal inline fun MethodVisitor.instructionAdapter(block: InstructionAdapter.( /** * Constructs a [Label], then applies it to this visitor. + * + * @author Iaroslav Postovalov */ internal fun MethodVisitor.label(): Label = Label().also { visitLabel(it) } @@ -56,9 +72,11 @@ internal fun MethodVisitor.label(): Label = Label().also { visitLabel(it) } * * This methods helps to avoid collisions of class name to prevent loading several classes with the same name. If there * is a colliding class, change [collision] parameter or leave it `0` to check existing classes recursively. + * + * @author Iaroslav Postovalov */ internal tailrec fun buildName(mst: MST, collision: Int = 0): String { - val name = "scientifik.kmath.asm.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" + val name = "kscience.kmath.asm.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" try { Class.forName(name) @@ -75,6 +93,11 @@ internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): Clas return ClassWriter(flags).apply(block) } +/** + * Invokes [visitField] and applies [block] to the [FieldVisitor]. + * + * @author Iaroslav Postovalov + */ internal inline fun ClassWriter.visitField( access: Int, name: String, @@ -104,7 +127,7 @@ private fun <T> AsmBuilder<T>.findSpecific(context: Algebra<T>, name: String, pa * Checks if the target [context] for code generation contains a method with needed [name] and arity, also builds * type expectation stack for needed arity. * - * @return `true` if contains, else `false`. + * @author Iaroslav Postovalov */ private fun <T> AsmBuilder<T>.buildExpectationStack( context: Algebra<T>, @@ -136,7 +159,7 @@ private fun <T> AsmBuilder<T>.mapTypes(method: Method, parameterTypes: Array<Mst * Checks if the target [context] for code generation contains a method with needed [name] and arity and inserts * [AsmBuilder.invokeAlgebraOperation] of this method. * - * @return `true` if contains, else `false`. + * @author Iaroslav Postovalov */ private fun <T> AsmBuilder<T>.tryInvokeSpecific( context: Algebra<T>, @@ -160,7 +183,9 @@ private fun <T> AsmBuilder<T>.tryInvokeSpecific( } /** - * Builds specialized algebra call with option to fallback to generic algebra operation accepting String. + * Builds specialized [context] call with option to fallback to generic algebra operation accepting [String]. + * + * @author Iaroslav Postovalov */ internal inline fun <T> AsmBuilder<T>.buildAlgebraOperationCall( context: Algebra<T>, diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/mapIntrinsics.kt similarity index 51% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt rename to kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/mapIntrinsics.kt index 80e83c1bf..708b3c2b4 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt +++ b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/mapIntrinsics.kt @@ -1,7 +1,12 @@ @file:JvmName("MapIntrinsics") -package scientifik.kmath.asm.internal +package kscience.kmath.asm.internal +/** + * Gets value with given [key] or throws [IllegalStateException] whenever it is not present. + * + * @author Iaroslav Postovalov + */ @JvmOverloads internal fun <K, V> Map<K, V>.getOrFail(key: K, default: V? = null): V = this[key] ?: default ?: error("Parameter not found: $key") diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/ast/parser.kt similarity index 71% rename from kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt rename to kmath-ast/src/jvmMain/kotlin/kscience/kmath/ast/parser.kt index cba335a8d..15e6625db 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/ast/parser.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.ast +package kscience.kmath.ast import com.github.h0tk3y.betterParse.combinators.* import com.github.h0tk3y.betterParse.grammar.Grammar @@ -10,15 +10,16 @@ import com.github.h0tk3y.betterParse.lexer.TokenMatch import com.github.h0tk3y.betterParse.lexer.regexToken import com.github.h0tk3y.betterParse.parser.ParseResult import com.github.h0tk3y.betterParse.parser.Parser -import scientifik.kmath.operations.FieldOperations -import scientifik.kmath.operations.PowerOperations -import scientifik.kmath.operations.RingOperations -import scientifik.kmath.operations.SpaceOperations +import kscience.kmath.operations.FieldOperations +import kscience.kmath.operations.PowerOperations +import kscience.kmath.operations.RingOperations +import kscience.kmath.operations.SpaceOperations /** - * TODO move to core + * TODO move to common after IR version is released + * @author Alexander Nozik and Iaroslav Postovalov */ -object ArithmeticsEvaluator : Grammar<MST>() { +public object ArithmeticsEvaluator : Grammar<MST>() { // TODO replace with "...".toRegex() when better-parse 0.4.1 is released private val num: Token by regexToken("[\\d.]+(?:[eE][-+]?\\d+)?") private val id: Token by regexToken("[a-z_A-Z][\\da-z_A-Z]*") @@ -35,23 +36,23 @@ object ArithmeticsEvaluator : Grammar<MST>() { private val number: Parser<MST> by num use { MST.Numeric(text.toDouble()) } private val singular: Parser<MST> by id use { MST.Symbolic(text) } - private val unaryFunction: Parser<MST> by (id and skip(lpar) and parser(::subSumChain) and skip(rpar)) + private val unaryFunction: Parser<MST> by (id and -lpar and parser(ArithmeticsEvaluator::subSumChain) and -rpar) .map { (id, term) -> MST.Unary(id.text, term) } private val binaryFunction: Parser<MST> by id - .and(skip(lpar)) - .and(parser(::subSumChain)) - .and(skip(comma)) - .and(parser(::subSumChain)) - .and(skip(rpar)) + .and(-lpar) + .and(parser(ArithmeticsEvaluator::subSumChain)) + .and(-comma) + .and(parser(ArithmeticsEvaluator::subSumChain)) + .and(-rpar) .map { (id, left, right) -> MST.Binary(id.text, left, right) } private val term: Parser<MST> by number .or(binaryFunction) .or(unaryFunction) .or(singular) - .or(skip(minus) and parser(::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) }) - .or(skip(lpar) and parser(::subSumChain) and skip(rpar)) + .or(-minus and parser(ArithmeticsEvaluator::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) }) + .or(-lpar and parser(ArithmeticsEvaluator::subSumChain) and -rpar) private val powChain: Parser<MST> by leftAssociative(term = term, operator = pow) { a, _, b -> MST.Binary(PowerOperations.POW_OPERATION, a, b) @@ -85,13 +86,15 @@ object ArithmeticsEvaluator : Grammar<MST>() { * * @receiver the string to parse. * @return the [MST] node. + * @author Alexander Nozik */ -fun String.tryParseMath(): ParseResult<MST> = ArithmeticsEvaluator.tryParseToEnd(this) +public fun String.tryParseMath(): ParseResult<MST> = ArithmeticsEvaluator.tryParseToEnd(this) /** * Parses the string into [MST]. * * @receiver the string to parse. * @return the [MST] node. + * @author Alexander Nozik */ -fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) +public fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt index 3acc6eb28..5d8f90c9b 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt @@ -1,12 +1,12 @@ package scietifik.kmath.asm -import scientifik.kmath.asm.compile -import scientifik.kmath.ast.mstInField -import scientifik.kmath.ast.mstInRing -import scientifik.kmath.ast.mstInSpace -import scientifik.kmath.expressions.invoke -import scientifik.kmath.operations.ByteRing -import scientifik.kmath.operations.RealField +import kscience.kmath.asm.compile +import kscience.kmath.ast.mstInField +import kscience.kmath.ast.mstInRing +import kscience.kmath.ast.mstInSpace +import kscience.kmath.expressions.invoke +import kscience.kmath.operations.ByteRing +import kscience.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt index 36c254c38..c2e45b8ff 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt @@ -1,10 +1,10 @@ package scietifik.kmath.asm -import scientifik.kmath.asm.compile -import scientifik.kmath.ast.mstInField -import scientifik.kmath.ast.mstInSpace -import scientifik.kmath.expressions.invoke -import scientifik.kmath.operations.RealField +import kscience.kmath.asm.compile +import kscience.kmath.ast.mstInField +import kscience.kmath.ast.mstInSpace +import kscience.kmath.expressions.invoke +import kscience.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt index a88431e9d..2b27fdc0f 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt @@ -1,9 +1,9 @@ package scietifik.kmath.asm -import scientifik.kmath.asm.compile -import scientifik.kmath.ast.mstInField -import scientifik.kmath.expressions.invoke -import scientifik.kmath.operations.RealField +import kscience.kmath.asm.compile +import kscience.kmath.ast.mstInField +import kscience.kmath.expressions.invoke +import kscience.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmVariables.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmVariables.kt index aafc75448..3511be97c 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmVariables.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmVariables.kt @@ -1,8 +1,8 @@ package scietifik.kmath.asm -import scientifik.kmath.ast.mstInRing -import scientifik.kmath.expressions.invoke -import scientifik.kmath.operations.ByteRing +import kscience.kmath.ast.mstInRing +import kscience.kmath.expressions.invoke +import kscience.kmath.operations.ByteRing import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt index 75659cc35..bb371bed0 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -1,12 +1,12 @@ package scietifik.kmath.ast -import scientifik.kmath.asm.compile -import scientifik.kmath.asm.expression -import scientifik.kmath.ast.mstInField -import scientifik.kmath.ast.parseMath -import scientifik.kmath.expressions.invoke -import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.ComplexField +import kscience.kmath.asm.compile +import kscience.kmath.asm.expression +import kscience.kmath.ast.mstInField +import kscience.kmath.ast.parseMath +import kscience.kmath.expressions.invoke +import kscience.kmath.operations.Complex +import kscience.kmath.operations.ComplexField import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt index 9bdbb12c9..21ee47d13 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt @@ -1,9 +1,9 @@ package scietifik.kmath.ast -import scientifik.kmath.ast.evaluate -import scientifik.kmath.ast.parseMath -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.RealField +import kscience.kmath.ast.evaluate +import kscience.kmath.ast.parseMath +import kscience.kmath.operations.Field +import kscience.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt index 9179c3428..8a5d6ba48 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -1,13 +1,13 @@ package scietifik.kmath.ast -import scientifik.kmath.ast.evaluate -import scientifik.kmath.ast.mstInField -import scientifik.kmath.ast.parseMath -import scientifik.kmath.expressions.invoke -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.ComplexField -import scientifik.kmath.operations.RealField +import kscience.kmath.ast.evaluate +import kscience.kmath.ast.mstInField +import kscience.kmath.ast.parseMath +import kscience.kmath.expressions.invoke +import kscience.kmath.operations.Algebra +import kscience.kmath.operations.Complex +import kscience.kmath.operations.ComplexField +import kscience.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-commons/build.gradle.kts b/kmath-commons/build.gradle.kts index a77d8ab47..ed6452ad8 100644 --- a/kmath-commons/build.gradle.kts +++ b/kmath-commons/build.gradle.kts @@ -1,13 +1,12 @@ plugins { - id("scientifik.jvm") + id("ru.mipt.npm.jvm") } - description = "Commons math binding for kmath" dependencies { api(project(":kmath-core")) api(project(":kmath-coroutines")) api(project(":kmath-prob")) - api(project(":kmath-functions")) +// api(project(":kmath-functions")) api("org.apache.commons:commons-math3:3.6.1") } diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt new file mode 100644 index 000000000..3ac908536 --- /dev/null +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt @@ -0,0 +1,128 @@ +package kscience.kmath.commons.expressions + +import kscience.kmath.expressions.Expression +import kscience.kmath.expressions.ExpressionAlgebra +import kscience.kmath.operations.ExtendedField +import kscience.kmath.operations.Field +import kscience.kmath.operations.invoke +import org.apache.commons.math3.analysis.differentiation.DerivativeStructure +import kotlin.properties.ReadOnlyProperty + +/** + * A field wrapping commons-math derivative structures + */ +public class DerivativeStructureField( + public val order: Int, + public val parameters: Map<String, Double> +) : ExtendedField<DerivativeStructure> { + public override val zero: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size) } + public override val one: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size, 1.0) } + + private val variables: Map<String, DerivativeStructure> = parameters.mapValues { (key, value) -> + DerivativeStructure(parameters.size, order, parameters.keys.indexOf(key), value) + } + + public val variable: ReadOnlyProperty<Any?, DerivativeStructure> = ReadOnlyProperty { _, property -> + variables[property.name] ?: error("A variable with name ${property.name} does not exist") + } + + public fun variable(name: String, default: DerivativeStructure? = null): DerivativeStructure = + variables[name] ?: default ?: error("A variable with name $name does not exist") + + public fun Number.const(): DerivativeStructure = DerivativeStructure(order, parameters.size, toDouble()) + + public fun DerivativeStructure.deriv(parName: String, order: Int = 1): Double { + return deriv(mapOf(parName to order)) + } + + public fun DerivativeStructure.deriv(orders: Map<String, Int>): Double { + return getPartialDerivative(*parameters.keys.map { orders[it] ?: 0 }.toIntArray()) + } + + public fun DerivativeStructure.deriv(vararg orders: Pair<String, Int>): Double = deriv(mapOf(*orders)) + public override fun add(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.add(b) + + public override fun multiply(a: DerivativeStructure, k: Number): DerivativeStructure = when (k) { + is Double -> a.multiply(k) + is Int -> a.multiply(k) + else -> a.multiply(k.toDouble()) + } + + public override fun multiply(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.multiply(b) + public override fun divide(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.divide(b) + public override fun sin(arg: DerivativeStructure): DerivativeStructure = arg.sin() + public override fun cos(arg: DerivativeStructure): DerivativeStructure = arg.cos() + public override fun tan(arg: DerivativeStructure): DerivativeStructure = arg.tan() + public override fun asin(arg: DerivativeStructure): DerivativeStructure = arg.asin() + public override fun acos(arg: DerivativeStructure): DerivativeStructure = arg.acos() + public override fun atan(arg: DerivativeStructure): DerivativeStructure = arg.atan() + public override fun sinh(arg: DerivativeStructure): DerivativeStructure = arg.sinh() + public override fun cosh(arg: DerivativeStructure): DerivativeStructure = arg.cosh() + public override fun tanh(arg: DerivativeStructure): DerivativeStructure = arg.tanh() + public override fun asinh(arg: DerivativeStructure): DerivativeStructure = arg.asinh() + public override fun acosh(arg: DerivativeStructure): DerivativeStructure = arg.acosh() + public override fun atanh(arg: DerivativeStructure): DerivativeStructure = arg.atanh() + + public override fun power(arg: DerivativeStructure, pow: Number): DerivativeStructure = when (pow) { + is Double -> arg.pow(pow) + is Int -> arg.pow(pow) + else -> arg.pow(pow.toDouble()) + } + + public fun power(arg: DerivativeStructure, pow: DerivativeStructure): DerivativeStructure = arg.pow(pow) + public override fun exp(arg: DerivativeStructure): DerivativeStructure = arg.exp() + public override fun ln(arg: DerivativeStructure): DerivativeStructure = arg.log() + + public override operator fun DerivativeStructure.plus(b: Number): DerivativeStructure = add(b.toDouble()) + public override operator fun DerivativeStructure.minus(b: Number): DerivativeStructure = subtract(b.toDouble()) + public override operator fun Number.plus(b: DerivativeStructure): DerivativeStructure = b + this + public override operator fun Number.minus(b: DerivativeStructure): DerivativeStructure = b - this +} + +/** + * A constructs that creates a derivative structure with required order on-demand + */ +public class DiffExpression(public val function: DerivativeStructureField.() -> DerivativeStructure) : + Expression<Double> { + public override operator fun invoke(arguments: Map<String, Double>): Double = DerivativeStructureField( + 0, + arguments + ).function().value + + /** + * Get the derivative expression with given orders + * TODO make result [DiffExpression] + */ + public fun derivative(orders: Map<String, Int>): Expression<Double> = Expression { arguments -> + (DerivativeStructureField(orders.values.max() ?: 0, arguments)) { function().deriv(orders) } + } + + //TODO add gradient and maybe other vector operators +} + +public fun DiffExpression.derivative(vararg orders: Pair<String, Int>): Expression<Double> = derivative(mapOf(*orders)) +public fun DiffExpression.derivative(name: String): Expression<Double> = derivative(name to 1) + +/** + * A context for [DiffExpression] (not to be confused with [DerivativeStructure]) + */ +public object DiffExpressionAlgebra : ExpressionAlgebra<Double, DiffExpression>, Field<DiffExpression> { + public override val zero: DiffExpression = DiffExpression { 0.0.const() } + public override val one: DiffExpression = DiffExpression { 1.0.const() } + + public override fun variable(name: String, default: Double?): DiffExpression = + DiffExpression { variable(name, default?.const()) } + + public override fun const(value: Double): DiffExpression = DiffExpression { value.const() } + + public override fun add(a: DiffExpression, b: DiffExpression): DiffExpression = + DiffExpression { a.function(this) + b.function(this) } + + public override fun multiply(a: DiffExpression, k: Number): DiffExpression = DiffExpression { a.function(this) * k } + + public override fun multiply(a: DiffExpression, b: DiffExpression): DiffExpression = + DiffExpression { a.function(this) * b.function(this) } + + public override fun divide(a: DiffExpression, b: DiffExpression): DiffExpression = + DiffExpression { a.function(this) / b.function(this) } +} diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt new file mode 100644 index 000000000..377d167bc --- /dev/null +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt @@ -0,0 +1,93 @@ +package kscience.kmath.commons.linear + +import kscience.kmath.linear.* +import kscience.kmath.structures.Matrix +import kscience.kmath.structures.NDStructure +import org.apache.commons.math3.linear.* + +public class CMMatrix(public val origin: RealMatrix, features: Set<MatrixFeature>? = null) : + FeaturedMatrix<Double> { + public override val rowNum: Int get() = origin.rowDimension + public override val colNum: Int get() = origin.columnDimension + + public override val features: Set<MatrixFeature> = features ?: sequence<MatrixFeature> { + if (origin is DiagonalMatrix) yield(DiagonalFeature) + }.toHashSet() + + public override fun suggestFeature(vararg features: MatrixFeature): CMMatrix = + CMMatrix(origin, this.features + features) + + public override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j) + + public override fun equals(other: Any?): Boolean { + return NDStructure.equals(this, other as? NDStructure<*> ?: return false) + } + + public override fun hashCode(): Int { + var result = origin.hashCode() + result = 31 * result + features.hashCode() + return result + } +} + +public fun Matrix<Double>.toCM(): CMMatrix = if (this is CMMatrix) { + this +} else { + //TODO add feature analysis + val array = Array(rowNum) { i -> DoubleArray(colNum) { j -> get(i, j) } } + CMMatrix(Array2DRowRealMatrix(array)) +} + +public fun RealMatrix.asMatrix(): CMMatrix = CMMatrix(this) + +public class CMVector(public val origin: RealVector) : Point<Double> { + public override val size: Int get() = origin.dimension + + public override operator fun get(index: Int): Double = origin.getEntry(index) + + public override operator fun iterator(): Iterator<Double> = origin.toArray().iterator() +} + +public fun Point<Double>.toCM(): CMVector = if (this is CMVector) this else { + val array = DoubleArray(size) { this[it] } + CMVector(ArrayRealVector(array)) +} + +public fun RealVector.toPoint(): CMVector = CMVector(this) + +public object CMMatrixContext : MatrixContext<Double> { + public override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): CMMatrix { + val array = Array(rows) { i -> DoubleArray(columns) { j -> initializer(i, j) } } + return CMMatrix(Array2DRowRealMatrix(array)) + } + + public override fun Matrix<Double>.dot(other: Matrix<Double>): CMMatrix = + CMMatrix(toCM().origin.multiply(other.toCM().origin)) + + public override fun Matrix<Double>.dot(vector: Point<Double>): CMVector = + CMVector(toCM().origin.preMultiply(vector.toCM().origin)) + + public override operator fun Matrix<Double>.unaryMinus(): CMMatrix = + produce(rowNum, colNum) { i, j -> -get(i, j) } + + public override fun add(a: Matrix<Double>, b: Matrix<Double>): CMMatrix = + CMMatrix(a.toCM().origin.multiply(b.toCM().origin)) + + public override operator fun Matrix<Double>.minus(b: Matrix<Double>): CMMatrix = + CMMatrix(toCM().origin.subtract(b.toCM().origin)) + + public override fun multiply(a: Matrix<Double>, k: Number): CMMatrix = + CMMatrix(a.toCM().origin.scalarMultiply(k.toDouble())) + + public override operator fun Matrix<Double>.times(value: Double): Matrix<Double> = + produce(rowNum, colNum) { i, j -> get(i, j) * value } +} + +public operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = + CMMatrix(origin.add(other.origin)) + +public operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = + CMMatrix(origin.subtract(other.origin)) + +public infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = + CMMatrix(origin.multiply(other.origin)) diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt new file mode 100644 index 000000000..210014e1a --- /dev/null +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMSolver.kt @@ -0,0 +1,41 @@ +package kscience.kmath.commons.linear + +import kscience.kmath.linear.Point +import kscience.kmath.structures.Matrix +import org.apache.commons.math3.linear.* + +public enum class CMDecomposition { + LUP, + QR, + RRQR, + EIGEN, + CHOLESKY +} + +public fun CMMatrixContext.solver( + a: Matrix<Double>, + decomposition: CMDecomposition = CMDecomposition.LUP +): DecompositionSolver = when (decomposition) { + CMDecomposition.LUP -> LUDecomposition(a.toCM().origin).solver + CMDecomposition.RRQR -> RRQRDecomposition(a.toCM().origin).solver + CMDecomposition.QR -> QRDecomposition(a.toCM().origin).solver + CMDecomposition.EIGEN -> EigenDecomposition(a.toCM().origin).solver + CMDecomposition.CHOLESKY -> CholeskyDecomposition(a.toCM().origin).solver +} + +public fun CMMatrixContext.solve( + a: Matrix<Double>, + b: Matrix<Double>, + decomposition: CMDecomposition = CMDecomposition.LUP +): CMMatrix = solver(a, decomposition).solve(b.toCM().origin).asMatrix() + +public fun CMMatrixContext.solve( + a: Matrix<Double>, + b: Point<Double>, + decomposition: CMDecomposition = CMDecomposition.LUP +): CMVector = solver(a, decomposition).solve(b.toCM().origin).toPoint() + +public fun CMMatrixContext.inverse( + a: Matrix<Double>, + decomposition: CMDecomposition = CMDecomposition.LUP +): CMMatrix = solver(a, decomposition).inverse.asMatrix() diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/random/CMRandomGeneratorWrapper.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/random/CMRandomGeneratorWrapper.kt new file mode 100644 index 000000000..58609deae --- /dev/null +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/random/CMRandomGeneratorWrapper.kt @@ -0,0 +1,33 @@ +package kscience.kmath.commons.random + +import kscience.kmath.prob.RandomGenerator + +public class CMRandomGeneratorWrapper(public val factory: (IntArray) -> RandomGenerator) : + org.apache.commons.math3.random.RandomGenerator { + private var generator: RandomGenerator = factory(intArrayOf()) + + public override fun nextBoolean(): Boolean = generator.nextBoolean() + public override fun nextFloat(): Float = generator.nextDouble().toFloat() + + public override fun setSeed(seed: Int) { + generator = factory(intArrayOf(seed)) + } + + public override fun setSeed(seed: IntArray) { + generator = factory(seed) + } + + public override fun setSeed(seed: Long) { + setSeed(seed.toInt()) + } + + public override fun nextBytes(bytes: ByteArray) { + generator.fillBytes(bytes) + } + + public override fun nextInt(): Int = generator.nextInt() + public override fun nextInt(n: Int): Int = generator.nextInt(n) + public override fun nextGaussian(): Double = TODO() + public override fun nextDouble(): Double = generator.nextDouble() + public override fun nextLong(): Long = generator.nextLong() +} diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/transform/Transformations.kt similarity index 81% rename from kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt rename to kmath-commons/src/main/kotlin/kscience/kmath/commons/transform/Transformations.kt index eb1b5b69a..cd2896be6 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/transform/Transformations.kt @@ -1,20 +1,19 @@ -package scientifik.kmath.commons.transform +package kscience.kmath.commons.transform import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import kscience.kmath.operations.Complex +import kscience.kmath.streaming.chunked +import kscience.kmath.streaming.spread +import kscience.kmath.structures.* import org.apache.commons.math3.transform.* -import scientifik.kmath.operations.Complex -import scientifik.kmath.streaming.chunked -import scientifik.kmath.streaming.spread -import scientifik.kmath.structures.* /** * Streaming and buffer transformations */ -object Transformations { - +public object Transformations { private fun Buffer<Complex>.toArray(): Array<org.apache.commons.math3.complex.Complex> = Array(size) { org.apache.commons.math3.complex.Complex(get(it).re, get(it).im) } @@ -32,35 +31,35 @@ object Transformations { Complex(value.real, value.imaginary) } - fun fourier( + public fun fourier( normalization: DftNormalization = DftNormalization.STANDARD, direction: TransformType = TransformType.FORWARD ): SuspendBufferTransform<Complex, Complex> = { FastFourierTransformer(normalization).transform(it.toArray(), direction).asBuffer() } - fun realFourier( + public fun realFourier( normalization: DftNormalization = DftNormalization.STANDARD, direction: TransformType = TransformType.FORWARD ): SuspendBufferTransform<Double, Complex> = { FastFourierTransformer(normalization).transform(it.asArray(), direction).asBuffer() } - fun sine( + public fun sine( normalization: DstNormalization = DstNormalization.STANDARD_DST_I, direction: TransformType = TransformType.FORWARD ): SuspendBufferTransform<Double, Double> = { FastSineTransformer(normalization).transform(it.asArray(), direction).asBuffer() } - fun cosine( + public fun cosine( normalization: DctNormalization = DctNormalization.STANDARD_DCT_I, direction: TransformType = TransformType.FORWARD ): SuspendBufferTransform<Double, Double> = { FastCosineTransformer(normalization).transform(it.asArray(), direction).asBuffer() } - fun hadamard( + public fun hadamard( direction: TransformType = TransformType.FORWARD ): SuspendBufferTransform<Double, Double> = { FastHadamardTransformer().transform(it.asArray(), direction).asBuffer() @@ -71,7 +70,7 @@ object Transformations { * Process given [Flow] with commons-math fft transformation */ @FlowPreview -fun Flow<Buffer<Complex>>.FFT( +public fun Flow<Buffer<Complex>>.FFT( normalization: DftNormalization = DftNormalization.STANDARD, direction: TransformType = TransformType.FORWARD ): Flow<Buffer<Complex>> { @@ -81,7 +80,7 @@ fun Flow<Buffer<Complex>>.FFT( @FlowPreview @JvmName("realFFT") -fun Flow<Buffer<Double>>.FFT( +public fun Flow<Buffer<Double>>.FFT( normalization: DftNormalization = DftNormalization.STANDARD, direction: TransformType = TransformType.FORWARD ): Flow<Buffer<Complex>> { @@ -90,20 +89,18 @@ fun Flow<Buffer<Double>>.FFT( } /** - * Process a continous flow of real numbers in FFT splitting it in chunks of [bufferSize]. + * Process a continuous flow of real numbers in FFT splitting it in chunks of [bufferSize]. */ @FlowPreview @JvmName("realFFT") -fun Flow<Double>.FFT( +public fun Flow<Double>.FFT( bufferSize: Int = Int.MAX_VALUE, normalization: DftNormalization = DftNormalization.STANDARD, direction: TransformType = TransformType.FORWARD -): Flow<Complex> { - return chunked(bufferSize).FFT(normalization,direction).spread() -} +): Flow<Complex> = chunked(bufferSize).FFT(normalization, direction).spread() /** * Map a complex flow into real flow by taking real part of each number */ @FlowPreview -fun Flow<Complex>.real(): Flow<Double> = map{it.re} \ No newline at end of file +public fun Flow<Complex>.real(): Flow<Double> = map { it.re } diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt deleted file mode 100644 index 9119991e5..000000000 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ /dev/null @@ -1,137 +0,0 @@ -package scientifik.kmath.commons.expressions - -import org.apache.commons.math3.analysis.differentiation.DerivativeStructure -import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionAlgebra -import scientifik.kmath.operations.ExtendedField -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.invoke -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -/** - * A field wrapping commons-math derivative structures - */ -class DerivativeStructureField( - val order: Int, - val parameters: Map<String, Double> -) : ExtendedField<DerivativeStructure> { - override val zero: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size) } - override val one: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size, 1.0) } - - private val variables: Map<String, DerivativeStructure> = parameters.mapValues { (key, value) -> - DerivativeStructure(parameters.size, order, parameters.keys.indexOf(key), value) - } - - val variable: ReadOnlyProperty<Any?, DerivativeStructure> = object : ReadOnlyProperty<Any?, DerivativeStructure> { - override fun getValue(thisRef: Any?, property: KProperty<*>): DerivativeStructure = - variables[property.name] ?: error("A variable with name ${property.name} does not exist") - } - - fun variable(name: String, default: DerivativeStructure? = null): DerivativeStructure = - variables[name] ?: default ?: error("A variable with name $name does not exist") - - fun Number.const(): DerivativeStructure = DerivativeStructure(order, parameters.size, toDouble()) - - fun DerivativeStructure.deriv(parName: String, order: Int = 1): Double { - return deriv(mapOf(parName to order)) - } - - fun DerivativeStructure.deriv(orders: Map<String, Int>): Double { - return getPartialDerivative(*parameters.keys.map { orders[it] ?: 0 }.toIntArray()) - } - - fun DerivativeStructure.deriv(vararg orders: Pair<String, Int>): Double = deriv(mapOf(*orders)) - - override fun add(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.add(b) - - override fun multiply(a: DerivativeStructure, k: Number): DerivativeStructure = when (k) { - is Double -> a.multiply(k) - is Int -> a.multiply(k) - else -> a.multiply(k.toDouble()) - } - - override fun multiply(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.multiply(b) - - override fun divide(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.divide(b) - - override fun sin(arg: DerivativeStructure): DerivativeStructure = arg.sin() - override fun cos(arg: DerivativeStructure): DerivativeStructure = arg.cos() - override fun tan(arg: DerivativeStructure): DerivativeStructure = arg.tan() - override fun asin(arg: DerivativeStructure): DerivativeStructure = arg.asin() - override fun acos(arg: DerivativeStructure): DerivativeStructure = arg.acos() - override fun atan(arg: DerivativeStructure): DerivativeStructure = arg.atan() - - override fun sinh(arg: DerivativeStructure): DerivativeStructure = arg.sinh() - override fun cosh(arg: DerivativeStructure): DerivativeStructure = arg.cosh() - override fun tanh(arg: DerivativeStructure): DerivativeStructure = arg.tanh() - override fun asinh(arg: DerivativeStructure): DerivativeStructure = arg.asinh() - override fun acosh(arg: DerivativeStructure): DerivativeStructure = arg.acosh() - override fun atanh(arg: DerivativeStructure): DerivativeStructure = arg.atanh() - - override fun power(arg: DerivativeStructure, pow: Number): DerivativeStructure = when (pow) { - is Double -> arg.pow(pow) - is Int -> arg.pow(pow) - else -> arg.pow(pow.toDouble()) - } - - fun power(arg: DerivativeStructure, pow: DerivativeStructure): DerivativeStructure = arg.pow(pow) - override fun exp(arg: DerivativeStructure): DerivativeStructure = arg.exp() - override fun ln(arg: DerivativeStructure): DerivativeStructure = arg.log() - - override operator fun DerivativeStructure.plus(b: Number): DerivativeStructure = add(b.toDouble()) - override operator fun DerivativeStructure.minus(b: Number): DerivativeStructure = subtract(b.toDouble()) - override operator fun Number.plus(b: DerivativeStructure): DerivativeStructure = b + this - override operator fun Number.minus(b: DerivativeStructure): DerivativeStructure = b - this -} - -/** - * A constructs that creates a derivative structure with required order on-demand - */ -class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) : Expression<Double> { - override operator fun invoke(arguments: Map<String, Double>): Double = DerivativeStructureField( - 0, - arguments - ).run(function).value - - /** - * Get the derivative expression with given orders - * TODO make result [DiffExpression] - */ - fun derivative(orders: Map<String, Int>): Expression<Double> = object : Expression<Double> { - override operator fun invoke(arguments: Map<String, Double>): Double = - (DerivativeStructureField(orders.values.max() ?: 0, arguments)) { function().deriv(orders) } - } - - //TODO add gradient and maybe other vector operators -} - -fun DiffExpression.derivative(vararg orders: Pair<String, Int>): Expression<Double> = derivative(mapOf(*orders)) -fun DiffExpression.derivative(name: String): Expression<Double> = derivative(name to 1) - -/** - * A context for [DiffExpression] (not to be confused with [DerivativeStructure]) - */ -object DiffExpressionAlgebra : ExpressionAlgebra<Double, DiffExpression>, Field<DiffExpression> { - override fun variable(name: String, default: Double?): DiffExpression = - DiffExpression { variable(name, default?.const()) } - - override fun const(value: Double): DiffExpression = - DiffExpression { value.const() } - - override fun add(a: DiffExpression, b: DiffExpression): DiffExpression = - DiffExpression { a.function(this) + b.function(this) } - - override val zero: DiffExpression = DiffExpression { 0.0.const() } - - override fun multiply(a: DiffExpression, k: Number): DiffExpression = - DiffExpression { a.function(this) * k } - - override val one: DiffExpression = DiffExpression { 1.0.const() } - - override fun multiply(a: DiffExpression, b: DiffExpression): DiffExpression = - DiffExpression { a.function(this) * b.function(this) } - - override fun divide(a: DiffExpression, b: DiffExpression): DiffExpression = - DiffExpression { a.function(this) / b.function(this) } -} diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt deleted file mode 100644 index f0bbdbe65..000000000 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt +++ /dev/null @@ -1,93 +0,0 @@ -package scientifik.kmath.commons.linear - -import org.apache.commons.math3.linear.* -import scientifik.kmath.linear.* -import scientifik.kmath.structures.Matrix -import scientifik.kmath.structures.NDStructure - -class CMMatrix(val origin: RealMatrix, features: Set<MatrixFeature>? = null) : - FeaturedMatrix<Double> { - override val rowNum: Int get() = origin.rowDimension - override val colNum: Int get() = origin.columnDimension - - override val features: Set<MatrixFeature> = features ?: sequence<MatrixFeature> { - if (origin is DiagonalMatrix) yield(DiagonalFeature) - }.toHashSet() - - override fun suggestFeature(vararg features: MatrixFeature): CMMatrix = - CMMatrix(origin, this.features + features) - - override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j) - - override fun equals(other: Any?): Boolean { - return NDStructure.equals(this, other as? NDStructure<*> ?: return false) - } - - override fun hashCode(): Int { - var result = origin.hashCode() - result = 31 * result + features.hashCode() - return result - } -} - -fun Matrix<Double>.toCM(): CMMatrix = if (this is CMMatrix) { - this -} else { - //TODO add feature analysis - val array = Array(rowNum) { i -> DoubleArray(colNum) { j -> get(i, j) } } - CMMatrix(Array2DRowRealMatrix(array)) -} - -fun RealMatrix.asMatrix(): CMMatrix = CMMatrix(this) - -class CMVector(val origin: RealVector) : Point<Double> { - override val size: Int get() = origin.dimension - - override operator fun get(index: Int): Double = origin.getEntry(index) - - override operator fun iterator(): Iterator<Double> = origin.toArray().iterator() -} - -fun Point<Double>.toCM(): CMVector = if (this is CMVector) this else { - val array = DoubleArray(size) { this[it] } - CMVector(ArrayRealVector(array)) -} - -fun RealVector.toPoint(): CMVector = CMVector(this) - -object CMMatrixContext : MatrixContext<Double> { - override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): CMMatrix { - val array = Array(rows) { i -> DoubleArray(columns) { j -> initializer(i, j) } } - return CMMatrix(Array2DRowRealMatrix(array)) - } - - override fun Matrix<Double>.dot(other: Matrix<Double>): CMMatrix = - CMMatrix(this.toCM().origin.multiply(other.toCM().origin)) - - override fun Matrix<Double>.dot(vector: Point<Double>): CMVector = - CMVector(this.toCM().origin.preMultiply(vector.toCM().origin)) - - override operator fun Matrix<Double>.unaryMinus(): CMMatrix = - produce(rowNum, colNum) { i, j -> -get(i, j) } - - override fun add(a: Matrix<Double>, b: Matrix<Double>): CMMatrix = - CMMatrix(a.toCM().origin.multiply(b.toCM().origin)) - - override operator fun Matrix<Double>.minus(b: Matrix<Double>): CMMatrix = - CMMatrix(this.toCM().origin.subtract(b.toCM().origin)) - - override fun multiply(a: Matrix<Double>, k: Number): CMMatrix = - CMMatrix(a.toCM().origin.scalarMultiply(k.toDouble())) - - override operator fun Matrix<Double>.times(value: Double): Matrix<Double> = - produce(rowNum, colNum) { i, j -> get(i, j) * value } -} - -operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = - CMMatrix(this.origin.add(other.origin)) - -operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = - CMMatrix(this.origin.subtract(other.origin)) - -infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = - CMMatrix(this.origin.multiply(other.origin)) diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMSolver.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMSolver.kt deleted file mode 100644 index 77b688e31..000000000 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMSolver.kt +++ /dev/null @@ -1,40 +0,0 @@ -package scientifik.kmath.commons.linear - -import org.apache.commons.math3.linear.* -import scientifik.kmath.linear.Point -import scientifik.kmath.structures.Matrix - -enum class CMDecomposition { - LUP, - QR, - RRQR, - EIGEN, - CHOLESKY -} - - -fun CMMatrixContext.solver(a: Matrix<Double>, decomposition: CMDecomposition = CMDecomposition.LUP) = - when (decomposition) { - CMDecomposition.LUP -> LUDecomposition(a.toCM().origin).solver - CMDecomposition.RRQR -> RRQRDecomposition(a.toCM().origin).solver - CMDecomposition.QR -> QRDecomposition(a.toCM().origin).solver - CMDecomposition.EIGEN -> EigenDecomposition(a.toCM().origin).solver - CMDecomposition.CHOLESKY -> CholeskyDecomposition(a.toCM().origin).solver - } - -fun CMMatrixContext.solve( - a: Matrix<Double>, - b: Matrix<Double>, - decomposition: CMDecomposition = CMDecomposition.LUP -) = solver(a, decomposition).solve(b.toCM().origin).asMatrix() - -fun CMMatrixContext.solve( - a: Matrix<Double>, - b: Point<Double>, - decomposition: CMDecomposition = CMDecomposition.LUP -) = solver(a, decomposition).solve(b.toCM().origin).toPoint() - -fun CMMatrixContext.inverse( - a: Matrix<Double>, - decomposition: CMDecomposition = CMDecomposition.LUP -) = solver(a, decomposition).inverse.asMatrix() diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt deleted file mode 100644 index cb2b5dd9c..000000000 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt +++ /dev/null @@ -1,33 +0,0 @@ -package scientifik.kmath.commons.random - -import scientifik.kmath.prob.RandomGenerator - -class CMRandomGeneratorWrapper(val factory: (IntArray) -> RandomGenerator) : - org.apache.commons.math3.random.RandomGenerator { - private var generator: RandomGenerator = factory(intArrayOf()) - - override fun nextBoolean(): Boolean = generator.nextBoolean() - override fun nextFloat(): Float = generator.nextDouble().toFloat() - - override fun setSeed(seed: Int) { - generator = factory(intArrayOf(seed)) - } - - override fun setSeed(seed: IntArray) { - generator = factory(seed) - } - - override fun setSeed(seed: Long) { - setSeed(seed.toInt()) - } - - override fun nextBytes(bytes: ByteArray) { - generator.fillBytes(bytes) - } - - override fun nextInt(): Int = generator.nextInt() - override fun nextInt(n: Int): Int = generator.nextInt(n) - override fun nextGaussian(): Double = TODO() - override fun nextDouble(): Double = generator.nextDouble() - override fun nextLong(): Long = generator.nextLong() -} diff --git a/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt b/kmath-commons/src/test/kotlin/kscience/kmath/commons/expressions/AutoDiffTest.kt similarity index 76% rename from kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt rename to kmath-commons/src/test/kotlin/kscience/kmath/commons/expressions/AutoDiffTest.kt index bbdcff2fc..f905e6818 100644 --- a/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt +++ b/kmath-commons/src/test/kotlin/kscience/kmath/commons/expressions/AutoDiffTest.kt @@ -1,17 +1,21 @@ -package scientifik.kmath.commons.expressions +package kscience.kmath.commons.expressions -import scientifik.kmath.expressions.invoke +import kscience.kmath.expressions.invoke import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.test.Test import kotlin.test.assertEquals -inline fun <R> diff(order: Int, vararg parameters: Pair<String, Double>, block: DerivativeStructureField.() -> R): R { +internal inline fun <R> diff( + order: Int, + vararg parameters: Pair<String, Double>, + block: DerivativeStructureField.() -> R +): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return DerivativeStructureField(order, mapOf(*parameters)).run(block) } -class AutoDiffTest { +internal class AutoDiffTest { @Test fun derivativeStructureFieldTest() { val res = diff(3, "x" to 1.0, "y" to 1.0) { @@ -33,4 +37,4 @@ class AutoDiffTest { assertEquals(10.0, f("x" to 1.0, "y" to 2.0)) assertEquals(6.0, f.derivative("x")("x" to 1.0, "y" to 2.0)) } -} \ No newline at end of file +} diff --git a/kmath-core/README.md b/kmath-core/README.md index aed33a257..9d05331f9 100644 --- a/kmath-core/README.md +++ b/kmath-core/README.md @@ -10,31 +10,31 @@ The core features of KMath: - Automatic differentiation. > #### Artifact: -> This module is distributed in the artifact `scientifik:kmath-core:0.1.4-dev-8`. +> This module is distributed in the artifact `kscience.kmath:kmath-core:0.1.4-dev-8`. > > **Gradle:** > > ```gradle > repositories { -> maven { url 'https://dl.bintray.com/mipt-npm/scientifik' } +> maven { url 'https://dl.bintray.com/mipt-npm/kscience' } > maven { url 'https://dl.bintray.com/mipt-npm/dev' } > maven { url https://dl.bintray.com/hotkeytlt/maven' } > } > > dependencies { -> implementation 'scientifik:kmath-core:0.1.4-dev-8' +> implementation 'kscience.kmath:kmath-core:0.1.4-dev-8' > } > ``` > **Gradle Kotlin DSL:** > > ```kotlin > repositories { -> maven("https://dl.bintray.com/mipt-npm/scientifik") +> maven("https://dl.bintray.com/mipt-npm/kscience") > maven("https://dl.bintray.com/mipt-npm/dev") > maven("https://dl.bintray.com/hotkeytlt/maven") > } > -> dependencies {`` -> implementation("scientifik:kmath-core:0.1.4-dev-8") +> dependencies { +> implementation("kscience.kmath:kmath-core:0.1.4-dev-8") > } > ``` diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 5a5a8be6d..e315e1640 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -1,6 +1,4 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("ru.mipt.npm.mpp") } kotlin.sourceSets.commonMain { dependencies { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/Domain.kt similarity index 54% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/domains/Domain.kt index 341383bfb..5c3cff2c5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/Domain.kt @@ -1,20 +1,20 @@ -package scientifik.kmath.domains +package kscience.kmath.domains -import scientifik.kmath.linear.Point +import kscience.kmath.linear.Point /** * A simple geometric domain. * * @param T the type of element of this domain. */ -interface Domain<T : Any> { +public interface Domain<T : Any> { /** * Checks if the specified point is contained in this domain. */ - operator fun contains(point: Point<T>): Boolean + public operator fun contains(point: Point<T>): Boolean /** * Number of hyperspace dimensions. */ - val dimension: Int + public val dimension: Int } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/HyperSquareDomain.kt similarity index 54% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/domains/HyperSquareDomain.kt index 66798c42f..b45cf6bf5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/HyperSquareDomain.kt @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package scientifik.kmath.domains +package kscience.kmath.domains -import scientifik.kmath.linear.Point -import scientifik.kmath.structures.RealBuffer -import scientifik.kmath.structures.indices +import kscience.kmath.linear.Point +import kscience.kmath.structures.RealBuffer +import kscience.kmath.structures.indices /** * @@ -25,23 +25,22 @@ import scientifik.kmath.structures.indices * * @author Alexander Nozik */ -class HyperSquareDomain(private val lower: RealBuffer, private val upper: RealBuffer) : RealDomain { +public class HyperSquareDomain(private val lower: RealBuffer, private val upper: RealBuffer) : RealDomain { + public override val dimension: Int get() = lower.size - override operator fun contains(point: Point<Double>): Boolean = point.indices.all { i -> + public override operator fun contains(point: Point<Double>): Boolean = point.indices.all { i -> point[i] in lower[i]..upper[i] } - override val dimension: Int get() = lower.size + public override fun getLowerBound(num: Int, point: Point<Double>): Double? = lower[num] - override fun getLowerBound(num: Int, point: Point<Double>): Double? = lower[num] + public override fun getLowerBound(num: Int): Double? = lower[num] - override fun getLowerBound(num: Int): Double? = lower[num] + public override fun getUpperBound(num: Int, point: Point<Double>): Double? = upper[num] - override fun getUpperBound(num: Int, point: Point<Double>): Double? = upper[num] + public override fun getUpperBound(num: Int): Double? = upper[num] - override fun getUpperBound(num: Int): Double? = upper[num] - - override fun nearestInDomain(point: Point<Double>): Point<Double> { + public override fun nearestInDomain(point: Point<Double>): Point<Double> { val res = DoubleArray(point.size) { i -> when { point[i] < lower[i] -> lower[i] @@ -53,16 +52,14 @@ class HyperSquareDomain(private val lower: RealBuffer, private val upper: RealBu return RealBuffer(*res) } - override fun volume(): Double { + public override fun volume(): Double { var res = 1.0 + for (i in 0 until dimension) { - if (lower[i].isInfinite() || upper[i].isInfinite()) { - return Double.POSITIVE_INFINITY - } - if (upper[i] > lower[i]) { - res *= upper[i] - lower[i] - } + if (lower[i].isInfinite() || upper[i].isInfinite()) return Double.POSITIVE_INFINITY + if (upper[i] > lower[i]) res *= upper[i] - lower[i] } + return res } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/RealDomain.kt similarity index 71% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/domains/RealDomain.kt index 7507ccd59..369b093bb 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/RealDomain.kt @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package scientifik.kmath.domains +package kscience.kmath.domains -import scientifik.kmath.linear.Point +import kscience.kmath.linear.Point /** * n-dimensional volume * * @author Alexander Nozik */ -interface RealDomain : Domain<Double> { - fun nearestInDomain(point: Point<Double>): Point<Double> +public interface RealDomain : Domain<Double> { + public fun nearestInDomain(point: Point<Double>): Point<Double> /** * The lower edge for the domain going down from point @@ -31,7 +31,7 @@ interface RealDomain : Domain<Double> { * @param point * @return */ - fun getLowerBound(num: Int, point: Point<Double>): Double? + public fun getLowerBound(num: Int, point: Point<Double>): Double? /** * The upper edge of the domain going up from point @@ -39,25 +39,25 @@ interface RealDomain : Domain<Double> { * @param point * @return */ - fun getUpperBound(num: Int, point: Point<Double>): Double? + public fun getUpperBound(num: Int, point: Point<Double>): Double? /** * Global lower edge * @param num * @return */ - fun getLowerBound(num: Int): Double? + public fun getLowerBound(num: Int): Double? /** * Global upper edge * @param num * @return */ - fun getUpperBound(num: Int): Double? + public fun getUpperBound(num: Int): Double? /** * Hyper volume * @return */ - fun volume(): Double + public fun volume(): Double } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/UnconstrainedDomain.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/UnconstrainedDomain.kt new file mode 100644 index 000000000..e2efb51ab --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/UnconstrainedDomain.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2015 Alexander Nozik. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package kscience.kmath.domains + +import kscience.kmath.linear.Point + +public class UnconstrainedDomain(public override val dimension: Int) : RealDomain { + public override operator fun contains(point: Point<Double>): Boolean = true + + public override fun getLowerBound(num: Int, point: Point<Double>): Double? = Double.NEGATIVE_INFINITY + + public override fun getLowerBound(num: Int): Double? = Double.NEGATIVE_INFINITY + + public override fun getUpperBound(num: Int, point: Point<Double>): Double? = Double.POSITIVE_INFINITY + + public override fun getUpperBound(num: Int): Double? = Double.POSITIVE_INFINITY + + public override fun nearestInDomain(point: Point<Double>): Point<Double> = point + + public override fun volume(): Double = Double.POSITIVE_INFINITY +} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/UnivariateDomain.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/UnivariateDomain.kt new file mode 100644 index 000000000..bf090f2e5 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/domains/UnivariateDomain.kt @@ -0,0 +1,49 @@ +package kscience.kmath.domains + +import kscience.kmath.linear.Point +import kscience.kmath.structures.asBuffer + +public inline class UnivariateDomain(public val range: ClosedFloatingPointRange<Double>) : RealDomain { + public override val dimension: Int + get() = 1 + + public operator fun contains(d: Double): Boolean = range.contains(d) + + public override operator fun contains(point: Point<Double>): Boolean { + require(point.size == 0) + return contains(point[0]) + } + + public override fun nearestInDomain(point: Point<Double>): Point<Double> { + require(point.size == 1) + val value = point[0] + + return when { + value in range -> point + value >= range.endInclusive -> doubleArrayOf(range.endInclusive).asBuffer() + else -> doubleArrayOf(range.start).asBuffer() + } + } + + public override fun getLowerBound(num: Int, point: Point<Double>): Double? { + require(num == 0) + return range.start + } + + public override fun getUpperBound(num: Int, point: Point<Double>): Double? { + require(num == 0) + return range.endInclusive + } + + public override fun getLowerBound(num: Int): Double? { + require(num == 0) + return range.start + } + + public override fun getUpperBound(num: Int): Double? { + require(num == 0) + return range.endInclusive + } + + public override fun volume(): Double = range.endInclusive - range.start +} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt new file mode 100644 index 000000000..5ade9e3ca --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt @@ -0,0 +1,41 @@ +package kscience.kmath.expressions + +import kscience.kmath.operations.Algebra + +/** + * An elementary function that could be invoked on a map of arguments + */ +public fun interface Expression<T> { + /** + * Calls this expression from arguments. + * + * @param arguments the map of arguments. + * @return the value. + */ + public operator fun invoke(arguments: Map<String, T>): T + + public companion object +} + +/** + * Calls this expression from arguments. + * + * @param pairs the pair of arguments' names to values. + * @return the value. + */ +public operator fun <T> Expression<T>.invoke(vararg pairs: Pair<String, T>): T = invoke(mapOf(*pairs)) + +/** + * A context for expression construction + */ +public interface ExpressionAlgebra<T, E> : Algebra<E> { + /** + * Introduce a variable into expression context + */ + public fun variable(name: String, default: T? = null): E + + /** + * A constant expression which does not depend on arguments + */ + public fun const(value: T): E +} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt new file mode 100644 index 000000000..49844a2be --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -0,0 +1,171 @@ +package kscience.kmath.expressions + +import kscience.kmath.operations.* + +internal class FunctionalUnaryOperation<T>(val context: Algebra<T>, val name: String, private val expr: Expression<T>) : + Expression<T> { + public override operator fun invoke(arguments: Map<String, T>): T = + context.unaryOperation(name, expr.invoke(arguments)) +} + +internal class FunctionalBinaryOperation<T>( + val context: Algebra<T>, + val name: String, + val first: Expression<T>, + val second: Expression<T> +) : Expression<T> { + public override operator fun invoke(arguments: Map<String, T>): T = + context.binaryOperation(name, first.invoke(arguments), second.invoke(arguments)) +} + +internal class FunctionalVariableExpression<T>(val name: String, val default: T? = null) : Expression<T> { + public override operator fun invoke(arguments: Map<String, T>): T = + arguments[name] ?: default ?: error("Parameter not found: $name") +} + +internal class FunctionalConstantExpression<T>(val value: T) : Expression<T> { + public override operator fun invoke(arguments: Map<String, T>): T = value +} + +internal class FunctionalConstProductExpression<T>( + val context: Space<T>, + private val expr: Expression<T>, + val const: Number +) : Expression<T> { + public override operator fun invoke(arguments: Map<String, T>): T = context.multiply(expr.invoke(arguments), const) +} + +/** + * A context class for [Expression] construction. + * + * @param algebra The algebra to provide for Expressions built. + */ +public abstract class FunctionalExpressionAlgebra<T, A : Algebra<T>>(public val algebra: A) : + ExpressionAlgebra<T, Expression<T>> { + /** + * Builds an Expression of constant expression which does not depend on arguments. + */ + public override fun const(value: T): Expression<T> = FunctionalConstantExpression(value) + + /** + * Builds an Expression to access a variable. + */ + public override fun variable(name: String, default: T?): Expression<T> = FunctionalVariableExpression(name, default) + + /** + * Builds an Expression of dynamic call of binary operation [operation] on [left] and [right]. + */ + public override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = + FunctionalBinaryOperation(algebra, operation, left, right) + + /** + * Builds an Expression of dynamic call of unary operation with name [operation] on [arg]. + */ + public override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = + FunctionalUnaryOperation(algebra, operation, arg) +} + +/** + * A context class for [Expression] construction for [Space] algebras. + */ +public open class FunctionalExpressionSpace<T, A : Space<T>>(algebra: A) : + FunctionalExpressionAlgebra<T, A>(algebra), Space<Expression<T>> { + public override val zero: Expression<T> get() = const(algebra.zero) + + /** + * Builds an Expression of addition of two another expressions. + */ + public override fun add(a: Expression<T>, b: Expression<T>): Expression<T> = + binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + + /** + * Builds an Expression of multiplication of expression by number. + */ + public override fun multiply(a: Expression<T>, k: Number): Expression<T> = + FunctionalConstProductExpression(algebra, a, k) + + public operator fun Expression<T>.plus(arg: T): Expression<T> = this + const(arg) + public operator fun Expression<T>.minus(arg: T): Expression<T> = this - const(arg) + public operator fun T.plus(arg: Expression<T>): Expression<T> = arg + this + public operator fun T.minus(arg: Expression<T>): Expression<T> = arg - this + + public override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = + super<FunctionalExpressionAlgebra>.unaryOperation(operation, arg) + + public override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = + super<FunctionalExpressionAlgebra>.binaryOperation(operation, left, right) +} + +public open class FunctionalExpressionRing<T, A>(algebra: A) : FunctionalExpressionSpace<T, A>(algebra), + Ring<Expression<T>> where A : Ring<T>, A : NumericAlgebra<T> { + public override val one: Expression<T> + get() = const(algebra.one) + + /** + * Builds an Expression of multiplication of two expressions. + */ + public override fun multiply(a: Expression<T>, b: Expression<T>): Expression<T> = + binaryOperation(RingOperations.TIMES_OPERATION, a, b) + + public operator fun Expression<T>.times(arg: T): Expression<T> = this * const(arg) + public operator fun T.times(arg: Expression<T>): Expression<T> = arg * this + + public override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = + super<FunctionalExpressionSpace>.unaryOperation(operation, arg) + + public override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = + super<FunctionalExpressionSpace>.binaryOperation(operation, left, right) +} + +public open class FunctionalExpressionField<T, A>(algebra: A) : + FunctionalExpressionRing<T, A>(algebra), + Field<Expression<T>> where A : Field<T>, A : NumericAlgebra<T> { + /** + * Builds an Expression of division an expression by another one. + */ + public override fun divide(a: Expression<T>, b: Expression<T>): Expression<T> = + binaryOperation(FieldOperations.DIV_OPERATION, a, b) + + public operator fun Expression<T>.div(arg: T): Expression<T> = this / const(arg) + public operator fun T.div(arg: Expression<T>): Expression<T> = arg / this + + public override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = + super<FunctionalExpressionRing>.unaryOperation(operation, arg) + + public override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = + super<FunctionalExpressionRing>.binaryOperation(operation, left, right) +} + +public open class FunctionalExpressionExtendedField<T, A>(algebra: A) : + FunctionalExpressionField<T, A>(algebra), + ExtendedField<Expression<T>> where A : ExtendedField<T>, A : NumericAlgebra<T> { + public override fun sin(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg) + public override fun cos(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.COS_OPERATION, arg) + public override fun asin(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.ASIN_OPERATION, arg) + public override fun acos(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.ACOS_OPERATION, arg) + public override fun atan(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.ATAN_OPERATION, arg) + + public override fun power(arg: Expression<T>, pow: Number): Expression<T> = + binaryOperation(PowerOperations.POW_OPERATION, arg, number(pow)) + + public override fun exp(arg: Expression<T>): Expression<T> = unaryOperation(ExponentialOperations.EXP_OPERATION, arg) + public override fun ln(arg: Expression<T>): Expression<T> = unaryOperation(ExponentialOperations.LN_OPERATION, arg) + + public override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = + super<FunctionalExpressionField>.unaryOperation(operation, arg) + + public override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = + super<FunctionalExpressionField>.binaryOperation(operation, left, right) +} + +public inline fun <T, A : Space<T>> A.expressionInSpace(block: FunctionalExpressionSpace<T, A>.() -> Expression<T>): Expression<T> = + FunctionalExpressionSpace(this).block() + +public inline fun <T, A : Ring<T>> A.expressionInRing(block: FunctionalExpressionRing<T, A>.() -> Expression<T>): Expression<T> = + FunctionalExpressionRing(this).block() + +public inline fun <T, A : Field<T>> A.expressionInField(block: FunctionalExpressionField<T, A>.() -> Expression<T>): Expression<T> = + FunctionalExpressionField(this).block() + +public inline fun <T, A : ExtendedField<T>> A.expressionInExtendedField(block: FunctionalExpressionExtendedField<T, A>.() -> Expression<T>): Expression<T> = + FunctionalExpressionExtendedField(this).block() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/expressionBuilders.kt similarity index 52% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/expressionBuilders.kt index 8090db75e..1702a5921 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/expressionBuilders.kt @@ -1,16 +1,16 @@ -package scientifik.kmath.expressions +package kscience.kmath.expressions -import scientifik.kmath.operations.ExtendedField -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +import kscience.kmath.operations.ExtendedField +import kscience.kmath.operations.Field +import kscience.kmath.operations.Ring +import kscience.kmath.operations.Space import kotlin.contracts.InvocationKind import kotlin.contracts.contract /** * Creates a functional expression with this [Space]. */ -inline fun <T> Space<T>.spaceExpression(block: FunctionalExpressionSpace<T, Space<T>>.() -> Expression<T>): Expression<T> { +public inline fun <T> Space<T>.spaceExpression(block: FunctionalExpressionSpace<T, Space<T>>.() -> Expression<T>): Expression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return FunctionalExpressionSpace(this).block() } @@ -18,7 +18,7 @@ inline fun <T> Space<T>.spaceExpression(block: FunctionalExpressionSpace<T, Spac /** * Creates a functional expression with this [Ring]. */ -inline fun <T> Ring<T>.ringExpression(block: FunctionalExpressionRing<T, Ring<T>>.() -> Expression<T>): Expression<T> { +public inline fun <T> Ring<T>.ringExpression(block: FunctionalExpressionRing<T, Ring<T>>.() -> Expression<T>): Expression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return FunctionalExpressionRing(this).block() } @@ -26,7 +26,7 @@ inline fun <T> Ring<T>.ringExpression(block: FunctionalExpressionRing<T, Ring<T> /** * Creates a functional expression with this [Field]. */ -inline fun <T> Field<T>.fieldExpression(block: FunctionalExpressionField<T, Field<T>>.() -> Expression<T>): Expression<T> { +public inline fun <T> Field<T>.fieldExpression(block: FunctionalExpressionField<T, Field<T>>.() -> Expression<T>): Expression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return FunctionalExpressionField(this).block() } @@ -34,7 +34,7 @@ inline fun <T> Field<T>.fieldExpression(block: FunctionalExpressionField<T, Fiel /** * Creates a functional expression with this [ExtendedField]. */ -inline fun <T> ExtendedField<T>.extendedFieldExpression(block: FunctionalExpressionExtendedField<T, ExtendedField<T>>.() -> Expression<T>): Expression<T> { +public inline fun <T> ExtendedField<T>.extendedFieldExpression(block: FunctionalExpressionExtendedField<T, ExtendedField<T>>.() -> Expression<T>): Expression<T> { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return FunctionalExpressionExtendedField(this).block() } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt new file mode 100644 index 000000000..d51f40890 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/BufferMatrix.kt @@ -0,0 +1,113 @@ +package kscience.kmath.linear + +import kscience.kmath.operations.RealField +import kscience.kmath.operations.Ring +import kscience.kmath.structures.* + +/** + * Basic implementation of Matrix space based on [NDStructure] + */ +public class BufferMatrixContext<T : Any, R : Ring<T>>( + public override val elementContext: R, + private val bufferFactory: BufferFactory<T> +) : GenericMatrixContext<T, R> { + public override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): BufferMatrix<T> { + val buffer = bufferFactory(rows * columns) { offset -> initializer(offset / columns, offset % columns) } + return BufferMatrix(rows, columns, buffer) + } + + public override fun point(size: Int, initializer: (Int) -> T): Point<T> = bufferFactory(size, initializer) + + public companion object +} + +@Suppress("OVERRIDE_BY_INLINE") +public object RealMatrixContext : GenericMatrixContext<Double, RealField> { + public override val elementContext: RealField + get() = RealField + + public override inline fun produce( + rows: Int, + columns: Int, + initializer: (i: Int, j: Int) -> Double + ): Matrix<Double> { + val buffer = RealBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) } + return BufferMatrix(rows, columns, buffer) + } + + public override inline fun point(size: Int, initializer: (Int) -> Double): Point<Double> = + RealBuffer(size, initializer) +} + +public class BufferMatrix<T : Any>( + public override val rowNum: Int, + public override val colNum: Int, + public val buffer: Buffer<out T>, + public override val features: Set<MatrixFeature> = emptySet() +) : FeaturedMatrix<T> { + override val shape: IntArray + get() = intArrayOf(rowNum, colNum) + + init { + require(buffer.size == rowNum * colNum) { "Dimension mismatch for matrix structure" } + } + + public override fun suggestFeature(vararg features: MatrixFeature): BufferMatrix<T> = + BufferMatrix(rowNum, colNum, buffer, this.features + features) + + public override operator fun get(index: IntArray): T = get(index[0], index[1]) + public override operator fun get(i: Int, j: Int): T = buffer[i * colNum + j] + + public override fun elements(): Sequence<Pair<IntArray, T>> = sequence { + for (i in 0 until rowNum) for (j in 0 until colNum) yield(intArrayOf(i, j) to get(i, j)) + } + + public override fun equals(other: Any?): Boolean { + if (this === other) return true + + return when (other) { + is NDStructure<*> -> return NDStructure.equals(this, other) + else -> false + } + } + + public override fun hashCode(): Int { + var result = buffer.hashCode() + result = 31 * result + features.hashCode() + return result + } + + public override fun toString(): String { + return if (rowNum <= 5 && colNum <= 5) + "Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)\n" + + rows.asSequence().joinToString(prefix = "(", postfix = ")", separator = "\n ") { buffer -> + buffer.asSequence().joinToString(separator = "\t") { it.toString() } + } + else "Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)" + } +} + +/** + * Optimized dot product for real matrices + */ +public infix fun BufferMatrix<Double>.dot(other: BufferMatrix<Double>): BufferMatrix<Double> { + require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" } + val array = DoubleArray(this.rowNum * other.colNum) + + //convert to array to insure there is not memory indirection + fun Buffer<out Double>.unsafeArray() = if (this is RealBuffer) + array + else + DoubleArray(size) { get(it) } + + val a = this.buffer.unsafeArray() + val b = other.buffer.unsafeArray() + + for (i in (0 until rowNum)) + for (j in (0 until other.colNum)) + for (k in (0 until colNum)) + array[i * other.colNum + j] += a[i * colNum + k] * b[k * other.colNum + j] + + val buffer = RealBuffer(array) + return BufferMatrix(rowNum, other.colNum, buffer) +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/FeaturedMatrix.kt similarity index 50% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/linear/FeaturedMatrix.kt index 7237739fb..65dc8df76 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/FeaturedMatrix.kt @@ -1,20 +1,17 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.operations.Ring -import scientifik.kmath.structures.Matrix -import scientifik.kmath.structures.Structure2D -import scientifik.kmath.structures.asBuffer -import kotlin.contracts.contract +import kscience.kmath.operations.Ring +import kscience.kmath.structures.Matrix +import kscience.kmath.structures.Structure2D +import kscience.kmath.structures.asBuffer import kotlin.math.sqrt /** * A 2d structure plus optional matrix-specific features */ -interface FeaturedMatrix<T : Any> : Matrix<T> { - +public interface FeaturedMatrix<T : Any> : Matrix<T> { override val shape: IntArray get() = intArrayOf(rowNum, colNum) - - val features: Set<MatrixFeature> + public val features: Set<MatrixFeature> /** * Suggest new feature for this matrix. The result is the new matrix that may or may not reuse existing data structure. @@ -22,44 +19,42 @@ interface FeaturedMatrix<T : Any> : Matrix<T> { * The implementation does not guarantee to check that matrix actually have the feature, so one should be careful to * add only those features that are valid. */ - fun suggestFeature(vararg features: MatrixFeature): FeaturedMatrix<T> + public fun suggestFeature(vararg features: MatrixFeature): FeaturedMatrix<T> - companion object + public companion object } -inline fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double): Matrix<Double> { - contract { callsInPlace(initializer) } - return MatrixContext.real.produce(rows, columns, initializer) -} +public inline fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double): Matrix<Double> = + MatrixContext.real.produce(rows, columns, initializer) /** * Build a square matrix from given elements. */ -fun <T : Any> Structure2D.Companion.square(vararg elements: T): FeaturedMatrix<T> { +public fun <T : Any> Structure2D.Companion.square(vararg elements: T): FeaturedMatrix<T> { val size: Int = sqrt(elements.size.toDouble()).toInt() require(size * size == elements.size) { "The number of elements ${elements.size} is not a full square" } val buffer = elements.asBuffer() return BufferMatrix(size, size, buffer) } -val Matrix<*>.features: Set<MatrixFeature> get() = (this as? FeaturedMatrix)?.features ?: emptySet() +public val Matrix<*>.features: Set<MatrixFeature> get() = (this as? FeaturedMatrix)?.features ?: emptySet() /** * Check if matrix has the given feature class */ -inline fun <reified T : Any> Matrix<*>.hasFeature(): Boolean = +public inline fun <reified T : Any> Matrix<*>.hasFeature(): Boolean = features.find { it is T } != null /** * Get the first feature matching given class. Does not guarantee that matrix has only one feature matching the criteria */ -inline fun <reified T : Any> Matrix<*>.getFeature(): T? = +public inline fun <reified T : Any> Matrix<*>.getFeature(): T? = features.filterIsInstance<T>().firstOrNull() /** * Diagonal matrix of ones. The matrix is virtual no actual matrix is created */ -fun <T : Any, R : Ring<T>> GenericMatrixContext<T, R>.one(rows: Int, columns: Int): FeaturedMatrix<T> = +public fun <T : Any, R : Ring<T>> GenericMatrixContext<T, R>.one(rows: Int, columns: Int): FeaturedMatrix<T> = VirtualMatrix(rows, columns, DiagonalFeature) { i, j -> if (i == j) elementContext.one else elementContext.zero } @@ -68,20 +63,20 @@ fun <T : Any, R : Ring<T>> GenericMatrixContext<T, R>.one(rows: Int, columns: In /** * A virtual matrix of zeroes */ -fun <T : Any, R : Ring<T>> GenericMatrixContext<T, R>.zero(rows: Int, columns: Int): FeaturedMatrix<T> = +public fun <T : Any, R : Ring<T>> GenericMatrixContext<T, R>.zero(rows: Int, columns: Int): FeaturedMatrix<T> = VirtualMatrix(rows, columns) { _, _ -> elementContext.zero } -class TransposedFeature<T : Any>(val original: Matrix<T>) : MatrixFeature +public class TransposedFeature<T : Any>(public val original: Matrix<T>) : MatrixFeature /** * Create a virtual transposed matrix without copying anything. `A.transpose().transpose() === A` */ -fun <T : Any> Matrix<T>.transpose(): Matrix<T> { - return this.getFeature<TransposedFeature<T>>()?.original ?: VirtualMatrix( - this.colNum, - this.rowNum, +public fun <T : Any> Matrix<T>.transpose(): Matrix<T> { + return getFeature<TransposedFeature<T>>()?.original ?: VirtualMatrix( + colNum, + rowNum, setOf(TransposedFeature(this)) ) { i, j -> get(j, i) } } -infix fun Matrix<Double>.dot(other: Matrix<Double>): Matrix<Double> = with(MatrixContext.real) { dot(other) } +public infix fun Matrix<Double>.dot(other: Matrix<Double>): Matrix<Double> = with(MatrixContext.real) { dot(other) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LUPDecomposition.kt similarity index 78% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LUPDecomposition.kt index f3e4f648f..bb80bcafd 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LUPDecomposition.kt @@ -1,25 +1,25 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.invoke -import scientifik.kmath.structures.BufferAccessor2D -import scientifik.kmath.structures.Matrix -import scientifik.kmath.structures.Structure2D +import kscience.kmath.operations.Field +import kscience.kmath.operations.RealField +import kscience.kmath.operations.Ring +import kscience.kmath.operations.invoke +import kscience.kmath.structures.BufferAccessor2D +import kscience.kmath.structures.Matrix +import kscience.kmath.structures.Structure2D import kotlin.reflect.KClass /** * Common implementation of [LUPDecompositionFeature] */ -class LUPDecomposition<T : Any>( - val context: GenericMatrixContext<T, out Field<T>>, - val lu: Structure2D<T>, - val pivot: IntArray, +public class LUPDecomposition<T : Any>( + public val context: GenericMatrixContext<T, out Field<T>>, + public val lu: Structure2D<T>, + public val pivot: IntArray, private val even: Boolean ) : LUPDecompositionFeature<T>, DeterminantFeature<T> { - - val elementContext: Field<T> get() = context.elementContext + public val elementContext: Field<T> + get() = context.elementContext /** * Returns the matrix L of the decomposition. @@ -44,7 +44,6 @@ class LUPDecomposition<T : Any>( if (j >= i) lu[i, j] else elementContext.zero } - /** * Returns the P rows permutation matrix. * @@ -55,7 +54,6 @@ class LUPDecomposition<T : Any>( if (j == pivot[i]) elementContext.one else elementContext.zero } - /** * Return the determinant of the matrix * @return determinant of the matrix @@ -66,22 +64,18 @@ class LUPDecomposition<T : Any>( } -fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.abs(value: T): T = +public fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.abs(value: T): T = if (value > elementContext.zero) value else elementContext { -value } - /** * Create a lup decomposition of generic matrix */ -fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lup( +public inline fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lup( type: KClass<T>, matrix: Matrix<T>, checkSingular: (T) -> Boolean ): LUPDecomposition<T> { - if (matrix.rowNum != matrix.colNum) { - error("LU decomposition supports only square matrices") - } - + require(matrix.rowNum == matrix.colNum) { "LU decomposition supports only square matrices" } val m = matrix.colNum val pivot = IntArray(matrix.rowNum) @@ -154,15 +148,15 @@ fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lup( } } -inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lup( +public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.lup( matrix: Matrix<T>, - noinline checkSingular: (T) -> Boolean + checkSingular: (T) -> Boolean ): LUPDecomposition<T> = lup(T::class, matrix, checkSingular) -fun GenericMatrixContext<Double, RealField>.lup(matrix: Matrix<Double>): LUPDecomposition<Double> = +public fun GenericMatrixContext<Double, RealField>.lup(matrix: Matrix<Double>): LUPDecomposition<Double> = lup(Double::class, matrix) { it < 1e-11 } -fun <T : Any> LUPDecomposition<T>.solve(type: KClass<T>, matrix: Matrix<T>): Matrix<T> { +public fun <T : Any> LUPDecomposition<T>.solve(type: KClass<T>, matrix: Matrix<T>): Matrix<T> { require(matrix.rowNum == pivot.size) { "Matrix dimension mismatch. Expected ${pivot.size}, but got ${matrix.colNum}" } BufferAccessor2D(type, matrix.rowNum, matrix.colNum).run { @@ -207,27 +201,27 @@ fun <T : Any> LUPDecomposition<T>.solve(type: KClass<T>, matrix: Matrix<T>): Mat } } -inline fun <reified T : Any> LUPDecomposition<T>.solve(matrix: Matrix<T>): Matrix<T> = solve(T::class, matrix) +public inline fun <reified T : Any> LUPDecomposition<T>.solve(matrix: Matrix<T>): Matrix<T> = solve(T::class, matrix) /** * Solve a linear equation **a*x = b** */ -inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.solve( +public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.solve( a: Matrix<T>, b: Matrix<T>, - noinline checkSingular: (T) -> Boolean + checkSingular: (T) -> Boolean ): Matrix<T> { // Use existing decomposition if it is provided by matrix val decomposition = a.getFeature() ?: lup(T::class, a, checkSingular) return decomposition.solve(T::class, b) } -fun RealMatrixContext.solve(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> = solve(a, b) { it < 1e-11 } +public fun RealMatrixContext.solve(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> = solve(a, b) { it < 1e-11 } -inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.inverse( +public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F>.inverse( matrix: Matrix<T>, - noinline checkSingular: (T) -> Boolean + checkSingular: (T) -> Boolean ): Matrix<T> = solve(matrix, one(matrix.rowNum, matrix.colNum), checkSingular) -fun RealMatrixContext.inverse(matrix: Matrix<Double>): Matrix<Double> = +public fun RealMatrixContext.inverse(matrix: Matrix<Double>): Matrix<Double> = solve(matrix, one(matrix.rowNum, matrix.colNum)) { it < 1e-11 } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LinearAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LinearAlgebra.kt new file mode 100644 index 000000000..034decc2f --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LinearAlgebra.kt @@ -0,0 +1,27 @@ +package kscience.kmath.linear + +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.Matrix +import kscience.kmath.structures.VirtualBuffer + +public typealias Point<T> = Buffer<T> + +/** + * A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors + */ +public interface LinearSolver<T : Any> { + public fun solve(a: Matrix<T>, b: Matrix<T>): Matrix<T> + public fun solve(a: Matrix<T>, b: Point<T>): Point<T> = solve(a, b.asMatrix()).asPoint() + public fun inverse(a: Matrix<T>): Matrix<T> +} + +/** + * Convert matrix to vector if it is possible + */ +public fun <T : Any> Matrix<T>.asPoint(): Point<T> = + if (this.colNum == 1) + VirtualBuffer(rowNum) { get(it, 0) } + else + error("Can't convert matrix with more than one column to vector") + +public fun <T : Any> Point<T>.asMatrix(): VirtualMatrix<T> = VirtualMatrix(size, 1) { i, _ -> get(i) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixBuilder.kt similarity index 54% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixBuilder.kt index 390362f8c..91c1ec824 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixBuilder.kt @@ -1,12 +1,12 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.BufferFactory -import scientifik.kmath.structures.Structure2D -import scientifik.kmath.structures.asBuffer +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.BufferFactory +import kscience.kmath.structures.Structure2D +import kscience.kmath.structures.asBuffer -class MatrixBuilder(val rows: Int, val columns: Int) { - operator fun <T : Any> invoke(vararg elements: T): FeaturedMatrix<T> { +public class MatrixBuilder(public val rows: Int, public val columns: Int) { + public operator fun <T : Any> invoke(vararg elements: T): FeaturedMatrix<T> { require(rows * columns == elements.size) { "The number of elements ${elements.size} is not equal $rows * $columns" } val buffer = elements.asBuffer() return BufferMatrix(rows, columns, buffer) @@ -15,14 +15,14 @@ class MatrixBuilder(val rows: Int, val columns: Int) { //TODO add specific matrix builder functions like diagonal, etc } -fun Structure2D.Companion.build(rows: Int, columns: Int): MatrixBuilder = MatrixBuilder(rows, columns) +public fun Structure2D.Companion.build(rows: Int, columns: Int): MatrixBuilder = MatrixBuilder(rows, columns) -fun <T : Any> Structure2D.Companion.row(vararg values: T): FeaturedMatrix<T> { +public fun <T : Any> Structure2D.Companion.row(vararg values: T): FeaturedMatrix<T> { val buffer = values.asBuffer() return BufferMatrix(1, values.size, buffer) } -inline fun <reified T : Any> Structure2D.Companion.row( +public inline fun <reified T : Any> Structure2D.Companion.row( size: Int, factory: BufferFactory<T> = Buffer.Companion::auto, noinline builder: (Int) -> T @@ -31,12 +31,12 @@ inline fun <reified T : Any> Structure2D.Companion.row( return BufferMatrix(1, size, buffer) } -fun <T : Any> Structure2D.Companion.column(vararg values: T): FeaturedMatrix<T> { +public fun <T : Any> Structure2D.Companion.column(vararg values: T): FeaturedMatrix<T> { val buffer = values.asBuffer() return BufferMatrix(values.size, 1, buffer) } -inline fun <reified T : Any> Structure2D.Companion.column( +public inline fun <reified T : Any> Structure2D.Companion.column( size: Int, factory: BufferFactory<T> = Buffer.Companion::auto, noinline builder: (Int) -> T diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixContext.kt similarity index 61% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixContext.kt index b7e79f1bc..f4dbce89a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixContext.kt @@ -1,24 +1,24 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.SpaceOperations -import scientifik.kmath.operations.invoke -import scientifik.kmath.operations.sum -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.BufferFactory -import scientifik.kmath.structures.Matrix -import scientifik.kmath.structures.asSequence +import kscience.kmath.operations.Ring +import kscience.kmath.operations.SpaceOperations +import kscience.kmath.operations.invoke +import kscience.kmath.operations.sum +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.BufferFactory +import kscience.kmath.structures.Matrix +import kscience.kmath.structures.asSequence /** * Basic operations on matrices. Operates on [Matrix] */ -interface MatrixContext<T : Any> : SpaceOperations<Matrix<T>> { +public interface MatrixContext<T : Any> : SpaceOperations<Matrix<T>> { /** * Produce a matrix with this context and given dimensions */ - fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): Matrix<T> + public fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): Matrix<T> - override fun binaryOperation(operation: String, left: Matrix<T>, right: Matrix<T>): Matrix<T> = when (operation) { + public override fun binaryOperation(operation: String, left: Matrix<T>, right: Matrix<T>): Matrix<T> = when (operation) { "dot" -> left dot right else -> super.binaryOperation(operation, left, right) } @@ -30,7 +30,7 @@ interface MatrixContext<T : Any> : SpaceOperations<Matrix<T>> { * @param other the multiplier. * @return the dot product. */ - infix fun Matrix<T>.dot(other: Matrix<T>): Matrix<T> + public infix fun Matrix<T>.dot(other: Matrix<T>): Matrix<T> /** * Computes the dot product of this matrix and a vector. @@ -39,7 +39,7 @@ interface MatrixContext<T : Any> : SpaceOperations<Matrix<T>> { * @param vector the multiplier. * @return the dot product. */ - infix fun Matrix<T>.dot(vector: Point<T>): Point<T> + public infix fun Matrix<T>.dot(vector: Point<T>): Point<T> /** * Multiplies a matrix by its element. @@ -48,7 +48,7 @@ interface MatrixContext<T : Any> : SpaceOperations<Matrix<T>> { * @param value the multiplier. * @receiver the product. */ - operator fun Matrix<T>.times(value: T): Matrix<T> + public operator fun Matrix<T>.times(value: T): Matrix<T> /** * Multiplies an element by a matrix of it. @@ -57,18 +57,18 @@ interface MatrixContext<T : Any> : SpaceOperations<Matrix<T>> { * @param value the multiplier. * @receiver the product. */ - operator fun T.times(m: Matrix<T>): Matrix<T> = m * this + public operator fun T.times(m: Matrix<T>): Matrix<T> = m * this - companion object { + public companion object { /** * Non-boxing double matrix */ - val real: RealMatrixContext = RealMatrixContext + public val real: RealMatrixContext = RealMatrixContext /** * A structured matrix with custom buffer */ - fun <T : Any, R : Ring<T>> buffered( + public fun <T : Any, R : Ring<T>> buffered( ring: R, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing ): GenericMatrixContext<T, R> = BufferMatrixContext(ring, bufferFactory) @@ -76,23 +76,23 @@ interface MatrixContext<T : Any> : SpaceOperations<Matrix<T>> { /** * Automatic buffered matrix, unboxed if it is possible */ - inline fun <reified T : Any, R : Ring<T>> auto(ring: R): GenericMatrixContext<T, R> = + public inline fun <reified T : Any, R : Ring<T>> auto(ring: R): GenericMatrixContext<T, R> = buffered(ring, Buffer.Companion::auto) } } -interface GenericMatrixContext<T : Any, R : Ring<T>> : MatrixContext<T> { +public interface GenericMatrixContext<T : Any, R : Ring<T>> : MatrixContext<T> { /** * The ring context for matrix elements */ - val elementContext: R + public val elementContext: R /** * Produce a point compatible with matrix space */ - fun point(size: Int, initializer: (Int) -> T): Point<T> + public fun point(size: Int, initializer: (Int) -> T): Point<T> - override infix fun Matrix<T>.dot(other: Matrix<T>): Matrix<T> { + public override infix fun Matrix<T>.dot(other: Matrix<T>): Matrix<T> { //TODO add typed error require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" } @@ -103,7 +103,7 @@ interface GenericMatrixContext<T : Any, R : Ring<T>> : MatrixContext<T> { } } - override infix fun Matrix<T>.dot(vector: Point<T>): Point<T> { + public override infix fun Matrix<T>.dot(vector: Point<T>): Point<T> { //TODO add typed error require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" } @@ -113,10 +113,10 @@ interface GenericMatrixContext<T : Any, R : Ring<T>> : MatrixContext<T> { } } - override operator fun Matrix<T>.unaryMinus(): Matrix<T> = + public override operator fun Matrix<T>.unaryMinus(): Matrix<T> = produce(rowNum, colNum) { i, j -> elementContext { -get(i, j) } } - override fun add(a: Matrix<T>, b: Matrix<T>): Matrix<T> { + public override fun add(a: Matrix<T>, b: Matrix<T>): Matrix<T> { require(a.rowNum == b.rowNum && a.colNum == b.colNum) { "Matrix operation dimension mismatch. [${a.rowNum},${a.colNum}] + [${b.rowNum},${b.colNum}]" } @@ -124,7 +124,7 @@ interface GenericMatrixContext<T : Any, R : Ring<T>> : MatrixContext<T> { return produce(a.rowNum, a.colNum) { i, j -> elementContext { a[i, j] + b[i, j] } } } - override operator fun Matrix<T>.minus(b: Matrix<T>): Matrix<T> { + public override operator fun Matrix<T>.minus(b: Matrix<T>): Matrix<T> { require(rowNum == b.rowNum && colNum == b.colNum) { "Matrix operation dimension mismatch. [$rowNum,$colNum] - [${b.rowNum},${b.colNum}]" } @@ -132,11 +132,11 @@ interface GenericMatrixContext<T : Any, R : Ring<T>> : MatrixContext<T> { return produce(rowNum, colNum) { i, j -> elementContext { get(i, j) + b[i, j] } } } - override fun multiply(a: Matrix<T>, k: Number): Matrix<T> = + public override fun multiply(a: Matrix<T>, k: Number): Matrix<T> = produce(a.rowNum, a.colNum) { i, j -> elementContext { a[i, j] * k } } - operator fun Number.times(matrix: FeaturedMatrix<T>): Matrix<T> = matrix * this + public operator fun Number.times(matrix: FeaturedMatrix<T>): Matrix<T> = matrix * this - override operator fun Matrix<T>.times(value: T): Matrix<T> = + public override operator fun Matrix<T>.times(value: T): Matrix<T> = produce(rowNum, colNum) { i, j -> elementContext { get(i, j) * value } } } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt new file mode 100644 index 000000000..a82032e50 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt @@ -0,0 +1,62 @@ +package kscience.kmath.linear + +/** + * A marker interface representing some matrix feature like diagonal, sparse, zero, etc. Features used to optimize matrix + * operations performance in some cases. + */ +public interface MatrixFeature + +/** + * The matrix with this feature is considered to have only diagonal non-null elements + */ +public object DiagonalFeature : MatrixFeature + +/** + * Matrix with this feature has all zero elements + */ +public object ZeroFeature : MatrixFeature + +/** + * Matrix with this feature have unit elements on diagonal and zero elements in all other places + */ +public object UnitFeature : MatrixFeature + +/** + * Inverted matrix feature + */ +public interface InverseMatrixFeature<T : Any> : MatrixFeature { + public val inverse: FeaturedMatrix<T> +} + +/** + * A determinant container + */ +public interface DeterminantFeature<T : Any> : MatrixFeature { + public val determinant: T +} + +@Suppress("FunctionName") +public fun <T : Any> DeterminantFeature(determinant: T): DeterminantFeature<T> = object : DeterminantFeature<T> { + override val determinant: T = determinant +} + +/** + * Lower triangular matrix + */ +public object LFeature : MatrixFeature + +/** + * Upper triangular feature + */ +public object UFeature : MatrixFeature + +/** + * TODO add documentation + */ +public interface LUPDecompositionFeature<T : Any> : MatrixFeature { + public val l: FeaturedMatrix<T> + public val u: FeaturedMatrix<T> + public val p: FeaturedMatrix<T> +} + +//TODO add sparse matrix feature diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VectorSpace.kt similarity index 65% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VectorSpace.kt index 82e5c7ef6..67056c6b2 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VectorSpace.kt @@ -1,21 +1,21 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.invoke -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.BufferFactory +import kscience.kmath.operations.RealField +import kscience.kmath.operations.Space +import kscience.kmath.operations.invoke +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.BufferFactory /** * A linear space for vectors. * Could be used on any point-like structure */ -interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> { - val size: Int - val space: S +public interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> { + public val size: Int + public val space: S override val zero: Point<T> get() = produce { space.zero } - fun produce(initializer: (Int) -> T): Point<T> + public fun produce(initializer: (Int) -> T): Point<T> /** * Produce a space-element of this vector space for expressions @@ -28,13 +28,13 @@ interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> { //TODO add basis - companion object { + public companion object { private val realSpaceCache: MutableMap<Int, BufferVectorSpace<Double, RealField>> = hashMapOf() /** * Non-boxing double vector space */ - fun real(size: Int): BufferVectorSpace<Double, RealField> = realSpaceCache.getOrPut(size) { + public fun real(size: Int): BufferVectorSpace<Double, RealField> = realSpaceCache.getOrPut(size) { BufferVectorSpace( size, RealField, @@ -45,7 +45,7 @@ interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> { /** * A structured vector space with custom buffer */ - fun <T : Any, S : Space<T>> buffered( + public fun <T : Any, S : Space<T>> buffered( size: Int, space: S, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing @@ -54,16 +54,16 @@ interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> { /** * Automatic buffered vector, unboxed if it is possible */ - inline fun <reified T : Any, S : Space<T>> auto(size: Int, space: S): VectorSpace<T, S> = + public inline fun <reified T : Any, S : Space<T>> auto(size: Int, space: S): VectorSpace<T, S> = buffered(size, space, Buffer.Companion::auto) } } -class BufferVectorSpace<T : Any, S : Space<T>>( +public class BufferVectorSpace<T : Any, S : Space<T>>( override val size: Int, override val space: S, - val bufferFactory: BufferFactory<T> + public val bufferFactory: BufferFactory<T> ) : VectorSpace<T, S> { override fun produce(initializer: (Int) -> T): Buffer<T> = bufferFactory(size, initializer) //override fun produceElement(initializer: (Int) -> T): Vector<T, S> = BufferVector(this, produce(initializer)) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VirtualMatrix.kt similarity index 70% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VirtualMatrix.kt index 5266dc884..e0a1d0026 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VirtualMatrix.kt @@ -1,15 +1,19 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.structures.Matrix +import kscience.kmath.structures.Matrix -class VirtualMatrix<T : Any>( +public class VirtualMatrix<T : Any>( override val rowNum: Int, override val colNum: Int, override val features: Set<MatrixFeature> = emptySet(), - val generator: (i: Int, j: Int) -> T + public val generator: (i: Int, j: Int) -> T ) : FeaturedMatrix<T> { - - constructor(rowNum: Int, colNum: Int, vararg features: MatrixFeature, generator: (i: Int, j: Int) -> T) : this( + public constructor( + rowNum: Int, + colNum: Int, + vararg features: MatrixFeature, + generator: (i: Int, j: Int) -> T + ) : this( rowNum, colNum, setOf(*features), @@ -42,18 +46,15 @@ class VirtualMatrix<T : Any>( } - companion object { + public companion object { /** * Wrap a matrix adding additional features to it */ - fun <T : Any> wrap(matrix: Matrix<T>, vararg features: MatrixFeature): FeaturedMatrix<T> { - return if (matrix is VirtualMatrix) { + public fun <T : Any> wrap(matrix: Matrix<T>, vararg features: MatrixFeature): FeaturedMatrix<T> { + return if (matrix is VirtualMatrix) VirtualMatrix(matrix.rowNum, matrix.colNum, matrix.features + features, matrix.generator) - } else { - VirtualMatrix(matrix.rowNum, matrix.colNum, matrix.features + features) { i, j -> - matrix[i, j] - } - } + else + VirtualMatrix(matrix.rowNum, matrix.colNum, matrix.features + features) { i, j -> matrix[i, j] } } } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/AutoDiff.kt similarity index 58% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/misc/AutoDiff.kt index 808312877..b4a610eb1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/AutoDiff.kt @@ -1,11 +1,11 @@ -package scientifik.kmath.misc +package kscience.kmath.misc -import scientifik.kmath.linear.Point -import scientifik.kmath.operations.ExtendedField -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.invoke -import scientifik.kmath.operations.sum -import scientifik.kmath.structures.asBuffer +import kscience.kmath.linear.Point +import kscience.kmath.operations.ExtendedField +import kscience.kmath.operations.Field +import kscience.kmath.operations.invoke +import kscience.kmath.operations.sum +import kscience.kmath.structures.asBuffer import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -18,24 +18,24 @@ import kotlin.contracts.contract * Differentiable variable with value and derivative of differentiation ([deriv]) result * with respect to this variable. */ -open class Variable<T : Any>(val value: T) +public open class Variable<T : Any>(public val value: T) -class DerivationResult<T : Any>( +public class DerivationResult<T : Any>( value: T, - val deriv: Map<Variable<T>, T>, - val context: Field<T> + public val deriv: Map<Variable<T>, T>, + public val context: Field<T> ) : Variable<T>(value) { - fun deriv(variable: Variable<T>): T = deriv[variable] ?: context.zero + public fun deriv(variable: Variable<T>): T = deriv[variable] ?: context.zero /** * compute divergence */ - fun div(): T = context { sum(deriv.values) } + public fun div(): T = context { sum(deriv.values) } /** * Compute a gradient for variables in given order */ - fun grad(vararg variables: Variable<T>): Point<T> { + public fun grad(vararg variables: Variable<T>): Point<T> { check(variables.isNotEmpty()) { "Variable order is not provided for gradient construction" } return variables.map(::deriv).asBuffer() } @@ -54,7 +54,7 @@ class DerivationResult<T : Any>( * assertEquals(9.0, x.d) // dy/dx * ``` */ -inline fun <T : Any, F : Field<T>> F.deriv(body: AutoDiffField<T, F>.() -> Variable<T>): DerivationResult<T> { +public inline fun <T : Any, F : Field<T>> F.deriv(body: AutoDiffField<T, F>.() -> Variable<T>): DerivationResult<T> { contract { callsInPlace(body, InvocationKind.EXACTLY_ONCE) } return (AutoDiffContext(this)) { @@ -65,15 +65,14 @@ inline fun <T : Any, F : Field<T>> F.deriv(body: AutoDiffField<T, F>.() -> Varia } } - -abstract class AutoDiffField<T : Any, F : Field<T>> : Field<Variable<T>> { - abstract val context: F +public abstract class AutoDiffField<T : Any, F : Field<T>> : Field<Variable<T>> { + public abstract val context: F /** * A variable accessing inner state of derivatives. * Use this function in inner builders to avoid creating additional derivative bindings */ - abstract var Variable<T>.d: T + public abstract var Variable<T>.d: T /** * Performs update of derivative after the rest of the formula in the back-pass. @@ -86,11 +85,11 @@ abstract class AutoDiffField<T : Any, F : Field<T>> : Field<Variable<T>> { * } * ``` */ - abstract fun <R> derive(value: R, block: F.(R) -> Unit): R + public abstract fun <R> derive(value: R, block: F.(R) -> Unit): R - abstract fun variable(value: T): Variable<T> + public abstract fun variable(value: T): Variable<T> - inline fun variable(block: F.() -> T): Variable<T> = variable(context.block()) + public inline fun variable(block: F.() -> T): Variable<T> = variable(context.block()) // Overloads for Double constants @@ -152,7 +151,6 @@ internal class AutoDiffContext<T : Any, F : Field<T>>(override val context: F) : // Basic math (+, -, *, /) - override fun add(a: Variable<T>, b: Variable<T>): Variable<T> = derive(variable { a.value + b.value }) { z -> a.d += z.d b.d += z.d @@ -176,35 +174,73 @@ internal class AutoDiffContext<T : Any, F : Field<T>>(override val context: F) : // Extensions for differentiation of various basic mathematical functions // x ^ 2 -fun <T : Any, F : Field<T>> AutoDiffField<T, F>.sqr(x: Variable<T>): Variable<T> = +public fun <T : Any, F : Field<T>> AutoDiffField<T, F>.sqr(x: Variable<T>): Variable<T> = derive(variable { x.value * x.value }) { z -> x.d += z.d * 2 * x.value } // x ^ 1/2 -fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.sqrt(x: Variable<T>): Variable<T> = +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.sqrt(x: Variable<T>): Variable<T> = derive(variable { sqrt(x.value) }) { z -> x.d += z.d * 0.5 / z.value } // x ^ y (const) -fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.pow(x: Variable<T>, y: Double): Variable<T> = +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.pow(x: Variable<T>, y: Double): Variable<T> = derive(variable { power(x.value, y) }) { z -> x.d += z.d * y * power(x.value, y - 1) } -fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.pow(x: Variable<T>, y: Int): Variable<T> = pow(x, y.toDouble()) +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.pow(x: Variable<T>, y: Int): Variable<T> = + pow(x, y.toDouble()) // exp(x) -fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.exp(x: Variable<T>): Variable<T> = +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.exp(x: Variable<T>): Variable<T> = derive(variable { exp(x.value) }) { z -> x.d += z.d * z.value } // ln(x) -fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.ln(x: Variable<T>): Variable<T> = +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.ln(x: Variable<T>): Variable<T> = derive(variable { ln(x.value) }) { z -> x.d += z.d / x.value } // x ^ y (any) -fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.pow(x: Variable<T>, y: Variable<T>): Variable<T> = +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.pow(x: Variable<T>, y: Variable<T>): Variable<T> = exp(y * ln(x)) // sin(x) -fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.sin(x: Variable<T>): Variable<T> = +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.sin(x: Variable<T>): Variable<T> = derive(variable { sin(x.value) }) { z -> x.d += z.d * cos(x.value) } // cos(x) -fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.cos(x: Variable<T>): Variable<T> = +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.cos(x: Variable<T>): Variable<T> = derive(variable { cos(x.value) }) { z -> x.d -= z.d * sin(x.value) } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.tan(x: Variable<T>): Variable<T> = + derive(variable { tan(x.value) }) { z -> + val c = cos(x.value) + x.d += z.d / (c * c) + } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.asin(x: Variable<T>): Variable<T> = + derive(variable { asin(x.value) }) { z -> x.d += z.d / sqrt(one - x.value * x.value) } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.acos(x: Variable<T>): Variable<T> = + derive(variable { acos(x.value) }) { z -> x.d -= z.d / sqrt(one - x.value * x.value) } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.atan(x: Variable<T>): Variable<T> = + derive(variable { atan(x.value) }) { z -> x.d += z.d / (one + x.value * x.value) } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.sinh(x: Variable<T>): Variable<T> = + derive(variable { sin(x.value) }) { z -> x.d += z.d * cosh(x.value) } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.cosh(x: Variable<T>): Variable<T> = + derive(variable { cos(x.value) }) { z -> x.d += z.d * sinh(x.value) } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.tanh(x: Variable<T>): Variable<T> = + derive(variable { tan(x.value) }) { z -> + val c = cosh(x.value) + x.d += z.d / (c * c) + } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.asinh(x: Variable<T>): Variable<T> = + derive(variable { asinh(x.value) }) { z -> x.d += z.d / sqrt(one + x.value * x.value) } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.acosh(x: Variable<T>): Variable<T> = + derive(variable { acosh(x.value) }) { z -> x.d += z.d / (sqrt((x.value - one) * (x.value + one))) } + +public fun <T : Any, F : ExtendedField<T>> AutoDiffField<T, F>.atanh(x: Variable<T>): Variable<T> = + derive(variable { atanh(x.value) }) { z -> x.d += z.d / (one - x.value * x.value) } + diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/Grids.kt similarity index 80% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/misc/Grids.kt index 1272ddd1c..4d058c366 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/Grids.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.misc +package kscience.kmath.misc import kotlin.math.abs @@ -10,17 +10,21 @@ import kotlin.math.abs * * If step is negative, the same goes from upper boundary downwards */ -fun ClosedFloatingPointRange<Double>.toSequenceWithStep(step: Double): Sequence<Double> = when { +public fun ClosedFloatingPointRange<Double>.toSequenceWithStep(step: Double): Sequence<Double> = when { step == 0.0 -> error("Zero step in double progression") + step > 0 -> sequence { var current = start + while (current <= endInclusive) { yield(current) current += step } } + else -> sequence { var current = endInclusive + while (current >= start) { yield(current) current += step @@ -31,7 +35,7 @@ fun ClosedFloatingPointRange<Double>.toSequenceWithStep(step: Double): Sequence< /** * Convert double range to sequence with the fixed number of points */ -fun ClosedFloatingPointRange<Double>.toSequenceWithPoints(numPoints: Int): Sequence<Double> { +public fun ClosedFloatingPointRange<Double>.toSequenceWithPoints(numPoints: Int): Sequence<Double> { require(numPoints > 1) { "The number of points should be more than 2" } return toSequenceWithStep(abs(endInclusive - start) / (numPoints - 1)) } @@ -40,7 +44,7 @@ fun ClosedFloatingPointRange<Double>.toSequenceWithPoints(numPoints: Int): Seque * Convert double range to array of evenly spaced doubles, where the size of array equals [numPoints] */ @Deprecated("Replace by 'toSequenceWithPoints'") -fun ClosedFloatingPointRange<Double>.toGrid(numPoints: Int): DoubleArray { +public fun ClosedFloatingPointRange<Double>.toGrid(numPoints: Int): DoubleArray { require(numPoints >= 2) { "Can't create generic grid with less than two points" } return DoubleArray(numPoints) { i -> start + (endInclusive - start) / (numPoints - 1) * i } } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/cumulative.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/cumulative.kt new file mode 100644 index 000000000..72d2f2388 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/cumulative.kt @@ -0,0 +1,74 @@ +package kscience.kmath.misc + +import kscience.kmath.operations.Space +import kscience.kmath.operations.invoke +import kotlin.jvm.JvmName + +/** + * Generic cumulative operation on iterator. + * + * @param T the type of initial iterable. + * @param R the type of resulting iterable. + * @param initial lazy evaluated. + */ +public inline fun <T, R> Iterator<T>.cumulative(initial: R, crossinline operation: (R, T) -> R): Iterator<R> = + object : Iterator<R> { + var state: R = initial + + override fun hasNext(): Boolean = this@cumulative.hasNext() + + override fun next(): R { + state = operation(state, this@cumulative.next()) + return state + } + } + +public inline fun <T, R> Iterable<T>.cumulative(initial: R, crossinline operation: (R, T) -> R): Iterable<R> = + Iterable { this@cumulative.iterator().cumulative(initial, operation) } + +public inline fun <T, R> Sequence<T>.cumulative(initial: R, crossinline operation: (R, T) -> R): Sequence<R> = + Sequence { this@cumulative.iterator().cumulative(initial, operation) } + +public fun <T, R> List<T>.cumulative(initial: R, operation: (R, T) -> R): List<R> = + iterator().cumulative(initial, operation).asSequence().toList() + +//Cumulative sum + +/** + * Cumulative sum with custom space + */ +public fun <T> Iterable<T>.cumulativeSum(space: Space<T>): Iterable<T> = + space { cumulative(zero) { element: T, sum: T -> sum + element } } + +@JvmName("cumulativeSumOfDouble") +public fun Iterable<Double>.cumulativeSum(): Iterable<Double> = cumulative(0.0) { element, sum -> sum + element } + +@JvmName("cumulativeSumOfInt") +public fun Iterable<Int>.cumulativeSum(): Iterable<Int> = cumulative(0) { element, sum -> sum + element } + +@JvmName("cumulativeSumOfLong") +public fun Iterable<Long>.cumulativeSum(): Iterable<Long> = cumulative(0L) { element, sum -> sum + element } + +public fun <T> Sequence<T>.cumulativeSum(space: Space<T>): Sequence<T> = + space { cumulative(zero) { element: T, sum: T -> sum + element } } + +@JvmName("cumulativeSumOfDouble") +public fun Sequence<Double>.cumulativeSum(): Sequence<Double> = cumulative(0.0) { element, sum -> sum + element } + +@JvmName("cumulativeSumOfInt") +public fun Sequence<Int>.cumulativeSum(): Sequence<Int> = cumulative(0) { element, sum -> sum + element } + +@JvmName("cumulativeSumOfLong") +public fun Sequence<Long>.cumulativeSum(): Sequence<Long> = cumulative(0L) { element, sum -> sum + element } + +public fun <T> List<T>.cumulativeSum(space: Space<T>): List<T> = + space { cumulative(zero) { element: T, sum: T -> sum + element } } + +@JvmName("cumulativeSumOfDouble") +public fun List<Double>.cumulativeSum(): List<Double> = cumulative(0.0) { element, sum -> sum + element } + +@JvmName("cumulativeSumOfInt") +public fun List<Int>.cumulativeSum(): List<Int> = cumulative(0) { element, sum -> sum + element } + +@JvmName("cumulativeSumOfLong") +public fun List<Long>.cumulativeSum(): List<Long> = cumulative(0L) { element, sum -> sum + element } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt similarity index 74% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt index f18bde597..12a45615a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt @@ -1,31 +1,31 @@ -package scientifik.kmath.operations +package kscience.kmath.operations /** * Stub for DSL the [Algebra] is. */ @DslMarker -annotation class KMathContext +public annotation class KMathContext /** * Represents an algebraic structure. * * @param T the type of element of this structure. */ -interface Algebra<T> { +public interface Algebra<T> { /** * Wrap raw string or variable */ - fun symbol(value: String): T = error("Wrapping of '$value' is not supported in $this") + public fun symbol(value: String): T = error("Wrapping of '$value' is not supported in $this") /** * Dynamic call of unary operation with name [operation] on [arg] */ - fun unaryOperation(operation: String, arg: T): T + public fun unaryOperation(operation: String, arg: T): T /** * Dynamic call of binary operation [operation] on [left] and [right] */ - fun binaryOperation(operation: String, left: T, right: T): T + public fun binaryOperation(operation: String, left: T, right: T): T } /** @@ -33,29 +33,30 @@ interface Algebra<T> { * * @param T the type of element of this structure. */ -interface NumericAlgebra<T> : Algebra<T> { +public interface NumericAlgebra<T> : Algebra<T> { /** * Wraps a number. */ - fun number(value: Number): T + public fun number(value: Number): T /** * Dynamic call of binary operation [operation] on [left] and [right] where left element is [Number]. */ - fun leftSideNumberOperation(operation: String, left: Number, right: T): T = + public fun leftSideNumberOperation(operation: String, left: Number, right: T): T = binaryOperation(operation, number(left), right) /** * Dynamic call of binary operation [operation] on [left] and [right] where right element is [Number]. */ - fun rightSideNumberOperation(operation: String, left: T, right: Number): T = + public fun rightSideNumberOperation(operation: String, left: T, right: Number): T = leftSideNumberOperation(operation, right, left) } /** * Call a block with an [Algebra] as receiver. */ -inline operator fun <A : Algebra<*>, R> A.invoke(block: A.() -> R): R = run(block) +// TODO add contract when KT-32313 is fixed +public inline operator fun <A : Algebra<*>, R> A.invoke(block: A.() -> R): R = block() /** * Represents "semispace", i.e. algebraic structure with associative binary operation called "addition" as well as @@ -63,7 +64,7 @@ inline operator fun <A : Algebra<*>, R> A.invoke(block: A.() -> R): R = run(bloc * * @param T the type of element of this semispace. */ -interface SpaceOperations<T> : Algebra<T> { +public interface SpaceOperations<T> : Algebra<T> { /** * Addition of two elements. * @@ -71,7 +72,7 @@ interface SpaceOperations<T> : Algebra<T> { * @param b the augend. * @return the sum. */ - fun add(a: T, b: T): T + public fun add(a: T, b: T): T /** * Multiplication of element by scalar. @@ -80,7 +81,7 @@ interface SpaceOperations<T> : Algebra<T> { * @param k the multiplicand. * @return the produce. */ - fun multiply(a: T, k: Number): T + public fun multiply(a: T, k: Number): T // Operations to be performed in this context. Could be moved to extensions in case of KEEP-176 @@ -90,7 +91,7 @@ interface SpaceOperations<T> : Algebra<T> { * @receiver this value. * @return the additive inverse of this value. */ - operator fun T.unaryMinus(): T = multiply(this, -1.0) + public operator fun T.unaryMinus(): T = multiply(this, -1.0) /** * Returns this value. @@ -98,7 +99,7 @@ interface SpaceOperations<T> : Algebra<T> { * @receiver this value. * @return this value. */ - operator fun T.unaryPlus(): T = this + public operator fun T.unaryPlus(): T = this /** * Addition of two elements. @@ -107,7 +108,7 @@ interface SpaceOperations<T> : Algebra<T> { * @param b the augend. * @return the sum. */ - operator fun T.plus(b: T): T = add(this, b) + public operator fun T.plus(b: T): T = add(this, b) /** * Subtraction of two elements. @@ -116,7 +117,7 @@ interface SpaceOperations<T> : Algebra<T> { * @param b the subtrahend. * @return the difference. */ - operator fun T.minus(b: T): T = add(this, -b) + public operator fun T.minus(b: T): T = add(this, -b) /** * Multiplication of this element by a scalar. @@ -125,7 +126,7 @@ interface SpaceOperations<T> : Algebra<T> { * @param k the multiplicand. * @return the product. */ - operator fun T.times(k: Number): T = multiply(this, k.toDouble()) + public operator fun T.times(k: Number): T = multiply(this, k.toDouble()) /** * Division of this element by scalar. @@ -134,7 +135,7 @@ interface SpaceOperations<T> : Algebra<T> { * @param k the divisor. * @return the quotient. */ - operator fun T.div(k: Number): T = multiply(this, 1.0 / k.toDouble()) + public operator fun T.div(k: Number): T = multiply(this, 1.0 / k.toDouble()) /** * Multiplication of this number by element. @@ -143,7 +144,7 @@ interface SpaceOperations<T> : Algebra<T> { * @param b the multiplicand. * @return the product. */ - operator fun Number.times(b: T): T = b * this + public operator fun Number.times(b: T): T = b * this override fun unaryOperation(operation: String, arg: T): T = when (operation) { PLUS_OPERATION -> arg @@ -157,18 +158,16 @@ interface SpaceOperations<T> : Algebra<T> { else -> error("Binary operation $operation not defined in $this") } - companion object { + public companion object { /** * The identifier of addition. */ - const val PLUS_OPERATION: String = "+" + public const val PLUS_OPERATION: String = "+" /** * The identifier of subtraction (and negation). */ - const val MINUS_OPERATION: String = "-" - - const val NOT_OPERATION: String = "!" + public const val MINUS_OPERATION: String = "-" } } @@ -178,11 +177,11 @@ interface SpaceOperations<T> : Algebra<T> { * * @param T the type of element of this group. */ -interface Space<T> : SpaceOperations<T> { +public interface Space<T> : SpaceOperations<T> { /** * The neutral element of addition. */ - val zero: T + public val zero: T } /** @@ -191,14 +190,14 @@ interface Space<T> : SpaceOperations<T> { * * @param T the type of element of this semiring. */ -interface RingOperations<T> : SpaceOperations<T> { +public interface RingOperations<T> : SpaceOperations<T> { /** * Multiplies two elements. * * @param a the multiplier. * @param b the multiplicand. */ - fun multiply(a: T, b: T): T + public fun multiply(a: T, b: T): T /** * Multiplies this element by scalar. @@ -206,18 +205,18 @@ interface RingOperations<T> : SpaceOperations<T> { * @receiver the multiplier. * @param b the multiplicand. */ - operator fun T.times(b: T): T = multiply(this, b) + public operator fun T.times(b: T): T = multiply(this, b) override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { TIMES_OPERATION -> multiply(left, right) else -> super.binaryOperation(operation, left, right) } - companion object { + public companion object { /** * The identifier of multiplication. */ - const val TIMES_OPERATION: String = "*" + public const val TIMES_OPERATION: String = "*" } } @@ -227,11 +226,11 @@ interface RingOperations<T> : SpaceOperations<T> { * * @param T the type of element of this ring. */ -interface Ring<T> : Space<T>, RingOperations<T>, NumericAlgebra<T> { +public interface Ring<T> : Space<T>, RingOperations<T>, NumericAlgebra<T> { /** * neutral operation for multiplication */ - val one: T + public val one: T override fun number(value: Number): T = one * value.toDouble() @@ -255,7 +254,7 @@ interface Ring<T> : Space<T>, RingOperations<T>, NumericAlgebra<T> { * @receiver the addend. * @param b the augend. */ - operator fun T.plus(b: Number): T = this + number(b) + public operator fun T.plus(b: Number): T = this + number(b) /** * Addition of scalar and element. @@ -263,7 +262,7 @@ interface Ring<T> : Space<T>, RingOperations<T>, NumericAlgebra<T> { * @receiver the addend. * @param b the augend. */ - operator fun Number.plus(b: T): T = b + this + public operator fun Number.plus(b: T): T = b + this /** * Subtraction of element from number. @@ -272,7 +271,7 @@ interface Ring<T> : Space<T>, RingOperations<T>, NumericAlgebra<T> { * @param b the subtrahend. * @receiver the difference. */ - operator fun T.minus(b: Number): T = this - number(b) + public operator fun T.minus(b: Number): T = this - number(b) /** * Subtraction of number from element. @@ -281,7 +280,7 @@ interface Ring<T> : Space<T>, RingOperations<T>, NumericAlgebra<T> { * @param b the subtrahend. * @receiver the difference. */ - operator fun Number.minus(b: T): T = -b + this + public operator fun Number.minus(b: T): T = -b + this } /** @@ -290,7 +289,7 @@ interface Ring<T> : Space<T>, RingOperations<T>, NumericAlgebra<T> { * * @param T the type of element of this semifield. */ -interface FieldOperations<T> : RingOperations<T> { +public interface FieldOperations<T> : RingOperations<T> { /** * Division of two elements. * @@ -298,7 +297,7 @@ interface FieldOperations<T> : RingOperations<T> { * @param b the divisor. * @return the quotient. */ - fun divide(a: T, b: T): T + public fun divide(a: T, b: T): T /** * Division of two elements. @@ -307,18 +306,18 @@ interface FieldOperations<T> : RingOperations<T> { * @param b the divisor. * @return the quotient. */ - operator fun T.div(b: T): T = divide(this, b) + public operator fun T.div(b: T): T = divide(this, b) override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { DIV_OPERATION -> divide(left, right) else -> super.binaryOperation(operation, left, right) } - companion object { + public companion object { /** * The identifier of division. */ - const val DIV_OPERATION: String = "/" + public const val DIV_OPERATION: String = "/" } } @@ -328,7 +327,7 @@ interface FieldOperations<T> : RingOperations<T> { * * @param T the type of element of this semifield. */ -interface Field<T> : Ring<T>, FieldOperations<T> { +public interface Field<T> : Ring<T>, FieldOperations<T> { /** * Division of element by scalar. * @@ -336,5 +335,5 @@ interface Field<T> : Ring<T>, FieldOperations<T> { * @param b the divisor. * @return the quotient. */ - operator fun Number.div(b: T): T = this * divide(one, b) + public operator fun Number.div(b: T): T = this * divide(one, b) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/AlgebraElements.kt similarity index 64% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/operations/AlgebraElements.kt index 197897c14..94328dcb5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/AlgebraElements.kt @@ -1,15 +1,15 @@ -package scientifik.kmath.operations +package kscience.kmath.operations /** * The generic mathematics elements which is able to store its context * * @param C the type of mathematical context for this element. */ -interface MathElement<C> { +public interface MathElement<C> { /** * The context this element belongs to. */ - val context: C + public val context: C } /** @@ -18,16 +18,16 @@ interface MathElement<C> { * @param T the type wrapped by this wrapper. * @param I the type of this wrapper. */ -interface MathWrapper<T, I> { +public interface MathWrapper<T, I> { /** * Unwraps [I] to [T]. */ - fun unwrap(): T + public fun unwrap(): T /** * Wraps [T] to [I]. */ - fun T.wrap(): I + public fun T.wrap(): I } /** @@ -37,14 +37,14 @@ interface MathWrapper<T, I> { * @param I self type of the element. Needed for static type checking. * @param S the type of space. */ -interface SpaceElement<T, I : SpaceElement<T, I, S>, S : Space<T>> : MathElement<S>, MathWrapper<T, I> { +public interface SpaceElement<T, I : SpaceElement<T, I, S>, S : Space<T>> : MathElement<S>, MathWrapper<T, I> { /** * Adds element to this one. * * @param b the augend. * @return the sum. */ - operator fun plus(b: T): I = context.add(unwrap(), b).wrap() + public operator fun plus(b: T): I = context.add(unwrap(), b).wrap() /** * Subtracts element from this one. @@ -52,7 +52,7 @@ interface SpaceElement<T, I : SpaceElement<T, I, S>, S : Space<T>> : MathElement * @param b the subtrahend. * @return the difference. */ - operator fun minus(b: T): I = context.add(unwrap(), context.multiply(b, -1.0)).wrap() + public operator fun minus(b: T): I = context.add(unwrap(), context.multiply(b, -1.0)).wrap() /** * Multiplies this element by number. @@ -60,7 +60,7 @@ interface SpaceElement<T, I : SpaceElement<T, I, S>, S : Space<T>> : MathElement * @param k the multiplicand. * @return the product. */ - operator fun times(k: Number): I = context.multiply(unwrap(), k.toDouble()).wrap() + public operator fun times(k: Number): I = context.multiply(unwrap(), k.toDouble()).wrap() /** * Divides this element by number. @@ -68,7 +68,7 @@ interface SpaceElement<T, I : SpaceElement<T, I, S>, S : Space<T>> : MathElement * @param k the divisor. * @return the quotient. */ - operator fun div(k: Number): I = context.multiply(unwrap(), 1.0 / k.toDouble()).wrap() + public operator fun div(k: Number): I = context.multiply(unwrap(), 1.0 / k.toDouble()).wrap() } /** @@ -78,14 +78,14 @@ interface SpaceElement<T, I : SpaceElement<T, I, S>, S : Space<T>> : MathElement * @param I self type of the element. Needed for static type checking. * @param R the type of space. */ -interface RingElement<T, I : RingElement<T, I, R>, R : Ring<T>> : SpaceElement<T, I, R> { +public interface RingElement<T, I : RingElement<T, I, R>, R : Ring<T>> : SpaceElement<T, I, R> { /** * Multiplies this element by another one. * * @param b the multiplicand. * @return the product. */ - operator fun times(b: T): I = context.multiply(unwrap(), b).wrap() + public operator fun times(b: T): I = context.multiply(unwrap(), b).wrap() } /** @@ -95,7 +95,7 @@ interface RingElement<T, I : RingElement<T, I, R>, R : Ring<T>> : SpaceElement<T * @param I self type of the element. Needed for static type checking. * @param F the type of field. */ -interface FieldElement<T, I : FieldElement<T, I, F>, F : Field<T>> : RingElement<T, I, F> { +public interface FieldElement<T, I : FieldElement<T, I, F>, F : Field<T>> : RingElement<T, I, F> { override val context: F /** @@ -104,5 +104,5 @@ interface FieldElement<T, I : FieldElement<T, I, F>, F : Field<T>> : RingElement * @param b the divisor. * @return the quotient. */ - operator fun div(b: T): I = context.divide(unwrap(), b).wrap() + public operator fun div(b: T): I = context.divide(unwrap(), b).wrap() } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/AlgebraExtensions.kt similarity index 72% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/operations/AlgebraExtensions.kt index 00b16dc98..657da6b4e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/AlgebraExtensions.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.operations +package kscience.kmath.operations /** * Returns the sum of all elements in the iterable in this [Space]. @@ -7,7 +7,7 @@ package scientifik.kmath.operations * @param data the iterable to sum up. * @return the sum. */ -fun <T> Space<T>.sum(data: Iterable<T>): T = data.fold(zero) { left, right -> add(left, right) } +public fun <T> Space<T>.sum(data: Iterable<T>): T = data.fold(zero) { left, right -> add(left, right) } /** * Returns the sum of all elements in the sequence in this [Space]. @@ -16,7 +16,7 @@ fun <T> Space<T>.sum(data: Iterable<T>): T = data.fold(zero) { left, right -> ad * @param data the sequence to sum up. * @return the sum. */ -fun <T> Space<T>.sum(data: Sequence<T>): T = data.fold(zero) { left, right -> add(left, right) } +public fun <T> Space<T>.sum(data: Sequence<T>): T = data.fold(zero) { left, right -> add(left, right) } /** * Returns an average value of elements in the iterable in this [Space]. @@ -24,8 +24,9 @@ fun <T> Space<T>.sum(data: Sequence<T>): T = data.fold(zero) { left, right -> ad * @receiver the algebra that provides addition and division. * @param data the iterable to find average. * @return the average value. + * @author Iaroslav Postovalov */ -fun <T> Space<T>.average(data: Iterable<T>): T = sum(data) / data.count() +public fun <T> Space<T>.average(data: Iterable<T>): T = sum(data) / data.count() /** * Returns an average value of elements in the sequence in this [Space]. @@ -33,8 +34,9 @@ fun <T> Space<T>.average(data: Iterable<T>): T = sum(data) / data.count() * @receiver the algebra that provides addition and division. * @param data the sequence to find average. * @return the average value. + * @author Iaroslav Postovalov */ -fun <T> Space<T>.average(data: Sequence<T>): T = sum(data) / data.count() +public fun <T> Space<T>.average(data: Sequence<T>): T = sum(data) / data.count() /** * Returns the sum of all elements in the iterable in provided space. @@ -43,7 +45,7 @@ fun <T> Space<T>.average(data: Sequence<T>): T = sum(data) / data.count() * @param space the algebra that provides addition. * @return the sum. */ -fun <T> Iterable<T>.sumWith(space: Space<T>): T = space.sum(this) +public fun <T> Iterable<T>.sumWith(space: Space<T>): T = space.sum(this) /** * Returns the sum of all elements in the sequence in provided space. @@ -52,7 +54,7 @@ fun <T> Iterable<T>.sumWith(space: Space<T>): T = space.sum(this) * @param space the algebra that provides addition. * @return the sum. */ -fun <T> Sequence<T>.sumWith(space: Space<T>): T = space.sum(this) +public fun <T> Sequence<T>.sumWith(space: Space<T>): T = space.sum(this) /** * Returns an average value of elements in the iterable in this [Space]. @@ -60,8 +62,9 @@ fun <T> Sequence<T>.sumWith(space: Space<T>): T = space.sum(this) * @receiver the iterable to find average. * @param space the algebra that provides addition and division. * @return the average value. + * @author Iaroslav Postovalov */ -fun <T> Iterable<T>.averageWith(space: Space<T>): T = space.average(this) +public fun <T> Iterable<T>.averageWith(space: Space<T>): T = space.average(this) /** * Returns an average value of elements in the sequence in this [Space]. @@ -69,8 +72,9 @@ fun <T> Iterable<T>.averageWith(space: Space<T>): T = space.average(this) * @receiver the sequence to find average. * @param space the algebra that provides addition and division. * @return the average value. + * @author Iaroslav Postovalov */ -fun <T> Sequence<T>.averageWith(space: Space<T>): T = space.average(this) +public fun <T> Sequence<T>.averageWith(space: Space<T>): T = space.average(this) //TODO optimized power operation @@ -82,7 +86,7 @@ fun <T> Sequence<T>.averageWith(space: Space<T>): T = space.average(this) * @param power the exponent. * @return the base raised to the power. */ -fun <T> Ring<T>.power(arg: T, power: Int): T { +public fun <T> Ring<T>.power(arg: T, power: Int): T { require(power >= 0) { "The power can't be negative." } require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." } if (power == 0) return one @@ -98,8 +102,9 @@ fun <T> Ring<T>.power(arg: T, power: Int): T { * @param arg the base. * @param power the exponent. * @return the base raised to the power. + * @author Iaroslav Postovalov */ -fun <T> Field<T>.power(arg: T, power: Int): T { +public fun <T> Field<T>.power(arg: T, power: Int): T { require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." } if (power == 0) return one if (power < 0) return one / (this as Ring<T>).power(arg, -power) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt similarity index 58% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt index f03275ed9..4590c58fc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt @@ -1,23 +1,22 @@ -package scientifik.kmath.operations +package kscience.kmath.operations -import scientifik.kmath.operations.BigInt.Companion.BASE -import scientifik.kmath.operations.BigInt.Companion.BASE_SIZE -import scientifik.kmath.structures.* -import kotlin.contracts.contract +import kscience.kmath.operations.BigInt.Companion.BASE +import kscience.kmath.operations.BigInt.Companion.BASE_SIZE +import kscience.kmath.structures.* import kotlin.math.log2 import kotlin.math.max import kotlin.math.min import kotlin.math.sign -typealias Magnitude = UIntArray -typealias TBase = ULong +public typealias Magnitude = UIntArray +public typealias TBase = ULong /** * Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger). * * @author Robert Drynkin (https://github.com/robdrynkin) and Peter Klimai (https://github.com/pklimai) */ -object BigIntField : Field<BigInt> { +public object BigIntField : Field<BigInt> { override val zero: BigInt = BigInt.ZERO override val one: BigInt = BigInt.ONE @@ -28,113 +27,92 @@ object BigIntField : Field<BigInt> { override fun multiply(a: BigInt, b: BigInt): BigInt = a.times(b) - operator fun String.unaryPlus(): BigInt = this.parseBigInteger() ?: error("Can't parse $this as big integer") + public operator fun String.unaryPlus(): BigInt = this.parseBigInteger() ?: error("Can't parse $this as big integer") - operator fun String.unaryMinus(): BigInt = + public operator fun String.unaryMinus(): BigInt = -(this.parseBigInteger() ?: error("Can't parse $this as big integer")) override fun divide(a: BigInt, b: BigInt): BigInt = a.div(b) } -class BigInt internal constructor( +public class BigInt internal constructor( private val sign: Byte, private val magnitude: Magnitude ) : Comparable<BigInt> { + public override fun compareTo(other: BigInt): Int = when { + (sign == 0.toByte()) and (other.sign == 0.toByte()) -> 0 + sign < other.sign -> -1 + sign > other.sign -> 1 + else -> sign * compareMagnitudes(magnitude, other.magnitude) + } - override fun compareTo(other: BigInt): Int { - return when { - (this.sign == 0.toByte()) and (other.sign == 0.toByte()) -> 0 - this.sign < other.sign -> -1 - this.sign > other.sign -> 1 - else -> this.sign * compareMagnitudes(this.magnitude, other.magnitude) + public override fun equals(other: Any?): Boolean = + if (other is BigInt) compareTo(other) == 0 else error("Can't compare KBigInteger to a different type") + + public override fun hashCode(): Int = magnitude.hashCode() + sign + + public fun abs(): BigInt = if (sign == 0.toByte()) this else BigInt(1, magnitude) + + public operator fun unaryMinus(): BigInt = + if (this.sign == 0.toByte()) this else BigInt((-this.sign).toByte(), this.magnitude) + + public operator fun plus(b: BigInt): BigInt = when { + b.sign == 0.toByte() -> this + sign == 0.toByte() -> b + this == -b -> ZERO + sign == b.sign -> BigInt(sign, addMagnitudes(magnitude, b.magnitude)) + + else -> { + val comp = compareMagnitudes(magnitude, b.magnitude) + + if (comp == 1) + BigInt(sign, subtractMagnitudes(magnitude, b.magnitude)) + else + BigInt((-sign).toByte(), subtractMagnitudes(b.magnitude, magnitude)) } } - override fun equals(other: Any?): Boolean { - if (other is BigInt) { - return this.compareTo(other) == 0 - } else error("Can't compare KBigInteger to a different type") - } + public operator fun minus(b: BigInt): BigInt = this + (-b) - override fun hashCode(): Int { - return magnitude.hashCode() + this.sign - } - - fun abs(): BigInt = if (sign == 0.toByte()) this else BigInt(1, magnitude) - - operator fun unaryMinus(): BigInt { - return if (this.sign == 0.toByte()) this else BigInt((-this.sign).toByte(), this.magnitude) - } - - operator fun plus(b: BigInt): BigInt { - return when { - b.sign == 0.toByte() -> this - this.sign == 0.toByte() -> b - this == -b -> ZERO - this.sign == b.sign -> BigInt(this.sign, addMagnitudes(this.magnitude, b.magnitude)) - else -> { - val comp: Int = compareMagnitudes(this.magnitude, b.magnitude) - - if (comp == 1) { - BigInt(this.sign, subtractMagnitudes(this.magnitude, b.magnitude)) - } else { - BigInt((-this.sign).toByte(), subtractMagnitudes(b.magnitude, this.magnitude)) - } - } - } - } - - operator fun minus(b: BigInt): BigInt { - return this + (-b) - } - - operator fun times(b: BigInt): BigInt { - return when { - this.sign == 0.toByte() -> ZERO - b.sign == 0.toByte() -> ZERO + public operator fun times(b: BigInt): BigInt = when { + this.sign == 0.toByte() -> ZERO + b.sign == 0.toByte() -> ZERO // TODO: Karatsuba - else -> BigInt((this.sign * b.sign).toByte(), multiplyMagnitudes(this.magnitude, b.magnitude)) - } + else -> BigInt((this.sign * b.sign).toByte(), multiplyMagnitudes(this.magnitude, b.magnitude)) } - operator fun times(other: UInt): BigInt { - return when { - this.sign == 0.toByte() -> ZERO - other == 0U -> ZERO - else -> BigInt(this.sign, multiplyMagnitudeByUInt(this.magnitude, other)) - } + public operator fun times(other: UInt): BigInt = when { + sign == 0.toByte() -> ZERO + other == 0U -> ZERO + else -> BigInt(sign, multiplyMagnitudeByUInt(magnitude, other)) } - operator fun times(other: Int): BigInt { - return if (other > 0) - this * kotlin.math.abs(other).toUInt() - else - -this * kotlin.math.abs(other).toUInt() - } + public operator fun times(other: Int): BigInt = if (other > 0) + this * kotlin.math.abs(other).toUInt() + else + -this * kotlin.math.abs(other).toUInt() - operator fun div(other: UInt): BigInt { - return BigInt(this.sign, divideMagnitudeByUInt(this.magnitude, other)) - } + public operator fun div(other: UInt): BigInt = BigInt(this.sign, divideMagnitudeByUInt(this.magnitude, other)) - operator fun div(other: Int): BigInt { - return BigInt( - (this.sign * other.sign).toByte(), - divideMagnitudeByUInt(this.magnitude, kotlin.math.abs(other).toUInt()) - ) - } + public operator fun div(other: Int): BigInt = BigInt( + (this.sign * other.sign).toByte(), + divideMagnitudeByUInt(this.magnitude, kotlin.math.abs(other).toUInt()) + ) private fun division(other: BigInt): Pair<BigInt, BigInt> { // Long division algorithm: // https://en.wikipedia.org/wiki/Division_algorithm#Integer_division_(unsigned)_with_remainder // TODO: Implement more effective algorithm - var q: BigInt = ZERO - var r: BigInt = ZERO + var q = ZERO + var r = ZERO val bitSize = (BASE_SIZE * (this.magnitude.size - 1) + log2(this.magnitude.lastOrNull()?.toFloat() ?: 0f + 1)).toInt() + for (i in bitSize downTo 0) { r = r shl 1 r = r or ((abs(this) shr i) and ONE) + if (r >= abs(other)) { r -= abs(other) q += (ONE shl i) @@ -144,99 +122,84 @@ class BigInt internal constructor( return Pair(BigInt((this.sign * other.sign).toByte(), q.magnitude), r) } - operator fun div(other: BigInt): BigInt { - return this.division(other).first - } + public operator fun div(other: BigInt): BigInt = division(other).first - infix fun shl(i: Int): BigInt { + public infix fun shl(i: Int): BigInt { if (this == ZERO) return ZERO if (i == 0) return this - val fullShifts = i / BASE_SIZE + 1 val relShift = i % BASE_SIZE val shiftLeft = { x: UInt -> if (relShift >= 32) 0U else x shl relShift } val shiftRight = { x: UInt -> if (BASE_SIZE - relShift >= 32) 0U else x shr (BASE_SIZE - relShift) } + val newMagnitude = Magnitude(magnitude.size + fullShifts) - val newMagnitude: Magnitude = Magnitude(this.magnitude.size + fullShifts) - - for (j in this.magnitude.indices) { + for (j in magnitude.indices) { newMagnitude[j + fullShifts - 1] = shiftLeft(this.magnitude[j]) - if (j != 0) { + + if (j != 0) newMagnitude[j + fullShifts - 1] = newMagnitude[j + fullShifts - 1] or shiftRight(this.magnitude[j - 1]) - } } - newMagnitude[this.magnitude.size + fullShifts - 1] = shiftRight(this.magnitude.last()) - + newMagnitude[magnitude.size + fullShifts - 1] = shiftRight(magnitude.last()) return BigInt(this.sign, stripLeadingZeros(newMagnitude)) } - infix fun shr(i: Int): BigInt { + public infix fun shr(i: Int): BigInt { if (this == ZERO) return ZERO if (i == 0) return this - val fullShifts = i / BASE_SIZE val relShift = i % BASE_SIZE val shiftRight = { x: UInt -> if (relShift >= 32) 0U else x shr relShift } val shiftLeft = { x: UInt -> if (BASE_SIZE - relShift >= 32) 0U else x shl (BASE_SIZE - relShift) } - if (this.magnitude.size - fullShifts <= 0) { - return ZERO - } - val newMagnitude: Magnitude = Magnitude(this.magnitude.size - fullShifts) + if (this.magnitude.size - fullShifts <= 0) return ZERO + val newMagnitude: Magnitude = Magnitude(magnitude.size - fullShifts) - for (j in fullShifts until this.magnitude.size) { - newMagnitude[j - fullShifts] = shiftRight(this.magnitude[j]) - if (j != this.magnitude.size - 1) { - newMagnitude[j - fullShifts] = newMagnitude[j - fullShifts] or shiftLeft(this.magnitude[j + 1]) - } + for (j in fullShifts until magnitude.size) { + newMagnitude[j - fullShifts] = shiftRight(magnitude[j]) + + if (j != magnitude.size - 1) + newMagnitude[j - fullShifts] = newMagnitude[j - fullShifts] or shiftLeft(magnitude[j + 1]) } return BigInt(this.sign, stripLeadingZeros(newMagnitude)) } - infix fun or(other: BigInt): BigInt { + public infix fun or(other: BigInt): BigInt { if (this == ZERO) return other if (other == ZERO) return this - val resSize = max(this.magnitude.size, other.magnitude.size) + val resSize = max(magnitude.size, other.magnitude.size) val newMagnitude: Magnitude = Magnitude(resSize) + for (i in 0 until resSize) { - if (i < this.magnitude.size) { - newMagnitude[i] = newMagnitude[i] or this.magnitude[i] - } - if (i < other.magnitude.size) { - newMagnitude[i] = newMagnitude[i] or other.magnitude[i] - } + if (i < magnitude.size) newMagnitude[i] = newMagnitude[i] or magnitude[i] + if (i < other.magnitude.size) newMagnitude[i] = newMagnitude[i] or other.magnitude[i] } + return BigInt(1, stripLeadingZeros(newMagnitude)) } - infix fun and(other: BigInt): BigInt { + public infix fun and(other: BigInt): BigInt { if ((this == ZERO) or (other == ZERO)) return ZERO val resSize = min(this.magnitude.size, other.magnitude.size) val newMagnitude: Magnitude = Magnitude(resSize) - for (i in 0 until resSize) { - newMagnitude[i] = this.magnitude[i] and other.magnitude[i] - } + for (i in 0 until resSize) newMagnitude[i] = this.magnitude[i] and other.magnitude[i] return BigInt(1, stripLeadingZeros(newMagnitude)) } - operator fun rem(other: Int): Int { + public operator fun rem(other: Int): Int { val res = this - (this / other) * other return if (res == ZERO) 0 else res.sign * res.magnitude[0].toInt() } - operator fun rem(other: BigInt): BigInt { - return this - (this / other) * other - } + public operator fun rem(other: BigInt): BigInt = this - (this / other) * other - fun modPow(exponent: BigInt, m: BigInt): BigInt { - return when { - exponent == ZERO -> ONE - exponent % 2 == 1 -> (this * modPow(exponent - ONE, m)) % m - else -> { - val sqRoot = modPow(exponent / 2, m) - (sqRoot * sqRoot) % m - } + public fun modPow(exponent: BigInt, m: BigInt): BigInt = when { + exponent == ZERO -> ONE + exponent % 2 == 1 -> (this * modPow(exponent - ONE, m)) % m + + else -> { + val sqRoot = modPow(exponent / 2, m) + (sqRoot * sqRoot) % m } } @@ -260,11 +223,11 @@ class BigInt internal constructor( return res } - companion object { - const val BASE: ULong = 0xffffffffUL - const val BASE_SIZE: Int = 32 - val ZERO: BigInt = BigInt(0, uintArrayOf()) - val ONE: BigInt = BigInt(1, uintArrayOf(1u)) + public companion object { + public const val BASE: ULong = 0xffffffffUL + public const val BASE_SIZE: Int = 32 + public val ZERO: BigInt = BigInt(0, uintArrayOf()) + public val ONE: BigInt = BigInt(1, uintArrayOf(1u)) private val hexMapping: HashMap<UInt, String> = hashMapOf( 0U to "0", 1U to "1", 2U to "2", 3U to "3", @@ -291,9 +254,9 @@ class BigInt internal constructor( } private fun addMagnitudes(mag1: Magnitude, mag2: Magnitude): Magnitude { - val resultLength: Int = max(mag1.size, mag2.size) + 1 + val resultLength = max(mag1.size, mag2.size) + 1 val result = Magnitude(resultLength) - var carry: TBase = 0UL + var carry = 0uL for (i in 0 until resultLength - 1) { val res = when { @@ -301,20 +264,22 @@ class BigInt internal constructor( i >= mag2.size -> mag1[i].toULong() + carry else -> mag1[i].toULong() + mag2[i].toULong() + carry } + result[i] = (res and BASE).toUInt() carry = (res shr BASE_SIZE) } + result[resultLength - 1] = carry.toUInt() return stripLeadingZeros(result) } private fun subtractMagnitudes(mag1: Magnitude, mag2: Magnitude): Magnitude { - val resultLength: Int = mag1.size + val resultLength = mag1.size val result = Magnitude(resultLength) var carry = 0L for (i in 0 until resultLength) { - var res: Long = + var res = if (i < mag2.size) mag1[i].toLong() - mag2[i].toLong() - carry else mag1[i].toLong() - carry @@ -328,9 +293,9 @@ class BigInt internal constructor( } private fun multiplyMagnitudeByUInt(mag: Magnitude, x: UInt): Magnitude { - val resultLength: Int = mag.size + 1 + val resultLength = mag.size + 1 val result = Magnitude(resultLength) - var carry: ULong = 0UL + var carry = 0uL for (i in mag.indices) { val cur: ULong = carry + mag[i].toULong() * x.toULong() @@ -343,16 +308,18 @@ class BigInt internal constructor( } private fun multiplyMagnitudes(mag1: Magnitude, mag2: Magnitude): Magnitude { - val resultLength: Int = mag1.size + mag2.size + val resultLength = mag1.size + mag2.size val result = Magnitude(resultLength) for (i in mag1.indices) { - var carry: ULong = 0UL + var carry = 0uL + for (j in mag2.indices) { val cur: ULong = result[i + j].toULong() + mag1[i].toULong() * mag2[j].toULong() + carry result[i + j] = (cur and BASE.toULong()).toUInt() carry = cur shr BASE_SIZE } + result[i + mag2.size] = (carry and BASE).toUInt() } @@ -360,48 +327,46 @@ class BigInt internal constructor( } private fun divideMagnitudeByUInt(mag: Magnitude, x: UInt): Magnitude { - val resultLength: Int = mag.size + val resultLength = mag.size val result = Magnitude(resultLength) - var carry: ULong = 0UL + var carry = 0uL for (i in mag.size - 1 downTo 0) { val cur: ULong = mag[i].toULong() + (carry shl BASE_SIZE) result[i] = (cur / x).toUInt() carry = cur % x } + return stripLeadingZeros(result) } - } - } - private fun stripLeadingZeros(mag: Magnitude): Magnitude { - if (mag.isEmpty() || mag.last() != 0U) { - return mag - } - var resSize: Int = mag.size - 1 + if (mag.isEmpty() || mag.last() != 0U) return mag + var resSize = mag.size - 1 + while (mag[resSize] == 0U) { - if (resSize == 0) - break + if (resSize == 0) break resSize -= 1 } + return mag.sliceArray(IntRange(0, resSize)) } -fun abs(x: BigInt): BigInt = x.abs() +public fun abs(x: BigInt): BigInt = x.abs() /** * Convert this [Int] to [BigInt] */ -fun Int.toBigInt(): BigInt = BigInt(sign.toByte(), uintArrayOf(kotlin.math.abs(this).toUInt())) +public fun Int.toBigInt(): BigInt = BigInt(sign.toByte(), uintArrayOf(kotlin.math.abs(this).toUInt())) /** * Convert this [Long] to [BigInt] */ -fun Long.toBigInt(): BigInt = BigInt( - sign.toByte(), stripLeadingZeros( +public fun Long.toBigInt(): BigInt = BigInt( + sign.toByte(), + stripLeadingZeros( uintArrayOf( (kotlin.math.abs(this).toULong() and BASE).toUInt(), ((kotlin.math.abs(this).toULong() shr BASE_SIZE) and BASE).toUInt() @@ -412,12 +377,12 @@ fun Long.toBigInt(): BigInt = BigInt( /** * Convert UInt to [BigInt] */ -fun UInt.toBigInt(): BigInt = BigInt(1, uintArrayOf(this)) +public fun UInt.toBigInt(): BigInt = BigInt(1, uintArrayOf(this)) /** * Convert ULong to [BigInt] */ -fun ULong.toBigInt(): BigInt = BigInt( +public fun ULong.toBigInt(): BigInt = BigInt( 1, stripLeadingZeros( uintArrayOf( @@ -430,12 +395,12 @@ fun ULong.toBigInt(): BigInt = BigInt( /** * Create a [BigInt] with this array of magnitudes with protective copy */ -fun UIntArray.toBigInt(sign: Byte): BigInt { +public fun UIntArray.toBigInt(sign: Byte): BigInt { require(sign != 0.toByte() || !isNotEmpty()) return BigInt(sign, copyOf()) } -val hexChToInt: MutableMap<Char, Int> = hashMapOf( +private val hexChToInt: MutableMap<Char, Int> = hashMapOf( '0' to 0, '1' to 1, '2' to 2, '3' to 3, '4' to 4, '5' to 5, '6' to 6, '7' to 7, '8' to 8, '9' to 9, 'A' to 10, 'B' to 11, @@ -445,9 +410,10 @@ val hexChToInt: MutableMap<Char, Int> = hashMapOf( /** * Returns null if a valid number can not be read from a string */ -fun String.parseBigInteger(): BigInt? { +public fun String.parseBigInteger(): BigInt? { val sign: Int val sPositive: String + when { this[0] == '+' -> { sign = +1 @@ -462,43 +428,42 @@ fun String.parseBigInteger(): BigInt? { sign = +1 } } + var res = BigInt.ZERO var digitValue = BigInt.ONE val sPositiveUpper = sPositive.toUpperCase() + if (sPositiveUpper.startsWith("0X")) { // hex representation val sHex = sPositiveUpper.substring(2) + for (ch in sHex.reversed()) { if (ch == '_') continue res += digitValue * (hexChToInt[ch] ?: return null) digitValue *= 16.toBigInt() } - } else { // decimal representation - for (ch in sPositiveUpper.reversed()) { - if (ch == '_') continue - if (ch !in '0'..'9') { - return null - } - res += digitValue * (ch.toInt() - '0'.toInt()) - digitValue *= 10.toBigInt() + } else for (ch in sPositiveUpper.reversed()) { + // decimal representation + if (ch == '_') continue + if (ch !in '0'..'9') { + return null } + res += digitValue * (ch.toInt() - '0'.toInt()) + digitValue *= 10.toBigInt() } + return res * sign } -inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): Buffer<BigInt> { - contract { callsInPlace(initializer) } - return boxing(size, initializer) -} +public inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): Buffer<BigInt> = + boxing(size, initializer) -inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer<BigInt> { - contract { callsInPlace(initializer) } - return boxing(size, initializer) -} +public inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer<BigInt> = + boxing(size, initializer) -fun NDAlgebra.Companion.bigInt(vararg shape: Int): BoxingNDRing<BigInt, BigIntField> = +public fun NDAlgebra.Companion.bigInt(vararg shape: Int): BoxingNDRing<BigInt, BigIntField> = BoxingNDRing(shape, BigIntField, Buffer.Companion::bigInt) -fun NDElement.Companion.bigInt( +public fun NDElement.Companion.bigInt( vararg shape: Int, initializer: BigIntField.(IntArray) -> BigInt ): BufferedNDRingElement<BigInt, BigIntField> = NDAlgebra.bigInt(*shape).produce(initializer) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt similarity index 73% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt index 49200b5f9..c0faf5dc5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt @@ -1,24 +1,23 @@ -package scientifik.kmath.operations +package kscience.kmath.operations -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.MemoryBuffer -import scientifik.kmath.structures.MutableBuffer -import scientifik.memory.MemoryReader -import scientifik.memory.MemorySpec -import scientifik.memory.MemoryWriter -import kotlin.contracts.contract +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.MemoryBuffer +import kscience.kmath.structures.MutableBuffer +import kscience.memory.MemoryReader +import kscience.memory.MemorySpec +import kscience.memory.MemoryWriter import kotlin.math.* /** * This complex's conjugate. */ -val Complex.conjugate: Complex +public val Complex.conjugate: Complex get() = Complex(re, -im) /** * This complex's reciprocal. */ -val Complex.reciprocal: Complex +public val Complex.reciprocal: Complex get() { val scale = re * re + im * im return Complex(re / scale, -im / scale) @@ -27,13 +26,13 @@ val Complex.reciprocal: Complex /** * Absolute value of complex number. */ -val Complex.r: Double +public val Complex.r: Double get() = sqrt(re * re + im * im) /** * An angle between vector represented by complex number and X axis. */ -val Complex.theta: Double +public val Complex.theta: Double get() = atan(im / re) private val PI_DIV_2 = Complex(PI / 2, 0) @@ -41,14 +40,14 @@ private val PI_DIV_2 = Complex(PI / 2, 0) /** * A field of [Complex]. */ -object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> { +public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> { override val zero: Complex = 0.0.toComplex() override val one: Complex = 1.0.toComplex() /** * The imaginary unit. */ - val i: Complex = Complex(0.0, 1.0) + public val i: Complex = Complex(0.0, 1.0) override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im) @@ -116,7 +115,7 @@ object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> { * @param c the augend. * @return the sum. */ - operator fun Double.plus(c: Complex): Complex = add(this.toComplex(), c) + public operator fun Double.plus(c: Complex): Complex = add(this.toComplex(), c) /** * Subtracts complex number from real one. @@ -125,7 +124,7 @@ object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> { * @param c the subtrahend. * @return the difference. */ - operator fun Double.minus(c: Complex): Complex = add(this.toComplex(), -c) + public operator fun Double.minus(c: Complex): Complex = add(this.toComplex(), -c) /** * Adds real number to complex one. @@ -134,7 +133,7 @@ object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> { * @param d the augend. * @return the sum. */ - operator fun Complex.plus(d: Double): Complex = d + this + public operator fun Complex.plus(d: Double): Complex = d + this /** * Subtracts real number from complex one. @@ -143,7 +142,7 @@ object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> { * @param d the subtrahend. * @return the difference. */ - operator fun Complex.minus(d: Double): Complex = add(this, -d.toComplex()) + public operator fun Complex.minus(d: Double): Complex = add(this, -d.toComplex()) /** * Multiplies real number by complex one. @@ -152,7 +151,7 @@ object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> { * @param c the multiplicand. * @receiver the product. */ - operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) + public operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) override fun norm(arg: Complex): Complex = sqrt(arg.conjugate * arg) @@ -165,8 +164,9 @@ object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> { * @property re The real part. * @property im The imaginary part. */ -data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Complex, ComplexField>, Comparable<Complex> { - constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble()) +public data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Complex, ComplexField>, + Comparable<Complex> { + public constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble()) override val context: ComplexField get() = ComplexField @@ -176,7 +176,7 @@ data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Compl override fun compareTo(other: Complex): Int = r.compareTo(other.r) - companion object : MemorySpec<Complex> { + public companion object : MemorySpec<Complex> { override val objectSize: Int = 16 override fun MemoryReader.read(offset: Int): Complex = @@ -195,14 +195,10 @@ data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Compl * @receiver the real part. * @return the new complex number. */ -fun Number.toComplex(): Complex = Complex(this, 0.0) +public fun Number.toComplex(): Complex = Complex(this, 0.0) -inline fun Buffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer<Complex> { - contract { callsInPlace(init) } - return MemoryBuffer.create(Complex, size, init) -} +public inline fun Buffer.Companion.complex(size: Int, init: (Int) -> Complex): Buffer<Complex> = + MemoryBuffer.create(Complex, size, init) -inline fun MutableBuffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer<Complex> { - contract { callsInPlace(init) } - return MemoryBuffer.create(Complex, size, init) -} +public inline fun MutableBuffer.Companion.complex(size: Int, init: (Int) -> Complex): Buffer<Complex> = + MemoryBuffer.create(Complex, size, init) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumberAlgebra.kt new file mode 100644 index 000000000..a2b33a0c4 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumberAlgebra.kt @@ -0,0 +1,266 @@ +package kscience.kmath.operations + +import kotlin.math.abs +import kotlin.math.pow as kpow + +/** + * Advanced Number-like semifield that implements basic operations. + */ +public interface ExtendedFieldOperations<T> : + FieldOperations<T>, + TrigonometricOperations<T>, + HyperbolicOperations<T>, + PowerOperations<T>, + ExponentialOperations<T> { + public override fun tan(arg: T): T = sin(arg) / cos(arg) + public override fun tanh(arg: T): T = sinh(arg) / cosh(arg) + + public override fun unaryOperation(operation: String, arg: T): T = when (operation) { + TrigonometricOperations.COS_OPERATION -> cos(arg) + TrigonometricOperations.SIN_OPERATION -> sin(arg) + TrigonometricOperations.TAN_OPERATION -> tan(arg) + TrigonometricOperations.ACOS_OPERATION -> acos(arg) + TrigonometricOperations.ASIN_OPERATION -> asin(arg) + TrigonometricOperations.ATAN_OPERATION -> atan(arg) + HyperbolicOperations.COSH_OPERATION -> cosh(arg) + HyperbolicOperations.SINH_OPERATION -> sinh(arg) + HyperbolicOperations.TANH_OPERATION -> tanh(arg) + HyperbolicOperations.ACOSH_OPERATION -> acosh(arg) + HyperbolicOperations.ASINH_OPERATION -> asinh(arg) + HyperbolicOperations.ATANH_OPERATION -> atanh(arg) + PowerOperations.SQRT_OPERATION -> sqrt(arg) + ExponentialOperations.EXP_OPERATION -> exp(arg) + ExponentialOperations.LN_OPERATION -> ln(arg) + else -> super.unaryOperation(operation, arg) + } +} + +/** + * Advanced Number-like field that implements basic operations. + */ +public interface ExtendedField<T> : ExtendedFieldOperations<T>, Field<T> { + public override fun sinh(arg: T): T = (exp(arg) - exp(-arg)) / 2 + public override fun cosh(arg: T): T = (exp(arg) + exp(-arg)) / 2 + public override fun tanh(arg: T): T = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) + public override fun asinh(arg: T): T = ln(sqrt(arg * arg + one) + arg) + public override fun acosh(arg: T): T = ln(arg + sqrt((arg - one) * (arg + one))) + public override fun atanh(arg: T): T = (ln(arg + one) - ln(one - arg)) / 2 + + public override fun rightSideNumberOperation(operation: String, left: T, right: Number): T = when (operation) { + PowerOperations.POW_OPERATION -> power(left, right) + else -> super.rightSideNumberOperation(operation, left, right) + } +} + +/** + * Real field element wrapping double. + * + * @property value the [Double] value wrapped by this [Real]. + * + * TODO inline does not work due to compiler bug. Waiting for fix for KT-27586 + */ +public inline class Real(public val value: Double) : FieldElement<Double, Real, RealField> { + public override val context: RealField + get() = RealField + + public override fun unwrap(): Double = value + public override fun Double.wrap(): Real = Real(value) + + public companion object +} + +/** + * A field for [Double] without boxing. Does not produce appropriate field element. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public object RealField : ExtendedField<Double>, Norm<Double, Double> { + public override val zero: Double + get() = 0.0 + + public override val one: Double + get() = 1.0 + + public override fun binaryOperation(operation: String, left: Double, right: Double): Double = when (operation) { + PowerOperations.POW_OPERATION -> left pow right + else -> super.binaryOperation(operation, left, right) + } + + public override inline fun add(a: Double, b: Double): Double = a + b + public override inline fun multiply(a: Double, k: Number): Double = a * k.toDouble() + + public override inline fun multiply(a: Double, b: Double): Double = a * b + + public override inline fun divide(a: Double, b: Double): Double = a / b + + public override inline fun sin(arg: Double): Double = kotlin.math.sin(arg) + public override inline fun cos(arg: Double): Double = kotlin.math.cos(arg) + public override inline fun tan(arg: Double): Double = kotlin.math.tan(arg) + public override inline fun acos(arg: Double): Double = kotlin.math.acos(arg) + public override inline fun asin(arg: Double): Double = kotlin.math.asin(arg) + public override inline fun atan(arg: Double): Double = kotlin.math.atan(arg) + + public override inline fun sinh(arg: Double): Double = kotlin.math.sinh(arg) + public override inline fun cosh(arg: Double): Double = kotlin.math.cosh(arg) + public override inline fun tanh(arg: Double): Double = kotlin.math.tanh(arg) + public override inline fun asinh(arg: Double): Double = kotlin.math.asinh(arg) + public override inline fun acosh(arg: Double): Double = kotlin.math.acosh(arg) + public override inline fun atanh(arg: Double): Double = kotlin.math.atanh(arg) + + public override inline fun power(arg: Double, pow: Number): Double = arg.kpow(pow.toDouble()) + public override inline fun exp(arg: Double): Double = kotlin.math.exp(arg) + public override inline fun ln(arg: Double): Double = kotlin.math.ln(arg) + + public override inline fun norm(arg: Double): Double = abs(arg) + + public override inline fun Double.unaryMinus(): Double = -this + public override inline fun Double.plus(b: Double): Double = this + b + public override inline fun Double.minus(b: Double): Double = this - b + public override inline fun Double.times(b: Double): Double = this * b + public override inline fun Double.div(b: Double): Double = this / b +} + +/** + * A field for [Float] without boxing. Does not produce appropriate field element. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public object FloatField : ExtendedField<Float>, Norm<Float, Float> { + public override val zero: Float + get() = 0.0f + + public override val one: Float + get() = 1.0f + + public override fun binaryOperation(operation: String, left: Float, right: Float): Float = when (operation) { + PowerOperations.POW_OPERATION -> left pow right + else -> super.binaryOperation(operation, left, right) + } + + public override inline fun add(a: Float, b: Float): Float = a + b + public override inline fun multiply(a: Float, k: Number): Float = a * k.toFloat() + + public override inline fun multiply(a: Float, b: Float): Float = a * b + + public override inline fun divide(a: Float, b: Float): Float = a / b + + public override inline fun sin(arg: Float): Float = kotlin.math.sin(arg) + public override inline fun cos(arg: Float): Float = kotlin.math.cos(arg) + public override inline fun tan(arg: Float): Float = kotlin.math.tan(arg) + public override inline fun acos(arg: Float): Float = kotlin.math.acos(arg) + public override inline fun asin(arg: Float): Float = kotlin.math.asin(arg) + public override inline fun atan(arg: Float): Float = kotlin.math.atan(arg) + + public override inline fun sinh(arg: Float): Float = kotlin.math.sinh(arg) + public override inline fun cosh(arg: Float): Float = kotlin.math.cosh(arg) + public override inline fun tanh(arg: Float): Float = kotlin.math.tanh(arg) + public override inline fun asinh(arg: Float): Float = kotlin.math.asinh(arg) + public override inline fun acosh(arg: Float): Float = kotlin.math.acosh(arg) + public override inline fun atanh(arg: Float): Float = kotlin.math.atanh(arg) + + public override inline fun power(arg: Float, pow: Number): Float = arg.kpow(pow.toFloat()) + public override inline fun exp(arg: Float): Float = kotlin.math.exp(arg) + public override inline fun ln(arg: Float): Float = kotlin.math.ln(arg) + + public override inline fun norm(arg: Float): Float = abs(arg) + + public override inline fun Float.unaryMinus(): Float = -this + public override inline fun Float.plus(b: Float): Float = this + b + public override inline fun Float.minus(b: Float): Float = this - b + public override inline fun Float.times(b: Float): Float = this * b + public override inline fun Float.div(b: Float): Float = this / b +} + +/** + * A field for [Int] without boxing. Does not produce corresponding ring element. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public object IntRing : Ring<Int>, Norm<Int, Int> { + public override val zero: Int + get() = 0 + + public override val one: Int + get() = 1 + + public override inline fun add(a: Int, b: Int): Int = a + b + public override inline fun multiply(a: Int, k: Number): Int = k.toInt() * a + + public override inline fun multiply(a: Int, b: Int): Int = a * b + + public override inline fun norm(arg: Int): Int = abs(arg) + + public override inline fun Int.unaryMinus(): Int = -this + public override inline fun Int.plus(b: Int): Int = this + b + public override inline fun Int.minus(b: Int): Int = this - b + public override inline fun Int.times(b: Int): Int = this * b +} + +/** + * A field for [Short] without boxing. Does not produce appropriate ring element. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public object ShortRing : Ring<Short>, Norm<Short, Short> { + public override val zero: Short + get() = 0 + + public override val one: Short + get() = 1 + + public override inline fun add(a: Short, b: Short): Short = (a + b).toShort() + public override inline fun multiply(a: Short, k: Number): Short = (a * k.toShort()).toShort() + + public override inline fun multiply(a: Short, b: Short): Short = (a * b).toShort() + + public override fun norm(arg: Short): Short = if (arg > 0) arg else (-arg).toShort() + + public override inline fun Short.unaryMinus(): Short = (-this).toShort() + public override inline fun Short.plus(b: Short): Short = (this + b).toShort() + public override inline fun Short.minus(b: Short): Short = (this - b).toShort() + public override inline fun Short.times(b: Short): Short = (this * b).toShort() +} + +/** + * A field for [Byte] without boxing. Does not produce appropriate ring element. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public object ByteRing : Ring<Byte>, Norm<Byte, Byte> { + public override val zero: Byte + get() = 0 + + public override val one: Byte + get() = 1 + + public override inline fun add(a: Byte, b: Byte): Byte = (a + b).toByte() + public override inline fun multiply(a: Byte, k: Number): Byte = (a * k.toByte()).toByte() + + public override inline fun multiply(a: Byte, b: Byte): Byte = (a * b).toByte() + + public override fun norm(arg: Byte): Byte = if (arg > 0) arg else (-arg).toByte() + + public override inline fun Byte.unaryMinus(): Byte = (-this).toByte() + public override inline fun Byte.plus(b: Byte): Byte = (this + b).toByte() + public override inline fun Byte.minus(b: Byte): Byte = (this - b).toByte() + public override inline fun Byte.times(b: Byte): Byte = (this * b).toByte() +} + +/** + * A field for [Double] without boxing. Does not produce appropriate ring element. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public object LongRing : Ring<Long>, Norm<Long, Long> { + public override val zero: Long + get() = 0 + + public override val one: Long + get() = 1 + + public override inline fun add(a: Long, b: Long): Long = a + b + public override inline fun multiply(a: Long, k: Number): Long = a * k.toLong() + + public override inline fun multiply(a: Long, b: Long): Long = a * b + + public override fun norm(arg: Long): Long = abs(arg) + + public override inline fun Long.unaryMinus(): Long = (-this) + public override inline fun Long.plus(b: Long): Long = (this + b) + public override inline fun Long.minus(b: Long): Long = (this - b) + public override inline fun Long.times(b: Long): Long = (this * b) +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/OptionalOperations.kt similarity index 52% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/operations/OptionalOperations.kt index 1dac649aa..f31d61ae1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/OptionalOperations.kt @@ -1,234 +1,234 @@ -package scientifik.kmath.operations +package kscience.kmath.operations /** * A container for trigonometric operations for specific type. * * @param T the type of element of this structure. */ -interface TrigonometricOperations<T> : Algebra<T> { +public interface TrigonometricOperations<T> : Algebra<T> { /** * Computes the sine of [arg]. */ - fun sin(arg: T): T + public fun sin(arg: T): T /** * Computes the cosine of [arg]. */ - fun cos(arg: T): T + public fun cos(arg: T): T /** * Computes the tangent of [arg]. */ - fun tan(arg: T): T + public fun tan(arg: T): T /** * Computes the inverse sine of [arg]. */ - fun asin(arg: T): T + public fun asin(arg: T): T /** * Computes the inverse cosine of [arg]. */ - fun acos(arg: T): T + public fun acos(arg: T): T /** * Computes the inverse tangent of [arg]. */ - fun atan(arg: T): T + public fun atan(arg: T): T - companion object { + public companion object { /** * The identifier of sine. */ - const val SIN_OPERATION: String = "sin" + public const val SIN_OPERATION: String = "sin" /** * The identifier of cosine. */ - const val COS_OPERATION: String = "cos" + public const val COS_OPERATION: String = "cos" /** * The identifier of tangent. */ - const val TAN_OPERATION: String = "tan" + public const val TAN_OPERATION: String = "tan" /** * The identifier of inverse sine. */ - const val ASIN_OPERATION: String = "asin" + public const val ASIN_OPERATION: String = "asin" /** * The identifier of inverse cosine. */ - const val ACOS_OPERATION: String = "acos" + public const val ACOS_OPERATION: String = "acos" /** * The identifier of inverse tangent. */ - const val ATAN_OPERATION: String = "atan" + public const val ATAN_OPERATION: String = "atan" } } /** * Computes the sine of [arg]. */ -fun <T : MathElement<out TrigonometricOperations<T>>> sin(arg: T): T = arg.context.sin(arg) +public fun <T : MathElement<out TrigonometricOperations<T>>> sin(arg: T): T = arg.context.sin(arg) /** * Computes the cosine of [arg]. */ -fun <T : MathElement<out TrigonometricOperations<T>>> cos(arg: T): T = arg.context.cos(arg) +public fun <T : MathElement<out TrigonometricOperations<T>>> cos(arg: T): T = arg.context.cos(arg) /** * Computes the tangent of [arg]. */ -fun <T : MathElement<out TrigonometricOperations<T>>> tan(arg: T): T = arg.context.tan(arg) +public fun <T : MathElement<out TrigonometricOperations<T>>> tan(arg: T): T = arg.context.tan(arg) /** * Computes the inverse sine of [arg]. */ -fun <T : MathElement<out TrigonometricOperations<T>>> asin(arg: T): T = arg.context.asin(arg) +public fun <T : MathElement<out TrigonometricOperations<T>>> asin(arg: T): T = arg.context.asin(arg) /** * Computes the inverse cosine of [arg]. */ -fun <T : MathElement<out TrigonometricOperations<T>>> acos(arg: T): T = arg.context.acos(arg) +public fun <T : MathElement<out TrigonometricOperations<T>>> acos(arg: T): T = arg.context.acos(arg) /** * Computes the inverse tangent of [arg]. */ -fun <T : MathElement<out TrigonometricOperations<T>>> atan(arg: T): T = arg.context.atan(arg) +public fun <T : MathElement<out TrigonometricOperations<T>>> atan(arg: T): T = arg.context.atan(arg) /** * A container for hyperbolic trigonometric operations for specific type. * * @param T the type of element of this structure. */ -interface HyperbolicOperations<T> : Algebra<T> { +public interface HyperbolicOperations<T> : Algebra<T> { /** * Computes the hyperbolic sine of [arg]. */ - fun sinh(arg: T): T + public fun sinh(arg: T): T /** * Computes the hyperbolic cosine of [arg]. */ - fun cosh(arg: T): T + public fun cosh(arg: T): T /** * Computes the hyperbolic tangent of [arg]. */ - fun tanh(arg: T): T + public fun tanh(arg: T): T /** * Computes the inverse hyperbolic sine of [arg]. */ - fun asinh(arg: T): T + public fun asinh(arg: T): T /** * Computes the inverse hyperbolic cosine of [arg]. */ - fun acosh(arg: T): T + public fun acosh(arg: T): T /** * Computes the inverse hyperbolic tangent of [arg]. */ - fun atanh(arg: T): T + public fun atanh(arg: T): T - companion object { + public companion object { /** * The identifier of hyperbolic sine. */ - const val SINH_OPERATION: String = "sinh" + public const val SINH_OPERATION: String = "sinh" /** * The identifier of hyperbolic cosine. */ - const val COSH_OPERATION: String = "cosh" + public const val COSH_OPERATION: String = "cosh" /** * The identifier of hyperbolic tangent. */ - const val TANH_OPERATION: String = "tanh" + public const val TANH_OPERATION: String = "tanh" /** * The identifier of inverse hyperbolic sine. */ - const val ASINH_OPERATION: String = "asinh" + public const val ASINH_OPERATION: String = "asinh" /** * The identifier of inverse hyperbolic cosine. */ - const val ACOSH_OPERATION: String = "acosh" + public const val ACOSH_OPERATION: String = "acosh" /** * The identifier of inverse hyperbolic tangent. */ - const val ATANH_OPERATION: String = "atanh" + public const val ATANH_OPERATION: String = "atanh" } } /** * Computes the hyperbolic sine of [arg]. */ -fun <T : MathElement<out HyperbolicOperations<T>>> sinh(arg: T): T = arg.context.sinh(arg) +public fun <T : MathElement<out HyperbolicOperations<T>>> sinh(arg: T): T = arg.context.sinh(arg) /** * Computes the hyperbolic cosine of [arg]. */ -fun <T : MathElement<out HyperbolicOperations<T>>> cosh(arg: T): T = arg.context.cosh(arg) +public fun <T : MathElement<out HyperbolicOperations<T>>> cosh(arg: T): T = arg.context.cosh(arg) /** * Computes the hyperbolic tangent of [arg]. */ -fun <T : MathElement<out HyperbolicOperations<T>>> tanh(arg: T): T = arg.context.tanh(arg) +public fun <T : MathElement<out HyperbolicOperations<T>>> tanh(arg: T): T = arg.context.tanh(arg) /** * Computes the inverse hyperbolic sine of [arg]. */ -fun <T : MathElement<out HyperbolicOperations<T>>> asinh(arg: T): T = arg.context.asinh(arg) +public fun <T : MathElement<out HyperbolicOperations<T>>> asinh(arg: T): T = arg.context.asinh(arg) /** * Computes the inverse hyperbolic cosine of [arg]. */ -fun <T : MathElement<out HyperbolicOperations<T>>> acosh(arg: T): T = arg.context.acosh(arg) +public fun <T : MathElement<out HyperbolicOperations<T>>> acosh(arg: T): T = arg.context.acosh(arg) /** * Computes the inverse hyperbolic tangent of [arg]. */ -fun <T : MathElement<out HyperbolicOperations<T>>> atanh(arg: T): T = arg.context.atanh(arg) +public fun <T : MathElement<out HyperbolicOperations<T>>> atanh(arg: T): T = arg.context.atanh(arg) /** * A context extension to include power operations based on exponentiation. * * @param T the type of element of this structure. */ -interface PowerOperations<T> : Algebra<T> { +public interface PowerOperations<T> : Algebra<T> { /** * Raises [arg] to the power [pow]. */ - fun power(arg: T, pow: Number): T + public fun power(arg: T, pow: Number): T /** * Computes the square root of the value [arg]. */ - fun sqrt(arg: T): T = power(arg, 0.5) + public fun sqrt(arg: T): T = power(arg, 0.5) /** * Raises this value to the power [pow]. */ - infix fun T.pow(pow: Number): T = power(this, pow) + public infix fun T.pow(pow: Number): T = power(this, pow) - companion object { + public companion object { /** * The identifier of exponentiation. */ - const val POW_OPERATION: String = "pow" + public const val POW_OPERATION: String = "pow" /** * The identifier of square root. */ - const val SQRT_OPERATION: String = "sqrt" + public const val SQRT_OPERATION: String = "sqrt" } } @@ -239,56 +239,56 @@ interface PowerOperations<T> : Algebra<T> { * @param power the exponent. * @return the base raised to the power. */ -infix fun <T : MathElement<out PowerOperations<T>>> T.pow(power: Double): T = context.power(this, power) +public infix fun <T : MathElement<out PowerOperations<T>>> T.pow(power: Double): T = context.power(this, power) /** * Computes the square root of the value [arg]. */ -fun <T : MathElement<out PowerOperations<T>>> sqrt(arg: T): T = arg pow 0.5 +public fun <T : MathElement<out PowerOperations<T>>> sqrt(arg: T): T = arg pow 0.5 /** * Computes the square of the value [arg]. */ -fun <T : MathElement<out PowerOperations<T>>> sqr(arg: T): T = arg pow 2.0 +public fun <T : MathElement<out PowerOperations<T>>> sqr(arg: T): T = arg pow 2.0 /** * A container for operations related to `exp` and `ln` functions. * * @param T the type of element of this structure. */ -interface ExponentialOperations<T> : Algebra<T> { +public interface ExponentialOperations<T> : Algebra<T> { /** * Computes Euler's number `e` raised to the power of the value [arg]. */ - fun exp(arg: T): T + public fun exp(arg: T): T /** * Computes the natural logarithm (base `e`) of the value [arg]. */ - fun ln(arg: T): T + public fun ln(arg: T): T - companion object { + public companion object { /** * The identifier of exponential function. */ - const val EXP_OPERATION: String = "exp" + public const val EXP_OPERATION: String = "exp" /** * The identifier of natural logarithm. */ - const val LN_OPERATION: String = "ln" + public const val LN_OPERATION: String = "ln" } } /** * The identifier of exponential function. */ -fun <T : MathElement<out ExponentialOperations<T>>> exp(arg: T): T = arg.context.exp(arg) +public fun <T : MathElement<out ExponentialOperations<T>>> exp(arg: T): T = arg.context.exp(arg) /** * The identifier of natural logarithm. */ -fun <T : MathElement<out ExponentialOperations<T>>> ln(arg: T): T = arg.context.ln(arg) +public fun <T : MathElement<out ExponentialOperations<T>>> ln(arg: T): T = arg.context.ln(arg) /** * A container for norm functional on element. @@ -296,14 +296,14 @@ fun <T : MathElement<out ExponentialOperations<T>>> ln(arg: T): T = arg.context. * @param T the type of element having norm defined. * @param R the type of norm. */ -interface Norm<in T : Any, out R> { +public interface Norm<in T : Any, out R> { /** * Computes the norm of [arg] (i.e. absolute value or vector length). */ - fun norm(arg: T): R + public fun norm(arg: T): R } /** * Computes the norm of [arg] (i.e. absolute value or vector length). */ -fun <T : MathElement<out Norm<T, R>>, R> norm(arg: T): R = arg.context.norm(arg) +public fun <T : MathElement<out Norm<T, R>>, R> norm(arg: T): R = arg.context.norm(arg) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt similarity index 63% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt index be71645d1..c3c859f7a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt @@ -1,30 +1,30 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.FieldElement +import kscience.kmath.operations.Field +import kscience.kmath.operations.FieldElement -class BoxingNDField<T, F : Field<T>>( - override val shape: IntArray, - override val elementContext: F, - val bufferFactory: BufferFactory<T> +public class BoxingNDField<T, F : Field<T>>( + public override val shape: IntArray, + public override val elementContext: F, + public val bufferFactory: BufferFactory<T> ) : BufferedNDField<T, F> { - override val zero: BufferedNDFieldElement<T, F> by lazy { produce { zero } } - override val one: BufferedNDFieldElement<T, F> by lazy { produce { one } } - override val strides: Strides = DefaultStrides(shape) + public override val zero: BufferedNDFieldElement<T, F> by lazy { produce { zero } } + public override val one: BufferedNDFieldElement<T, F> by lazy { produce { one } } + public override val strides: Strides = DefaultStrides(shape) - fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T> = + public fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T> = bufferFactory(size, initializer) - override fun check(vararg elements: NDBuffer<T>) { + public override fun check(vararg elements: NDBuffer<T>) { check(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } } - override fun produce(initializer: F.(IntArray) -> T): BufferedNDFieldElement<T, F> = + public override fun produce(initializer: F.(IntArray) -> T): BufferedNDFieldElement<T, F> = BufferedNDFieldElement( this, buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }) - override fun map(arg: NDBuffer<T>, transform: F.(T) -> T): BufferedNDFieldElement<T, F> { + public override fun map(arg: NDBuffer<T>, transform: F.(T) -> T): BufferedNDFieldElement<T, F> { check(arg) return BufferedNDFieldElement( @@ -36,7 +36,7 @@ class BoxingNDField<T, F : Field<T>>( } - override fun mapIndexed( + public override fun mapIndexed( arg: NDBuffer<T>, transform: F.(index: IntArray, T) -> T ): BufferedNDFieldElement<T, F> { @@ -55,7 +55,7 @@ class BoxingNDField<T, F : Field<T>>( // return BufferedNDFieldElement(this, buffer) } - override fun combine( + public override fun combine( a: NDBuffer<T>, b: NDBuffer<T>, transform: F.(T, T) -> T @@ -66,11 +66,11 @@ class BoxingNDField<T, F : Field<T>>( buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) } - override fun NDBuffer<T>.toElement(): FieldElement<NDBuffer<T>, *, out BufferedNDField<T, F>> = + public override fun NDBuffer<T>.toElement(): FieldElement<NDBuffer<T>, *, out BufferedNDField<T, F>> = BufferedNDFieldElement(this@BoxingNDField, buffer) } -inline fun <T : Any, F : Field<T>, R> F.nd( +public inline fun <T : Any, F : Field<T>, R> F.nd( noinline bufferFactory: BufferFactory<T>, vararg shape: Int, action: NDField<T, F, *>.() -> R diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt similarity index 87% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt index 91b945e79..461b0387c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt @@ -1,18 +1,18 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.RingElement +import kscience.kmath.operations.Ring +import kscience.kmath.operations.RingElement -class BoxingNDRing<T, R : Ring<T>>( +public class BoxingNDRing<T, R : Ring<T>>( override val shape: IntArray, override val elementContext: R, - val bufferFactory: BufferFactory<T> + public val bufferFactory: BufferFactory<T> ) : BufferedNDRing<T, R> { override val strides: Strides = DefaultStrides(shape) override val zero: BufferedNDRingElement<T, R> by lazy { produce { zero } } override val one: BufferedNDRingElement<T, R> by lazy { produce { one } } - fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T> = bufferFactory(size, initializer) + public fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T> = bufferFactory(size, initializer) override fun check(vararg elements: NDBuffer<T>) { require(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } @@ -59,6 +59,7 @@ class BoxingNDRing<T, R : Ring<T>>( transform: R.(T, T) -> T ): BufferedNDRingElement<T, R> { check(a, b) + return BufferedNDRingElement( this, buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferAccessor2D.kt similarity index 53% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferAccessor2D.kt index 2c3d69094..00fc4aef0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferAccessor2D.kt @@ -1,28 +1,27 @@ -package scientifik.kmath.structures +package kscience.kmath.structures import kotlin.reflect.KClass /** * A context that allows to operate on a [MutableBuffer] as on 2d array */ -class BufferAccessor2D<T : Any>(val type: KClass<T>, val rowNum: Int, val colNum: Int) { - operator fun Buffer<T>.get(i: Int, j: Int): T = get(i + colNum * j) +public class BufferAccessor2D<T : Any>(public val type: KClass<T>, public val rowNum: Int, public val colNum: Int) { + public operator fun Buffer<T>.get(i: Int, j: Int): T = get(i + colNum * j) - operator fun MutableBuffer<T>.set(i: Int, j: Int, value: T) { + public operator fun MutableBuffer<T>.set(i: Int, j: Int, value: T) { set(i + colNum * j, value) } - inline fun create(init: (i: Int, j: Int) -> T): MutableBuffer<T> = + public inline fun create(init: (i: Int, j: Int) -> T): MutableBuffer<T> = MutableBuffer.auto(type, rowNum * colNum) { offset -> init(offset / colNum, offset % colNum) } - fun create(mat: Structure2D<T>): MutableBuffer<T> = create { i, j -> mat[i, j] } + public fun create(mat: Structure2D<T>): MutableBuffer<T> = create { i, j -> mat[i, j] } //TODO optimize wrapper - fun MutableBuffer<T>.collect(): Structure2D<T> = + public fun MutableBuffer<T>.collect(): Structure2D<T> = NDStructure.auto(type, rowNum, colNum) { (i, j) -> get(i, j) }.as2D() - - inner class Row(val buffer: MutableBuffer<T>, val rowIndex: Int) : MutableBuffer<T> { + public inner class Row(public val buffer: MutableBuffer<T>, public val rowIndex: Int) : MutableBuffer<T> { override val size: Int get() = colNum override operator fun get(index: Int): T = buffer[rowIndex, index] @@ -39,5 +38,5 @@ class BufferAccessor2D<T : Any>(val type: KClass<T>, val rowNum: Int, val colNum /** * Get row */ - fun MutableBuffer<T>.row(i: Int): Row = Row(this, i) + public fun MutableBuffer<T>.row(i: Int): Row = Row(this, i) } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt new file mode 100644 index 000000000..66b4f19e1 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt @@ -0,0 +1,41 @@ +package kscience.kmath.structures + +import kscience.kmath.operations.* + +public interface BufferedNDAlgebra<T, C> : NDAlgebra<T, C, NDBuffer<T>> { + public val strides: Strides + + public override fun check(vararg elements: NDBuffer<T>): Unit = + require(elements.all { it.strides == strides }) { ("Strides mismatch") } + + /** + * Convert any [NDStructure] to buffered structure using strides from this context. + * If the structure is already [NDBuffer], conversion is free. If not, it could be expensive because iteration over + * indices. + * + * If the argument is [NDBuffer] with different strides structure, the new element will be produced. + */ + public fun NDStructure<T>.toBuffer(): NDBuffer<T> = + if (this is NDBuffer<T> && this.strides == this@BufferedNDAlgebra.strides) + this + else + produce { index -> this@toBuffer[index] } + + /** + * Convert a buffer to element of this algebra + */ + public fun NDBuffer<T>.toElement(): MathElement<out BufferedNDAlgebra<T, C>> +} + + +public interface BufferedNDSpace<T, S : Space<T>> : NDSpace<T, S, NDBuffer<T>>, BufferedNDAlgebra<T, S> { + public override fun NDBuffer<T>.toElement(): SpaceElement<NDBuffer<T>, *, out BufferedNDSpace<T, S>> +} + +public interface BufferedNDRing<T, R : Ring<T>> : NDRing<T, R, NDBuffer<T>>, BufferedNDSpace<T, R> { + override fun NDBuffer<T>.toElement(): RingElement<NDBuffer<T>, *, out BufferedNDRing<T, R>> +} + +public interface BufferedNDField<T, F : Field<T>> : NDField<T, F, NDBuffer<T>>, BufferedNDRing<T, F> { + override fun NDBuffer<T>.toElement(): FieldElement<NDBuffer<T>, *, out BufferedNDField<T, F>> +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDElement.kt similarity index 70% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDElement.kt index 20e34fadd..d53702566 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDElement.kt @@ -1,11 +1,11 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.* +import kscience.kmath.operations.* /** * Base class for an element with context, containing strides */ -abstract class BufferedNDElement<T, C> : NDBuffer<T>(), NDElement<T, C, NDBuffer<T>> { +public abstract class BufferedNDElement<T, C> : NDBuffer<T>(), NDElement<T, C, NDBuffer<T>> { abstract override val context: BufferedNDAlgebra<T, C> override val strides: Strides get() = context.strides @@ -13,7 +13,7 @@ abstract class BufferedNDElement<T, C> : NDBuffer<T>(), NDElement<T, C, NDBuffer override val shape: IntArray get() = context.shape } -class BufferedNDSpaceElement<T, S : Space<T>>( +public class BufferedNDSpaceElement<T, S : Space<T>>( override val context: BufferedNDSpace<T, S>, override val buffer: Buffer<T> ) : BufferedNDElement<T, S>(), SpaceElement<NDBuffer<T>, BufferedNDSpaceElement<T, S>, BufferedNDSpace<T, S>> { @@ -26,7 +26,7 @@ class BufferedNDSpaceElement<T, S : Space<T>>( } } -class BufferedNDRingElement<T, R : Ring<T>>( +public class BufferedNDRingElement<T, R : Ring<T>>( override val context: BufferedNDRing<T, R>, override val buffer: Buffer<T> ) : BufferedNDElement<T, R>(), RingElement<NDBuffer<T>, BufferedNDRingElement<T, R>, BufferedNDRing<T, R>> { @@ -38,7 +38,7 @@ class BufferedNDRingElement<T, R : Ring<T>>( } } -class BufferedNDFieldElement<T, F : Field<T>>( +public class BufferedNDFieldElement<T, F : Field<T>>( override val context: BufferedNDField<T, F>, override val buffer: Buffer<T> ) : BufferedNDElement<T, F>(), FieldElement<NDBuffer<T>, BufferedNDFieldElement<T, F>, BufferedNDField<T, F>> { @@ -54,7 +54,7 @@ class BufferedNDFieldElement<T, F : Field<T>>( /** * Element by element application of any operation on elements to the whole array. Just like in numpy. */ -operator fun <T : Any, F : Field<T>> Function1<T, T>.invoke(ndElement: BufferedNDElement<T, F>): MathElement<out BufferedNDAlgebra<T, F>> = +public operator fun <T : Any, F : Field<T>> Function1<T, T>.invoke(ndElement: BufferedNDElement<T, F>): MathElement<out BufferedNDAlgebra<T, F>> = ndElement.context.run { map(ndElement) { invoke(it) }.toElement() } /* plus and minus */ @@ -62,13 +62,13 @@ operator fun <T : Any, F : Field<T>> Function1<T, T>.invoke(ndElement: BufferedN /** * Summation operation for [BufferedNDElement] and single element */ -operator fun <T : Any, F : Space<T>> BufferedNDElement<T, F>.plus(arg: T): NDElement<T, F, NDBuffer<T>> = +public operator fun <T : Any, F : Space<T>> BufferedNDElement<T, F>.plus(arg: T): NDElement<T, F, NDBuffer<T>> = context.map(this) { it + arg }.wrap() /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun <T : Any, F : Space<T>> BufferedNDElement<T, F>.minus(arg: T): NDElement<T, F, NDBuffer<T>> = +public operator fun <T : Any, F : Space<T>> BufferedNDElement<T, F>.minus(arg: T): NDElement<T, F, NDBuffer<T>> = context.map(this) { it - arg }.wrap() /* prod and div */ @@ -76,11 +76,11 @@ operator fun <T : Any, F : Space<T>> BufferedNDElement<T, F>.minus(arg: T): NDEl /** * Product operation for [BufferedNDElement] and single element */ -operator fun <T : Any, F : Ring<T>> BufferedNDElement<T, F>.times(arg: T): NDElement<T, F, NDBuffer<T>> = +public operator fun <T : Any, F : Ring<T>> BufferedNDElement<T, F>.times(arg: T): NDElement<T, F, NDBuffer<T>> = context.map(this) { it * arg }.wrap() /** * Division operation between [BufferedNDElement] and single element */ -operator fun <T : Any, F : Field<T>> BufferedNDElement<T, F>.div(arg: T): NDElement<T, F, NDBuffer<T>> = +public operator fun <T : Any, F : Field<T>> BufferedNDElement<T, F>.div(arg: T): NDElement<T, F, NDBuffer<T>> = context.map(this) { it / arg }.wrap() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt similarity index 70% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt index 1ea8ca3d5..53587e503 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt @@ -1,8 +1,7 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.complex -import kotlin.contracts.contract +import kscience.kmath.operations.Complex +import kscience.kmath.operations.complex import kotlin.reflect.KClass /** @@ -10,44 +9,44 @@ import kotlin.reflect.KClass * * @param T the type of buffer. */ -typealias BufferFactory<T> = (Int, (Int) -> T) -> Buffer<T> +public typealias BufferFactory<T> = (Int, (Int) -> T) -> Buffer<T> /** * Function that produces [MutableBuffer] from its size and function that supplies values. * * @param T the type of buffer. */ -typealias MutableBufferFactory<T> = (Int, (Int) -> T) -> MutableBuffer<T> +public typealias MutableBufferFactory<T> = (Int, (Int) -> T) -> MutableBuffer<T> /** * A generic immutable random-access structure for both primitives and objects. * * @param T the type of elements contained in the buffer. */ -interface Buffer<T> { +public interface Buffer<T> { /** * The size of this buffer. */ - val size: Int + public val size: Int /** * Gets element at given index. */ - operator fun get(index: Int): T + public operator fun get(index: Int): T /** * Iterates over all elements. */ - operator fun iterator(): Iterator<T> + public operator fun iterator(): Iterator<T> /** * Checks content equality with another buffer. */ - fun contentEquals(other: Buffer<*>): Boolean = + public fun contentEquals(other: Buffer<*>): Boolean = asSequence().mapIndexed { index, value -> value == other[index] }.all { it } - companion object { - inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer { + public companion object { + public inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer { val array = DoubleArray(size) { initializer(it) } return RealBuffer(array) } @@ -55,10 +54,11 @@ interface Buffer<T> { /** * Create a boxing buffer of given type */ - inline fun <T> boxing(size: Int, initializer: (Int) -> T): Buffer<T> = ListBuffer(List(size, initializer)) + public inline fun <T> boxing(size: Int, initializer: (Int) -> T): Buffer<T> = + ListBuffer(List(size, initializer)) @Suppress("UNCHECKED_CAST") - inline fun <T : Any> auto(type: KClass<T>, size: Int, crossinline initializer: (Int) -> T): Buffer<T> { + public inline fun <T : Any> auto(type: KClass<T>, size: Int, crossinline initializer: (Int) -> T): Buffer<T> { //TODO add resolution based on Annotation or companion resolution return when (type) { Double::class -> RealBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer<T> @@ -74,7 +74,7 @@ interface Buffer<T> { * Create most appropriate immutable buffer for given type avoiding boxing wherever possible */ @Suppress("UNCHECKED_CAST") - inline fun <reified T : Any> auto(size: Int, crossinline initializer: (Int) -> T): Buffer<T> = + public inline fun <reified T : Any> auto(size: Int, crossinline initializer: (Int) -> T): Buffer<T> = auto(T::class, size, initializer) } } @@ -82,43 +82,43 @@ interface Buffer<T> { /** * Creates a sequence that returns all elements from this [Buffer]. */ -fun <T> Buffer<T>.asSequence(): Sequence<T> = Sequence(::iterator) +public fun <T> Buffer<T>.asSequence(): Sequence<T> = Sequence(::iterator) /** * Creates an iterable that returns all elements from this [Buffer]. */ -fun <T> Buffer<T>.asIterable(): Iterable<T> = Iterable(::iterator) +public fun <T> Buffer<T>.asIterable(): Iterable<T> = Iterable(::iterator) /** * Returns an [IntRange] of the valid indices for this [Buffer]. */ -val Buffer<*>.indices: IntRange get() = 0 until size +public val Buffer<*>.indices: IntRange get() = 0 until size /** * A generic mutable random-access structure for both primitives and objects. * * @param T the type of elements contained in the buffer. */ -interface MutableBuffer<T> : Buffer<T> { +public interface MutableBuffer<T> : Buffer<T> { /** * Sets the array element at the specified [index] to the specified [value]. */ - operator fun set(index: Int, value: T) + public operator fun set(index: Int, value: T) /** * Returns a shallow copy of the buffer. */ - fun copy(): MutableBuffer<T> + public fun copy(): MutableBuffer<T> - companion object { + public companion object { /** * Create a boxing mutable buffer of given type */ - inline fun <T> boxing(size: Int, initializer: (Int) -> T): MutableBuffer<T> = + public inline fun <T> boxing(size: Int, initializer: (Int) -> T): MutableBuffer<T> = MutableListBuffer(MutableList(size, initializer)) @Suppress("UNCHECKED_CAST") - inline fun <T : Any> auto(type: KClass<out T>, size: Int, initializer: (Int) -> T): MutableBuffer<T> = + public inline fun <T : Any> auto(type: KClass<out T>, size: Int, initializer: (Int) -> T): MutableBuffer<T> = when (type) { Double::class -> RealBuffer(DoubleArray(size) { initializer(it) as Double }) as MutableBuffer<T> Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as MutableBuffer<T> @@ -131,12 +131,11 @@ interface MutableBuffer<T> : Buffer<T> { * Create most appropriate mutable buffer for given type avoiding boxing wherever possible */ @Suppress("UNCHECKED_CAST") - inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> = + public inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> = auto(T::class, size, initializer) - val real: MutableBufferFactory<Double> = { size: Int, initializer: (Int) -> Double -> - RealBuffer(DoubleArray(size) { initializer(it) }) - } + public val real: MutableBufferFactory<Double> = + { size, initializer -> RealBuffer(DoubleArray(size) { initializer(it) }) } } } @@ -146,7 +145,7 @@ interface MutableBuffer<T> : Buffer<T> { * @param T the type of elements contained in the buffer. * @property list The underlying list. */ -inline class ListBuffer<T>(val list: List<T>) : Buffer<T> { +public inline class ListBuffer<T>(public val list: List<T>) : Buffer<T> { override val size: Int get() = list.size @@ -157,7 +156,7 @@ inline class ListBuffer<T>(val list: List<T>) : Buffer<T> { /** * Returns an [ListBuffer] that wraps the original list. */ -fun <T> List<T>.asBuffer(): ListBuffer<T> = ListBuffer(this) +public fun <T> List<T>.asBuffer(): ListBuffer<T> = ListBuffer(this) /** * Creates a new [ListBuffer] with the specified [size], where each element is calculated by calling the specified @@ -166,10 +165,7 @@ fun <T> List<T>.asBuffer(): ListBuffer<T> = ListBuffer(this) * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an array element given its index. */ -inline fun <T> ListBuffer(size: Int, init: (Int) -> T): ListBuffer<T> { - contract { callsInPlace(init) } - return List(size, init).asBuffer() -} +public inline fun <T> ListBuffer(size: Int, init: (Int) -> T): ListBuffer<T> = List(size, init).asBuffer() /** * [MutableBuffer] implementation over [MutableList]. @@ -177,7 +173,7 @@ inline fun <T> ListBuffer(size: Int, init: (Int) -> T): ListBuffer<T> { * @param T the type of elements contained in the buffer. * @property list The underlying list. */ -inline class MutableListBuffer<T>(val list: MutableList<T>) : MutableBuffer<T> { +public inline class MutableListBuffer<T>(public val list: MutableList<T>) : MutableBuffer<T> { override val size: Int get() = list.size @@ -197,7 +193,7 @@ inline class MutableListBuffer<T>(val list: MutableList<T>) : MutableBuffer<T> { * @param T the type of elements contained in the buffer. * @property array The underlying array. */ -class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> { +public class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> { // Can't inline because array is invariant override val size: Int get() = array.size @@ -215,7 +211,7 @@ class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> { /** * Returns an [ArrayBuffer] that wraps the original array. */ -fun <T> Array<T>.asBuffer(): ArrayBuffer<T> = ArrayBuffer(this) +public fun <T> Array<T>.asBuffer(): ArrayBuffer<T> = ArrayBuffer(this) /** * Immutable wrapper for [MutableBuffer]. @@ -223,7 +219,7 @@ fun <T> Array<T>.asBuffer(): ArrayBuffer<T> = ArrayBuffer(this) * @param T the type of elements contained in the buffer. * @property buffer The underlying buffer. */ -inline class ReadOnlyBuffer<T>(val buffer: MutableBuffer<T>) : Buffer<T> { +public inline class ReadOnlyBuffer<T>(public val buffer: MutableBuffer<T>) : Buffer<T> { override val size: Int get() = buffer.size override operator fun get(index: Int): T = buffer[index] @@ -237,7 +233,7 @@ inline class ReadOnlyBuffer<T>(val buffer: MutableBuffer<T>) : Buffer<T> { * * @param T the type of elements provided by the buffer. */ -class VirtualBuffer<T>(override val size: Int, private val generator: (Int) -> T) : Buffer<T> { +public class VirtualBuffer<T>(override val size: Int, private val generator: (Int) -> T) : Buffer<T> { override operator fun get(index: Int): T { if (index < 0 || index >= size) throw IndexOutOfBoundsException("Expected index from 0 to ${size - 1}, but found $index") return generator(index) @@ -257,14 +253,14 @@ class VirtualBuffer<T>(override val size: Int, private val generator: (Int) -> T /** * Convert this buffer to read-only buffer. */ -fun <T> Buffer<T>.asReadOnly(): Buffer<T> = if (this is MutableBuffer) ReadOnlyBuffer(this) else this +public fun <T> Buffer<T>.asReadOnly(): Buffer<T> = if (this is MutableBuffer) ReadOnlyBuffer(this) else this /** * Typealias for buffer transformations. */ -typealias BufferTransform<T, R> = (Buffer<T>) -> Buffer<R> +public typealias BufferTransform<T, R> = (Buffer<T>) -> Buffer<R> /** * Typealias for buffer transformations with suspend function. */ -typealias SuspendBufferTransform<T, R> = suspend (Buffer<T>) -> Buffer<R> +public typealias SuspendBufferTransform<T, R> = suspend (Buffer<T>) -> Buffer<R> diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt similarity index 73% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt index 5364115d4..f1f1074e5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt @@ -1,18 +1,18 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.ComplexField -import scientifik.kmath.operations.FieldElement -import scientifik.kmath.operations.complex +import kscience.kmath.operations.Complex +import kscience.kmath.operations.ComplexField +import kscience.kmath.operations.FieldElement +import kscience.kmath.operations.complex import kotlin.contracts.InvocationKind import kotlin.contracts.contract -typealias ComplexNDElement = BufferedNDFieldElement<Complex, ComplexField> +public typealias ComplexNDElement = BufferedNDFieldElement<Complex, ComplexField> /** * An optimized nd-field for complex numbers */ -class ComplexNDField(override val shape: IntArray) : +public class ComplexNDField(override val shape: IntArray) : BufferedNDField<Complex, ComplexField>, ExtendedNDField<Complex, ComplexField, NDBuffer<Complex>> { @@ -21,7 +21,7 @@ class ComplexNDField(override val shape: IntArray) : override val zero: ComplexNDElement by lazy { produce { zero } } override val one: ComplexNDElement by lazy { produce { one } } - inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Complex): Buffer<Complex> = + public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Complex): Buffer<Complex> = Buffer.complex(size) { initializer(it) } /** @@ -97,7 +97,7 @@ class ComplexNDField(override val shape: IntArray) : /** * Fast element production using function inlining */ -inline fun BufferedNDField<Complex, ComplexField>.produceInline(crossinline initializer: ComplexField.(Int) -> Complex): ComplexNDElement { +public inline fun BufferedNDField<Complex, ComplexField>.produceInline(initializer: ComplexField.(Int) -> Complex): ComplexNDElement { val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.initializer(offset) } return BufferedNDFieldElement(this, buffer) } @@ -105,14 +105,13 @@ inline fun BufferedNDField<Complex, ComplexField>.produceInline(crossinline init /** * Map one [ComplexNDElement] using function with indices. */ -inline fun ComplexNDElement.mapIndexed(crossinline transform: ComplexField.(index: IntArray, Complex) -> Complex): ComplexNDElement = +public inline fun ComplexNDElement.mapIndexed(transform: ComplexField.(index: IntArray, Complex) -> Complex): ComplexNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** * Map one [ComplexNDElement] using function without indices. */ -inline fun ComplexNDElement.map(crossinline transform: ComplexField.(Complex) -> Complex): ComplexNDElement { - contract { callsInPlace(transform) } +public inline fun ComplexNDElement.map(transform: ComplexField.(Complex) -> Complex): ComplexNDElement { val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.transform(buffer[offset]) } return BufferedNDFieldElement(context, buffer) } @@ -120,38 +119,35 @@ inline fun ComplexNDElement.map(crossinline transform: ComplexField.(Complex) -> /** * Element by element application of any operation on elements to the whole array. Just like in numpy */ -operator fun Function1<Complex, Complex>.invoke(ndElement: ComplexNDElement): ComplexNDElement = +public operator fun Function1<Complex, Complex>.invoke(ndElement: ComplexNDElement): ComplexNDElement = ndElement.map { this@invoke(it) } - /* plus and minus */ /** * Summation operation for [BufferedNDElement] and single element */ -operator fun ComplexNDElement.plus(arg: Complex): ComplexNDElement = map { it + arg } +public operator fun ComplexNDElement.plus(arg: Complex): ComplexNDElement = map { it + arg } /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun ComplexNDElement.minus(arg: Complex): ComplexNDElement = - map { it - arg } +public operator fun ComplexNDElement.minus(arg: Complex): ComplexNDElement = map { it - arg } -operator fun ComplexNDElement.plus(arg: Double): ComplexNDElement = - map { it + arg } +public operator fun ComplexNDElement.plus(arg: Double): ComplexNDElement = map { it + arg } +public operator fun ComplexNDElement.minus(arg: Double): ComplexNDElement = map { it - arg } -operator fun ComplexNDElement.minus(arg: Double): ComplexNDElement = - map { it - arg } +public fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDField(shape) -fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDField(shape) - -fun NDElement.Companion.complex(vararg shape: Int, initializer: ComplexField.(IntArray) -> Complex): ComplexNDElement = - NDField.complex(*shape).produce(initializer) +public fun NDElement.Companion.complex( + vararg shape: Int, + initializer: ComplexField.(IntArray) -> Complex +): ComplexNDElement = NDField.complex(*shape).produce(initializer) /** * Produce a context for n-dimensional operations inside this real field */ -inline fun <R> ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R { +public inline fun <R> ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } return NDField.complex(*shape).action() } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ExtendedNDField.kt similarity index 86% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ExtendedNDField.kt index 24aa48c6b..a9fa2763b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ExtendedNDField.kt @@ -1,6 +1,6 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.ExtendedField +import kscience.kmath.operations.ExtendedField /** * [ExtendedField] over [NDStructure]. @@ -9,7 +9,7 @@ import scientifik.kmath.operations.ExtendedField * @param N the type of ND structure. * @param F the extended field of structure elements. */ -interface ExtendedNDField<T : Any, F : ExtendedField<T>, N : NDStructure<T>> : NDField<T, F, N>, ExtendedField<N> +public interface ExtendedNDField<T : Any, F : ExtendedField<T>, N : NDStructure<T>> : NDField<T, F, N>, ExtendedField<N> ///** // * NDField that supports [ExtendedField] operations on its elements diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt similarity index 65% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt index 5146c0668..e3fda0e10 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FlaggedBuffer.kt @@ -1,6 +1,5 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import kotlin.contracts.contract import kotlin.experimental.and /** @@ -8,7 +7,7 @@ import kotlin.experimental.and * * @property mask bit mask value of this flag. */ -enum class ValueFlag(val mask: Byte) { +public enum class ValueFlag(public val mask: Byte) { /** * Reports the value is NaN. */ @@ -33,23 +32,23 @@ enum class ValueFlag(val mask: Byte) { /** * A buffer with flagged values. */ -interface FlaggedBuffer<T> : Buffer<T> { - fun getFlag(index: Int): Byte +public interface FlaggedBuffer<T> : Buffer<T> { + public fun getFlag(index: Int): Byte } /** * The value is valid if all flags are down */ -fun FlaggedBuffer<*>.isValid(index: Int): Boolean = getFlag(index) != 0.toByte() +public fun FlaggedBuffer<*>.isValid(index: Int): Boolean = getFlag(index) != 0.toByte() -fun FlaggedBuffer<*>.hasFlag(index: Int, flag: ValueFlag): Boolean = (getFlag(index) and flag.mask) != 0.toByte() +public fun FlaggedBuffer<*>.hasFlag(index: Int, flag: ValueFlag): Boolean = (getFlag(index) and flag.mask) != 0.toByte() -fun FlaggedBuffer<*>.isMissing(index: Int): Boolean = hasFlag(index, ValueFlag.MISSING) +public fun FlaggedBuffer<*>.isMissing(index: Int): Boolean = hasFlag(index, ValueFlag.MISSING) /** * A real buffer which supports flags for each value like NaN or Missing */ -class FlaggedRealBuffer(val values: DoubleArray, val flags: ByteArray) : FlaggedBuffer<Double?>, Buffer<Double?> { +public class FlaggedRealBuffer(public val values: DoubleArray, public val flags: ByteArray) : FlaggedBuffer<Double?>, Buffer<Double?> { init { require(values.size == flags.size) { "Values and flags must have the same dimensions" } } @@ -65,9 +64,7 @@ class FlaggedRealBuffer(val values: DoubleArray, val flags: ByteArray) : Flagged }.iterator() } -inline fun FlaggedRealBuffer.forEachValid(block: (Double) -> Unit) { - contract { callsInPlace(block) } - +public inline fun FlaggedRealBuffer.forEachValid(block: (Double) -> Unit) { indices .asSequence() .filter(::isValid) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FloatBuffer.kt similarity index 69% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FloatBuffer.kt index 3beb110cf..e96c45572 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/FloatBuffer.kt @@ -1,13 +1,12 @@ -package scientifik.kmath.structures - -import kotlin.contracts.contract +package kscience.kmath.structures /** * Specialized [MutableBuffer] implementation over [FloatArray]. * * @property array the underlying array. + * @author Iaroslav Postovalov */ -inline class FloatBuffer(val array: FloatArray) : MutableBuffer<Float> { +public inline class FloatBuffer(public val array: FloatArray) : MutableBuffer<Float> { override val size: Int get() = array.size override operator fun get(index: Int): Float = array[index] @@ -29,20 +28,17 @@ inline class FloatBuffer(val array: FloatArray) : MutableBuffer<Float> { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun FloatBuffer(size: Int, init: (Int) -> Float): FloatBuffer { - contract { callsInPlace(init) } - return FloatBuffer(FloatArray(size) { init(it) }) -} +public inline fun FloatBuffer(size: Int, init: (Int) -> Float): FloatBuffer = FloatBuffer(FloatArray(size) { init(it) }) /** * Returns a new [FloatBuffer] of given elements. */ -fun FloatBuffer(vararg floats: Float): FloatBuffer = FloatBuffer(floats) +public fun FloatBuffer(vararg floats: Float): FloatBuffer = FloatBuffer(floats) /** * Returns a [FloatArray] containing all of the elements of this [MutableBuffer]. */ -val MutableBuffer<out Float>.array: FloatArray +public val MutableBuffer<out Float>.array: FloatArray get() = (if (this is FloatBuffer) array else FloatArray(size) { get(it) }) /** @@ -51,4 +47,4 @@ val MutableBuffer<out Float>.array: FloatArray * @receiver the array. * @return the new buffer. */ -fun FloatArray.asBuffer(): FloatBuffer = FloatBuffer(this) +public fun FloatArray.asBuffer(): FloatBuffer = FloatBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/IntBuffer.kt similarity index 73% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/IntBuffer.kt index f88ed7d66..0fe68803b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/IntBuffer.kt @@ -1,13 +1,11 @@ -package scientifik.kmath.structures - -import kotlin.contracts.contract +package kscience.kmath.structures /** * Specialized [MutableBuffer] implementation over [IntArray]. * * @property array the underlying array. */ -inline class IntBuffer(val array: IntArray) : MutableBuffer<Int> { +public inline class IntBuffer(public val array: IntArray) : MutableBuffer<Int> { override val size: Int get() = array.size override operator fun get(index: Int): Int = array[index] @@ -29,17 +27,17 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer<Int> { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer = IntBuffer(IntArray(size) { init(it) }) +public inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer = IntBuffer(IntArray(size) { init(it) }) /** * Returns a new [IntBuffer] of given elements. */ -fun IntBuffer(vararg ints: Int): IntBuffer = IntBuffer(ints) +public fun IntBuffer(vararg ints: Int): IntBuffer = IntBuffer(ints) /** * Returns a [IntArray] containing all of the elements of this [MutableBuffer]. */ -val MutableBuffer<out Int>.array: IntArray +public val MutableBuffer<out Int>.array: IntArray get() = (if (this is IntBuffer) array else IntArray(size) { get(it) }) /** @@ -48,4 +46,4 @@ val MutableBuffer<out Int>.array: IntArray * @receiver the array. * @return the new buffer. */ -fun IntArray.asBuffer(): IntBuffer = IntBuffer(this) +public fun IntArray.asBuffer(): IntBuffer = IntBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/LongBuffer.kt similarity index 70% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/LongBuffer.kt index 3a1d679c2..87853c251 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/LongBuffer.kt @@ -1,13 +1,11 @@ -package scientifik.kmath.structures - -import kotlin.contracts.contract +package kscience.kmath.structures /** * Specialized [MutableBuffer] implementation over [LongArray]. * * @property array the underlying array. */ -inline class LongBuffer(val array: LongArray) : MutableBuffer<Long> { +public inline class LongBuffer(public val array: LongArray) : MutableBuffer<Long> { override val size: Int get() = array.size override operator fun get(index: Int): Long = array[index] @@ -20,7 +18,6 @@ inline class LongBuffer(val array: LongArray) : MutableBuffer<Long> { override fun copy(): MutableBuffer<Long> = LongBuffer(array.copyOf()) - } /** @@ -30,20 +27,17 @@ inline class LongBuffer(val array: LongArray) : MutableBuffer<Long> { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun LongBuffer(size: Int, init: (Int) -> Long): LongBuffer { - contract { callsInPlace(init) } - return LongBuffer(LongArray(size) { init(it) }) -} +public inline fun LongBuffer(size: Int, init: (Int) -> Long): LongBuffer = LongBuffer(LongArray(size) { init(it) }) /** * Returns a new [LongBuffer] of given elements. */ -fun LongBuffer(vararg longs: Long): LongBuffer = LongBuffer(longs) +public fun LongBuffer(vararg longs: Long): LongBuffer = LongBuffer(longs) /** * Returns a [IntArray] containing all of the elements of this [MutableBuffer]. */ -val MutableBuffer<out Long>.array: LongArray +public val MutableBuffer<out Long>.array: LongArray get() = (if (this is LongBuffer) array else LongArray(size) { get(it) }) /** @@ -52,4 +46,4 @@ val MutableBuffer<out Long>.array: LongArray * @receiver the array. * @return the new buffer. */ -fun LongArray.asBuffer(): LongBuffer = LongBuffer(this) +public fun LongArray.asBuffer(): LongBuffer = LongBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt similarity index 54% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt index 83c50b14b..b7e6a8218 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt @@ -1,6 +1,6 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.memory.* +import kscience.memory.* /** * A non-boxing buffer over [Memory] object. @@ -9,7 +9,7 @@ import scientifik.memory.* * @property memory the underlying memory segment. * @property spec the spec of [T] type. */ -open class MemoryBuffer<T : Any>(protected val memory: Memory, protected val spec: MemorySpec<T>) : Buffer<T> { +public open class MemoryBuffer<T : Any>(protected val memory: Memory, protected val spec: MemorySpec<T>) : Buffer<T> { override val size: Int get() = memory.size / spec.objectSize private val reader: MemoryReader = memory.reader() @@ -17,20 +17,17 @@ open class MemoryBuffer<T : Any>(protected val memory: Memory, protected val spe override operator fun get(index: Int): T = reader.read(spec, spec.objectSize * index) override operator fun iterator(): Iterator<T> = (0 until size).asSequence().map { get(it) }.iterator() - companion object { - fun <T : Any> create(spec: MemorySpec<T>, size: Int): MemoryBuffer<T> = + public companion object { + public fun <T : Any> create(spec: MemorySpec<T>, size: Int): MemoryBuffer<T> = MemoryBuffer(Memory.allocate(size * spec.objectSize), spec) - inline fun <T : Any> create( + public inline fun <T : Any> create( spec: MemorySpec<T>, size: Int, - crossinline initializer: (Int) -> T - ): MemoryBuffer<T> = - MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer -> - (0 until size).forEach { - buffer[it] = initializer(it) - } - } + initializer: (Int) -> T + ): MemoryBuffer<T> = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer -> + (0 until size).forEach { buffer[it] = initializer(it) } + } } } @@ -41,7 +38,7 @@ open class MemoryBuffer<T : Any>(protected val memory: Memory, protected val spe * @property memory the underlying memory segment. * @property spec the spec of [T] type. */ -class MutableMemoryBuffer<T : Any>(memory: Memory, spec: MemorySpec<T>) : MemoryBuffer<T>(memory, spec), +public class MutableMemoryBuffer<T : Any>(memory: Memory, spec: MemorySpec<T>) : MemoryBuffer<T>(memory, spec), MutableBuffer<T> { private val writer: MemoryWriter = memory.writer() @@ -49,19 +46,16 @@ class MutableMemoryBuffer<T : Any>(memory: Memory, spec: MemorySpec<T>) : Memory override operator fun set(index: Int, value: T): Unit = writer.write(spec, spec.objectSize * index, value) override fun copy(): MutableBuffer<T> = MutableMemoryBuffer(memory.copy(), spec) - companion object { - fun <T : Any> create(spec: MemorySpec<T>, size: Int): MutableMemoryBuffer<T> = + public companion object { + public fun <T : Any> create(spec: MemorySpec<T>, size: Int): MutableMemoryBuffer<T> = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec) - inline fun <T : Any> create( + public inline fun <T : Any> create( spec: MemorySpec<T>, size: Int, crossinline initializer: (Int) -> T - ): MutableMemoryBuffer<T> = - MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer -> - (0 until size).forEach { - buffer[it] = initializer(it) - } - } + ): MutableMemoryBuffer<T> = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer -> + (0 until size).forEach { buffer[it] = initializer(it) } + } } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt similarity index 51% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt index f09db3c72..28eaef2f1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt @@ -1,16 +1,14 @@ -package scientifik.kmath.structures - -import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +package kscience.kmath.structures +import kscience.kmath.operations.Complex +import kscience.kmath.operations.Field +import kscience.kmath.operations.Ring +import kscience.kmath.operations.Space /** * An exception is thrown when the expected ans actual shape of NDArray differs */ -class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : RuntimeException() - +public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) : RuntimeException() /** * The base interface for all nd-algebra implementations @@ -18,53 +16,49 @@ class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : Run * @param C the type of the element context * @param N the type of the structure */ -interface NDAlgebra<T, C, N : NDStructure<T>> { - val shape: IntArray - val elementContext: C +public interface NDAlgebra<T, C, N : NDStructure<T>> { + public val shape: IntArray + public val elementContext: C /** * Produce a new [N] structure using given initializer function */ - fun produce(initializer: C.(IntArray) -> T): N + public fun produce(initializer: C.(IntArray) -> T): N /** * Map elements from one structure to another one */ - fun map(arg: N, transform: C.(T) -> T): N + public fun map(arg: N, transform: C.(T) -> T): N /** * Map indexed elements */ - fun mapIndexed(arg: N, transform: C.(index: IntArray, T) -> T): N + public fun mapIndexed(arg: N, transform: C.(index: IntArray, T) -> T): N /** * Combine two structures into one */ - fun combine(a: N, b: N, transform: C.(T, T) -> T): N + public fun combine(a: N, b: N, transform: C.(T, T) -> T): N /** * Check if given elements are consistent with this context */ - fun check(vararg elements: N) { - elements.forEach { - if (!shape.contentEquals(it.shape)) { - throw ShapeMismatchException(shape, it.shape) - } - } + public fun check(vararg elements: N): Unit = elements.forEach { + if (!shape.contentEquals(it.shape)) throw ShapeMismatchException(shape, it.shape) } /** * element-by-element invoke a function working on [T] on a [NDStructure] */ - operator fun Function1<T, T>.invoke(structure: N): N = map(structure) { value -> this@invoke(value) } + public operator fun Function1<T, T>.invoke(structure: N): N = map(structure) { value -> this@invoke(value) } - companion object + public companion object } /** * An nd-space over element space */ -interface NDSpace<T, S : Space<T>, N : NDStructure<T>> : Space<N>, NDAlgebra<T, S, N> { +public interface NDSpace<T, S : Space<T>, N : NDStructure<T>> : Space<N>, NDAlgebra<T, S, N> { /** * Element-by-element addition */ @@ -76,32 +70,31 @@ interface NDSpace<T, S : Space<T>, N : NDStructure<T>> : Space<N>, NDAlgebra<T, override fun multiply(a: N, k: Number): N = map(a) { multiply(it, k) } //TODO move to extensions after KEEP-176 - operator fun N.plus(arg: T): N = map(this) { value -> add(arg, value) } + public operator fun N.plus(arg: T): N = map(this) { value -> add(arg, value) } - operator fun N.minus(arg: T): N = map(this) { value -> add(arg, -value) } + public operator fun N.minus(arg: T): N = map(this) { value -> add(arg, -value) } - operator fun T.plus(arg: N): N = map(arg) { value -> add(this@plus, value) } - operator fun T.minus(arg: N): N = map(arg) { value -> add(-this@minus, value) } + public operator fun T.plus(arg: N): N = map(arg) { value -> add(this@plus, value) } + public operator fun T.minus(arg: N): N = map(arg) { value -> add(-this@minus, value) } - companion object + public companion object } /** * An nd-ring over element ring */ -interface NDRing<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T, R, N> { - +public interface NDRing<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T, R, N> { /** * Element-by-element multiplication */ override fun multiply(a: N, b: N): N = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } //TODO move to extensions after KEEP-176 - operator fun N.times(arg: T): N = map(this) { value -> multiply(arg, value) } + public operator fun N.times(arg: T): N = map(this) { value -> multiply(arg, value) } - operator fun T.times(arg: N): N = map(arg) { value -> multiply(this@times, value) } + public operator fun T.times(arg: N): N = map(arg) { value -> multiply(this@times, value) } - companion object + public companion object } /** @@ -111,31 +104,29 @@ interface NDRing<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T, R, N> * @param N the type of ND structure. * @param F field of structure elements. */ -interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F, N> { - +public interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F, N> { /** * Element-by-element division */ override fun divide(a: N, b: N): N = combine(a, b) { aValue, bValue -> divide(aValue, bValue) } //TODO move to extensions after KEEP-176 - operator fun N.div(arg: T): N = map(this) { value -> divide(arg, value) } + public operator fun N.div(arg: T): N = map(this) { value -> divide(arg, value) } - operator fun T.div(arg: N): N = map(arg) { divide(it, this@div) } + public operator fun T.div(arg: N): N = map(arg) { divide(it, this@div) } - companion object { - - private val realNDFieldCache = HashMap<IntArray, RealNDField>() + public companion object { + private val realNDFieldCache: MutableMap<IntArray, RealNDField> = hashMapOf() /** * Create a nd-field for [Double] values or pull it from cache if it was created previously */ - fun real(vararg shape: Int): RealNDField = realNDFieldCache.getOrPut(shape) { RealNDField(shape) } + public fun real(vararg shape: Int): RealNDField = realNDFieldCache.getOrPut(shape) { RealNDField(shape) } /** * Create a nd-field with boxing generic buffer */ - fun <T : Any, F : Field<T>> boxing( + public fun <T : Any, F : Field<T>> boxing( field: F, vararg shape: Int, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing @@ -145,7 +136,7 @@ interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F, * Create a most suitable implementation for nd-field using reified class. */ @Suppress("UNCHECKED_CAST") - inline fun <reified T : Any, F : Field<T>> auto(field: F, vararg shape: Int): BufferedNDField<T, F> = + public inline fun <reified T : Any, F : Field<T>> auto(field: F, vararg shape: Int): BufferedNDField<T, F> = when { T::class == Double::class -> real(*shape) as BufferedNDField<T, F> T::class == Complex::class -> complex(*shape) as BufferedNDField<T, F> diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDElement.kt similarity index 65% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDElement.kt index 6cc0a72c0..f2f565064 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDElement.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +import kscience.kmath.operations.Field +import kscience.kmath.operations.RealField +import kscience.kmath.operations.Ring +import kscience.kmath.operations.Space /** * The root for all [NDStructure] based algebra elements. Does not implement algebra element root because of problems with recursive self-types @@ -11,31 +11,30 @@ import scientifik.kmath.operations.Space * @param C the type of the context for the element * @param N the type of the underlying [NDStructure] */ -interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> { +public interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> { + public val context: NDAlgebra<T, C, N> - val context: NDAlgebra<T, C, N> + public fun unwrap(): N - fun unwrap(): N + public fun N.wrap(): NDElement<T, C, N> - fun N.wrap(): NDElement<T, C, N> - - companion object { + public companion object { /** * Create a optimized NDArray of doubles */ - fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }): RealNDElement = + public fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }): RealNDElement = NDField.real(*shape).produce(initializer) - inline fun real1D(dim: Int, crossinline initializer: (Int) -> Double = { _ -> 0.0 }): RealNDElement = + public inline fun real1D(dim: Int, crossinline initializer: (Int) -> Double = { _ -> 0.0 }): RealNDElement = real(intArrayOf(dim)) { initializer(it[0]) } - inline fun real2D( + public inline fun real2D( dim1: Int, dim2: Int, crossinline initializer: (Int, Int) -> Double = { _, _ -> 0.0 } ): RealNDElement = real(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } - inline fun real3D( + public inline fun real3D( dim1: Int, dim2: Int, dim3: Int, @@ -46,7 +45,7 @@ interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> { /** * Simple boxing NDArray */ - fun <T : Any, F : Field<T>> boxing( + public fun <T : Any, F : Field<T>> boxing( shape: IntArray, field: F, initializer: F.(IntArray) -> T @@ -55,7 +54,7 @@ interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> { return ndField.produce(initializer) } - inline fun <reified T : Any, F : Field<T>> auto( + public inline fun <reified T : Any, F : Field<T>> auto( shape: IntArray, field: F, noinline initializer: F.(IntArray) -> T @@ -66,17 +65,16 @@ interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> { } } - -fun <T, C, N : NDStructure<T>> NDElement<T, C, N>.mapIndexed(transform: C.(index: IntArray, T) -> T): NDElement<T, C, N> = +public fun <T, C, N : NDStructure<T>> NDElement<T, C, N>.mapIndexed(transform: C.(index: IntArray, T) -> T): NDElement<T, C, N> = context.mapIndexed(unwrap(), transform).wrap() -fun <T, C, N : NDStructure<T>> NDElement<T, C, N>.map(transform: C.(T) -> T): NDElement<T, C, N> = +public fun <T, C, N : NDStructure<T>> NDElement<T, C, N>.map(transform: C.(T) -> T): NDElement<T, C, N> = context.map(unwrap(), transform).wrap() /** * Element by element application of any operation on elements to the whole [NDElement] */ -operator fun <T, C, N : NDStructure<T>> Function1<T, T>.invoke(ndElement: NDElement<T, C, N>): NDElement<T, C, N> = +public operator fun <T, C, N : NDStructure<T>> Function1<T, T>.invoke(ndElement: NDElement<T, C, N>): NDElement<T, C, N> = ndElement.map { value -> this@invoke(value) } /* plus and minus */ @@ -84,13 +82,13 @@ operator fun <T, C, N : NDStructure<T>> Function1<T, T>.invoke(ndElement: NDElem /** * Summation operation for [NDElement] and single element */ -operator fun <T, S : Space<T>, N : NDStructure<T>> NDElement<T, S, N>.plus(arg: T): NDElement<T, S, N> = +public operator fun <T, S : Space<T>, N : NDStructure<T>> NDElement<T, S, N>.plus(arg: T): NDElement<T, S, N> = map { value -> arg + value } /** * Subtraction operation between [NDElement] and single element */ -operator fun <T, S : Space<T>, N : NDStructure<T>> NDElement<T, S, N>.minus(arg: T): NDElement<T, S, N> = +public operator fun <T, S : Space<T>, N : NDStructure<T>> NDElement<T, S, N>.minus(arg: T): NDElement<T, S, N> = map { value -> arg - value } /* prod and div */ @@ -98,13 +96,13 @@ operator fun <T, S : Space<T>, N : NDStructure<T>> NDElement<T, S, N>.minus(arg: /** * Product operation for [NDElement] and single element */ -operator fun <T, R : Ring<T>, N : NDStructure<T>> NDElement<T, R, N>.times(arg: T): NDElement<T, R, N> = +public operator fun <T, R : Ring<T>, N : NDStructure<T>> NDElement<T, R, N>.times(arg: T): NDElement<T, R, N> = map { value -> arg * value } /** * Division operation between [NDElement] and single element */ -operator fun <T, F : Field<T>, N : NDStructure<T>> NDElement<T, F, N>.div(arg: T): NDElement<T, F, N> = +public operator fun <T, F : Field<T>, N : NDStructure<T>> NDElement<T, F, N>.div(arg: T): NDElement<T, F, N> = map { value -> arg / value } // /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt similarity index 80% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt index 01a692a65..4ab49c9bc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt @@ -1,6 +1,5 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import kotlin.contracts.contract import kotlin.jvm.JvmName import kotlin.reflect.KClass @@ -11,17 +10,17 @@ import kotlin.reflect.KClass * * @param T the type of items. */ -interface NDStructure<T> { +public interface NDStructure<T> { /** * The shape of structure, i.e. non-empty sequence of non-negative integers that specify sizes of dimensions of * this structure. */ - val shape: IntArray + public val shape: IntArray /** * The count of dimensions in this structure. It should be equal to size of [shape]. */ - val dimension: Int get() = shape.size + public val dimension: Int get() = shape.size /** * Returns the value at the specified indices. @@ -29,24 +28,24 @@ interface NDStructure<T> { * @param index the indices. * @return the value. */ - operator fun get(index: IntArray): T + public operator fun get(index: IntArray): T /** * Returns the sequence of all the elements associated by their indices. * * @return the lazy sequence of pairs of indices to values. */ - fun elements(): Sequence<Pair<IntArray, T>> + public fun elements(): Sequence<Pair<IntArray, T>> override fun equals(other: Any?): Boolean override fun hashCode(): Int - companion object { + public companion object { /** * Indicates whether some [NDStructure] is equal to another one. */ - fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean { + public fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean { if (st1 === st2) return true // fast comparison of buffers if possible @@ -67,7 +66,7 @@ interface NDStructure<T> { * * Strides should be reused if possible. */ - fun <T> build( + public fun <T> build( strides: Strides, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing, initializer: (IntArray) -> T @@ -77,39 +76,39 @@ interface NDStructure<T> { /** * Inline create NDStructure with non-boxing buffer implementation if it is possible */ - inline fun <reified T : Any> auto( + public inline fun <reified T : Any> auto( strides: Strides, crossinline initializer: (IntArray) -> T ): BufferNDStructure<T> = BufferNDStructure(strides, Buffer.auto(strides.linearSize) { i -> initializer(strides.index(i)) }) - inline fun <T : Any> auto( + public inline fun <T : Any> auto( type: KClass<T>, strides: Strides, crossinline initializer: (IntArray) -> T ): BufferNDStructure<T> = BufferNDStructure(strides, Buffer.auto(type, strides.linearSize) { i -> initializer(strides.index(i)) }) - fun <T> build( + public fun <T> build( shape: IntArray, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing, initializer: (IntArray) -> T ): BufferNDStructure<T> = build(DefaultStrides(shape), bufferFactory, initializer) - inline fun <reified T : Any> auto( + public inline fun <reified T : Any> auto( shape: IntArray, crossinline initializer: (IntArray) -> T ): BufferNDStructure<T> = auto(DefaultStrides(shape), initializer) @JvmName("autoVarArg") - inline fun <reified T : Any> auto( + public inline fun <reified T : Any> auto( vararg shape: Int, crossinline initializer: (IntArray) -> T ): BufferNDStructure<T> = auto(DefaultStrides(shape), initializer) - inline fun <T : Any> auto( + public inline fun <T : Any> auto( type: KClass<T>, vararg shape: Int, crossinline initializer: (IntArray) -> T @@ -124,68 +123,68 @@ interface NDStructure<T> { * @param index the indices. * @return the value. */ -operator fun <T> NDStructure<T>.get(vararg index: Int): T = get(index) +public operator fun <T> NDStructure<T>.get(vararg index: Int): T = get(index) /** * Represents mutable [NDStructure]. */ -interface MutableNDStructure<T> : NDStructure<T> { +public interface MutableNDStructure<T> : NDStructure<T> { /** * Inserts an item at the specified indices. * * @param index the indices. * @param value the value. */ - operator fun set(index: IntArray, value: T) + public operator fun set(index: IntArray, value: T) } -inline fun <T> MutableNDStructure<T>.mapInPlace(action: (IntArray, T) -> T) { - contract { callsInPlace(action) } +public inline fun <T> MutableNDStructure<T>.mapInPlace(action: (IntArray, T) -> T): Unit = elements().forEach { (index, oldValue) -> this[index] = action(index, oldValue) } -} /** * A way to convert ND index to linear one and back. */ -interface Strides { +public interface Strides { /** * Shape of NDstructure */ - val shape: IntArray + public val shape: IntArray /** * Array strides */ - val strides: List<Int> + public val strides: List<Int> /** * Get linear index from multidimensional index */ - fun offset(index: IntArray): Int + public fun offset(index: IntArray): Int /** * Get multidimensional from linear */ - fun index(offset: Int): IntArray + public fun index(offset: Int): IntArray /** * The size of linear buffer to accommodate all elements of ND-structure corresponding to strides */ - val linearSize: Int + public val linearSize: Int + + // TODO introduce a fast way to calculate index of the next element? /** * Iterate over ND indices in a natural order */ - fun indices(): Sequence<IntArray> { - //TODO introduce a fast way to calculate index of the next element? - return (0 until linearSize).asSequence().map { index(it) } - } + public fun indices(): Sequence<IntArray> = (0 until linearSize).asSequence().map { index(it) } } /** * Simple implementation of [Strides]. */ -class DefaultStrides private constructor(override val shape: IntArray) : Strides { +public class DefaultStrides private constructor(override val shape: IntArray) : Strides { + override val linearSize: Int + get() = strides[shape.size] + /** * Strides for memory access */ @@ -193,6 +192,7 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides sequence { var current = 1 yield(1) + shape.forEach { current *= it yield(current) @@ -211,17 +211,16 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides val res = IntArray(shape.size) var current = offset var strideIndex = strides.size - 2 + while (strideIndex >= 0) { res[strideIndex] = (current / strides[strideIndex]) current %= strides[strideIndex] strideIndex-- } + return res } - override val linearSize: Int - get() = strides[shape.size] - override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is DefaultStrides) return false @@ -231,13 +230,14 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides override fun hashCode(): Int = shape.contentHashCode() - companion object { + public companion object { private val defaultStridesCache = HashMap<IntArray, Strides>() /** * Cached builder for default strides */ - operator fun invoke(shape: IntArray): Strides = defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } + public operator fun invoke(shape: IntArray): Strides = + defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } } } @@ -246,16 +246,16 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides * * @param T the type of items. */ -abstract class NDBuffer<T> : NDStructure<T> { +public abstract class NDBuffer<T> : NDStructure<T> { /** * The underlying buffer. */ - abstract val buffer: Buffer<T> + public abstract val buffer: Buffer<T> /** * The strides to access elements of [Buffer] by linear indices. */ - abstract val strides: Strides + public abstract val strides: Strides override operator fun get(index: IntArray): T = buffer[strides.offset(index)] @@ -277,7 +277,7 @@ abstract class NDBuffer<T> : NDStructure<T> { /** * Boxing generic [NDStructure] */ -class BufferNDStructure<T>( +public class BufferNDStructure<T>( override val strides: Strides, override val buffer: Buffer<T> ) : NDBuffer<T>() { @@ -291,13 +291,13 @@ class BufferNDStructure<T>( /** * Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferNDStructure] */ -inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer( +public inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer( factory: BufferFactory<R> = Buffer.Companion::auto, crossinline transform: (T) -> R ): BufferNDStructure<R> { - return if (this is BufferNDStructure<T>) { + return if (this is BufferNDStructure<T>) BufferNDStructure(this.strides, factory.invoke(strides.linearSize) { transform(buffer[it]) }) - } else { + else { val strides = DefaultStrides(shape) BufferNDStructure(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) } @@ -306,7 +306,7 @@ inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer( /** * Mutable ND buffer based on linear [MutableBuffer]. */ -class MutableBufferNDStructure<T>( +public class MutableBufferNDStructure<T>( override val strides: Strides, override val buffer: MutableBuffer<T> ) : NDBuffer<T>(), MutableNDStructure<T> { @@ -320,7 +320,7 @@ class MutableBufferNDStructure<T>( override operator fun set(index: IntArray, value: T): Unit = buffer.set(strides.offset(index), value) } -inline fun <reified T : Any> NDStructure<T>.combine( +public inline fun <reified T : Any> NDStructure<T>.combine( struct: NDStructure<T>, crossinline block: (T, T) -> T ): NDStructure<T> { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBuffer.kt similarity index 70% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBuffer.kt index 3c92fb0ce..2a03e2dd3 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBuffer.kt @@ -1,13 +1,11 @@ -package scientifik.kmath.structures - -import kotlin.contracts.contract +package kscience.kmath.structures /** * Specialized [MutableBuffer] implementation over [DoubleArray]. * * @property array the underlying array. */ -inline class RealBuffer(val array: DoubleArray) : MutableBuffer<Double> { +public inline class RealBuffer(public val array: DoubleArray) : MutableBuffer<Double> { override val size: Int get() = array.size override operator fun get(index: Int): Double = array[index] @@ -29,20 +27,17 @@ inline class RealBuffer(val array: DoubleArray) : MutableBuffer<Double> { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer { - contract { callsInPlace(init) } - return RealBuffer(DoubleArray(size) { init(it) }) -} +public inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer = RealBuffer(DoubleArray(size) { init(it) }) /** * Returns a new [RealBuffer] of given elements. */ -fun RealBuffer(vararg doubles: Double): RealBuffer = RealBuffer(doubles) +public fun RealBuffer(vararg doubles: Double): RealBuffer = RealBuffer(doubles) /** * Returns a [DoubleArray] containing all of the elements of this [MutableBuffer]. */ -val MutableBuffer<out Double>.array: DoubleArray +public val MutableBuffer<out Double>.array: DoubleArray get() = (if (this is RealBuffer) array else DoubleArray(size) { get(it) }) /** @@ -51,4 +46,4 @@ val MutableBuffer<out Double>.array: DoubleArray * @receiver the array. * @return the new buffer. */ -fun DoubleArray.asBuffer(): RealBuffer = RealBuffer(this) +public fun DoubleArray.asBuffer(): RealBuffer = RealBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBufferField.kt similarity index 62% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBufferField.kt index a11826e7e..f7b2ee31e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBufferField.kt @@ -1,15 +1,14 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.ExtendedField -import scientifik.kmath.operations.ExtendedFieldOperations +import kscience.kmath.operations.ExtendedField +import kscience.kmath.operations.ExtendedFieldOperations import kotlin.math.* - /** * [ExtendedFieldOperations] over [RealBuffer]. */ -object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> { - override fun add(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { +public object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> { + public override fun add(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } @@ -21,7 +20,7 @@ object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> { } else RealBuffer(DoubleArray(a.size) { a[it] + b[it] }) } - override fun multiply(a: Buffer<Double>, k: Number): RealBuffer { + public override fun multiply(a: Buffer<Double>, k: Number): RealBuffer { val kValue = k.toDouble() return if (a is RealBuffer) { @@ -30,7 +29,7 @@ object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> { } else RealBuffer(DoubleArray(a.size) { a[it] * kValue }) } - override fun multiply(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { + public override fun multiply(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } @@ -43,7 +42,7 @@ object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> { RealBuffer(DoubleArray(a.size) { a[it] * b[it] }) } - override fun divide(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { + public override fun divide(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } @@ -55,84 +54,91 @@ object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> { } else RealBuffer(DoubleArray(a.size) { a[it] / b[it] }) } - override fun sin(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun sin(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { sin(array[it]) }) } else RealBuffer(DoubleArray(arg.size) { sin(arg[it]) }) - override fun cos(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun cos(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { cos(array[it]) }) } else RealBuffer(DoubleArray(arg.size) { cos(arg[it]) }) - override fun tan(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun tan(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { tan(array[it]) }) } else RealBuffer(DoubleArray(arg.size) { tan(arg[it]) }) - override fun asin(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun asin(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { asin(array[it]) }) - } else { + } else RealBuffer(DoubleArray(arg.size) { asin(arg[it]) }) - } - override fun acos(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun acos(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { acos(array[it]) }) } else RealBuffer(DoubleArray(arg.size) { acos(arg[it]) }) - override fun atan(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun atan(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { atan(array[it]) }) } else RealBuffer(DoubleArray(arg.size) { atan(arg[it]) }) - override fun sinh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun sinh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { sinh(array[it]) }) - } else RealBuffer(DoubleArray(arg.size) { sinh(arg[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { sinh(arg[it]) }) - override fun cosh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun cosh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { cosh(array[it]) }) - } else RealBuffer(DoubleArray(arg.size) { cosh(arg[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { cosh(arg[it]) }) - override fun tanh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun tanh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { tanh(array[it]) }) - } else RealBuffer(DoubleArray(arg.size) { tanh(arg[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { tanh(arg[it]) }) - override fun asinh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun asinh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { asinh(array[it]) }) - } else RealBuffer(DoubleArray(arg.size) { asinh(arg[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { asinh(arg[it]) }) - override fun acosh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun acosh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { acosh(array[it]) }) - } else RealBuffer(DoubleArray(arg.size) { acosh(arg[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { acosh(arg[it]) }) - override fun atanh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun atanh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { atanh(array[it]) }) - } else RealBuffer(DoubleArray(arg.size) { atanh(arg[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { atanh(arg[it]) }) - override fun power(arg: Buffer<Double>, pow: Number): RealBuffer = if (arg is RealBuffer) { + public override fun power(arg: Buffer<Double>, pow: Number): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) }) - } else RealBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) + } else + RealBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) - override fun exp(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun exp(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { exp(array[it]) }) } else RealBuffer(DoubleArray(arg.size) { exp(arg[it]) }) - override fun ln(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { + public override fun ln(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { ln(array[it]) }) - } else RealBuffer(DoubleArray(arg.size) { ln(arg[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { ln(arg[it]) }) } /** @@ -140,101 +146,101 @@ object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> { * * @property size the size of buffers to operate on. */ -class RealBufferField(val size: Int) : ExtendedField<Buffer<Double>> { - override val zero: Buffer<Double> by lazy { RealBuffer(size) { 0.0 } } - override val one: Buffer<Double> by lazy { RealBuffer(size) { 1.0 } } +public class RealBufferField(public val size: Int) : ExtendedField<Buffer<Double>> { + public override val zero: Buffer<Double> by lazy { RealBuffer(size) { 0.0 } } + public override val one: Buffer<Double> by lazy { RealBuffer(size) { 1.0 } } - override fun add(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { + public override fun add(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.add(a, b) } - override fun multiply(a: Buffer<Double>, k: Number): RealBuffer { + public override fun multiply(a: Buffer<Double>, k: Number): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.multiply(a, k) } - override fun multiply(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { + public override fun multiply(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.multiply(a, b) } - override fun divide(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { + public override fun divide(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.divide(a, b) } - override fun sin(arg: Buffer<Double>): RealBuffer { + public override fun sin(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.sin(arg) } - override fun cos(arg: Buffer<Double>): RealBuffer { + public override fun cos(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.cos(arg) } - override fun tan(arg: Buffer<Double>): RealBuffer { + public override fun tan(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.tan(arg) } - override fun asin(arg: Buffer<Double>): RealBuffer { + public override fun asin(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.asin(arg) } - override fun acos(arg: Buffer<Double>): RealBuffer { + public override fun acos(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.acos(arg) } - override fun atan(arg: Buffer<Double>): RealBuffer { + public override fun atan(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.atan(arg) } - override fun sinh(arg: Buffer<Double>): RealBuffer { + public override fun sinh(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.sinh(arg) } - override fun cosh(arg: Buffer<Double>): RealBuffer { + public override fun cosh(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.cosh(arg) } - override fun tanh(arg: Buffer<Double>): RealBuffer { + public override fun tanh(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.tanh(arg) } - override fun asinh(arg: Buffer<Double>): RealBuffer { + public override fun asinh(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.asinh(arg) } - override fun acosh(arg: Buffer<Double>): RealBuffer { + public override fun acosh(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.acosh(arg) } - override fun atanh(arg: Buffer<Double>): RealBuffer { + public override fun atanh(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.atanh(arg) } - override fun power(arg: Buffer<Double>, pow: Number): RealBuffer { + public override fun power(arg: Buffer<Double>, pow: Number): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.power(arg, pow) } - override fun exp(arg: Buffer<Double>): RealBuffer { + public override fun exp(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.exp(arg) } - override fun ln(arg: Buffer<Double>): RealBuffer { + public override fun ln(arg: Buffer<Double>): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.ln(arg) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt similarity index 79% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt index 6533f64be..ed28fb9f2 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt @@ -1,11 +1,11 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.FieldElement -import scientifik.kmath.operations.RealField +import kscience.kmath.operations.FieldElement +import kscience.kmath.operations.RealField -typealias RealNDElement = BufferedNDFieldElement<Double, RealField> +public typealias RealNDElement = BufferedNDFieldElement<Double, RealField> -class RealNDField(override val shape: IntArray) : +public class RealNDField(override val shape: IntArray) : BufferedNDField<Double, RealField>, ExtendedNDField<Double, RealField, NDBuffer<Double>> { @@ -15,7 +15,7 @@ class RealNDField(override val shape: IntArray) : override val zero: RealNDElement by lazy { produce { zero } } override val one: RealNDElement by lazy { produce { one } } - inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer<Double> = + public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer<Double> = RealBuffer(DoubleArray(size) { initializer(it) }) /** @@ -90,7 +90,7 @@ class RealNDField(override val shape: IntArray) : /** * Fast element production using function inlining */ -inline fun BufferedNDField<Double, RealField>.produceInline(crossinline initializer: RealField.(Int) -> Double): RealNDElement { +public inline fun BufferedNDField<Double, RealField>.produceInline(crossinline initializer: RealField.(Int) -> Double): RealNDElement { val array = DoubleArray(strides.linearSize) { offset -> RealField.initializer(offset) } return BufferedNDFieldElement(this, RealBuffer(array)) } @@ -98,13 +98,13 @@ inline fun BufferedNDField<Double, RealField>.produceInline(crossinline initiali /** * Map one [RealNDElement] using function with indices. */ -inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double): RealNDElement = +public inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double): RealNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** * Map one [RealNDElement] using function without indices. */ -inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double): RealNDElement { +public inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double): RealNDElement { val array = DoubleArray(strides.linearSize) { offset -> RealField.transform(buffer[offset]) } return BufferedNDFieldElement(context, RealBuffer(array)) } @@ -112,26 +112,22 @@ inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double /** * Element by element application of any operation on elements to the whole array. Just like in numpy. */ -operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement): RealNDElement = +public operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement): RealNDElement = ndElement.map { this@invoke(it) } - /* plus and minus */ /** * Summation operation for [BufferedNDElement] and single element */ -operator fun RealNDElement.plus(arg: Double): RealNDElement = - map { it + arg } +public operator fun RealNDElement.plus(arg: Double): RealNDElement = map { it + arg } /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun RealNDElement.minus(arg: Double): RealNDElement = - map { it - arg } +public operator fun RealNDElement.minus(arg: Double): RealNDElement = map { it - arg } /** * Produce a context for n-dimensional operations inside this real field */ - -inline fun <R> RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R = NDField.real(*shape).run(action) +public inline fun <R> RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R = NDField.real(*shape).run(action) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortBuffer.kt similarity index 50% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortBuffer.kt index 1feba1e6b..0d9222320 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortBuffer.kt @@ -1,25 +1,21 @@ -package scientifik.kmath.structures - -import kotlin.contracts.contract +package kscience.kmath.structures /** * Specialized [MutableBuffer] implementation over [ShortArray]. * * @property array the underlying array. */ -inline class ShortBuffer(val array: ShortArray) : MutableBuffer<Short> { - override val size: Int get() = array.size +public inline class ShortBuffer(public val array: ShortArray) : MutableBuffer<Short> { + public override val size: Int get() = array.size - override operator fun get(index: Int): Short = array[index] + public override operator fun get(index: Int): Short = array[index] - override operator fun set(index: Int, value: Short) { + public override operator fun set(index: Int, value: Short) { array[index] = value } - override operator fun iterator(): ShortIterator = array.iterator() - - override fun copy(): MutableBuffer<Short> = - ShortBuffer(array.copyOf()) + public override operator fun iterator(): ShortIterator = array.iterator() + public override fun copy(): MutableBuffer<Short> = ShortBuffer(array.copyOf()) } /** @@ -29,20 +25,17 @@ inline class ShortBuffer(val array: ShortArray) : MutableBuffer<Short> { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun ShortBuffer(size: Int, init: (Int) -> Short): ShortBuffer { - contract { callsInPlace(init) } - return ShortBuffer(ShortArray(size) { init(it) }) -} +public inline fun ShortBuffer(size: Int, init: (Int) -> Short): ShortBuffer = ShortBuffer(ShortArray(size) { init(it) }) /** * Returns a new [ShortBuffer] of given elements. */ -fun ShortBuffer(vararg shorts: Short): ShortBuffer = ShortBuffer(shorts) +public fun ShortBuffer(vararg shorts: Short): ShortBuffer = ShortBuffer(shorts) /** * Returns a [ShortArray] containing all of the elements of this [MutableBuffer]. */ -val MutableBuffer<out Short>.array: ShortArray +public val MutableBuffer<out Short>.array: ShortArray get() = (if (this is ShortBuffer) array else ShortArray(size) { get(it) }) /** @@ -51,4 +44,4 @@ val MutableBuffer<out Short>.array: ShortArray * @receiver the array. * @return the new buffer. */ -fun ShortArray.asBuffer(): ShortBuffer = ShortBuffer(this) +public fun ShortArray.asBuffer(): ShortBuffer = ShortBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortNDRing.kt similarity index 74% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortNDRing.kt index f404a2a27..3b506a26a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ShortNDRing.kt @@ -1,21 +1,19 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.RingElement -import scientifik.kmath.operations.ShortRing +import kscience.kmath.operations.RingElement +import kscience.kmath.operations.ShortRing +public typealias ShortNDElement = BufferedNDRingElement<Short, ShortRing> -typealias ShortNDElement = BufferedNDRingElement<Short, ShortRing> - -class ShortNDRing(override val shape: IntArray) : +public class ShortNDRing(override val shape: IntArray) : BufferedNDRing<Short, ShortRing> { override val strides: Strides = DefaultStrides(shape) - override val elementContext: ShortRing get() = ShortRing override val zero: ShortNDElement by lazy { produce { zero } } override val one: ShortNDElement by lazy { produce { one } } - inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Short): Buffer<Short> = + public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Short): Buffer<Short> = ShortBuffer(ShortArray(size) { initializer(it) }) /** @@ -70,15 +68,13 @@ class ShortNDRing(override val shape: IntArray) : /** * Fast element production using function inlining. */ -inline fun BufferedNDRing<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): ShortNDElement { - val array = ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) } - return BufferedNDRingElement(this, ShortBuffer(array)) -} +public inline fun BufferedNDRing<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): ShortNDElement = + BufferedNDRingElement(this, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) })) /** * Element by element application of any operation on elements to the whole array. */ -operator fun Function1<Short, Short>.invoke(ndElement: ShortNDElement): ShortNDElement = +public operator fun Function1<Short, Short>.invoke(ndElement: ShortNDElement): ShortNDElement = ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) } @@ -87,11 +83,11 @@ operator fun Function1<Short, Short>.invoke(ndElement: ShortNDElement): ShortNDE /** * Summation operation for [ShortNDElement] and single element. */ -operator fun ShortNDElement.plus(arg: Short): ShortNDElement = +public operator fun ShortNDElement.plus(arg: Short): ShortNDElement = context.produceInline { i -> (buffer[i] + arg).toShort() } /** * Subtraction operation between [ShortNDElement] and single element. */ -operator fun ShortNDElement.minus(arg: Short): ShortNDElement = +public operator fun ShortNDElement.minus(arg: Short): ShortNDElement = context.produceInline { i -> (buffer[i] - arg).toShort() } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure1D.kt similarity index 63% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure1D.kt index a796c2037..af5cc9e3f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure1D.kt @@ -1,29 +1,27 @@ -package scientifik.kmath.structures +package kscience.kmath.structures /** * A structure that is guaranteed to be one-dimensional */ -interface Structure1D<T> : NDStructure<T>, Buffer<T> { - override val dimension: Int get() = 1 +public interface Structure1D<T> : NDStructure<T>, Buffer<T> { + public override val dimension: Int get() = 1 - override operator fun get(index: IntArray): T { + public override operator fun get(index: IntArray): T { require(index.size == 1) { "Index dimension mismatch. Expected 1 but found ${index.size}" } return get(index[0]) } - override operator fun iterator(): Iterator<T> = (0 until size).asSequence().map { get(it) }.iterator() + public override operator fun iterator(): Iterator<T> = (0 until size).asSequence().map(::get).iterator() } /** * A 1D wrapper for nd-structure */ -private inline class Structure1DWrapper<T>(val structure: NDStructure<T>) : Structure1D<T> { - +private inline class Structure1DWrapper<T>(public val structure: NDStructure<T>) : Structure1D<T> { override val shape: IntArray get() = structure.shape override val size: Int get() = structure.shape[0] override operator fun get(index: Int): T = structure[index] - override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements() } @@ -33,7 +31,6 @@ private inline class Structure1DWrapper<T>(val structure: NDStructure<T>) : Stru */ private inline class Buffer1DWrapper<T>(val buffer: Buffer<T>) : Structure1D<T> { override val shape: IntArray get() = intArrayOf(buffer.size) - override val size: Int get() = buffer.size override fun elements(): Sequence<Pair<IntArray, T>> = @@ -45,18 +42,12 @@ private inline class Buffer1DWrapper<T>(val buffer: Buffer<T>) : Structure1D<T> /** * Represent a [NDStructure] as [Structure1D]. Throw error in case of dimension mismatch */ -fun <T> NDStructure<T>.as1D(): Structure1D<T> = if (shape.size == 1) { - if (this is NDBuffer) { - Buffer1DWrapper(this.buffer) - } else { - Structure1DWrapper(this) - } -} else { +public fun <T> NDStructure<T>.as1D(): Structure1D<T> = if (shape.size == 1) { + if (this is NDBuffer) Buffer1DWrapper(this.buffer) else Structure1DWrapper(this) +} else error("Can't create 1d-structure from ${shape.size}d-structure") -} - /** * Represent this buffer as 1D structure */ -fun <T> Buffer<T>.asND(): Structure1D<T> = Buffer1DWrapper(this) +public fun <T> Buffer<T>.asND(): Structure1D<T> = Buffer1DWrapper(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt similarity index 53% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt index eeb6bd3dc..25fdf3f3d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt @@ -1,38 +1,31 @@ -package scientifik.kmath.structures +package kscience.kmath.structures /** * A structure that is guaranteed to be two-dimensional */ -interface Structure2D<T> : NDStructure<T> { - val rowNum: Int get() = shape[0] - val colNum: Int get() = shape[1] +public interface Structure2D<T> : NDStructure<T> { + public val rowNum: Int get() = shape[0] + public val colNum: Int get() = shape[1] - operator fun get(i: Int, j: Int): T + public operator fun get(i: Int, j: Int): T override operator fun get(index: IntArray): T { require(index.size == 2) { "Index dimension mismatch. Expected 2 but found ${index.size}" } return get(index[0], index[1]) } - val rows: Buffer<Buffer<T>> - get() = VirtualBuffer(rowNum) { i -> - VirtualBuffer(colNum) { j -> get(i, j) } - } + public val rows: Buffer<Buffer<T>> + get() = VirtualBuffer(rowNum) { i -> VirtualBuffer(colNum) { j -> get(i, j) } } - val columns: Buffer<Buffer<T>> - get() = VirtualBuffer(colNum) { j -> - VirtualBuffer(rowNum) { i -> get(i, j) } - } + public val columns: Buffer<Buffer<T>> + get() = VirtualBuffer(colNum) { j -> VirtualBuffer(rowNum) { i -> get(i, j) } } override fun elements(): Sequence<Pair<IntArray, T>> = sequence { - for (i in (0 until rowNum)) { - for (j in (0 until colNum)) { - yield(intArrayOf(i, j) to get(i, j)) - } - } + for (i in (0 until rowNum)) + for (j in (0 until colNum)) yield(intArrayOf(i, j) to get(i, j)) } - companion object + public companion object } /** @@ -49,10 +42,9 @@ private inline class Structure2DWrapper<T>(val structure: NDStructure<T>) : Stru /** * Represent a [NDStructure] as [Structure1D]. Throw error in case of dimension mismatch */ -fun <T> NDStructure<T>.as2D(): Structure2D<T> = if (shape.size == 2) { +public fun <T> NDStructure<T>.as2D(): Structure2D<T> = if (shape.size == 2) Structure2DWrapper(this) -} else { +else error("Can't create 2d-structure from ${shape.size}d-structure") -} -typealias Matrix<T> = Structure2D<T> +public typealias Matrix<T> = Structure2D<T> diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt deleted file mode 100644 index 595a3dbe7..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2015 Alexander Nozik. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package scientifik.kmath.domains - -import scientifik.kmath.linear.Point - -class UnconstrainedDomain(override val dimension: Int) : RealDomain { - override operator fun contains(point: Point<Double>): Boolean = true - - override fun getLowerBound(num: Int, point: Point<Double>): Double? = Double.NEGATIVE_INFINITY - - override fun getLowerBound(num: Int): Double? = Double.NEGATIVE_INFINITY - - override fun getUpperBound(num: Int, point: Point<Double>): Double? = Double.POSITIVE_INFINITY - - override fun getUpperBound(num: Int): Double? = Double.POSITIVE_INFINITY - - override fun nearestInDomain(point: Point<Double>): Point<Double> = point - - override fun volume(): Double = Double.POSITIVE_INFINITY -} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt deleted file mode 100644 index 280dc7d66..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt +++ /dev/null @@ -1,47 +0,0 @@ -package scientifik.kmath.domains - -import scientifik.kmath.linear.Point -import scientifik.kmath.structures.asBuffer - -inline class UnivariateDomain(val range: ClosedFloatingPointRange<Double>) : RealDomain { - operator fun contains(d: Double): Boolean = range.contains(d) - - override operator fun contains(point: Point<Double>): Boolean { - require(point.size == 0) - return contains(point[0]) - } - - override fun nearestInDomain(point: Point<Double>): Point<Double> { - require(point.size == 1) - val value = point[0] - return when { - value in range -> point - value >= range.endInclusive -> doubleArrayOf(range.endInclusive).asBuffer() - else -> doubleArrayOf(range.start).asBuffer() - } - } - - override fun getLowerBound(num: Int, point: Point<Double>): Double? { - require(num == 0) - return range.start - } - - override fun getUpperBound(num: Int, point: Point<Double>): Double? { - require(num == 0) - return range.endInclusive - } - - override fun getLowerBound(num: Int): Double? { - require(num == 0) - return range.start - } - - override fun getUpperBound(num: Int): Double? { - require(num == 0) - return range.endInclusive - } - - override fun volume(): Double = range.endInclusive - range.start - - override val dimension: Int get() = 1 -} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt deleted file mode 100644 index fd11c246d..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ /dev/null @@ -1,49 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.operations.Algebra - -/** - * An elementary function that could be invoked on a map of arguments - */ -interface Expression<T> { - /** - * Calls this expression from arguments. - * - * @param arguments the map of arguments. - * @return the value. - */ - operator fun invoke(arguments: Map<String, T>): T - - companion object -} - -/** - * Create simple lazily evaluated expression inside given algebra - */ -fun <T> Algebra<T>.expression(block: Algebra<T>.(arguments: Map<String, T>) -> T): Expression<T> = - object : Expression<T> { - override operator fun invoke(arguments: Map<String, T>): T = block(arguments) - } - -/** - * Calls this expression from arguments. - * - * @param pairs the pair of arguments' names to values. - * @return the value. - */ -operator fun <T> Expression<T>.invoke(vararg pairs: Pair<String, T>): T = invoke(mapOf(*pairs)) - -/** - * A context for expression construction - */ -interface ExpressionAlgebra<T, E> : Algebra<E> { - /** - * Introduce a variable into expression context - */ - fun variable(name: String, default: T? = null): E - - /** - * A constant expression which does not depend on arguments - */ - fun const(value: T): E -} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt deleted file mode 100644 index d36c31a0d..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ /dev/null @@ -1,169 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.operations.* - -internal class FunctionalUnaryOperation<T>(val context: Algebra<T>, val name: String, private val expr: Expression<T>) : - Expression<T> { - override operator fun invoke(arguments: Map<String, T>): T = context.unaryOperation(name, expr.invoke(arguments)) -} - -internal class FunctionalBinaryOperation<T>( - val context: Algebra<T>, - val name: String, - val first: Expression<T>, - val second: Expression<T> -) : Expression<T> { - override operator fun invoke(arguments: Map<String, T>): T = - context.binaryOperation(name, first.invoke(arguments), second.invoke(arguments)) -} - -internal class FunctionalVariableExpression<T>(val name: String, val default: T? = null) : Expression<T> { - override operator fun invoke(arguments: Map<String, T>): T = - arguments[name] ?: default ?: error("Parameter not found: $name") -} - -internal class FunctionalConstantExpression<T>(val value: T) : Expression<T> { - override operator fun invoke(arguments: Map<String, T>): T = value -} - -internal class FunctionalConstProductExpression<T>( - val context: Space<T>, - private val expr: Expression<T>, - val const: Number -) : Expression<T> { - override operator fun invoke(arguments: Map<String, T>): T = context.multiply(expr.invoke(arguments), const) -} - -/** - * A context class for [Expression] construction. - * - * @param algebra The algebra to provide for Expressions built. - */ -abstract class FunctionalExpressionAlgebra<T, A : Algebra<T>>(val algebra: A) : ExpressionAlgebra<T, Expression<T>> { - /** - * Builds an Expression of constant expression which does not depend on arguments. - */ - override fun const(value: T): Expression<T> = FunctionalConstantExpression(value) - - /** - * Builds an Expression to access a variable. - */ - override fun variable(name: String, default: T?): Expression<T> = FunctionalVariableExpression(name, default) - - /** - * Builds an Expression of dynamic call of binary operation [operation] on [left] and [right]. - */ - override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = - FunctionalBinaryOperation(algebra, operation, left, right) - - /** - * Builds an Expression of dynamic call of unary operation with name [operation] on [arg]. - */ - override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = - FunctionalUnaryOperation(algebra, operation, arg) -} - -/** - * A context class for [Expression] construction for [Space] algebras. - */ -open class FunctionalExpressionSpace<T, A : Space<T>>(algebra: A) : - FunctionalExpressionAlgebra<T, A>(algebra), Space<Expression<T>> { - override val zero: Expression<T> get() = const(algebra.zero) - - /** - * Builds an Expression of addition of two another expressions. - */ - override fun add(a: Expression<T>, b: Expression<T>): Expression<T> = - binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) - - /** - * Builds an Expression of multiplication of expression by number. - */ - override fun multiply(a: Expression<T>, k: Number): Expression<T> = - FunctionalConstProductExpression(algebra, a, k) - - operator fun Expression<T>.plus(arg: T): Expression<T> = this + const(arg) - operator fun Expression<T>.minus(arg: T): Expression<T> = this - const(arg) - operator fun T.plus(arg: Expression<T>): Expression<T> = arg + this - operator fun T.minus(arg: Expression<T>): Expression<T> = arg - this - - override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = - super<FunctionalExpressionAlgebra>.unaryOperation(operation, arg) - - override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = - super<FunctionalExpressionAlgebra>.binaryOperation(operation, left, right) -} - -open class FunctionalExpressionRing<T, A>(algebra: A) : FunctionalExpressionSpace<T, A>(algebra), - Ring<Expression<T>> where A : Ring<T>, A : NumericAlgebra<T> { - override val one: Expression<T> - get() = const(algebra.one) - - /** - * Builds an Expression of multiplication of two expressions. - */ - override fun multiply(a: Expression<T>, b: Expression<T>): Expression<T> = - binaryOperation(RingOperations.TIMES_OPERATION, a, b) - - operator fun Expression<T>.times(arg: T): Expression<T> = this * const(arg) - operator fun T.times(arg: Expression<T>): Expression<T> = arg * this - - override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = - super<FunctionalExpressionSpace>.unaryOperation(operation, arg) - - override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = - super<FunctionalExpressionSpace>.binaryOperation(operation, left, right) -} - -open class FunctionalExpressionField<T, A>(algebra: A) : - FunctionalExpressionRing<T, A>(algebra), - Field<Expression<T>> where A : Field<T>, A : NumericAlgebra<T> { - /** - * Builds an Expression of division an expression by another one. - */ - override fun divide(a: Expression<T>, b: Expression<T>): Expression<T> = - binaryOperation(FieldOperations.DIV_OPERATION, a, b) - - operator fun Expression<T>.div(arg: T): Expression<T> = this / const(arg) - operator fun T.div(arg: Expression<T>): Expression<T> = arg / this - - override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = - super<FunctionalExpressionRing>.unaryOperation(operation, arg) - - override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = - super<FunctionalExpressionRing>.binaryOperation(operation, left, right) -} - -open class FunctionalExpressionExtendedField<T, A>(algebra: A) : - FunctionalExpressionField<T, A>(algebra), - ExtendedField<Expression<T>> where A : ExtendedField<T>, A : NumericAlgebra<T> { - override fun sin(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg) - override fun cos(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.COS_OPERATION, arg) - override fun asin(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.ASIN_OPERATION, arg) - override fun acos(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.ACOS_OPERATION, arg) - override fun atan(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.ATAN_OPERATION, arg) - - override fun power(arg: Expression<T>, pow: Number): Expression<T> = - binaryOperation(PowerOperations.POW_OPERATION, arg, number(pow)) - - override fun exp(arg: Expression<T>): Expression<T> = unaryOperation(ExponentialOperations.EXP_OPERATION, arg) - override fun ln(arg: Expression<T>): Expression<T> = unaryOperation(ExponentialOperations.LN_OPERATION, arg) - - override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> = - super<FunctionalExpressionField>.unaryOperation(operation, arg) - - override fun binaryOperation(operation: String, left: Expression<T>, right: Expression<T>): Expression<T> = - super<FunctionalExpressionField>.binaryOperation(operation, left, right) -} - -inline fun <T, A : Space<T>> A.expressionInSpace(block: FunctionalExpressionSpace<T, A>.() -> Expression<T>): Expression<T> = - FunctionalExpressionSpace(this).block() - -inline fun <T, A : Ring<T>> A.expressionInRing(block: FunctionalExpressionRing<T, A>.() -> Expression<T>): Expression<T> = - FunctionalExpressionRing(this).block() - -inline fun <T, A : Field<T>> A.expressionInField(block: FunctionalExpressionField<T, A>.() -> Expression<T>): Expression<T> = - FunctionalExpressionField(this).block() - -inline fun <T, A : ExtendedField<T>> A.expressionInExtendedField(block: FunctionalExpressionExtendedField<T, A>.() -> Expression<T>): Expression<T> = - FunctionalExpressionExtendedField(this).block() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt deleted file mode 100644 index 343b8287e..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt +++ /dev/null @@ -1,118 +0,0 @@ -package scientifik.kmath.linear - -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.Ring -import scientifik.kmath.structures.* - -/** - * Basic implementation of Matrix space based on [NDStructure] - */ -class BufferMatrixContext<T : Any, R : Ring<T>>( - override val elementContext: R, - private val bufferFactory: BufferFactory<T> -) : GenericMatrixContext<T, R> { - - override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): BufferMatrix<T> { - val buffer = bufferFactory(rows * columns) { offset -> initializer(offset / columns, offset % columns) } - return BufferMatrix(rows, columns, buffer) - } - - override fun point(size: Int, initializer: (Int) -> T): Point<T> = bufferFactory(size, initializer) - - companion object -} - -@Suppress("OVERRIDE_BY_INLINE") -object RealMatrixContext : GenericMatrixContext<Double, RealField> { - - override val elementContext: RealField get() = RealField - - override inline fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): Matrix<Double> { - val buffer = RealBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) } - return BufferMatrix(rows, columns, buffer) - } - - override inline fun point(size: Int, initializer: (Int) -> Double): Point<Double> = RealBuffer(size, initializer) -} - -class BufferMatrix<T : Any>( - override val rowNum: Int, - override val colNum: Int, - val buffer: Buffer<out T>, - override val features: Set<MatrixFeature> = emptySet() -) : FeaturedMatrix<T> { - - init { - if (buffer.size != rowNum * colNum) { - error("Dimension mismatch for matrix structure") - } - } - - override val shape: IntArray get() = intArrayOf(rowNum, colNum) - - override fun suggestFeature(vararg features: MatrixFeature): BufferMatrix<T> = - BufferMatrix(rowNum, colNum, buffer, this.features + features) - - override operator fun get(index: IntArray): T = get(index[0], index[1]) - - override operator fun get(i: Int, j: Int): T = buffer[i * colNum + j] - - override fun elements(): Sequence<Pair<IntArray, T>> = sequence { - for (i in 0 until rowNum) for (j in 0 until colNum) yield(intArrayOf(i, j) to get(i, j)) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - return when (other) { - is NDStructure<*> -> return NDStructure.equals(this, other) - else -> false - } - } - - override fun hashCode(): Int { - var result = buffer.hashCode() - result = 31 * result + features.hashCode() - return result - } - - override fun toString(): String { - return if (rowNum <= 5 && colNum <= 5) { - "Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)\n" + - rows.asSequence().joinToString(prefix = "(", postfix = ")", separator = "\n ") { buffer -> - buffer.asSequence().joinToString(separator = "\t") { it.toString() } - } - } else { - "Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)" - } - } -} - -/** - * Optimized dot product for real matrices - */ -infix fun BufferMatrix<Double>.dot(other: BufferMatrix<Double>): BufferMatrix<Double> { - require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" } - - val array = DoubleArray(this.rowNum * other.colNum) - - //convert to array to insure there is not memory indirection - fun Buffer<out Double>.unsafeArray(): DoubleArray = if (this is RealBuffer) { - array - } else { - DoubleArray(size) { get(it) } - } - - val a = this.buffer.unsafeArray() - val b = other.buffer.unsafeArray() - - for (i in (0 until rowNum)) { - for (j in (0 until other.colNum)) { - for (k in (0 until colNum)) { - array[i * other.colNum + j] += a[i * colNum + k] * b[k * other.colNum + j] - } - } - } - - val buffer = RealBuffer(array) - return BufferMatrix(rowNum, other.colNum, buffer) -} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt deleted file mode 100644 index fb49d18ed..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt +++ /dev/null @@ -1,28 +0,0 @@ -package scientifik.kmath.linear - -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.Matrix -import scientifik.kmath.structures.VirtualBuffer - -typealias Point<T> = Buffer<T> - -/** - * A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors - */ -interface LinearSolver<T : Any> { - fun solve(a: Matrix<T>, b: Matrix<T>): Matrix<T> - fun solve(a: Matrix<T>, b: Point<T>): Point<T> = solve(a, b.asMatrix()).asPoint() - fun inverse(a: Matrix<T>): Matrix<T> -} - -/** - * Convert matrix to vector if it is possible - */ -fun <T : Any> Matrix<T>.asPoint(): Point<T> = - if (this.colNum == 1) { - VirtualBuffer(rowNum) { get(it, 0) } - } else { - error("Can't convert matrix with more than one column to vector") - } - -fun <T : Any> Point<T>.asMatrix(): VirtualMatrix<T> = VirtualMatrix(size, 1) { i, _ -> get(i) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt deleted file mode 100644 index 87cfe21b0..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt +++ /dev/null @@ -1,62 +0,0 @@ -package scientifik.kmath.linear - -/** - * A marker interface representing some matrix feature like diagonal, sparse, zero, etc. Features used to optimize matrix - * operations performance in some cases. - */ -interface MatrixFeature - -/** - * The matrix with this feature is considered to have only diagonal non-null elements - */ -object DiagonalFeature : MatrixFeature - -/** - * Matrix with this feature has all zero elements - */ -object ZeroFeature : MatrixFeature - -/** - * Matrix with this feature have unit elements on diagonal and zero elements in all other places - */ -object UnitFeature : MatrixFeature - -/** - * Inverted matrix feature - */ -interface InverseMatrixFeature<T : Any> : MatrixFeature { - val inverse: FeaturedMatrix<T> -} - -/** - * A determinant container - */ -interface DeterminantFeature<T : Any> : MatrixFeature { - val determinant: T -} - -@Suppress("FunctionName") -fun <T : Any> DeterminantFeature(determinant: T): DeterminantFeature<T> = object : DeterminantFeature<T> { - override val determinant: T = determinant -} - -/** - * Lower triangular matrix - */ -object LFeature : MatrixFeature - -/** - * Upper triangular feature - */ -object UFeature : MatrixFeature - -/** - * TODO add documentation - */ -interface LUPDecompositionFeature<T : Any> : MatrixFeature { - val l: FeaturedMatrix<T> - val u: FeaturedMatrix<T> - val p: FeaturedMatrix<T> -} - -//TODO add sparse matrix feature diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt deleted file mode 100644 index 6ac03f463..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt +++ /dev/null @@ -1,79 +0,0 @@ -package scientifik.kmath.misc - -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.invoke -import kotlin.contracts.contract -import kotlin.jvm.JvmName - -/** - * Generic cumulative operation on iterator. - * - * @param T the type of initial iterable. - * @param R the type of resulting iterable. - * @param initial lazy evaluated. - */ -inline fun <T, R> Iterator<T>.cumulative(initial: R, crossinline operation: (R, T) -> R): Iterator<R> { - contract { callsInPlace(operation) } - - return object : Iterator<R> { - var state: R = initial - - override fun hasNext(): Boolean = this@cumulative.hasNext() - - override fun next(): R { - state = operation(state, this@cumulative.next()) - return state - } - } -} - -inline fun <T, R> Iterable<T>.cumulative(initial: R, crossinline operation: (R, T) -> R): Iterable<R> = - Iterable { this@cumulative.iterator().cumulative(initial, operation) } - -inline fun <T, R> Sequence<T>.cumulative(initial: R, crossinline operation: (R, T) -> R): Sequence<R> = Sequence { - this@cumulative.iterator().cumulative(initial, operation) -} - -fun <T, R> List<T>.cumulative(initial: R, operation: (R, T) -> R): List<R> = - iterator().cumulative(initial, operation).asSequence().toList() - -//Cumulative sum - -/** - * Cumulative sum with custom space - */ -fun <T> Iterable<T>.cumulativeSum(space: Space<T>): Iterable<T> = - space { cumulative(zero) { element: T, sum: T -> sum + element } } - -@JvmName("cumulativeSumOfDouble") -fun Iterable<Double>.cumulativeSum(): Iterable<Double> = cumulative(0.0) { element, sum -> sum + element } - -@JvmName("cumulativeSumOfInt") -fun Iterable<Int>.cumulativeSum(): Iterable<Int> = cumulative(0) { element, sum -> sum + element } - -@JvmName("cumulativeSumOfLong") -fun Iterable<Long>.cumulativeSum(): Iterable<Long> = cumulative(0L) { element, sum -> sum + element } - -fun <T> Sequence<T>.cumulativeSum(space: Space<T>): Sequence<T> = - space { cumulative(zero) { element: T, sum: T -> sum + element } } - -@JvmName("cumulativeSumOfDouble") -fun Sequence<Double>.cumulativeSum(): Sequence<Double> = cumulative(0.0) { element, sum -> sum + element } - -@JvmName("cumulativeSumOfInt") -fun Sequence<Int>.cumulativeSum(): Sequence<Int> = cumulative(0) { element, sum -> sum + element } - -@JvmName("cumulativeSumOfLong") -fun Sequence<Long>.cumulativeSum(): Sequence<Long> = cumulative(0L) { element, sum -> sum + element } - -fun <T> List<T>.cumulativeSum(space: Space<T>): List<T> = - space { cumulative(zero) { element: T, sum: T -> sum + element } } - -@JvmName("cumulativeSumOfDouble") -fun List<Double>.cumulativeSum(): List<Double> = cumulative(0.0) { element, sum -> sum + element } - -@JvmName("cumulativeSumOfInt") -fun List<Int>.cumulativeSum(): List<Int> = cumulative(0) { element, sum -> sum + element } - -@JvmName("cumulativeSumOfLong") -fun List<Long>.cumulativeSum(): List<Long> = cumulative(0L) { element, sum -> sum + element } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt deleted file mode 100644 index 0735a96da..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ /dev/null @@ -1,270 +0,0 @@ -package scientifik.kmath.operations - -import scientifik.kmath.operations.RealField.pow -import kotlin.math.abs -import kotlin.math.pow as kpow - -/** - * Advanced Number-like semifield that implements basic operations. - */ -interface ExtendedFieldOperations<T> : - FieldOperations<T>, - TrigonometricOperations<T>, - HyperbolicOperations<T>, - PowerOperations<T>, - ExponentialOperations<T> { - - override fun tan(arg: T): T = sin(arg) / cos(arg) - override fun tanh(arg: T): T = sinh(arg) / cosh(arg) - - override fun unaryOperation(operation: String, arg: T): T = when (operation) { - TrigonometricOperations.COS_OPERATION -> cos(arg) - TrigonometricOperations.SIN_OPERATION -> sin(arg) - TrigonometricOperations.TAN_OPERATION -> tan(arg) - TrigonometricOperations.ACOS_OPERATION -> acos(arg) - TrigonometricOperations.ASIN_OPERATION -> asin(arg) - TrigonometricOperations.ATAN_OPERATION -> atan(arg) - HyperbolicOperations.COSH_OPERATION -> cosh(arg) - HyperbolicOperations.SINH_OPERATION -> sinh(arg) - HyperbolicOperations.TANH_OPERATION -> tanh(arg) - HyperbolicOperations.ACOSH_OPERATION -> acosh(arg) - HyperbolicOperations.ASINH_OPERATION -> asinh(arg) - HyperbolicOperations.ATANH_OPERATION -> atanh(arg) - PowerOperations.SQRT_OPERATION -> sqrt(arg) - ExponentialOperations.EXP_OPERATION -> exp(arg) - ExponentialOperations.LN_OPERATION -> ln(arg) - else -> super.unaryOperation(operation, arg) - } -} - - -/** - * Advanced Number-like field that implements basic operations. - */ -interface ExtendedField<T> : ExtendedFieldOperations<T>, Field<T> { - override fun sinh(arg: T): T = (exp(arg) - exp(-arg)) / 2 - override fun cosh(arg: T): T = (exp(arg) + exp(-arg)) / 2 - override fun tanh(arg: T): T = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) - override fun asinh(arg: T): T = ln(sqrt(arg * arg + one) + arg) - override fun acosh(arg: T): T = ln(arg + sqrt((arg - one) * (arg + one))) - override fun atanh(arg: T): T = (ln(arg + one) - ln(one - arg)) / 2 - - override fun rightSideNumberOperation(operation: String, left: T, right: Number): T = when (operation) { - PowerOperations.POW_OPERATION -> power(left, right) - else -> super.rightSideNumberOperation(operation, left, right) - } -} - -/** - * Real field element wrapping double. - * - * @property value the [Double] value wrapped by this [Real]. - * - * TODO inline does not work due to compiler bug. Waiting for fix for KT-27586 - */ -inline class Real(val value: Double) : FieldElement<Double, Real, RealField> { - override val context: RealField - get() = RealField - - override fun unwrap(): Double = value - - override fun Double.wrap(): Real = Real(value) - - companion object -} - -/** - * A field for [Double] without boxing. Does not produce appropriate field element. - */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object RealField : ExtendedField<Double>, Norm<Double, Double> { - override val zero: Double - get() = 0.0 - - override val one: Double - get() = 1.0 - - override fun binaryOperation(operation: String, left: Double, right: Double): Double = when (operation) { - PowerOperations.POW_OPERATION -> left pow right - else -> super.binaryOperation(operation, left, right) - } - - override inline fun add(a: Double, b: Double): Double = a + b - override inline fun multiply(a: Double, k: Number): Double = a * k.toDouble() - - override inline fun multiply(a: Double, b: Double): Double = a * b - - override inline fun divide(a: Double, b: Double): Double = a / b - - override inline fun sin(arg: Double): Double = kotlin.math.sin(arg) - override inline fun cos(arg: Double): Double = kotlin.math.cos(arg) - override inline fun tan(arg: Double): Double = kotlin.math.tan(arg) - override inline fun acos(arg: Double): Double = kotlin.math.acos(arg) - override inline fun asin(arg: Double): Double = kotlin.math.asin(arg) - override inline fun atan(arg: Double): Double = kotlin.math.atan(arg) - - override inline fun sinh(arg: Double): Double = kotlin.math.sinh(arg) - override inline fun cosh(arg: Double): Double = kotlin.math.cosh(arg) - override inline fun tanh(arg: Double): Double = kotlin.math.tanh(arg) - override inline fun asinh(arg: Double): Double = kotlin.math.asinh(arg) - override inline fun acosh(arg: Double): Double = kotlin.math.acosh(arg) - override inline fun atanh(arg: Double): Double = kotlin.math.atanh(arg) - - override inline fun power(arg: Double, pow: Number): Double = arg.kpow(pow.toDouble()) - override inline fun exp(arg: Double): Double = kotlin.math.exp(arg) - override inline fun ln(arg: Double): Double = kotlin.math.ln(arg) - - override inline fun norm(arg: Double): Double = abs(arg) - - override inline fun Double.unaryMinus(): Double = -this - override inline fun Double.plus(b: Double): Double = this + b - override inline fun Double.minus(b: Double): Double = this - b - override inline fun Double.times(b: Double): Double = this * b - override inline fun Double.div(b: Double): Double = this / b -} - -/** - * A field for [Float] without boxing. Does not produce appropriate field element. - */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object FloatField : ExtendedField<Float>, Norm<Float, Float> { - override val zero: Float - get() = 0.0f - - override val one: Float - get() = 1.0f - - override fun binaryOperation(operation: String, left: Float, right: Float): Float = when (operation) { - PowerOperations.POW_OPERATION -> left pow right - else -> super.binaryOperation(operation, left, right) - } - - override inline fun add(a: Float, b: Float): Float = a + b - override inline fun multiply(a: Float, k: Number): Float = a * k.toFloat() - - override inline fun multiply(a: Float, b: Float): Float = a * b - - override inline fun divide(a: Float, b: Float): Float = a / b - - override inline fun sin(arg: Float): Float = kotlin.math.sin(arg) - override inline fun cos(arg: Float): Float = kotlin.math.cos(arg) - override inline fun tan(arg: Float): Float = kotlin.math.tan(arg) - override inline fun acos(arg: Float): Float = kotlin.math.acos(arg) - override inline fun asin(arg: Float): Float = kotlin.math.asin(arg) - override inline fun atan(arg: Float): Float = kotlin.math.atan(arg) - - override inline fun sinh(arg: Float): Float = kotlin.math.sinh(arg) - override inline fun cosh(arg: Float): Float = kotlin.math.cosh(arg) - override inline fun tanh(arg: Float): Float = kotlin.math.tanh(arg) - override inline fun asinh(arg: Float): Float = kotlin.math.asinh(arg) - override inline fun acosh(arg: Float): Float = kotlin.math.acosh(arg) - override inline fun atanh(arg: Float): Float = kotlin.math.atanh(arg) - - override inline fun power(arg: Float, pow: Number): Float = arg.kpow(pow.toFloat()) - override inline fun exp(arg: Float): Float = kotlin.math.exp(arg) - override inline fun ln(arg: Float): Float = kotlin.math.ln(arg) - - override inline fun norm(arg: Float): Float = abs(arg) - - override inline fun Float.unaryMinus(): Float = -this - override inline fun Float.plus(b: Float): Float = this + b - override inline fun Float.minus(b: Float): Float = this - b - override inline fun Float.times(b: Float): Float = this * b - override inline fun Float.div(b: Float): Float = this / b -} - -/** - * A field for [Int] without boxing. Does not produce corresponding ring element. - */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object IntRing : Ring<Int>, Norm<Int, Int> { - override val zero: Int - get() = 0 - - override val one: Int - get() = 1 - - override inline fun add(a: Int, b: Int): Int = a + b - override inline fun multiply(a: Int, k: Number): Int = k.toInt() * a - - override inline fun multiply(a: Int, b: Int): Int = a * b - - override inline fun norm(arg: Int): Int = abs(arg) - - override inline fun Int.unaryMinus(): Int = -this - override inline fun Int.plus(b: Int): Int = this + b - override inline fun Int.minus(b: Int): Int = this - b - override inline fun Int.times(b: Int): Int = this * b -} - -/** - * A field for [Short] without boxing. Does not produce appropriate ring element. - */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object ShortRing : Ring<Short>, Norm<Short, Short> { - override val zero: Short - get() = 0 - - override val one: Short - get() = 1 - - override inline fun add(a: Short, b: Short): Short = (a + b).toShort() - override inline fun multiply(a: Short, k: Number): Short = (a * k.toShort()).toShort() - - override inline fun multiply(a: Short, b: Short): Short = (a * b).toShort() - - override fun norm(arg: Short): Short = if (arg > 0) arg else (-arg).toShort() - - override inline fun Short.unaryMinus(): Short = (-this).toShort() - override inline fun Short.plus(b: Short): Short = (this + b).toShort() - override inline fun Short.minus(b: Short): Short = (this - b).toShort() - override inline fun Short.times(b: Short): Short = (this * b).toShort() -} - -/** - * A field for [Byte] without boxing. Does not produce appropriate ring element. - */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object ByteRing : Ring<Byte>, Norm<Byte, Byte> { - override val zero: Byte - get() = 0 - - override val one: Byte - get() = 1 - - override inline fun add(a: Byte, b: Byte): Byte = (a + b).toByte() - override inline fun multiply(a: Byte, k: Number): Byte = (a * k.toByte()).toByte() - - override inline fun multiply(a: Byte, b: Byte): Byte = (a * b).toByte() - - override fun norm(arg: Byte): Byte = if (arg > 0) arg else (-arg).toByte() - - override inline fun Byte.unaryMinus(): Byte = (-this).toByte() - override inline fun Byte.plus(b: Byte): Byte = (this + b).toByte() - override inline fun Byte.minus(b: Byte): Byte = (this - b).toByte() - override inline fun Byte.times(b: Byte): Byte = (this * b).toByte() -} - -/** - * A field for [Double] without boxing. Does not produce appropriate ring element. - */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object LongRing : Ring<Long>, Norm<Long, Long> { - override val zero: Long - get() = 0 - - override val one: Long - get() = 1 - - override inline fun add(a: Long, b: Long): Long = a + b - override inline fun multiply(a: Long, k: Number): Long = a * k.toLong() - - override inline fun multiply(a: Long, b: Long): Long = a * b - - override fun norm(arg: Long): Long = abs(arg) - - override inline fun Long.unaryMinus(): Long = (-this) - override inline fun Long.plus(b: Long): Long = (this + b) - override inline fun Long.minus(b: Long): Long = (this - b) - override inline fun Long.times(b: Long): Long = (this * b) -} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt deleted file mode 100644 index 2c0c2021f..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt +++ /dev/null @@ -1,43 +0,0 @@ -package scientifik.kmath.structures - -import scientifik.kmath.operations.* - -interface BufferedNDAlgebra<T, C> : NDAlgebra<T, C, NDBuffer<T>> { - val strides: Strides - - override fun check(vararg elements: NDBuffer<T>): Unit = - require(elements.all { it.strides == strides }) { ("Strides mismatch") } - - /** - * Convert any [NDStructure] to buffered structure using strides from this context. - * If the structure is already [NDBuffer], conversion is free. If not, it could be expensive because iteration over - * indices. - * - * If the argument is [NDBuffer] with different strides structure, the new element will be produced. - */ - fun NDStructure<T>.toBuffer(): NDBuffer<T> { - return if (this is NDBuffer<T> && this.strides == this@BufferedNDAlgebra.strides) { - this - } else { - produce { index -> get(index) } - } - } - - /** - * Convert a buffer to element of this algebra - */ - fun NDBuffer<T>.toElement(): MathElement<out BufferedNDAlgebra<T, C>> -} - - -interface BufferedNDSpace<T, S : Space<T>> : NDSpace<T, S, NDBuffer<T>>, BufferedNDAlgebra<T, S> { - override fun NDBuffer<T>.toElement(): SpaceElement<NDBuffer<T>, *, out BufferedNDSpace<T, S>> -} - -interface BufferedNDRing<T, R : Ring<T>> : NDRing<T, R, NDBuffer<T>>, BufferedNDSpace<T, R> { - override fun NDBuffer<T>.toElement(): RingElement<NDBuffer<T>, *, out BufferedNDRing<T, R>> -} - -interface BufferedNDField<T, F : Field<T>> : NDField<T, F, NDBuffer<T>>, BufferedNDRing<T, F> { - override fun NDBuffer<T>.toElement(): FieldElement<NDBuffer<T>, *, out BufferedNDField<T, F>> -} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/expressions/ExpressionFieldTest.kt similarity index 87% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/expressions/ExpressionFieldTest.kt index 485de08b4..1d3f520f6 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/expressions/ExpressionFieldTest.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.expressions +package kscience.kmath.expressions -import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.ComplexField -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.invoke +import kscience.kmath.operations.Complex +import kscience.kmath.operations.ComplexField +import kscience.kmath.operations.RealField +import kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/MatrixTest.kt similarity index 91% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/linear/MatrixTest.kt index 52a2f80a6..7cfa25a66 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/MatrixTest.kt @@ -1,8 +1,8 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.structures.Matrix -import scientifik.kmath.structures.NDStructure -import scientifik.kmath.structures.as2D +import kscience.kmath.structures.Matrix +import kscience.kmath.structures.NDStructure +import kscience.kmath.structures.as2D import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt similarity index 92% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt index 9de9fb575..de07d3639 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt @@ -1,6 +1,6 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.structures.Matrix +import kscience.kmath.structures.Matrix import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/VectorSpaceTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/VectorSpaceTest.kt similarity index 100% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/VectorSpaceTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/linear/VectorSpaceTest.kt diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/misc/AutoDiffTest.kt similarity index 51% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/misc/AutoDiffTest.kt index c08a63ccb..3b1813185 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/misc/AutoDiffTest.kt @@ -1,21 +1,21 @@ -package scientifik.kmath.misc +package kscience.kmath.misc -import scientifik.kmath.operations.RealField -import scientifik.kmath.structures.asBuffer +import kscience.kmath.operations.RealField +import kscience.kmath.structures.asBuffer import kotlin.math.PI +import kotlin.math.pow +import kotlin.math.sqrt import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue class AutoDiffTest { - fun Variable(int: Int): Variable<Double> = Variable(int.toDouble()) - - fun deriv(body: AutoDiffField<Double, RealField>.() -> Variable<Double>): DerivationResult<Double> = + inline fun deriv(body: AutoDiffField<Double, RealField>.() -> Variable<Double>): DerivationResult<Double> = RealField.deriv(body) @Test fun testPlusX2() { - val x = Variable(3) // diff w.r.t this x at 3 + val x = Variable(3.0) // diff w.r.t this x at 3 val y = deriv { x + x } assertEquals(6.0, y.value) // y = x + x = 6 assertEquals(2.0, y.deriv(x)) // dy/dx = 2 @@ -24,8 +24,8 @@ class AutoDiffTest { @Test fun testPlus() { // two variables - val x = Variable(2) - val y = Variable(3) + val x = Variable(2.0) + val y = Variable(3.0) val z = deriv { x + y } assertEquals(5.0, z.value) // z = x + y = 5 assertEquals(1.0, z.deriv(x)) // dz/dx = 1 @@ -35,8 +35,8 @@ class AutoDiffTest { @Test fun testMinus() { // two variables - val x = Variable(7) - val y = Variable(3) + val x = Variable(7.0) + val y = Variable(3.0) val z = deriv { x - y } assertEquals(4.0, z.value) // z = x - y = 4 assertEquals(1.0, z.deriv(x)) // dz/dx = 1 @@ -45,7 +45,7 @@ class AutoDiffTest { @Test fun testMulX2() { - val x = Variable(3) // diff w.r.t this x at 3 + val x = Variable(3.0) // diff w.r.t this x at 3 val y = deriv { x * x } assertEquals(9.0, y.value) // y = x * x = 9 assertEquals(6.0, y.deriv(x)) // dy/dx = 2 * x = 7 @@ -53,7 +53,7 @@ class AutoDiffTest { @Test fun testSqr() { - val x = Variable(3) + val x = Variable(3.0) val y = deriv { sqr(x) } assertEquals(9.0, y.value) // y = x ^ 2 = 9 assertEquals(6.0, y.deriv(x)) // dy/dx = 2 * x = 7 @@ -61,7 +61,7 @@ class AutoDiffTest { @Test fun testSqrSqr() { - val x = Variable(2) + val x = Variable(2.0) val y = deriv { sqr(sqr(x)) } assertEquals(16.0, y.value) // y = x ^ 4 = 16 assertEquals(32.0, y.deriv(x)) // dy/dx = 4 * x^3 = 32 @@ -69,7 +69,7 @@ class AutoDiffTest { @Test fun testX3() { - val x = Variable(2) // diff w.r.t this x at 2 + val x = Variable(2.0) // diff w.r.t this x at 2 val y = deriv { x * x * x } assertEquals(8.0, y.value) // y = x * x * x = 8 assertEquals(12.0, y.deriv(x)) // dy/dx = 3 * x * x = 12 @@ -77,8 +77,8 @@ class AutoDiffTest { @Test fun testDiv() { - val x = Variable(5) - val y = Variable(2) + val x = Variable(5.0) + val y = Variable(2.0) val z = deriv { x / y } assertEquals(2.5, z.value) // z = x / y = 2.5 assertEquals(0.5, z.deriv(x)) // dz/dx = 1 / y = 0.5 @@ -87,7 +87,7 @@ class AutoDiffTest { @Test fun testPow3() { - val x = Variable(2) // diff w.r.t this x at 2 + val x = Variable(2.0) // diff w.r.t this x at 2 val y = deriv { pow(x, 3) } assertEquals(8.0, y.value) // y = x ^ 3 = 8 assertEquals(12.0, y.deriv(x)) // dy/dx = 3 * x ^ 2 = 12 @@ -95,8 +95,8 @@ class AutoDiffTest { @Test fun testPowFull() { - val x = Variable(2) - val y = Variable(3) + val x = Variable(2.0) + val y = Variable(3.0) val z = deriv { pow(x, y) } assertApprox(8.0, z.value) // z = x ^ y = 8 assertApprox(12.0, z.deriv(x)) // dz/dx = y * x ^ (y - 1) = 12 @@ -105,7 +105,7 @@ class AutoDiffTest { @Test fun testFromPaper() { - val x = Variable(3) + val x = Variable(3.0) val y = deriv { 2 * x + x * x * x } assertEquals(33.0, y.value) // y = 2 * x + x * x * x = 33 assertEquals(29.0, y.deriv(x)) // dy/dx = 2 + 3 * x * x = 29 @@ -113,9 +113,9 @@ class AutoDiffTest { @Test fun testInnerVariable() { - val x = Variable(1) + val x = Variable(1.0) val y = deriv { - Variable(1) * x + Variable(1.0) * x } assertEquals(1.0, y.value) // y = x ^ n = 1 assertEquals(1.0, y.deriv(x)) // dy/dx = n * x ^ (n - 1) = n - 1 @@ -124,9 +124,9 @@ class AutoDiffTest { @Test fun testLongChain() { val n = 10_000 - val x = Variable(1) + val x = Variable(1.0) val y = deriv { - var res = Variable(1) + var res = Variable(1.0) for (i in 1..n) res *= x res } @@ -136,7 +136,7 @@ class AutoDiffTest { @Test fun testExample() { - val x = Variable(2) + val x = Variable(2.0) val y = deriv { sqr(x) + 5 * x + 3 } assertEquals(17.0, y.value) // the value of result (y) assertEquals(9.0, y.deriv(x)) // dy/dx @@ -144,7 +144,7 @@ class AutoDiffTest { @Test fun testSqrt() { - val x = Variable(16) + val x = Variable(16.0) val y = deriv { sqrt(x) } assertEquals(4.0, y.value) // y = x ^ 1/2 = 4 assertEquals(1.0 / 8, y.deriv(x)) // dy/dx = 1/2 / x ^ 1/4 = 1/8 @@ -152,18 +152,98 @@ class AutoDiffTest { @Test fun testSin() { - val x = Variable(PI / 6) + val x = Variable(PI / 6.0) val y = deriv { sin(x) } - assertApprox(0.5, y.value) // y = sin(PI/6) = 0.5 - assertApprox(kotlin.math.sqrt(3.0) / 2, y.deriv(x)) // dy/dx = cos(PI/6) = sqrt(3)/2 + assertApprox(0.5, y.value) // y = sin(PI/6) = 0.5 + assertApprox(sqrt(3.0) / 2, y.deriv(x)) // dy/dx = cos(pi/6) = sqrt(3)/2 } @Test fun testCos() { val x = Variable(PI / 6) val y = deriv { cos(x) } - assertApprox(kotlin.math.sqrt(3.0) / 2, y.value) // y = cos(PI/6) = sqrt(3)/2 - assertApprox(-0.5, y.deriv(x)) // dy/dx = -sin(PI/6) = -0.5 + assertApprox(sqrt(3.0) / 2, y.value) //y = cos(pi/6) = sqrt(3)/2 + assertApprox(-0.5, y.deriv(x)) // dy/dx = -sin(pi/6) = -0.5 + } + + @Test + fun testTan() { + val x = Variable(PI / 6) + val y = deriv { tan(x) } + assertApprox(1.0 / sqrt(3.0), y.value) // y = tan(pi/6) = 1/sqrt(3) + assertApprox(4.0 / 3.0, y.deriv(x)) // dy/dx = sec(pi/6)^2 = 4/3 + } + + @Test + fun testAsin() { + val x = Variable(PI / 6) + val y = deriv { asin(x) } + assertApprox(kotlin.math.asin(PI / 6.0), y.value) // y = asin(pi/6) + assertApprox(6.0 / sqrt(36 - PI * PI), y.deriv(x)) // dy/dx = 6/sqrt(36-pi^2) + } + + @Test + fun testAcos() { + val x = Variable(PI / 6) + val y = deriv { acos(x) } + assertApprox(kotlin.math.acos(PI / 6.0), y.value) // y = acos(pi/6) + assertApprox(-6.0 / sqrt(36.0 - PI * PI), y.deriv(x)) // dy/dx = -6/sqrt(36-pi^2) + } + + @Test + fun testAtan() { + val x = Variable(PI / 6) + val y = deriv { atan(x) } + assertApprox(kotlin.math.atan(PI / 6.0), y.value) // y = atan(pi/6) + assertApprox(36.0 / (36.0 + PI * PI), y.deriv(x)) // dy/dx = 36/(36+pi^2) + } + + @Test + fun testSinh() { + val x = Variable(0.0) + val y = deriv { sinh(x) } + assertApprox(kotlin.math.sinh(0.0), y.value) // y = sinh(0) + assertApprox(kotlin.math.cosh(0.0), y.deriv(x)) // dy/dx = cosh(0) + } + + @Test + fun testCosh() { + val x = Variable(0.0) + val y = deriv { cosh(x) } + assertApprox(1.0, y.value) //y = cosh(0) + assertApprox(0.0, y.deriv(x)) // dy/dx = sinh(0) + } + + @Test + fun testTanh() { + val x = Variable(PI / 6) + val y = deriv { tanh(x) } + assertApprox(1.0 / sqrt(3.0), y.value) // y = tanh(pi/6) + assertApprox(1.0 / kotlin.math.cosh(PI / 6.0).pow(2), y.deriv(x)) // dy/dx = sech(pi/6)^2 + } + + @Test + fun testAsinh() { + val x = Variable(PI / 6) + val y = deriv { asinh(x) } + assertApprox(kotlin.math.asinh(PI / 6.0), y.value) // y = asinh(pi/6) + assertApprox(6.0 / sqrt(36 + PI * PI), y.deriv(x)) // dy/dx = 6/sqrt(pi^2+36) + } + + @Test + fun testAcosh() { + val x = Variable(PI / 6) + val y = deriv { acosh(x) } + assertApprox(kotlin.math.acosh(PI / 6.0), y.value) // y = acosh(pi/6) + assertApprox(-6.0 / sqrt(36.0 - PI * PI), y.deriv(x)) // dy/dx = -6/sqrt(36-pi^2) + } + + @Test + fun testAtanh() { + val x = Variable(PI / 6.0) + val y = deriv { atanh(x) } + assertApprox(kotlin.math.atanh(PI / 6.0), y.value) // y = atanh(pi/6) + assertApprox(-36.0 / (PI * PI - 36.0), y.deriv(x)) // dy/dx = -36/(pi^2-36) } @Test diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/misc/CumulativeKtTest.kt similarity index 90% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/misc/CumulativeKtTest.kt index 82ea5318f..1e6d2fd5d 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/misc/CumulativeKtTest.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.misc +package kscience.kmath.misc import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntAlgebraTest.kt similarity index 94% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntAlgebraTest.kt index d140f1017..78611e5d2 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntAlgebraTest.kt @@ -1,6 +1,6 @@ -package scientifik.kmath.operations +package kscience.kmath.operations -import scientifik.kmath.operations.internal.RingVerifier +import kscience.kmath.operations.internal.RingVerifier import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntConstructorTest.kt similarity index 93% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntConstructorTest.kt index 5e3f6d1b0..ba2582bbf 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntConstructorTest.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.operations +package kscience.kmath.operations import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntConversionsTest.kt similarity index 96% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntConversionsTest.kt index 41df1968d..0b433c436 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntConversionsTest.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.operations +package kscience.kmath.operations import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntOperationsTest.kt similarity index 99% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntOperationsTest.kt index b7f4cf43b..a3ed85c7b 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/BigIntOperationsTest.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.operations +package kscience.kmath.operations import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/ComplexFieldTest.kt similarity index 96% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/ComplexFieldTest.kt index 2c480ebea..c0b4853f4 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/ComplexFieldTest.kt @@ -1,6 +1,6 @@ -package scientifik.kmath.operations +package kscience.kmath.operations -import scientifik.kmath.operations.internal.FieldVerifier +import kscience.kmath.operations.internal.FieldVerifier import kotlin.math.PI import kotlin.math.abs import kotlin.test.Test diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/ComplexTest.kt similarity index 96% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/ComplexTest.kt index e8d698c70..f8b9b7262 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/ComplexTest.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.operations +package kscience.kmath.operations import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/RealFieldTest.kt similarity index 75% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/RealFieldTest.kt index a168b4afd..5705733cf 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/RealFieldTest.kt @@ -1,6 +1,6 @@ -package scientifik.kmath.operations +package kscience.kmath.operations -import scientifik.kmath.operations.internal.FieldVerifier +import kscience.kmath.operations.internal.FieldVerifier import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/AlgebraicVerifier.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/AlgebraicVerifier.kt similarity index 55% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/AlgebraicVerifier.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/AlgebraicVerifier.kt index cb097d46e..7334c13a3 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/AlgebraicVerifier.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/AlgebraicVerifier.kt @@ -1,6 +1,6 @@ -package scientifik.kmath.operations.internal +package kscience.kmath.operations.internal -import scientifik.kmath.operations.Algebra +import kscience.kmath.operations.Algebra internal interface AlgebraicVerifier<T, out A> where A : Algebra<T> { val algebra: A diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/FieldVerifier.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/FieldVerifier.kt similarity index 88% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/FieldVerifier.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/FieldVerifier.kt index 973fd00b1..1ca09ab0c 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/FieldVerifier.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/FieldVerifier.kt @@ -1,7 +1,7 @@ -package scientifik.kmath.operations.internal +package kscience.kmath.operations.internal -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.invoke +import kscience.kmath.operations.Field +import kscience.kmath.operations.invoke import kotlin.test.assertEquals import kotlin.test.assertNotEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/RingVerifier.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/RingVerifier.kt similarity index 92% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/RingVerifier.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/RingVerifier.kt index 047a213e9..863169b9b 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/RingVerifier.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/RingVerifier.kt @@ -1,7 +1,7 @@ -package scientifik.kmath.operations.internal +package kscience.kmath.operations.internal -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.invoke +import kscience.kmath.operations.Ring +import kscience.kmath.operations.invoke import kotlin.test.assertEquals internal open class RingVerifier<T>(override val algebra: Ring<T>, a: T, b: T, c: T, x: Number) : diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/SpaceVerifier.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/SpaceVerifier.kt similarity index 92% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/SpaceVerifier.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/SpaceVerifier.kt index bc241c97d..4dc855829 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/SpaceVerifier.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/operations/internal/SpaceVerifier.kt @@ -1,7 +1,7 @@ -package scientifik.kmath.operations.internal +package kscience.kmath.operations.internal -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.invoke +import kscience.kmath.operations.Space +import kscience.kmath.operations.invoke import kotlin.test.assertEquals import kotlin.test.assertNotEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/ComplexBufferSpecTest.kt similarity index 68% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/structures/ComplexBufferSpecTest.kt index cbbe6f0f4..4837236db 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/ComplexBufferSpecTest.kt @@ -1,7 +1,7 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.complex +import kscience.kmath.operations.Complex +import kscience.kmath.operations.complex import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt similarity index 87% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt index 7abeefca6..79b56ea4a 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.structures +package kscience.kmath.structures import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt similarity index 90% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt rename to kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt index b7e2594ec..f5e008ef3 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt @@ -1,8 +1,8 @@ -package scientifik.kmath.structures +package kscience.kmath.structures -import scientifik.kmath.operations.Norm -import scientifik.kmath.operations.invoke -import scientifik.kmath.structures.NDElement.Companion.real2D +import kscience.kmath.operations.Norm +import kscience.kmath.operations.invoke +import kscience.kmath.structures.NDElement.Companion.real2D import kotlin.math.abs import kotlin.math.pow import kotlin.test.Test diff --git a/kmath-core/src/jvmMain/kotlin/kscience/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/kscience/kmath/operations/BigNumbers.kt new file mode 100644 index 000000000..2f0978237 --- /dev/null +++ b/kmath-core/src/jvmMain/kotlin/kscience/kmath/operations/BigNumbers.kt @@ -0,0 +1,59 @@ +package kscience.kmath.operations + +import java.math.BigDecimal +import java.math.BigInteger +import java.math.MathContext + +/** + * A field over [BigInteger]. + */ +public object JBigIntegerField : Field<BigInteger> { + public override val zero: BigInteger + get() = BigInteger.ZERO + + public override val one: BigInteger + get() = BigInteger.ONE + + public override fun number(value: Number): BigInteger = BigInteger.valueOf(value.toLong()) + public override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) + public override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) + public override operator fun BigInteger.minus(b: BigInteger): BigInteger = subtract(b) + public override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) + public override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) + public override operator fun BigInteger.unaryMinus(): BigInteger = negate() +} + +/** + * An abstract field over [BigDecimal]. + * + * @property mathContext the [MathContext] to use. + */ +public abstract class JBigDecimalFieldBase internal constructor(public val mathContext: MathContext = MathContext.DECIMAL64) : + Field<BigDecimal>, + PowerOperations<BigDecimal> { + public override val zero: BigDecimal + get() = BigDecimal.ZERO + + public override val one: BigDecimal + get() = BigDecimal.ONE + + public override fun add(a: BigDecimal, b: BigDecimal): BigDecimal = a.add(b) + public override operator fun BigDecimal.minus(b: BigDecimal): BigDecimal = subtract(b) + public override fun number(value: Number): BigDecimal = BigDecimal.valueOf(value.toDouble()) + + public override fun multiply(a: BigDecimal, k: Number): BigDecimal = + a.multiply(k.toDouble().toBigDecimal(mathContext), mathContext) + + public override fun multiply(a: BigDecimal, b: BigDecimal): BigDecimal = a.multiply(b, mathContext) + public override fun divide(a: BigDecimal, b: BigDecimal): BigDecimal = a.divide(b, mathContext) + public override fun power(arg: BigDecimal, pow: Number): BigDecimal = arg.pow(pow.toInt(), mathContext) + public override fun sqrt(arg: BigDecimal): BigDecimal = arg.sqrt(mathContext) + public override operator fun BigDecimal.unaryMinus(): BigDecimal = negate(mathContext) +} + +/** + * A field over [BigDecimal]. + */ +public class JBigDecimalField(mathContext: MathContext = MathContext.DECIMAL64) : JBigDecimalFieldBase(mathContext) { + public companion object : JBigDecimalFieldBase() +} diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt deleted file mode 100644 index f10ef24da..000000000 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ /dev/null @@ -1,59 +0,0 @@ -package scientifik.kmath.operations - -import java.math.BigDecimal -import java.math.BigInteger -import java.math.MathContext - -/** - * A field over [BigInteger]. - */ -object JBigIntegerField : Field<BigInteger> { - override val zero: BigInteger - get() = BigInteger.ZERO - - override val one: BigInteger - get() = BigInteger.ONE - - override fun number(value: Number): BigInteger = BigInteger.valueOf(value.toLong()) - override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) - override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) - override operator fun BigInteger.minus(b: BigInteger): BigInteger = subtract(b) - override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) - override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) - override operator fun BigInteger.unaryMinus(): BigInteger = negate() -} - -/** - * An abstract field over [BigDecimal]. - * - * @property mathContext the [MathContext] to use. - */ -abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathContext = MathContext.DECIMAL64) : - Field<BigDecimal>, - PowerOperations<BigDecimal> { - override val zero: BigDecimal - get() = BigDecimal.ZERO - - override val one: BigDecimal - get() = BigDecimal.ONE - - override fun add(a: BigDecimal, b: BigDecimal): BigDecimal = a.add(b) - override operator fun BigDecimal.minus(b: BigDecimal): BigDecimal = subtract(b) - override fun number(value: Number): BigDecimal = BigDecimal.valueOf(value.toDouble()) - - override fun multiply(a: BigDecimal, k: Number): BigDecimal = - a.multiply(k.toDouble().toBigDecimal(mathContext), mathContext) - - override fun multiply(a: BigDecimal, b: BigDecimal): BigDecimal = a.multiply(b, mathContext) - override fun divide(a: BigDecimal, b: BigDecimal): BigDecimal = a.divide(b, mathContext) - override fun power(arg: BigDecimal, pow: Number): BigDecimal = arg.pow(pow.toInt(), mathContext) - override fun sqrt(arg: BigDecimal): BigDecimal = arg.sqrt(mathContext) - override operator fun BigDecimal.unaryMinus(): BigDecimal = negate(mathContext) -} - -/** - * A field over [BigDecimal]. - */ -class JBigDecimalField(mathContext: MathContext = MathContext.DECIMAL64) : JBigDecimalFieldBase(mathContext) { - companion object : JBigDecimalFieldBase() -} diff --git a/kmath-coroutines/build.gradle.kts b/kmath-coroutines/build.gradle.kts index bbbddeba3..e108c2755 100644 --- a/kmath-coroutines/build.gradle.kts +++ b/kmath-coroutines/build.gradle.kts @@ -1,7 +1,4 @@ -plugins { - id("scientifik.mpp") - //id("scientifik.atomic") -} +plugins { id("ru.mipt.npm.mpp") } kotlin.sourceSets { all { @@ -15,15 +12,7 @@ kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-core")) - api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Scientifik.coroutinesVersion}") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}") } } - - jvmMain { - dependencies { api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Scientifik.coroutinesVersion}") } - } - - jsMain { - dependencies { api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Scientifik.coroutinesVersion}") } - } } diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingIntChain.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingIntChain.kt new file mode 100644 index 000000000..6088267a2 --- /dev/null +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingIntChain.kt @@ -0,0 +1,12 @@ +package kscience.kmath.chains + +/** + * Performance optimized chain for integer values + */ +public abstract class BlockingIntChain : Chain<Int> { + public abstract fun nextInt(): Int + + override suspend fun next(): Int = nextInt() + + public fun nextBlock(size: Int): IntArray = IntArray(size) { nextInt() } +} diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt new file mode 100644 index 000000000..718b3a18b --- /dev/null +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt @@ -0,0 +1,12 @@ +package kscience.kmath.chains + +/** + * Performance optimized chain for real values + */ +public abstract class BlockingRealChain : Chain<Double> { + public abstract fun nextDouble(): Double + + override suspend fun next(): Double = nextDouble() + + public fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt similarity index 62% rename from kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt rename to kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt index f0ffd13cd..8c15e52c7 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package scientifik.kmath.chains +package kscience.kmath.chains import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.FlowCollector @@ -26,47 +26,44 @@ import kotlinx.coroutines.sync.withLock * A not-necessary-Markov chain of some type * @param R - the chain element type */ -interface Chain<out R> : Flow<R> { +public interface Chain<out R> : Flow<R> { /** * Generate next value, changing state if needed */ - suspend fun next(): R + public suspend fun next(): R /** * Create a copy of current chain state. Consuming resulting chain does not affect initial chain */ - fun fork(): Chain<R> + public fun fork(): Chain<R> override suspend fun collect(collector: FlowCollector<R>): Unit = flow { while (true) emit(next()) }.collect(collector) - companion object + public companion object } - -fun <T> Iterator<T>.asChain(): Chain<T> = SimpleChain { next() } -fun <T> Sequence<T>.asChain(): Chain<T> = iterator().asChain() +public fun <T> Iterator<T>.asChain(): Chain<T> = SimpleChain { next() } +public fun <T> Sequence<T>.asChain(): Chain<T> = iterator().asChain() /** * A simple chain of independent tokens */ -class SimpleChain<out R>(private val gen: suspend () -> R) : Chain<R> { - override suspend fun next(): R = gen() - override fun fork(): Chain<R> = this +public class SimpleChain<out R>(private val gen: suspend () -> R) : Chain<R> { + public override suspend fun next(): R = gen() + public override fun fork(): Chain<R> = this } /** * A stateless Markov chain */ -class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain<R> { - - private val mutex = Mutex() - +public class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain<R> { + private val mutex: Mutex = Mutex() private var value: R? = null - fun value(): R? = value + public fun value(): R? = value - override suspend fun next(): R { + public override suspend fun next(): R { mutex.withLock { val newValue = gen(value ?: seed()) value = newValue @@ -74,9 +71,7 @@ class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val ge } } - override fun fork(): Chain<R> { - return MarkovChain(seed = { value ?: seed() }, gen = gen) - } + public override fun fork(): Chain<R> = MarkovChain(seed = { value ?: seed() }, gen = gen) } /** @@ -84,19 +79,18 @@ class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val ge * @param S - the state of the chain * @param forkState - the function to copy current state without modifying it */ -class StatefulChain<S, out R>( +public class StatefulChain<S, out R>( private val state: S, private val seed: S.() -> R, private val forkState: ((S) -> S), private val gen: suspend S.(R) -> R ) : Chain<R> { private val mutex: Mutex = Mutex() - private var value: R? = null - fun value(): R? = value + public fun value(): R? = value - override suspend fun next(): R { + public override suspend fun next(): R { mutex.withLock { val newValue = state.gen(value ?: state.seed()) value = newValue @@ -104,25 +98,22 @@ class StatefulChain<S, out R>( } } - override fun fork(): Chain<R> = StatefulChain(forkState(state), seed, forkState, gen) + public override fun fork(): Chain<R> = StatefulChain(forkState(state), seed, forkState, gen) } /** * A chain that repeats the same value */ -class ConstantChain<out T>(val value: T) : Chain<T> { - override suspend fun next(): T = value - - override fun fork(): Chain<T> { - return this - } +public class ConstantChain<out T>(public val value: T) : Chain<T> { + public override suspend fun next(): T = value + public override fun fork(): Chain<T> = this } /** * 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 */ -fun <T, R> Chain<T>.map(func: suspend (T) -> R): Chain<R> = object : Chain<R> { +public fun <T, R> Chain<T>.map(func: suspend (T) -> R): Chain<R> = object : Chain<R> { override suspend fun next(): R = func(this@map.next()) override fun fork(): Chain<R> = this@map.fork().map(func) } @@ -130,7 +121,7 @@ fun <T, R> Chain<T>.map(func: suspend (T) -> R): Chain<R> = object : Chain<R> { /** * [block] must be a pure function or at least not use external random variables, otherwise fork could be broken */ -fun <T> Chain<T>.filter(block: (T) -> Boolean): Chain<T> = object : Chain<T> { +public fun <T> Chain<T>.filter(block: (T) -> Boolean): Chain<T> = object : Chain<T> { override suspend fun next(): T { var next: T @@ -146,23 +137,26 @@ fun <T> Chain<T>.filter(block: (T) -> Boolean): Chain<T> = object : Chain<T> { /** * Map the whole chain */ -fun <T, R> Chain<T>.collect(mapper: suspend (Chain<T>) -> R): Chain<R> = object : Chain<R> { +public fun <T, R> Chain<T>.collect(mapper: suspend (Chain<T>) -> R): Chain<R> = object : Chain<R> { override suspend fun next(): R = mapper(this@collect) override fun fork(): Chain<R> = this@collect.fork().collect(mapper) } -fun <T, S, R> Chain<T>.collectWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain<T>) -> R): Chain<R> = - object : Chain<R> { - override suspend fun next(): R = state.mapper(this@collectWithState) +public fun <T, S, R> Chain<T>.collectWithState( + state: S, + stateFork: (S) -> S, + mapper: suspend S.(Chain<T>) -> R +): Chain<R> = object : Chain<R> { + override suspend fun next(): R = state.mapper(this@collectWithState) - override fun fork(): Chain<R> = - this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) - } + override fun fork(): Chain<R> = + this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) +} /** * Zip two chains together using given transformation */ -fun <T, U, R> Chain<T>.zip(other: Chain<U>, block: suspend (T, U) -> R): Chain<R> = object : Chain<R> { +public fun <T, U, R> Chain<T>.zip(other: Chain<U>, block: suspend (T, U) -> R): Chain<R> = object : Chain<R> { override suspend fun next(): R = block(this@zip.next(), other.next()) override fun fork(): Chain<R> = this@zip.fork().zip(other.fork(), block) } diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/flowExtra.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/flowExtra.kt new file mode 100644 index 000000000..6b14057fe --- /dev/null +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/flowExtra.kt @@ -0,0 +1,26 @@ +package kscience.kmath.chains + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.runningReduce +import kotlinx.coroutines.flow.scan +import kscience.kmath.operations.Space +import kscience.kmath.operations.SpaceOperations +import kscience.kmath.operations.invoke + +@ExperimentalCoroutinesApi +public fun <T> Flow<T>.cumulativeSum(space: SpaceOperations<T>): Flow<T> = + space { runningReduce { sum, element -> sum + element } } + +@ExperimentalCoroutinesApi +public fun <T> Flow<T>.mean(space: Space<T>): Flow<T> = space { + data class Accumulator(var sum: T, var num: Int) + + scan(Accumulator(zero, 0)) { sum, element -> + sum.apply { + this.sum += element + this.num += 1 + } + }.map { it.sum / it.num } +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/coroutines/coroutinesExtra.kt similarity index 60% rename from kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt rename to kmath-coroutines/src/commonMain/kotlin/kscience/kmath/coroutines/coroutinesExtra.kt index 692f89589..351207111 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/coroutines/coroutinesExtra.kt @@ -1,11 +1,10 @@ -package scientifik.kmath.coroutines +package kscience.kmath.coroutines import kotlinx.coroutines.* import kotlinx.coroutines.channels.produce import kotlinx.coroutines.flow.* -import kotlin.contracts.contract -val Dispatchers.Math: CoroutineDispatcher +public val Dispatchers.Math: CoroutineDispatcher get() = Default /** @@ -15,31 +14,25 @@ internal class LazyDeferred<T>(val dispatcher: CoroutineDispatcher, val block: s private var deferred: Deferred<T>? = null internal fun start(scope: CoroutineScope) { - if (deferred == null) { - deferred = scope.async(dispatcher, block = block) - } + if (deferred == null) deferred = scope.async(dispatcher, block = block) } suspend fun await(): T = deferred?.await() ?: error("Coroutine not started") } -class AsyncFlow<T> internal constructor(internal val deferredFlow: Flow<LazyDeferred<T>>) : Flow<T> { - override suspend fun collect(collector: FlowCollector<T>) { - deferredFlow.collect { collector.emit((it.await())) } - } +public class AsyncFlow<T> internal constructor(internal val deferredFlow: Flow<LazyDeferred<T>>) : Flow<T> { + override suspend fun collect(collector: FlowCollector<T>): Unit = deferredFlow.collect { collector.emit((it.await())) } } -fun <T, R> Flow<T>.async( +public fun <T, R> Flow<T>.async( dispatcher: CoroutineDispatcher = Dispatchers.Default, block: suspend CoroutineScope.(T) -> R ): AsyncFlow<R> { - val flow = map { - LazyDeferred(dispatcher) { block(it) } - } + val flow = map { LazyDeferred(dispatcher) { block(it) } } return AsyncFlow(flow) } -fun <T, R> AsyncFlow<T>.map(action: (T) -> R): AsyncFlow<R> = +public fun <T, R> AsyncFlow<T>.map(action: (T) -> R): AsyncFlow<R> = AsyncFlow(deferredFlow.map { input -> //TODO add function composition LazyDeferred(input.dispatcher) { @@ -48,7 +41,7 @@ fun <T, R> AsyncFlow<T>.map(action: (T) -> R): AsyncFlow<R> = } }) -suspend fun <T> AsyncFlow<T>.collect(concurrency: Int, collector: FlowCollector<T>) { +public suspend fun <T> AsyncFlow<T>.collect(concurrency: Int, collector: FlowCollector<T>) { require(concurrency >= 1) { "Buffer size should be more than 1, but was $concurrency" } coroutineScope { @@ -76,18 +69,14 @@ suspend fun <T> AsyncFlow<T>.collect(concurrency: Int, collector: FlowCollector< } } -suspend inline fun <T> AsyncFlow<T>.collect(concurrency: Int, crossinline action: suspend (value: T) -> Unit) { - contract { callsInPlace(action) } +public suspend inline fun <T> AsyncFlow<T>.collect( + concurrency: Int, + crossinline action: suspend (value: T) -> Unit +): Unit = collect(concurrency, object : FlowCollector<T> { + override suspend fun emit(value: T): Unit = action(value) +}) - collect(concurrency, object : FlowCollector<T> { - override suspend fun emit(value: T): Unit = action(value) - }) -} - -inline fun <T, R> Flow<T>.mapParallel( +public inline fun <T, R> Flow<T>.mapParallel( dispatcher: CoroutineDispatcher = Dispatchers.Default, crossinline transform: suspend (T) -> R -): Flow<R> { - contract { callsInPlace(transform) } - return flatMapMerge { value -> flow { emit(transform(value)) } }.flowOn(dispatcher) -} +): Flow<R> = flatMapMerge { value -> flow { emit(transform(value)) } }.flowOn(dispatcher) diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/BufferFlow.kt similarity index 62% rename from kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt rename to kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/BufferFlow.kt index 9b7e82da5..328a7807c 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/BufferFlow.kt @@ -1,28 +1,28 @@ -package scientifik.kmath.streaming +package kscience.kmath.streaming import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.* -import scientifik.kmath.chains.BlockingRealChain -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.BufferFactory -import scientifik.kmath.structures.RealBuffer -import scientifik.kmath.structures.asBuffer +import kscience.kmath.chains.BlockingRealChain +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.BufferFactory +import kscience.kmath.structures.RealBuffer +import kscience.kmath.structures.asBuffer /** * Create a [Flow] from buffer */ -fun <T> Buffer<T>.asFlow(): Flow<T> = iterator().asFlow() +public fun <T> Buffer<T>.asFlow(): Flow<T> = iterator().asFlow() /** * Flat map a [Flow] of [Buffer] into continuous [Flow] of elements */ @FlowPreview -fun <T> Flow<Buffer<out T>>.spread(): Flow<T> = flatMapConcat { it.asFlow() } +public fun <T> Flow<Buffer<out T>>.spread(): Flow<T> = flatMapConcat { it.asFlow() } /** * Collect incoming flow into fixed size chunks */ -fun <T> Flow<T>.chunked(bufferSize: Int, bufferFactory: BufferFactory<T>): Flow<Buffer<T>> = flow { +public fun <T> Flow<T>.chunked(bufferSize: Int, bufferFactory: BufferFactory<T>): Flow<Buffer<T>> = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } val list = ArrayList<T>(bufferSize) var counter = 0 @@ -30,6 +30,7 @@ fun <T> Flow<T>.chunked(bufferSize: Int, bufferFactory: BufferFactory<T>): Flow< this@chunked.collect { element -> list.add(element) counter++ + if (counter == bufferSize) { val buffer = bufferFactory(bufferSize) { list[it] } emit(buffer) @@ -37,22 +38,19 @@ fun <T> Flow<T>.chunked(bufferSize: Int, bufferFactory: BufferFactory<T>): Flow< counter = 0 } } - if (counter > 0) { - emit(bufferFactory(counter) { list[it] }) - } + + if (counter > 0) emit(bufferFactory(counter) { list[it] }) } /** * Specialized flow chunker for real buffer */ -fun Flow<Double>.chunked(bufferSize: Int): Flow<RealBuffer> = flow { +public fun Flow<Double>.chunked(bufferSize: Int): Flow<RealBuffer> = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } if (this@chunked is BlockingRealChain) { - //performance optimization for blocking primitive chain - while (true) { - emit(nextBlock(bufferSize).asBuffer()) - } + // performance optimization for blocking primitive chain + while (true) emit(nextBlock(bufferSize).asBuffer()) } else { val array = DoubleArray(bufferSize) var counter = 0 @@ -60,15 +58,15 @@ fun Flow<Double>.chunked(bufferSize: Int): Flow<RealBuffer> = flow { this@chunked.collect { element -> array[counter] = element counter++ + if (counter == bufferSize) { val buffer = RealBuffer(array) emit(buffer) counter = 0 } } - if (counter > 0) { - emit(RealBuffer(counter) { array[it] }) - } + + if (counter > 0) emit(RealBuffer(counter) { array[it] }) } } @@ -76,9 +74,10 @@ fun Flow<Double>.chunked(bufferSize: Int): Flow<RealBuffer> = flow { * Map a flow to a moving window buffer. The window step is one. * In order to get different steps, one could use skip operation. */ -fun <T> Flow<T>.windowed(window: Int): Flow<Buffer<T>> = flow { +public fun <T> Flow<T>.windowed(window: Int): Flow<Buffer<T>> = flow { require(window > 1) { "Window size must be more than one" } val ringBuffer = RingBuffer.boxing<T>(window) + this@windowed.collect { element -> ringBuffer.push(element) emit(ringBuffer.snapshot()) diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/RingBuffer.kt similarity index 65% rename from kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt rename to kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/RingBuffer.kt index f1c0bfc6a..385bbaae2 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/RingBuffer.kt @@ -1,37 +1,37 @@ -package scientifik.kmath.streaming +package kscience.kmath.streaming import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.MutableBuffer -import scientifik.kmath.structures.VirtualBuffer +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.MutableBuffer +import kscience.kmath.structures.VirtualBuffer /** * Thread-safe ring buffer */ @Suppress("UNCHECKED_CAST") -class RingBuffer<T>( +public class RingBuffer<T>( private val buffer: MutableBuffer<T?>, private var startIndex: Int = 0, size: Int = 0 ) : Buffer<T> { private val mutex: Mutex = Mutex() - override var size: Int = size + public override var size: Int = size private set - override operator fun get(index: Int): T { + public override operator fun get(index: Int): T { require(index >= 0) { "Index must be positive" } require(index < size) { "Index $index is out of circular buffer size $size" } return buffer[startIndex.forward(index)] as T } - fun isFull(): Boolean = size == buffer.size + public fun isFull(): Boolean = size == buffer.size /** * Iterator could provide wrong results if buffer is changed in initialization (iteration is safe) */ - override operator fun iterator(): Iterator<T> = object : AbstractIterator<T>() { + public override operator fun iterator(): Iterator<T> = object : AbstractIterator<T>() { private var count = size private var index = startIndex val copy = buffer.copy() @@ -48,23 +48,17 @@ class RingBuffer<T>( /** * A safe snapshot operation */ - suspend fun snapshot(): Buffer<T> { + public suspend fun snapshot(): Buffer<T> { mutex.withLock { val copy = buffer.copy() - return VirtualBuffer(size) { i -> - copy[startIndex.forward(i)] as T - } + return VirtualBuffer(size) { i -> copy[startIndex.forward(i)] as T } } } - suspend fun push(element: T) { + public suspend fun push(element: T) { mutex.withLock { buffer[startIndex.forward(size)] = element - if (isFull()) { - startIndex++ - } else { - size++ - } + if (isFull()) startIndex++ else size++ } } @@ -72,8 +66,8 @@ class RingBuffer<T>( @Suppress("NOTHING_TO_INLINE") private inline fun Int.forward(n: Int): Int = (this + n) % (buffer.size) - companion object { - inline fun <reified T : Any> build(size: Int, empty: T): RingBuffer<T> { + public companion object { + public inline fun <reified T : Any> build(size: Int, empty: T): RingBuffer<T> { val buffer = MutableBuffer.auto(size) { empty } as MutableBuffer<T?> return RingBuffer(buffer) } @@ -81,7 +75,7 @@ class RingBuffer<T>( /** * Slow yet universal buffer */ - fun <T> boxing(size: Int): RingBuffer<T> { + public fun <T> boxing(size: Int): RingBuffer<T> { val buffer: MutableBuffer<T?> = MutableBuffer.boxing(size) { null } return RingBuffer(buffer) } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt deleted file mode 100644 index e9b499d71..000000000 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt +++ /dev/null @@ -1,12 +0,0 @@ -package scientifik.kmath.chains - -/** - * Performance optimized chain for integer values - */ -abstract class BlockingIntChain : Chain<Int> { - abstract fun nextInt(): Int - - override suspend fun next(): Int = nextInt() - - fun nextBlock(size: Int): IntArray = IntArray(size) { nextInt() } -} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt deleted file mode 100644 index ab819d327..000000000 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt +++ /dev/null @@ -1,12 +0,0 @@ -package scientifik.kmath.chains - -/** - * Performance optimized chain for real values - */ -abstract class BlockingRealChain : Chain<Double> { - abstract fun nextDouble(): Double - - override suspend fun next(): Double = nextDouble() - - fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } -} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt deleted file mode 100644 index 5db660c39..000000000 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt +++ /dev/null @@ -1,27 +0,0 @@ -package scientifik.kmath.chains - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.scan -import kotlinx.coroutines.flow.scanReduce -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.SpaceOperations -import scientifik.kmath.operations.invoke - -@ExperimentalCoroutinesApi -fun <T> Flow<T>.cumulativeSum(space: SpaceOperations<T>): Flow<T> = space { - scanReduce { sum: T, element: T -> sum + element } -} - -@ExperimentalCoroutinesApi -fun <T> Flow<T>.mean(space: Space<T>): Flow<T> = space { - class Accumulator(var sum: T, var num: Int) - - scan(Accumulator(zero, 0)) { sum, element -> - sum.apply { - this.sum += element - this.num += 1 - } - }.map { it.sum / it.num } -} diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt b/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/chains/ChainExt.kt similarity index 55% rename from kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt rename to kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/chains/ChainExt.kt index 5686b0ac0..3dfeddbac 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/chains/ChainExt.kt @@ -1,17 +1,16 @@ -package scientifik.kmath.chains +package kscience.kmath.chains import kotlinx.coroutines.runBlocking /** * Represent a chain as regular iterator (uses blocking calls) */ -operator fun <R> Chain<R>.iterator(): Iterator<R> = object : Iterator<R> { +public operator fun <R> Chain<R>.iterator(): Iterator<R> = object : Iterator<R> { override fun hasNext(): Boolean = true - override fun next(): R = runBlocking { next() } } /** * Represent a chain as a sequence */ -fun <R> Chain<R>.asSequence(): Sequence<R> = Sequence { this@asSequence.iterator() } \ No newline at end of file +public fun <R> Chain<R>.asSequence(): Sequence<R> = Sequence { this@asSequence.iterator() } diff --git a/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt new file mode 100644 index 000000000..bb0d19c23 --- /dev/null +++ b/kmath-coroutines/src/jvmMain/kotlin/kscience/kmath/structures/LazyNDStructure.kt @@ -0,0 +1,56 @@ +package kscience.kmath.structures + +import kotlinx.coroutines.* +import kscience.kmath.coroutines.Math + +public class LazyNDStructure<T>( + public val scope: CoroutineScope, + public override val shape: IntArray, + public val function: suspend (IntArray) -> T +) : NDStructure<T> { + private val cache: MutableMap<IntArray, Deferred<T>> = hashMapOf() + + public fun deferred(index: IntArray): Deferred<T> = cache.getOrPut(index) { + scope.async(context = Dispatchers.Math) { function(index) } + } + + public suspend fun await(index: IntArray): T = deferred(index).await() + public override operator fun get(index: IntArray): T = runBlocking { deferred(index).await() } + + public override fun elements(): Sequence<Pair<IntArray, T>> { + val strides = DefaultStrides(shape) + val res = runBlocking { strides.indices().toList().map { index -> index to await(index) } } + return res.asSequence() + } + + public override fun equals(other: Any?): Boolean { + return NDStructure.equals(this, other as? NDStructure<*> ?: return false) + } + + public override fun hashCode(): Int { + var result = scope.hashCode() + result = 31 * result + shape.contentHashCode() + result = 31 * result + function.hashCode() + result = 31 * result + cache.hashCode() + return result + } +} + +public fun <T> NDStructure<T>.deferred(index: IntArray): Deferred<T> = + if (this is LazyNDStructure<T>) deferred(index) else CompletableDeferred(get(index)) + +public suspend fun <T> NDStructure<T>.await(index: IntArray): T = + if (this is LazyNDStructure<T>) await(index) else get(index) + +/** + * PENDING would benefit from KEEP-176 + */ +public inline fun <T, R> NDStructure<T>.mapAsyncIndexed( + scope: CoroutineScope, + crossinline function: suspend (T, index: IntArray) -> R +): LazyNDStructure<R> = LazyNDStructure(scope, shape) { index -> function(get(index), index) } + +public inline fun <T, R> NDStructure<T>.mapAsync( + scope: CoroutineScope, + crossinline function: suspend (T) -> R +): LazyNDStructure<R> = LazyNDStructure(scope, shape) { index -> function(get(index)) } diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt deleted file mode 100644 index ff732a06b..000000000 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt +++ /dev/null @@ -1,63 +0,0 @@ -package scientifik.kmath.structures - -import kotlinx.coroutines.* -import scientifik.kmath.coroutines.Math - -class LazyNDStructure<T>( - val scope: CoroutineScope, - override val shape: IntArray, - val function: suspend (IntArray) -> T -) : NDStructure<T> { - private val cache: MutableMap<IntArray, Deferred<T>> = hashMapOf() - - fun deferred(index: IntArray): Deferred<T> = cache.getOrPut(index) { - scope.async(context = Dispatchers.Math) { - function(index) - } - } - - suspend fun await(index: IntArray): T = deferred(index).await() - - override operator fun get(index: IntArray): T = runBlocking { - deferred(index).await() - } - - override fun elements(): Sequence<Pair<IntArray, T>> { - val strides = DefaultStrides(shape) - val res = runBlocking { - strides.indices().toList().map { index -> index to await(index) } - } - return res.asSequence() - } - - override fun equals(other: Any?): Boolean { - return NDStructure.equals(this, other as? NDStructure<*> ?: return false) - } - - override fun hashCode(): Int { - var result = scope.hashCode() - result = 31 * result + shape.contentHashCode() - result = 31 * result + function.hashCode() - result = 31 * result + cache.hashCode() - return result - } -} - -fun <T> NDStructure<T>.deferred(index: IntArray): Deferred<T> = - if (this is LazyNDStructure<T>) this.deferred(index) else CompletableDeferred(get(index)) - -suspend fun <T> NDStructure<T>.await(index: IntArray): T = - if (this is LazyNDStructure<T>) this.await(index) else get(index) - -/** - * PENDING would benefit from KEEP-176 - */ -inline fun <T, R> NDStructure<T>.mapAsyncIndexed( - scope: CoroutineScope, - crossinline function: suspend (T, index: IntArray) -> R -): LazyNDStructure<R> = LazyNDStructure(scope, shape) { index -> function(get(index), index) } - -inline fun <T, R> NDStructure<T>.mapAsync( - scope: CoroutineScope, - crossinline function: suspend (T) -> R -): LazyNDStructure<R> = LazyNDStructure(scope, shape) { index -> function(get(index)) } diff --git a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt b/kmath-coroutines/src/jvmTest/kotlin/kscience/kmath/streaming/BufferFlowTest.kt similarity index 86% rename from kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt rename to kmath-coroutines/src/jvmTest/kotlin/kscience/kmath/streaming/BufferFlowTest.kt index 427349072..a9bf38c12 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/kscience/kmath/streaming/BufferFlowTest.kt @@ -1,12 +1,12 @@ -package scientifik.kmath.streaming +package kscience.kmath.streaming import kotlinx.coroutines.* import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.collect +import kscience.kmath.coroutines.async +import kscience.kmath.coroutines.collect +import kscience.kmath.coroutines.mapParallel import org.junit.jupiter.api.Timeout -import scientifik.kmath.coroutines.async -import scientifik.kmath.coroutines.collect -import scientifik.kmath.coroutines.mapParallel import java.util.concurrent.Executors import kotlin.test.Test @@ -14,7 +14,7 @@ import kotlin.test.Test @ExperimentalCoroutinesApi @InternalCoroutinesApi @FlowPreview -class BufferFlowTest { +internal class BufferFlowTest { val dispatcher: CoroutineDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher() @Test diff --git a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt b/kmath-coroutines/src/jvmTest/kotlin/kscience/kmath/streaming/RingBufferTest.kt similarity index 88% rename from kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt rename to kmath-coroutines/src/jvmTest/kotlin/kscience/kmath/streaming/RingBufferTest.kt index c84ef89ef..5bb0c1d40 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/kscience/kmath/streaming/RingBufferTest.kt @@ -1,12 +1,12 @@ -package scientifik.kmath.streaming +package kscience.kmath.streaming import kotlinx.coroutines.flow.* import kotlinx.coroutines.runBlocking -import scientifik.kmath.structures.asSequence +import kscience.kmath.structures.asSequence import kotlin.test.Test import kotlin.test.assertEquals -class RingBufferTest { +internal class RingBufferTest { @Test fun push() { val buffer = RingBuffer.build(20, Double.NaN) diff --git a/kmath-dimensions/build.gradle.kts b/kmath-dimensions/build.gradle.kts index 597ce8799..0a36e4435 100644 --- a/kmath-dimensions/build.gradle.kts +++ b/kmath-dimensions/build.gradle.kts @@ -1,8 +1,6 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("ru.mipt.npm.mpp") } -description = "A proof of concept module for adding typ-safe dimensions to structures" +description = "A proof of concept module for adding type-safe dimensions to structures" kotlin.sourceSets { commonMain { diff --git a/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Dimensions.kt b/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Dimensions.kt new file mode 100644 index 000000000..f49e1e0f0 --- /dev/null +++ b/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Dimensions.kt @@ -0,0 +1,33 @@ +package kscience.kmath.dimensions + +import kotlin.reflect.KClass + +/** + * An abstract class which is not used in runtime. Designates a size of some structure. + * Could be replaced later by fully inline constructs + */ +public interface Dimension { + public val dim: UInt + + public companion object +} + +public fun <D : Dimension> KClass<D>.dim(): UInt = Dimension.resolve(this).dim + +public expect fun <D : Dimension> Dimension.Companion.resolve(type: KClass<D>): D + +public expect fun Dimension.Companion.of(dim: UInt): Dimension + +public inline fun <reified D : Dimension> Dimension.Companion.dim(): UInt = D::class.dim() + +public object D1 : Dimension { + override val dim: UInt get() = 1U +} + +public object D2 : Dimension { + override val dim: UInt get() = 2U +} + +public object D3 : Dimension { + override val dim: UInt get() = 3U +} diff --git a/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Wrappers.kt b/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Wrappers.kt new file mode 100644 index 000000000..4111cb78d --- /dev/null +++ b/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Wrappers.kt @@ -0,0 +1,156 @@ +package kscience.kmath.dimensions + +import kscience.kmath.linear.GenericMatrixContext +import kscience.kmath.linear.MatrixContext +import kscience.kmath.linear.Point +import kscience.kmath.linear.transpose +import kscience.kmath.operations.RealField +import kscience.kmath.operations.Ring +import kscience.kmath.operations.invoke +import kscience.kmath.structures.Matrix +import kscience.kmath.structures.Structure2D + +/** + * A matrix with compile-time controlled dimension + */ +public interface DMatrix<T, R : Dimension, C : Dimension> : Structure2D<T> { + public companion object { + /** + * Coerces a regular matrix to a matrix with type-safe dimensions and throws a error if coercion failed + */ + public inline fun <T, reified R : Dimension, reified C : Dimension> coerce(structure: Structure2D<T>): DMatrix<T, R, C> { + require(structure.rowNum == Dimension.dim<R>().toInt()) { + "Row number mismatch: expected ${Dimension.dim<R>()} but found ${structure.rowNum}" + } + + require(structure.colNum == Dimension.dim<C>().toInt()) { + "Column number mismatch: expected ${Dimension.dim<C>()} but found ${structure.colNum}" + } + + return DMatrixWrapper(structure) + } + + /** + * The same as [DMatrix.coerce] but without dimension checks. Use with caution + */ + public fun <T, R : Dimension, C : Dimension> coerceUnsafe(structure: Structure2D<T>): DMatrix<T, R, C> = + DMatrixWrapper(structure) + } +} + +/** + * An inline wrapper for a Matrix + */ +public inline class DMatrixWrapper<T, R : Dimension, C : Dimension>( + public val structure: Structure2D<T> +) : DMatrix<T, R, C> { + override val shape: IntArray get() = structure.shape + override operator fun get(i: Int, j: Int): T = structure[i, j] +} + +/** + * Dimension-safe point + */ +public interface DPoint<T, D : Dimension> : Point<T> { + public companion object { + public inline fun <T, reified D : Dimension> coerce(point: Point<T>): DPoint<T, D> { + require(point.size == Dimension.dim<D>().toInt()) { + "Vector dimension mismatch: expected ${Dimension.dim<D>()}, but found ${point.size}" + } + + return DPointWrapper(point) + } + + public fun <T, D : Dimension> coerceUnsafe(point: Point<T>): DPoint<T, D> = DPointWrapper(point) + } +} + +/** + * Dimension-safe point wrapper + */ +public inline class DPointWrapper<T, D : Dimension>(public val point: Point<T>) : + DPoint<T, D> { + override val size: Int get() = point.size + + override operator fun get(index: Int): T = point[index] + + override operator fun iterator(): Iterator<T> = point.iterator() +} + + +/** + * Basic operations on dimension-safe matrices. Operates on [Matrix] + */ +public inline class DMatrixContext<T : Any, Ri : Ring<T>>(public val context: GenericMatrixContext<T, Ri>) { + public inline fun <reified R : Dimension, reified C : Dimension> Matrix<T>.coerce(): DMatrix<T, R, C> { + require(rowNum == Dimension.dim<R>().toInt()) { + "Row number mismatch: expected ${Dimension.dim<R>()} but found $rowNum" + } + + require(colNum == Dimension.dim<C>().toInt()) { + "Column number mismatch: expected ${Dimension.dim<C>()} but found $colNum" + } + + return DMatrix.coerceUnsafe(this) + } + + /** + * Produce a matrix with this context and given dimensions + */ + public inline fun <reified R : Dimension, reified C : Dimension> produce(noinline initializer: (i: Int, j: Int) -> T): DMatrix<T, R, C> { + val rows = Dimension.dim<R>() + val cols = Dimension.dim<C>() + return context.produce(rows.toInt(), cols.toInt(), initializer).coerce<R, C>() + } + + public inline fun <reified D : Dimension> point(noinline initializer: (Int) -> T): DPoint<T, D> { + val size = Dimension.dim<D>() + + return DPoint.coerceUnsafe( + context.point( + size.toInt(), + initializer + ) + ) + } + + public inline infix fun <reified R1 : Dimension, reified C1 : Dimension, reified C2 : Dimension> DMatrix<T, R1, C1>.dot( + other: DMatrix<T, C1, C2> + ): DMatrix<T, R1, C2> = context { this@dot dot other }.coerce() + + public inline infix fun <reified R : Dimension, reified C : Dimension> DMatrix<T, R, C>.dot(vector: DPoint<T, C>): DPoint<T, R> = + DPoint.coerceUnsafe(context { this@dot dot vector }) + + public inline operator fun <reified R : Dimension, reified C : Dimension> DMatrix<T, R, C>.times(value: T): DMatrix<T, R, C> = + context { this@times.times(value) }.coerce() + + public inline operator fun <reified R : Dimension, reified C : Dimension> T.times(m: DMatrix<T, R, C>): DMatrix<T, R, C> = + m * this + + public inline operator fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.plus(other: DMatrix<T, C, R>): DMatrix<T, C, R> = + context { this@plus + other }.coerce() + + public inline operator fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.minus(other: DMatrix<T, C, R>): DMatrix<T, C, R> = + context { this@minus + other }.coerce() + + public inline operator fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.unaryMinus(): DMatrix<T, C, R> = + context { this@unaryMinus.unaryMinus() }.coerce() + + public inline fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.transpose(): DMatrix<T, R, C> = + context { (this@transpose as Matrix<T>).transpose() }.coerce() + + /** + * A square unit matrix + */ + public inline fun <reified D : Dimension> one(): DMatrix<T, D, D> = produce { i, j -> + if (i == j) context.elementContext.one else context.elementContext.zero + } + + public inline fun <reified R : Dimension, reified C : Dimension> zero(): DMatrix<T, R, C> = produce { _, _ -> + context.elementContext.zero + } + + public companion object { + public val real: DMatrixContext<Double, RealField> = DMatrixContext(MatrixContext.real) + } +} diff --git a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt b/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt deleted file mode 100644 index f40483cfd..000000000 --- a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt +++ /dev/null @@ -1,35 +0,0 @@ -package scientifik.kmath.dimensions - -import kotlin.reflect.KClass - -/** - * An abstract class which is not used in runtime. Designates a size of some structure. - * Could be replaced later by fully inline constructs - */ -interface Dimension { - - val dim: UInt - companion object { - - } -} - -fun <D : Dimension> KClass<D>.dim(): UInt = Dimension.resolve(this).dim - -expect fun <D : Dimension> Dimension.Companion.resolve(type: KClass<D>): D - -expect fun Dimension.Companion.of(dim: UInt): Dimension - -inline fun <reified D : Dimension> Dimension.Companion.dim(): UInt = D::class.dim() - -object D1 : Dimension { - override val dim: UInt get() = 1U -} - -object D2 : Dimension { - override val dim: UInt get() = 2U -} - -object D3 : Dimension { - override val dim: UInt get() = 3U -} diff --git a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Wrappers.kt b/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Wrappers.kt deleted file mode 100644 index 7b0244bdf..000000000 --- a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Wrappers.kt +++ /dev/null @@ -1,157 +0,0 @@ -package scientifik.kmath.dimensions - -import scientifik.kmath.linear.GenericMatrixContext -import scientifik.kmath.linear.MatrixContext -import scientifik.kmath.linear.Point -import scientifik.kmath.linear.transpose -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.invoke -import scientifik.kmath.structures.Matrix -import scientifik.kmath.structures.Structure2D - -/** - * A matrix with compile-time controlled dimension - */ -interface DMatrix<T, R : Dimension, C : Dimension> : Structure2D<T> { - companion object { - /** - * Coerces a regular matrix to a matrix with type-safe dimensions and throws a error if coercion failed - */ - inline fun <T, reified R : Dimension, reified C : Dimension> coerce(structure: Structure2D<T>): DMatrix<T, R, C> { - if (structure.rowNum != Dimension.dim<R>().toInt()) { - error("Row number mismatch: expected ${Dimension.dim<R>()} but found ${structure.rowNum}") - } - if (structure.colNum != Dimension.dim<C>().toInt()) { - error("Column number mismatch: expected ${Dimension.dim<C>()} but found ${structure.colNum}") - } - return DMatrixWrapper(structure) - } - - /** - * The same as [coerce] but without dimension checks. Use with caution - */ - fun <T, R : Dimension, C : Dimension> coerceUnsafe(structure: Structure2D<T>): DMatrix<T, R, C> { - return DMatrixWrapper(structure) - } - } -} - -/** - * An inline wrapper for a Matrix - */ -inline class DMatrixWrapper<T, R : Dimension, C : Dimension>( - val structure: Structure2D<T> -) : DMatrix<T, R, C> { - override val shape: IntArray get() = structure.shape - override operator fun get(i: Int, j: Int): T = structure[i, j] -} - -/** - * Dimension-safe point - */ -interface DPoint<T, D : Dimension> : Point<T> { - companion object { - inline fun <T, reified D : Dimension> coerce(point: Point<T>): DPoint<T, D> { - if (point.size != Dimension.dim<D>().toInt()) { - error("Vector dimension mismatch: expected ${Dimension.dim<D>()}, but found ${point.size}") - } - return DPointWrapper(point) - } - - fun <T, D : Dimension> coerceUnsafe(point: Point<T>): DPoint<T, D> { - return DPointWrapper(point) - } - } -} - -/** - * Dimension-safe point wrapper - */ -inline class DPointWrapper<T, D : Dimension>(val point: Point<T>) : - DPoint<T, D> { - override val size: Int get() = point.size - - override operator fun get(index: Int): T = point[index] - - override operator fun iterator(): Iterator<T> = point.iterator() -} - - -/** - * Basic operations on dimension-safe matrices. Operates on [Matrix] - */ -inline class DMatrixContext<T : Any, Ri : Ring<T>>(val context: GenericMatrixContext<T, Ri>) { - - inline fun <reified R : Dimension, reified C : Dimension> Matrix<T>.coerce(): DMatrix<T, R, C> { - check( - rowNum == Dimension.dim<R>().toInt() - ) { "Row number mismatch: expected ${Dimension.dim<R>()} but found $rowNum" } - - check( - colNum == Dimension.dim<C>().toInt() - ) { "Column number mismatch: expected ${Dimension.dim<C>()} but found $colNum" } - - return DMatrix.coerceUnsafe(this) - } - - /** - * Produce a matrix with this context and given dimensions - */ - inline fun <reified R : Dimension, reified C : Dimension> produce(noinline initializer: (i: Int, j: Int) -> T): DMatrix<T, R, C> { - val rows = Dimension.dim<R>() - val cols = Dimension.dim<C>() - return context.produce(rows.toInt(), cols.toInt(), initializer).coerce<R, C>() - } - - inline fun <reified D : Dimension> point(noinline initializer: (Int) -> T): DPoint<T, D> { - val size = Dimension.dim<D>() - - return DPoint.coerceUnsafe( - context.point( - size.toInt(), - initializer - ) - ) - } - - inline infix fun <reified R1 : Dimension, reified C1 : Dimension, reified C2 : Dimension> DMatrix<T, R1, C1>.dot( - other: DMatrix<T, C1, C2> - ): DMatrix<T, R1, C2> = context { this@dot dot other }.coerce() - - inline infix fun <reified R : Dimension, reified C : Dimension> DMatrix<T, R, C>.dot(vector: DPoint<T, C>): DPoint<T, R> = - DPoint.coerceUnsafe(context { this@dot dot vector }) - - inline operator fun <reified R : Dimension, reified C : Dimension> DMatrix<T, R, C>.times(value: T): DMatrix<T, R, C> = - context { this@times.times(value) }.coerce() - - inline operator fun <reified R : Dimension, reified C : Dimension> T.times(m: DMatrix<T, R, C>): DMatrix<T, R, C> = - m * this - - inline operator fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.plus(other: DMatrix<T, C, R>): DMatrix<T, C, R> = - context { this@plus + other }.coerce() - - inline operator fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.minus(other: DMatrix<T, C, R>): DMatrix<T, C, R> = - context { this@minus + other }.coerce() - - inline operator fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.unaryMinus(): DMatrix<T, C, R> = - context { this@unaryMinus.unaryMinus() }.coerce() - - inline fun <reified R : Dimension, reified C : Dimension> DMatrix<T, C, R>.transpose(): DMatrix<T, R, C> = - context { (this@transpose as Matrix<T>).transpose() }.coerce() - - /** - * A square unit matrix - */ - inline fun <reified D : Dimension> one(): DMatrix<T, D, D> = produce { i, j -> - if (i == j) context.elementContext.one else context.elementContext.zero - } - - inline fun <reified R : Dimension, reified C : Dimension> zero(): DMatrix<T, R, C> = produce { _, _ -> - context.elementContext.zero - } - - companion object { - val real: DMatrixContext<Double, RealField> = DMatrixContext(MatrixContext.real) - } -} diff --git a/kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt b/kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt index 8dabdeeac..f44b16753 100644 --- a/kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt +++ b/kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt @@ -1,11 +1,11 @@ -package scientifik.dimensions +package kscience.dimensions -import scientifik.kmath.dimensions.D2 -import scientifik.kmath.dimensions.D3 -import scientifik.kmath.dimensions.DMatrixContext +import kscience.kmath.dimensions.D2 +import kscience.kmath.dimensions.D3 +import kscience.kmath.dimensions.DMatrixContext import kotlin.test.Test -class DMatrixContextTest { +internal class DMatrixContextTest { @Test fun testDimensionSafeMatrix() { val res = with(DMatrixContext.real) { @@ -26,4 +26,4 @@ class DMatrixContextTest { m1.transpose() + m2 } } -} \ No newline at end of file +} diff --git a/kmath-dimensions/src/jsMain/kotlin/kscience/kmath/dimensions/dim.kt b/kmath-dimensions/src/jsMain/kotlin/kscience/kmath/dimensions/dim.kt new file mode 100644 index 000000000..4230da156 --- /dev/null +++ b/kmath-dimensions/src/jsMain/kotlin/kscience/kmath/dimensions/dim.kt @@ -0,0 +1,18 @@ +package kscience.kmath.dimensions + +import kotlin.reflect.KClass + +private val dimensionMap: MutableMap<UInt, Dimension> = hashMapOf(1u to D1, 2u to D2, 3u to D3) + +@Suppress("UNCHECKED_CAST") +public actual fun <D : Dimension> Dimension.Companion.resolve(type: KClass<D>): D = dimensionMap + .entries + .map(MutableMap.MutableEntry<UInt, Dimension>::value) + .find { it::class == type } as? D + ?: error("Can't resolve dimension $type") + +public actual fun Dimension.Companion.of(dim: UInt): Dimension = dimensionMap.getOrPut(dim) { + object : Dimension { + override val dim: UInt get() = dim + } +} diff --git a/kmath-dimensions/src/jsMain/kotlin/scientifik/kmath/dimensions/dim.kt b/kmath-dimensions/src/jsMain/kotlin/scientifik/kmath/dimensions/dim.kt deleted file mode 100644 index bbd580629..000000000 --- a/kmath-dimensions/src/jsMain/kotlin/scientifik/kmath/dimensions/dim.kt +++ /dev/null @@ -1,22 +0,0 @@ -package scientifik.kmath.dimensions - -import kotlin.reflect.KClass - -private val dimensionMap = hashMapOf<UInt, Dimension>( - 1u to D1, - 2u to D2, - 3u to D3 -) - -@Suppress("UNCHECKED_CAST") -actual fun <D : Dimension> Dimension.Companion.resolve(type: KClass<D>): D { - return dimensionMap.entries.find { it.value::class == type }?.value as? D ?: error("Can't resolve dimension $type") -} - -actual fun Dimension.Companion.of(dim: UInt): Dimension { - return dimensionMap.getOrPut(dim) { - object : Dimension { - override val dim: UInt get() = dim - } - } -} \ No newline at end of file diff --git a/kmath-dimensions/src/jvmMain/kotlin/kscience/kmath/dimensions/dim.kt b/kmath-dimensions/src/jvmMain/kotlin/kscience/kmath/dimensions/dim.kt new file mode 100644 index 000000000..dec3979ef --- /dev/null +++ b/kmath-dimensions/src/jvmMain/kotlin/kscience/kmath/dimensions/dim.kt @@ -0,0 +1,16 @@ +package kscience.kmath.dimensions + +import kotlin.reflect.KClass + +public actual fun <D : Dimension> Dimension.Companion.resolve(type: KClass<D>): D = + type.objectInstance ?: error("No object instance for dimension class") + +public actual fun Dimension.Companion.of(dim: UInt): Dimension = when (dim) { + 1u -> D1 + 2u -> D2 + 3u -> D3 + + else -> object : Dimension { + override val dim: UInt get() = dim + } +} \ No newline at end of file diff --git a/kmath-dimensions/src/jvmMain/kotlin/scientifik/kmath/dimensions/dim.kt b/kmath-dimensions/src/jvmMain/kotlin/scientifik/kmath/dimensions/dim.kt deleted file mode 100644 index e8fe8f59b..000000000 --- a/kmath-dimensions/src/jvmMain/kotlin/scientifik/kmath/dimensions/dim.kt +++ /dev/null @@ -1,18 +0,0 @@ -package scientifik.kmath.dimensions - -import kotlin.reflect.KClass - -actual fun <D:Dimension> Dimension.Companion.resolve(type: KClass<D>): D{ - return type.objectInstance ?: error("No object instance for dimension class") -} - -actual fun Dimension.Companion.of(dim: UInt): Dimension{ - return when(dim){ - 1u -> D1 - 2u -> D2 - 3u -> D3 - else -> object : Dimension { - override val dim: UInt get() = dim - } - } -} \ No newline at end of file diff --git a/kmath-for-real/build.gradle.kts b/kmath-for-real/build.gradle.kts index 675457ac5..2a4539c10 100644 --- a/kmath-for-real/build.gradle.kts +++ b/kmath-for-real/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("scientifik.mpp") + id("ru.mipt.npm.mpp") } kotlin.sourceSets.commonMain { diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt new file mode 100644 index 000000000..ba5f8444b --- /dev/null +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt @@ -0,0 +1,46 @@ +package kscience.kmath.real + +import kscience.kmath.linear.BufferVectorSpace +import kscience.kmath.linear.Point +import kscience.kmath.linear.VectorSpace +import kscience.kmath.operations.Norm +import kscience.kmath.operations.RealField +import kscience.kmath.operations.SpaceElement +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.RealBuffer +import kscience.kmath.structures.asBuffer +import kscience.kmath.structures.asIterable +import kotlin.math.sqrt + +public typealias RealPoint = Point<Double> + +public fun DoubleArray.asVector(): RealVector = RealVector(asBuffer()) +public fun List<Double>.asVector(): RealVector = RealVector(asBuffer()) + +public object VectorL2Norm : Norm<Point<out Number>, Double> { + override fun norm(arg: Point<out Number>): Double = sqrt(arg.asIterable().sumByDouble(Number::toDouble)) +} + +public inline class RealVector(private val point: Point<Double>) : + SpaceElement<RealPoint, RealVector, VectorSpace<Double, RealField>>, RealPoint { + public override val size: Int get() = point.size + public override val context: VectorSpace<Double, RealField> get() = space(point.size) + + public override fun unwrap(): RealPoint = point + public override fun RealPoint.wrap(): RealVector = RealVector(this) + public override operator fun get(index: Int): Double = point[index] + public override operator fun iterator(): Iterator<Double> = point.iterator() + + public companion object { + private val spaceCache: MutableMap<Int, BufferVectorSpace<Double, RealField>> = hashMapOf() + + public inline operator fun invoke(dim: Int, initializer: (Int) -> Double): RealVector = + RealVector(RealBuffer(dim, initializer)) + + public operator fun invoke(vararg values: Double): RealVector = values.asVector() + + public fun space(dim: Int): BufferVectorSpace<Double, RealField> = spaceCache.getOrPut(dim) { + BufferVectorSpace(dim, RealField) { size, init -> Buffer.real(size, init) } + } + } +} diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realBuffer.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realBuffer.kt new file mode 100644 index 000000000..0a2119b0d --- /dev/null +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realBuffer.kt @@ -0,0 +1,8 @@ +package kscience.kmath.real + +import kscience.kmath.structures.RealBuffer + +/** + * Simplified [RealBuffer] to array comparison + */ +public fun RealBuffer.contentEquals(vararg doubles: Double): Boolean = array.contentEquals(doubles) diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realMatrix.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realMatrix.kt new file mode 100644 index 000000000..1860b5870 --- /dev/null +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/realMatrix.kt @@ -0,0 +1,157 @@ +package kscience.kmath.real + +import kscience.kmath.linear.MatrixContext +import kscience.kmath.linear.RealMatrixContext.elementContext +import kscience.kmath.linear.VirtualMatrix +import kscience.kmath.operations.invoke +import kscience.kmath.operations.sum +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.Matrix +import kscience.kmath.structures.RealBuffer +import kscience.kmath.structures.asIterable +import kotlin.math.pow + +/* + * Functions for convenient "numpy-like" operations with Double matrices. + * + * Initial implementation of these functions is taken from: + * https://github.com/thomasnield/numky/blob/master/src/main/kotlin/org/nield/numky/linear/DoubleOperators.kt + * + */ + +/* + * Functions that help create a real (Double) matrix + */ + +public typealias RealMatrix = Matrix<Double> + +public fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum, initializer) + +public fun Array<DoubleArray>.toMatrix(): RealMatrix { + return MatrixContext.real.produce(size, this[0].size) { row, col -> this[row][col] } +} + +public fun Sequence<DoubleArray>.toMatrix(): RealMatrix = toList().let { + MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] } +} + +public fun Matrix<Double>.repeatStackVertical(n: Int): RealMatrix = + VirtualMatrix(rowNum * n, colNum) { row, col -> + get(if (row == 0) 0 else row % rowNum, col) + } + +/* + * Operations for matrix and real number + */ + +public operator fun Matrix<Double>.times(double: Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] * double + } + +public operator fun Matrix<Double>.plus(double: Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] + double + } + +public operator fun Matrix<Double>.minus(double: Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] - double + } + +public operator fun Matrix<Double>.div(double: Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] / double + } + +public operator fun Double.times(matrix: Matrix<Double>): RealMatrix = + MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> + this * matrix[row, col] + } + +public operator fun Double.plus(matrix: Matrix<Double>): RealMatrix = + MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> + this + matrix[row, col] + } + +public operator fun Double.minus(matrix: Matrix<Double>): RealMatrix = + MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> + this - matrix[row, col] + } + +// TODO: does this operation make sense? Should it be 'this/matrix[row, col]'? +//operator fun Double.div(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { +// row, col -> matrix[row, col] / this +//} + +/* + * Per-element (!) square and power operations + */ + +public fun Matrix<Double>.square(): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col].pow(2) +} + +public fun Matrix<Double>.pow(n: Int): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { i, j -> + this[i, j].pow(n) +} + +/* + * Operations on two matrices (per-element!) + */ + +public operator fun Matrix<Double>.times(other: Matrix<Double>): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> this[row, col] * other[row, col] } + +public operator fun Matrix<Double>.plus(other: Matrix<Double>): RealMatrix = + MatrixContext.real.add(this, other) + +public operator fun Matrix<Double>.minus(other: Matrix<Double>): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> this[row, col] - other[row, col] } + +/* + * Operations on columns + */ + +public inline fun Matrix<Double>.appendColumn(crossinline mapper: (Buffer<Double>) -> Double): Matrix<Double> = + MatrixContext.real.produce(rowNum, colNum + 1) { row, col -> + if (col < colNum) + this[row, col] + else + mapper(rows[row]) + } + +public fun Matrix<Double>.extractColumns(columnRange: IntRange): RealMatrix = + MatrixContext.real.produce(rowNum, columnRange.count()) { row, col -> + this[row, columnRange.first + col] + } + +public fun Matrix<Double>.extractColumn(columnIndex: Int): RealMatrix = + extractColumns(columnIndex..columnIndex) + +public fun Matrix<Double>.sumByColumn(): RealBuffer = RealBuffer(colNum) { j -> + val column = columns[j] + elementContext { sum(column.asIterable()) } +} + +public fun Matrix<Double>.minByColumn(): RealBuffer = RealBuffer(colNum) { j -> + columns[j].asIterable().minOrNull() ?: error("Cannot produce min on empty column") +} + +public fun Matrix<Double>.maxByColumn(): RealBuffer = RealBuffer(colNum) { j -> + columns[j].asIterable().maxOrNull() ?: error("Cannot produce min on empty column") +} + +public fun Matrix<Double>.averageByColumn(): RealBuffer = RealBuffer(colNum) { j -> + columns[j].asIterable().average() +} + +/* + * Operations processing all elements + */ + +public fun Matrix<Double>.sum(): Double = elements().map { (_, value) -> value }.sum() +public fun Matrix<Double>.min(): Double? = elements().map { (_, value) -> value }.minOrNull() +public fun Matrix<Double>.max(): Double? = elements().map { (_, value) -> value }.maxOrNull() +public fun Matrix<Double>.average(): Double = elements().map { (_, value) -> value }.average() diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt deleted file mode 100644 index 811b54d7c..000000000 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt +++ /dev/null @@ -1,51 +0,0 @@ -package scientifik.kmath.real - -import scientifik.kmath.linear.BufferVectorSpace -import scientifik.kmath.linear.Point -import scientifik.kmath.linear.VectorSpace -import scientifik.kmath.operations.Norm -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.SpaceElement -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.RealBuffer -import scientifik.kmath.structures.asBuffer -import scientifik.kmath.structures.asIterable -import kotlin.math.sqrt - -typealias RealPoint = Point<Double> - -fun DoubleArray.asVector(): RealVector = RealVector(this.asBuffer()) -fun List<Double>.asVector(): RealVector = RealVector(this.asBuffer()) - -object VectorL2Norm : Norm<Point<out Number>, Double> { - override fun norm(arg: Point<out Number>): Double = sqrt(arg.asIterable().sumByDouble { it.toDouble() }) -} - -inline class RealVector(private val point: Point<Double>) : - SpaceElement<RealPoint, RealVector, VectorSpace<Double, RealField>>, RealPoint { - - override val context: VectorSpace<Double, RealField> get() = space(point.size) - - override fun unwrap(): RealPoint = point - - override fun RealPoint.wrap(): RealVector = RealVector(this) - - override val size: Int get() = point.size - - override operator fun get(index: Int): Double = point[index] - - override operator fun iterator(): Iterator<Double> = point.iterator() - - companion object { - private val spaceCache: MutableMap<Int, BufferVectorSpace<Double, RealField>> = hashMapOf() - - inline operator fun invoke(dim: Int, initializer: (Int) -> Double): RealVector = - RealVector(RealBuffer(dim, initializer)) - - operator fun invoke(vararg values: Double): RealVector = values.asVector() - - fun space(dim: Int): BufferVectorSpace<Double, RealField> = spaceCache.getOrPut(dim) { - BufferVectorSpace(dim, RealField) { size, init -> Buffer.real(size, init) } - } - } -} diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt deleted file mode 100644 index 82c0e86b2..000000000 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt +++ /dev/null @@ -1,8 +0,0 @@ -package scientifik.kmath.real - -import scientifik.kmath.structures.RealBuffer - -/** - * Simplified [RealBuffer] to array comparison - */ -fun RealBuffer.contentEquals(vararg doubles: Double) = array.contentEquals(doubles) \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt deleted file mode 100644 index b5569cca6..000000000 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt +++ /dev/null @@ -1,165 +0,0 @@ -package scientifik.kmath.real - -import scientifik.kmath.linear.MatrixContext -import scientifik.kmath.linear.RealMatrixContext.elementContext -import scientifik.kmath.linear.VirtualMatrix -import scientifik.kmath.operations.invoke -import scientifik.kmath.operations.sum -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.Matrix -import scientifik.kmath.structures.RealBuffer -import scientifik.kmath.structures.asIterable -import kotlin.contracts.contract -import kotlin.math.pow - -/* - * Functions for convenient "numpy-like" operations with Double matrices. - * - * Initial implementation of these functions is taken from: - * https://github.com/thomasnield/numky/blob/master/src/main/kotlin/org/nield/numky/linear/DoubleOperators.kt - * - */ - -/* - * Functions that help create a real (Double) matrix - */ - -typealias RealMatrix = Matrix<Double> - -fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix = - MatrixContext.real.produce(rowNum, colNum, initializer) - -fun Array<DoubleArray>.toMatrix(): RealMatrix { - return MatrixContext.real.produce(size, this[0].size) { row, col -> this[row][col] } -} - -fun Sequence<DoubleArray>.toMatrix(): RealMatrix = toList().let { - MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] } -} - -fun Matrix<Double>.repeatStackVertical(n: Int): RealMatrix = - VirtualMatrix(rowNum * n, colNum) { row, col -> - get(if (row == 0) 0 else row % rowNum, col) - } - -/* - * Operations for matrix and real number - */ - -operator fun Matrix<Double>.times(double: Double): RealMatrix = - MatrixContext.real.produce(rowNum, colNum) { row, col -> - this[row, col] * double - } - -operator fun Matrix<Double>.plus(double: Double): RealMatrix = - MatrixContext.real.produce(rowNum, colNum) { row, col -> - this[row, col] + double - } - -operator fun Matrix<Double>.minus(double: Double): RealMatrix = - MatrixContext.real.produce(rowNum, colNum) { row, col -> - this[row, col] - double - } - -operator fun Matrix<Double>.div(double: Double): RealMatrix = - MatrixContext.real.produce(rowNum, colNum) { row, col -> - this[row, col] / double - } - -operator fun Double.times(matrix: Matrix<Double>): RealMatrix = - MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> - this * matrix[row, col] - } - -operator fun Double.plus(matrix: Matrix<Double>): RealMatrix = - MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> - this + matrix[row, col] - } - -operator fun Double.minus(matrix: Matrix<Double>): RealMatrix = - MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> - this - matrix[row, col] - } - -// TODO: does this operation make sense? Should it be 'this/matrix[row, col]'? -//operator fun Double.div(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { -// row, col -> matrix[row, col] / this -//} - -/* - * Per-element (!) square and power operations - */ - -fun Matrix<Double>.square(): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> - this[row, col].pow(2) -} - -fun Matrix<Double>.pow(n: Int): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { i, j -> - this[i, j].pow(n) -} - -/* - * Operations on two matrices (per-element!) - */ - -operator fun Matrix<Double>.times(other: Matrix<Double>): RealMatrix = - MatrixContext.real.produce(rowNum, colNum) { row, col -> - this[row, col] * other[row, col] - } - -operator fun Matrix<Double>.plus(other: Matrix<Double>): RealMatrix = - MatrixContext.real.add(this, other) - -operator fun Matrix<Double>.minus(other: Matrix<Double>): RealMatrix = - MatrixContext.real.produce(rowNum, colNum) { row, col -> - this[row, col] - other[row, col] - } - -/* - * Operations on columns - */ - -inline fun Matrix<Double>.appendColumn(crossinline mapper: (Buffer<Double>) -> Double): Matrix<Double> { - contract { callsInPlace(mapper) } - - return MatrixContext.real.produce(rowNum, colNum + 1) { row, col -> - if (col < colNum) - this[row, col] - else - mapper(rows[row]) - } -} - -fun Matrix<Double>.extractColumns(columnRange: IntRange): RealMatrix = - MatrixContext.real.produce(rowNum, columnRange.count()) { row, col -> - this[row, columnRange.first + col] - } - -fun Matrix<Double>.extractColumn(columnIndex: Int): RealMatrix = - extractColumns(columnIndex..columnIndex) - -fun Matrix<Double>.sumByColumn(): RealBuffer = RealBuffer(colNum) { j -> - val column = columns[j] - elementContext { sum(column.asIterable()) } -} - -fun Matrix<Double>.minByColumn(): RealBuffer = RealBuffer(colNum) { j -> - columns[j].asIterable().min() ?: error("Cannot produce min on empty column") -} - -fun Matrix<Double>.maxByColumn(): RealBuffer = RealBuffer(colNum) { j -> - columns[j].asIterable().max() ?: error("Cannot produce min on empty column") -} - -fun Matrix<Double>.averageByColumn(): RealBuffer = RealBuffer(colNum) { j -> - columns[j].asIterable().average() -} - -/* - * Operations processing all elements - */ - -fun Matrix<Double>.sum(): Double = elements().map { (_, value) -> value }.sum() -fun Matrix<Double>.min(): Double? = elements().map { (_, value) -> value }.min() -fun Matrix<Double>.max(): Double? = elements().map { (_, value) -> value }.max() -fun Matrix<Double>.average(): Double = elements().map { (_, value) -> value }.average() diff --git a/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt b/kmath-for-real/src/commonTest/kotlin/kscience/kmath/linear/VectorTest.kt similarity index 86% rename from kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt rename to kmath-for-real/src/commonTest/kotlin/kscience/kmath/linear/VectorTest.kt index ef7f40afe..17ff4ef20 100644 --- a/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/kscience/kmath/linear/VectorTest.kt @@ -1,11 +1,11 @@ -package scientifik.kmath.linear +package kscience.kmath.linear -import scientifik.kmath.operations.invoke -import scientifik.kmath.real.RealVector +import kscience.kmath.operations.invoke +import kscience.kmath.real.RealVector import kotlin.test.Test import kotlin.test.assertEquals -class VectorTest { +internal class VectorTest { @Test fun testSum() { val vector1 = RealVector(5) { it.toDouble() } diff --git a/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt b/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt index 8918fb300..859938481 100644 --- a/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt @@ -1,14 +1,14 @@ package scientific.kmath.real -import scientifik.kmath.linear.VirtualMatrix -import scientifik.kmath.linear.build -import scientifik.kmath.real.* -import scientifik.kmath.structures.Matrix +import kscience.kmath.linear.VirtualMatrix +import kscience.kmath.linear.build +import kscience.kmath.real.* +import kscience.kmath.structures.Matrix import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class RealMatrixTest { +internal class RealMatrixTest { @Test fun testSum() { val m = realMatrix(10, 10) { i, j -> (i + j).toDouble() } diff --git a/kmath-functions/build.gradle.kts b/kmath-functions/build.gradle.kts index 675457ac5..2a4539c10 100644 --- a/kmath-functions/build.gradle.kts +++ b/kmath-functions/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("scientifik.mpp") + id("ru.mipt.npm.mpp") } kotlin.sourceSets.commonMain { diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Piecewise.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt similarity index 56% rename from kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Piecewise.kt rename to kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt index 16f8aa12b..a8c020c05 100644 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Piecewise.kt +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt @@ -1,48 +1,46 @@ -package scientifik.kmath.functions +package kscience.kmath.functions -import scientifik.kmath.operations.Ring +import kscience.kmath.operations.Ring -interface Piecewise<T, R> { - fun findPiece(arg: T): R? +public fun interface Piecewise<T, R> { + public fun findPiece(arg: T): R? } -interface PiecewisePolynomial<T : Any> : +public fun interface PiecewisePolynomial<T : Any> : Piecewise<T, Polynomial<T>> /** * Ordered list of pieces in piecewise function */ -class OrderedPiecewisePolynomial<T : Comparable<T>>(delimeter: T) : +public class OrderedPiecewisePolynomial<T : Comparable<T>>(delimiter: T) : PiecewisePolynomial<T> { - - private val delimiters: ArrayList<T> = arrayListOf(delimeter) - private val pieces: ArrayList<Polynomial<T>> = ArrayList() + private val delimiters: MutableList<T> = arrayListOf(delimiter) + private val pieces: MutableList<Polynomial<T>> = arrayListOf() /** * Dynamically add a piece to the "right" side (beyond maximum argument value of previous piece) * @param right new rightmost position. If is less then current rightmost position, a error is thrown. */ - fun putRight(right: T, piece: Polynomial<T>) { + public fun putRight(right: T, piece: Polynomial<T>) { require(right > delimiters.last()) { "New delimiter should be to the right of old one" } delimiters.add(right) pieces.add(piece) } - fun putLeft(left: T, piece: Polynomial<T>) { + public fun putLeft(left: T, piece: Polynomial<T>) { require(left < delimiters.first()) { "New delimiter should be to the left of old one" } delimiters.add(0, left) pieces.add(0, piece) } override fun findPiece(arg: T): Polynomial<T>? { - if (arg < delimiters.first() || arg >= delimiters.last()) { + if (arg < delimiters.first() || arg >= delimiters.last()) return null - } else { - for (index in 1 until delimiters.size) { - if (arg < delimiters[index]) { + else { + for (index in 1 until delimiters.size) + if (arg < delimiters[index]) return pieces[index - 1] - } - } + error("Piece not found") } } @@ -51,7 +49,7 @@ class OrderedPiecewisePolynomial<T : Comparable<T>>(delimeter: T) : /** * Return a value of polynomial function with given [ring] an given [arg] or null if argument is outside of piecewise definition. */ -fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.value(ring: C, arg: T): T? = +public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.value(ring: C, arg: T): T? = findPiece(arg)?.value(ring, arg) -fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.asFunction(ring: C): (T) -> T? = { value(ring, it) } \ No newline at end of file +public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.asFunction(ring: C): (T) -> T? = { value(ring, it) } \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Polynomial.kt new file mode 100644 index 000000000..c513a6889 --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Polynomial.kt @@ -0,0 +1,74 @@ +package kscience.kmath.functions + +import kscience.kmath.operations.Ring +import kscience.kmath.operations.Space +import kscience.kmath.operations.invoke +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.math.max +import kotlin.math.pow + +// TODO make `inline`, when KT-41771 gets fixed +/** + * Polynomial coefficients without fixation on specific context they are applied to + * @param coefficients constant is the leftmost coefficient + */ +public inline class Polynomial<T : Any>(public val coefficients: List<T>) + +public fun <T : Any> Polynomial(vararg coefficients: T): Polynomial<T> = Polynomial(coefficients.toList()) + +public fun Polynomial<Double>.value(): Double = coefficients.reduceIndexed { index, acc, d -> acc + d.pow(index) } + +public fun <T : Any, C : Ring<T>> Polynomial<T>.value(ring: C, arg: T): T = ring { + if (coefficients.isEmpty()) return@ring zero + var res = coefficients.first() + var powerArg = arg + + for (index in 1 until coefficients.size) { + res += coefficients[index] * powerArg + // recalculating power on each step to avoid power costs on long polynomials + powerArg *= arg + } + + res +} + +/** + * Represent a polynomial as a context-dependent function + */ +public fun <T : Any, C : Ring<T>> Polynomial<T>.asMathFunction(): MathFunction<T, C, T> = + object : MathFunction<T, C, T> { + override fun C.invoke(arg: T): T = value(this, arg) + } + +/** + * Represent the polynomial as a regular context-less function + */ +public fun <T : Any, C : Ring<T>> Polynomial<T>.asFunction(ring: C): (T) -> T = { value(ring, it) } + +/** + * An algebra for polynomials + */ +public class PolynomialSpace<T : Any, C : Ring<T>>(public val ring: C) : Space<Polynomial<T>> { + public override val zero: Polynomial<T> = Polynomial(emptyList()) + + public override fun add(a: Polynomial<T>, b: Polynomial<T>): Polynomial<T> { + val dim = max(a.coefficients.size, b.coefficients.size) + + return ring { + Polynomial(List(dim) { index -> + a.coefficients.getOrElse(index) { zero } + b.coefficients.getOrElse(index) { zero } + }) + } + } + + public override fun multiply(a: Polynomial<T>, k: Number): Polynomial<T> = + ring { Polynomial(List(a.coefficients.size) { index -> a.coefficients[index] * k }) } + + public operator fun Polynomial<T>.invoke(arg: T): T = value(ring, arg) +} + +public inline fun <T : Any, C : Ring<T>, R> C.polynomial(block: PolynomialSpace<T, C>.() -> R): R { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return PolynomialSpace(this).block() +} diff --git a/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/functions.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/functions.kt new file mode 100644 index 000000000..d780c16f3 --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/functions.kt @@ -0,0 +1,34 @@ +package kscience.kmath.functions + +import kscience.kmath.operations.Algebra +import kscience.kmath.operations.RealField + +// TODO make fun interface when KT-41770 is fixed +/** + * A regular function that could be called only inside specific algebra context + * @param T source type + * @param C source algebra constraint + * @param R result type + */ +public /*fun*/ interface MathFunction<T, C : Algebra<T>, R> { + public operator fun C.invoke(arg: T): R +} + +public fun <R> MathFunction<Double, RealField, R>.invoke(arg: Double): R = RealField.invoke(arg) + +/** + * A suspendable function defined in algebraic context + */ +// TODO make fun interface, when the new JVM IR is enabled +public interface SuspendableMathFunction<T, C : Algebra<T>, R> { + public suspend operator fun C.invoke(arg: T): R +} + +public suspend fun <R> SuspendableMathFunction<Double, RealField, R>.invoke(arg: Double): R = RealField.invoke(arg) + +/** + * A parametric function with parameter + */ +public fun interface ParametricFunction<T, P, C : Algebra<T>> { + public operator fun C.invoke(arg: T, parameter: P): T +} diff --git a/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/Interpolator.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/Interpolator.kt new file mode 100644 index 000000000..0620b4aa8 --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/Interpolator.kt @@ -0,0 +1,45 @@ +package kscience.kmath.interpolation + +import kscience.kmath.functions.PiecewisePolynomial +import kscience.kmath.functions.value +import kscience.kmath.operations.Ring +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.asBuffer + +public fun interface Interpolator<X, Y> { + public fun interpolate(points: XYPointSet<X, Y>): (X) -> Y +} + +public interface PolynomialInterpolator<T : Comparable<T>> : Interpolator<T, T> { + public val algebra: Ring<T> + + public fun getDefaultValue(): T = error("Out of bounds") + + public fun interpolatePolynomials(points: XYPointSet<T, T>): PiecewisePolynomial<T> + + override fun interpolate(points: XYPointSet<T, T>): (T) -> T = { x -> + interpolatePolynomials(points).value(algebra, x) ?: getDefaultValue() + } +} + +public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( + x: Buffer<T>, + y: Buffer<T> +): PiecewisePolynomial<T> { + val pointSet = BufferXYPointSet(x, y) + return interpolatePolynomials(pointSet) +} + +public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( + data: Map<T, T> +): PiecewisePolynomial<T> { + val pointSet = BufferXYPointSet(data.keys.toList().asBuffer(), data.values.toList().asBuffer()) + return interpolatePolynomials(pointSet) +} + +public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( + data: List<Pair<T, T>> +): PiecewisePolynomial<T> { + val pointSet = BufferXYPointSet(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer()) + return interpolatePolynomials(pointSet) +} diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LinearInterpolator.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/LinearInterpolator.kt similarity index 57% rename from kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LinearInterpolator.kt rename to kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/LinearInterpolator.kt index a7925180d..377aa1fbe 100644 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LinearInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/LinearInterpolator.kt @@ -1,16 +1,16 @@ -package scientifik.kmath.interpolation +package kscience.kmath.interpolation -import scientifik.kmath.functions.OrderedPiecewisePolynomial -import scientifik.kmath.functions.PiecewisePolynomial -import scientifik.kmath.functions.Polynomial -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.invoke +import kscience.kmath.functions.OrderedPiecewisePolynomial +import kscience.kmath.functions.PiecewisePolynomial +import kscience.kmath.functions.Polynomial +import kscience.kmath.operations.Field +import kscience.kmath.operations.invoke /** * Reference JVM implementation: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/analysis/interpolation/LinearInterpolator.java */ -class LinearInterpolator<T : Comparable<T>>(override val algebra: Field<T>) : PolynomialInterpolator<T> { - override fun interpolatePolynomials(points: XYPointSet<T, T>): PiecewisePolynomial<T> = algebra { +public class LinearInterpolator<T : Comparable<T>>(public override val algebra: Field<T>) : PolynomialInterpolator<T> { + public override fun interpolatePolynomials(points: XYPointSet<T, T>): PiecewisePolynomial<T> = algebra { require(points.size > 0) { "Point array should not be empty" } insureSorted(points) diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LoessInterpolator.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/LoessInterpolator.kt similarity index 98% rename from kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LoessInterpolator.kt rename to kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/LoessInterpolator.kt index 6707bd8bc..6931857b1 100644 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LoessInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/LoessInterpolator.kt @@ -1,8 +1,8 @@ -package scientifik.kmath.interpolation +package kscience.kmath.interpolation // -//import scientifik.kmath.functions.PiecewisePolynomial -//import scientifik.kmath.operations.Ring -//import scientifik.kmath.structures.Buffer +//import kscience.kmath.functions.PiecewisePolynomial +//import kscience.kmath.operations.Ring +//import kscience.kmath.structures.Buffer //import kotlin.math.abs //import kotlin.math.sqrt // diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/SplineInterpolator.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/SplineInterpolator.kt similarity index 69% rename from kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/SplineInterpolator.kt rename to kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/SplineInterpolator.kt index b709c4e87..6cda45f72 100644 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/SplineInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/SplineInterpolator.kt @@ -1,29 +1,25 @@ -package scientifik.kmath.interpolation +package kscience.kmath.interpolation -import scientifik.kmath.functions.OrderedPiecewisePolynomial -import scientifik.kmath.functions.PiecewisePolynomial -import scientifik.kmath.functions.Polynomial -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.invoke -import scientifik.kmath.structures.MutableBufferFactory +import kscience.kmath.functions.OrderedPiecewisePolynomial +import kscience.kmath.functions.PiecewisePolynomial +import kscience.kmath.functions.Polynomial +import kscience.kmath.operations.Field +import kscience.kmath.operations.invoke +import kscience.kmath.structures.MutableBufferFactory /** * Generic spline interpolator. Not recommended for performance critical places, use platform-specific and type specific ones. * Based on https://github.com/apache/commons-math/blob/eb57d6d457002a0bb5336d789a3381a24599affe/src/main/java/org/apache/commons/math4/analysis/interpolation/SplineInterpolator.java */ -class SplineInterpolator<T : Comparable<T>>( - override val algebra: Field<T>, - val bufferFactory: MutableBufferFactory<T> +public class SplineInterpolator<T : Comparable<T>>( + public override val algebra: Field<T>, + public val bufferFactory: MutableBufferFactory<T> ) : PolynomialInterpolator<T> { - //TODO possibly optimize zeroed buffers - override fun interpolatePolynomials(points: XYPointSet<T, T>): PiecewisePolynomial<T> = algebra { - if (points.size < 3) { - error("Can't use spline interpolator with less than 3 points") - } + public override fun interpolatePolynomials(points: XYPointSet<T, T>): PiecewisePolynomial<T> = algebra { + require(points.size >= 3) { "Can't use spline interpolator with less than 3 points" } insureSorted(points) - // Number of intervals. The number of data points is n + 1. val n = points.size - 1 // Differences between knot points @@ -34,6 +30,7 @@ class SplineInterpolator<T : Comparable<T>>( for (i in 1 until n) { val g = 2.0 * (points.x[i + 1] - points.x[i - 1]) - h[i - 1] * mu[i - 1] mu[i] = h[i] / g + z[i] = (3.0 * (points.y[i + 1] * h[i - 1] - points.x[i] * (points.x[i + 1] - points.x[i - 1]) + points.y[i - 1] * h[i]) / (h[i - 1] * h[i]) - h[i - 1] * z[i - 1]) / g @@ -41,8 +38,9 @@ class SplineInterpolator<T : Comparable<T>>( // cubic spline coefficients -- b is linear, c quadratic, d is cubic (original y's are constants) - OrderedPiecewisePolynomial<T>(points.x[points.size - 1]).apply { + OrderedPiecewisePolynomial(points.x[points.size - 1]).apply { var cOld = zero + for (j in n - 1 downTo 0) { val c = z[j] - mu[j] * cOld val a = points.y[j] @@ -53,7 +51,5 @@ class SplineInterpolator<T : Comparable<T>>( putLeft(points.x[j], polynomial) } } - } - } diff --git a/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/XYPointSet.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/XYPointSet.kt new file mode 100644 index 000000000..2abb7742c --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/interpolation/XYPointSet.kt @@ -0,0 +1,53 @@ +package kscience.kmath.interpolation + +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.Structure2D + +public interface XYPointSet<X, Y> { + public val size: Int + public val x: Buffer<X> + public val y: Buffer<Y> +} + +public interface XYZPointSet<X, Y, Z> : XYPointSet<X, Y> { + public val z: Buffer<Z> +} + +internal fun <T : Comparable<T>> insureSorted(points: XYPointSet<T, *>) { + for (i in 0 until points.size - 1) + require(points.x[i + 1] > points.x[i]) { "Input data is not sorted at index $i" } +} + +public class NDStructureColumn<T>(public val structure: Structure2D<T>, public val column: Int) : Buffer<T> { + public override val size: Int + get() = structure.rowNum + + init { + require(column < structure.colNum) { "Column index is outside of structure column range" } + } + + public override operator fun get(index: Int): T = structure[index, column] + public override operator fun iterator(): Iterator<T> = sequence { repeat(size) { yield(get(it)) } }.iterator() +} + +public class BufferXYPointSet<X, Y>( + public override val x: Buffer<X>, + public override val y: Buffer<Y> +) : XYPointSet<X, Y> { + public override val size: Int + get() = x.size + + init { + require(x.size == y.size) { "Sizes of x and y buffers should be the same" } + } +} + +public fun <T> Structure2D<T>.asXYPointSet(): XYPointSet<T, T> { + require(shape[1] == 2) { "Structure second dimension should be of size 2" } + + return object : XYPointSet<T, T> { + override val size: Int get() = this@asXYPointSet.shape[0] + override val x: Buffer<T> get() = NDStructureColumn(this@asXYPointSet, 0) + override val y: Buffer<T> get() = NDStructureColumn(this@asXYPointSet, 1) + } +} diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt deleted file mode 100644 index fcfa9f1aa..000000000 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt +++ /dev/null @@ -1,76 +0,0 @@ -package scientifik.kmath.functions - -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.invoke -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.math.max -import kotlin.math.pow - -/** - * Polynomial coefficients without fixation on specific context they are applied to - * @param coefficients constant is the leftmost coefficient - */ -inline class Polynomial<T : Any>(val coefficients: List<T>) { - constructor(vararg coefficients: T) : this(coefficients.toList()) -} - -fun Polynomial<Double>.value(): Double = - coefficients.reduceIndexed { index: Int, acc: Double, d: Double -> acc + d.pow(index) } - -fun <T : Any, C : Ring<T>> Polynomial<T>.value(ring: C, arg: T): T = ring { - if (coefficients.isEmpty()) return@ring zero - var res = coefficients.first() - var powerArg = arg - - for (index in 1 until coefficients.size) { - res += coefficients[index] * powerArg - //recalculating power on each step to avoid power costs on long polynomials - powerArg *= arg - } - - res -} - -/** - * Represent a polynomial as a context-dependent function - */ -fun <T : Any, C : Ring<T>> Polynomial<T>.asMathFunction(): MathFunction<T, out C, T> = object : - MathFunction<T, C, T> { - override operator fun C.invoke(arg: T): T = value(this, arg) -} - -/** - * Represent the polynomial as a regular context-less function - */ -fun <T : Any, C : Ring<T>> Polynomial<T>.asFunction(ring: C): (T) -> T = { value(ring, it) } - -/** - * An algebra for polynomials - */ -class PolynomialSpace<T : Any, C : Ring<T>>(val ring: C) : Space<Polynomial<T>> { - - override fun add(a: Polynomial<T>, b: Polynomial<T>): Polynomial<T> { - val dim = max(a.coefficients.size, b.coefficients.size) - - return ring { - Polynomial(List(dim) { index -> - a.coefficients.getOrElse(index) { zero } + b.coefficients.getOrElse(index) { zero } - }) - } - } - - override fun multiply(a: Polynomial<T>, k: Number): Polynomial<T> = - ring { Polynomial(List(a.coefficients.size) { index -> a.coefficients[index] * k }) } - - override val zero: Polynomial<T> = - Polynomial(emptyList()) - - operator fun Polynomial<T>.invoke(arg: T): T = value(ring, arg) -} - -inline fun <T : Any, C : Ring<T>, R> C.polynomial(block: PolynomialSpace<T, C>.() -> R): R { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return PolynomialSpace(this).block() -} diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/functions.kt b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/functions.kt deleted file mode 100644 index 2b822b3ba..000000000 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/functions.kt +++ /dev/null @@ -1,33 +0,0 @@ -package scientifik.kmath.functions - -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.RealField - -/** - * A regular function that could be called only inside specific algebra context - * @param T source type - * @param C source algebra constraint - * @param R result type - */ -interface MathFunction<T, C : Algebra<T>, R> { - operator fun C.invoke(arg: T): R -} - -fun <R> MathFunction<Double, RealField, R>.invoke(arg: Double): R = RealField.invoke(arg) - -/** - * A suspendable function defined in algebraic context - */ -interface SuspendableMathFunction<T, C : Algebra<T>, R> { - suspend operator fun C.invoke(arg: T): R -} - -suspend fun <R> SuspendableMathFunction<Double, RealField, R>.invoke(arg: Double) = RealField.invoke(arg) - - -/** - * A parametric function with parameter - */ -interface ParametricFunction<T, P, C : Algebra<T>> { - operator fun C.invoke(arg: T, parameter: P): T -} diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/Interpolator.kt b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/Interpolator.kt deleted file mode 100644 index 8d83e4198..000000000 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/Interpolator.kt +++ /dev/null @@ -1,45 +0,0 @@ -package scientifik.kmath.interpolation - -import scientifik.kmath.functions.PiecewisePolynomial -import scientifik.kmath.functions.value -import scientifik.kmath.operations.Ring -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.asBuffer - -interface Interpolator<X, Y> { - fun interpolate(points: XYPointSet<X, Y>): (X) -> Y -} - -interface PolynomialInterpolator<T : Comparable<T>> : Interpolator<T, T> { - val algebra: Ring<T> - - fun getDefaultValue(): T = error("Out of bounds") - - fun interpolatePolynomials(points: XYPointSet<T, T>): PiecewisePolynomial<T> - - override fun interpolate(points: XYPointSet<T, T>): (T) -> T = { x -> - interpolatePolynomials(points).value(algebra, x) ?: getDefaultValue() - } -} - -fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( - x: Buffer<T>, - y: Buffer<T> -): PiecewisePolynomial<T> { - val pointSet = BufferXYPointSet(x, y) - return interpolatePolynomials(pointSet) -} - -fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( - data: Map<T, T> -): PiecewisePolynomial<T> { - val pointSet = BufferXYPointSet(data.keys.toList().asBuffer(), data.values.toList().asBuffer()) - return interpolatePolynomials(pointSet) -} - -fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( - data: List<Pair<T, T>> -): PiecewisePolynomial<T> { - val pointSet = BufferXYPointSet(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer()) - return interpolatePolynomials(pointSet) -} \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/XYPointSet.kt b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/XYPointSet.kt deleted file mode 100644 index 56953f9fc..000000000 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/XYPointSet.kt +++ /dev/null @@ -1,52 +0,0 @@ -package scientifik.kmath.interpolation - -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.Structure2D - -interface XYPointSet<X, Y> { - val size: Int - val x: Buffer<X> - val y: Buffer<Y> -} - -interface XYZPointSet<X, Y, Z> : XYPointSet<X, Y> { - val z: Buffer<Z> -} - -internal fun <T : Comparable<T>> insureSorted(points: XYPointSet<T, *>) { - for (i in 0 until points.size - 1) require(points.x[i + 1] > points.x[i]) { "Input data is not sorted at index $i" } -} - -class NDStructureColumn<T>(val structure: Structure2D<T>, val column: Int) : Buffer<T> { - init { - require(column < structure.colNum) { "Column index is outside of structure column range" } - } - - override val size: Int get() = structure.rowNum - - override operator fun get(index: Int): T = structure[index, column] - - override operator fun iterator(): Iterator<T> = sequence { - repeat(size) { - yield(get(it)) - } - }.iterator() -} - -class BufferXYPointSet<X, Y>(override val x: Buffer<X>, override val y: Buffer<Y>) : XYPointSet<X, Y> { - init { - require(x.size == y.size) { "Sizes of x and y buffers should be the same" } - } - - override val size: Int - get() = x.size -} - -fun <T> Structure2D<T>.asXYPointSet(): XYPointSet<T, T> { - require(shape[1] == 2) { "Structure second dimension should be of size 2" } - return object : XYPointSet<T, T> { - override val size: Int get() = this@asXYPointSet.shape[0] - override val x: Buffer<T> get() = NDStructureColumn(this@asXYPointSet, 0) - override val y: Buffer<T> get() = NDStructureColumn(this@asXYPointSet, 1) - } -} \ No newline at end of file diff --git a/kmath-functions/src/commonTest/kotlin/scientifik/kmath/interpolation/LinearInterpolatorTest.kt b/kmath-functions/src/commonTest/kotlin/kscience/kmath/interpolation/LinearInterpolatorTest.kt similarity index 72% rename from kmath-functions/src/commonTest/kotlin/scientifik/kmath/interpolation/LinearInterpolatorTest.kt rename to kmath-functions/src/commonTest/kotlin/kscience/kmath/interpolation/LinearInterpolatorTest.kt index 23acd835c..303615676 100644 --- a/kmath-functions/src/commonTest/kotlin/scientifik/kmath/interpolation/LinearInterpolatorTest.kt +++ b/kmath-functions/src/commonTest/kotlin/kscience/kmath/interpolation/LinearInterpolatorTest.kt @@ -1,13 +1,12 @@ -package scientifik.kmath.interpolation +package kscience.kmath.interpolation -import scientifik.kmath.functions.PiecewisePolynomial -import scientifik.kmath.functions.asFunction -import scientifik.kmath.operations.RealField +import kscience.kmath.functions.PiecewisePolynomial +import kscience.kmath.functions.asFunction +import kscience.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals - -class LinearInterpolatorTest { +internal class LinearInterpolatorTest { @Test fun testInterpolation() { val data = listOf( @@ -16,12 +15,12 @@ class LinearInterpolatorTest { 2.0 to 3.0, 3.0 to 4.0 ) + val polynomial: PiecewisePolynomial<Double> = LinearInterpolator(RealField).interpolatePolynomials(data) val function = polynomial.asFunction(RealField) - assertEquals(null, function(-1.0)) assertEquals(0.5, function(0.5)) assertEquals(2.0, function(1.5)) assertEquals(3.0, function(2.0)) } -} \ No newline at end of file +} diff --git a/kmath-geometry/build.gradle.kts b/kmath-geometry/build.gradle.kts index 39aa833ad..00abcb934 100644 --- a/kmath-geometry/build.gradle.kts +++ b/kmath-geometry/build.gradle.kts @@ -1,9 +1,7 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("ru.mipt.npm.mpp") } kotlin.sourceSets.commonMain { dependencies { api(project(":kmath-core")) } -} \ No newline at end of file +} diff --git a/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/Euclidean2DSpace.kt b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/Euclidean2DSpace.kt new file mode 100644 index 000000000..c2a883a64 --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/Euclidean2DSpace.kt @@ -0,0 +1,47 @@ +package kscience.kmath.geometry + +import kscience.kmath.linear.Point +import kscience.kmath.operations.SpaceElement +import kscience.kmath.operations.invoke +import kotlin.math.sqrt + +public interface Vector2D : Point<Double>, Vector, SpaceElement<Vector2D, Vector2D, Euclidean2DSpace> { + public val x: Double + public val y: Double + public override val context: Euclidean2DSpace get() = Euclidean2DSpace + public override val size: Int get() = 2 + + public override operator fun get(index: Int): Double = when (index) { + 1 -> x + 2 -> y + else -> error("Accessing outside of point bounds") + } + + public override operator fun iterator(): Iterator<Double> = listOf(x, y).iterator() + public override fun unwrap(): Vector2D = this + public override fun Vector2D.wrap(): Vector2D = this +} + +public val Vector2D.r: Double + get() = Euclidean2DSpace { sqrt(norm()) } + +@Suppress("FunctionName") +public fun Vector2D(x: Double, y: Double): Vector2D = Vector2DImpl(x, y) + +private data class Vector2DImpl( + override val x: Double, + override val y: Double +) : Vector2D + +/** + * 2D Euclidean space + */ +public object Euclidean2DSpace : GeometrySpace<Vector2D> { + public override val zero: Vector2D by lazy { Vector2D(0.0, 0.0) } + + public fun Vector2D.norm(): Double = sqrt(x * x + y * y) + public override fun Vector2D.distanceTo(other: Vector2D): Double = (this - other).norm() + public override fun add(a: Vector2D, b: Vector2D): Vector2D = Vector2D(a.x + b.x, a.y + b.y) + public override fun multiply(a: Vector2D, k: Number): Vector2D = Vector2D(a.x * k.toDouble(), a.y * k.toDouble()) + public override fun Vector2D.dot(other: Vector2D): Double = x * other.x + y * other.y +} diff --git a/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/Euclidean3DSpace.kt b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/Euclidean3DSpace.kt new file mode 100644 index 000000000..e0052d791 --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/Euclidean3DSpace.kt @@ -0,0 +1,53 @@ +package kscience.kmath.geometry + +import kscience.kmath.linear.Point +import kscience.kmath.operations.SpaceElement +import kscience.kmath.operations.invoke +import kotlin.math.sqrt + +public interface Vector3D : Point<Double>, Vector, SpaceElement<Vector3D, Vector3D, Euclidean3DSpace> { + public val x: Double + public val y: Double + public val z: Double + public override val context: Euclidean3DSpace get() = Euclidean3DSpace + public override val size: Int get() = 3 + + public override operator fun get(index: Int): Double = when (index) { + 1 -> x + 2 -> y + 3 -> z + else -> error("Accessing outside of point bounds") + } + + public override operator fun iterator(): Iterator<Double> = listOf(x, y, z).iterator() + public override fun unwrap(): Vector3D = this + public override fun Vector3D.wrap(): Vector3D = this +} + +@Suppress("FunctionName") +public fun Vector3D(x: Double, y: Double, z: Double): Vector3D = Vector3DImpl(x, y, z) + +public val Vector3D.r: Double get() = Euclidean3DSpace { sqrt(norm()) } + +private data class Vector3DImpl( + override val x: Double, + override val y: Double, + override val z: Double +) : Vector3D + +public object Euclidean3DSpace : GeometrySpace<Vector3D> { + public override val zero: Vector3D by lazy { Vector3D(0.0, 0.0, 0.0) } + + public fun Vector3D.norm(): Double = sqrt(x * x + y * y + z * z) + + public override fun Vector3D.distanceTo(other: Vector3D): Double = (this - other).norm() + + public override fun add(a: Vector3D, b: Vector3D): Vector3D = + Vector3D(a.x + b.x, a.y + b.y, a.z + b.z) + + public override fun multiply(a: Vector3D, k: Number): Vector3D = + Vector3D(a.x * k.toDouble(), a.y * k.toDouble(), a.z * k.toDouble()) + + public override fun Vector3D.dot(other: Vector3D): Double = + x * other.x + y * other.y + z * other.z +} diff --git a/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/GeometrySpace.kt b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/GeometrySpace.kt new file mode 100644 index 000000000..64badacf5 --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/GeometrySpace.kt @@ -0,0 +1,17 @@ +package kscience.kmath.geometry + +import kscience.kmath.operations.Space + +public interface Vector + +public interface GeometrySpace<V: Vector>: Space<V> { + /** + * L2 distance + */ + public fun V.distanceTo(other: V): Double + + /** + * Scalar product + */ + public infix fun V.dot(other: V): Double +} \ No newline at end of file diff --git a/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/Line.kt b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/Line.kt new file mode 100644 index 000000000..ec2ce31ca --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/Line.kt @@ -0,0 +1,6 @@ +package kscience.kmath.geometry + +public data class Line<V : Vector>(val base: V, val direction: V) + +public typealias Line2D = Line<Vector2D> +public typealias Line3D = Line<Vector3D> diff --git a/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/ReferenceFrame.kt b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/ReferenceFrame.kt new file mode 100644 index 000000000..f9de7b51f --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/kscience/kmath/geometry/ReferenceFrame.kt @@ -0,0 +1,3 @@ +package kscience.kmath.geometry + +public interface ReferenceFrame diff --git a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean2DSpace.kt b/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean2DSpace.kt deleted file mode 100644 index f0dc49882..000000000 --- a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean2DSpace.kt +++ /dev/null @@ -1,54 +0,0 @@ -package scientifik.kmath.geometry - -import scientifik.kmath.linear.Point -import scientifik.kmath.operations.SpaceElement -import scientifik.kmath.operations.invoke -import kotlin.math.sqrt - - -interface Vector2D : Point<Double>, Vector, SpaceElement<Vector2D, Vector2D, Euclidean2DSpace> { - val x: Double - val y: Double - override val context: Euclidean2DSpace get() = Euclidean2DSpace - override val size: Int get() = 2 - - override operator fun get(index: Int): Double = when (index) { - 1 -> x - 2 -> y - else -> error("Accessing outside of point bounds") - } - - override operator fun iterator(): Iterator<Double> = listOf(x, y).iterator() - override fun unwrap(): Vector2D = this - override fun Vector2D.wrap(): Vector2D = this -} - -val Vector2D.r: Double get() = Euclidean2DSpace { sqrt(norm()) } - -@Suppress("FunctionName") -fun Vector2D(x: Double, y: Double): Vector2D = Vector2DImpl(x, y) - -private data class Vector2DImpl( - override val x: Double, - override val y: Double -) : Vector2D - -/** - * 2D Euclidean space - */ -object Euclidean2DSpace : GeometrySpace<Vector2D> { - fun Vector2D.norm(): Double = sqrt(x * x + y * y) - - override fun Vector2D.distanceTo(other: Vector2D): Double = (this - other).norm() - - override fun add(a: Vector2D, b: Vector2D): Vector2D = - Vector2D(a.x + b.x, a.y + b.y) - - override fun multiply(a: Vector2D, k: Number): Vector2D = - Vector2D(a.x * k.toDouble(), a.y * k.toDouble()) - - override val zero: Vector2D = Vector2D(0.0, 0.0) - - override fun Vector2D.dot(other: Vector2D): Double = - x * other.x + y * other.y -} \ No newline at end of file diff --git a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean3DSpace.kt b/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean3DSpace.kt deleted file mode 100644 index 3748e58c7..000000000 --- a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean3DSpace.kt +++ /dev/null @@ -1,56 +0,0 @@ -package scientifik.kmath.geometry - -import scientifik.kmath.linear.Point -import scientifik.kmath.operations.SpaceElement -import scientifik.kmath.operations.invoke -import kotlin.math.sqrt - - -interface Vector3D : Point<Double>, Vector, SpaceElement<Vector3D, Vector3D, Euclidean3DSpace> { - val x: Double - val y: Double - val z: Double - override val context: Euclidean3DSpace get() = Euclidean3DSpace - override val size: Int get() = 3 - - override operator fun get(index: Int): Double = when (index) { - 1 -> x - 2 -> y - 3 -> z - else -> error("Accessing outside of point bounds") - } - - override operator fun iterator(): Iterator<Double> = listOf(x, y, z).iterator() - - override fun unwrap(): Vector3D = this - - override fun Vector3D.wrap(): Vector3D = this -} - -@Suppress("FunctionName") -fun Vector3D(x: Double, y: Double, z: Double): Vector3D = Vector3DImpl(x, y, z) - -val Vector3D.r: Double get() = Euclidean3DSpace { sqrt(norm()) } - -private data class Vector3DImpl( - override val x: Double, - override val y: Double, - override val z: Double -) : Vector3D - -object Euclidean3DSpace : GeometrySpace<Vector3D> { - override val zero: Vector3D = Vector3D(0.0, 0.0, 0.0) - - fun Vector3D.norm(): Double = sqrt(x * x + y * y + z * z) - - override fun Vector3D.distanceTo(other: Vector3D): Double = (this - other).norm() - - override fun add(a: Vector3D, b: Vector3D): Vector3D = - Vector3D(a.x + b.x, a.y + b.y, a.z + b.z) - - override fun multiply(a: Vector3D, k: Number): Vector3D = - Vector3D(a.x * k.toDouble(), a.y * k.toDouble(), a.z * k.toDouble()) - - override fun Vector3D.dot(other: Vector3D): Double = - x * other.x + y * other.y + z * other.z -} diff --git a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/GeometrySpace.kt b/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/GeometrySpace.kt deleted file mode 100644 index b65a8dd3a..000000000 --- a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/GeometrySpace.kt +++ /dev/null @@ -1,17 +0,0 @@ -package scientifik.kmath.geometry - -import scientifik.kmath.operations.Space - -interface Vector - -interface GeometrySpace<V: Vector>: Space<V> { - /** - * L2 distance - */ - fun V.distanceTo(other: V): Double - - /** - * Scalar product - */ - infix fun V.dot(other: V): Double -} \ No newline at end of file diff --git a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Line.kt b/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Line.kt deleted file mode 100644 index d802a103f..000000000 --- a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Line.kt +++ /dev/null @@ -1,6 +0,0 @@ -package scientifik.kmath.geometry - -data class Line<V: Vector>(val base: V, val direction: V) - -typealias Line2D = Line<Vector2D> -typealias Line3D = Line<Vector3D> diff --git a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/ReferenceFrame.kt b/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/ReferenceFrame.kt deleted file mode 100644 index 420e38ce2..000000000 --- a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/ReferenceFrame.kt +++ /dev/null @@ -1,4 +0,0 @@ -package scientifik.kmath.geometry - -interface ReferenceFrame { -} \ No newline at end of file diff --git a/kmath-histograms/build.gradle.kts b/kmath-histograms/build.gradle.kts index 993bfed8e..7de21ad89 100644 --- a/kmath-histograms/build.gradle.kts +++ b/kmath-histograms/build.gradle.kts @@ -1,10 +1,8 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("ru.mipt.npm.mpp") } kotlin.sourceSets.commonMain { dependencies { api(project(":kmath-core")) api(project(":kmath-for-real")) } -} \ No newline at end of file +} diff --git a/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Counters.kt b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Counters.kt new file mode 100644 index 000000000..7a263a9fc --- /dev/null +++ b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Counters.kt @@ -0,0 +1,20 @@ +package kscience.kmath.histogram + +/* + * Common representation for atomic counters + * TODO replace with atomics + */ + +public expect class LongCounter() { + public fun decrement() + public fun increment() + public fun reset() + public fun sum(): Long + public fun add(l: Long) +} + +public expect class DoubleCounter() { + public fun reset() + public fun sum(): Double + public fun add(d: Double) +} diff --git a/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Histogram.kt b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Histogram.kt new file mode 100644 index 000000000..98300dada --- /dev/null +++ b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Histogram.kt @@ -0,0 +1,53 @@ +package kscience.kmath.histogram + +import kscience.kmath.domains.Domain +import kscience.kmath.linear.Point +import kscience.kmath.structures.ArrayBuffer +import kscience.kmath.structures.RealBuffer + +/** + * The bin in the histogram. The histogram is by definition always done in the real space + */ +public interface Bin<T : Any> : Domain<T> { + /** + * The value of this bin + */ + public val value: Number + public val center: Point<T> +} + +public interface Histogram<T : Any, out B : Bin<T>> : Iterable<B> { + /** + * Find existing bin, corresponding to given coordinates + */ + public operator fun get(point: Point<out T>): B? + + /** + * Dimension of the histogram + */ + public val dimension: Int +} + +public interface MutableHistogram<T : Any, out B : Bin<T>> : Histogram<T, B> { + + /** + * Increment appropriate bin + */ + public fun putWithWeight(point: Point<out T>, weight: Double) + + public fun put(point: Point<out T>): Unit = putWithWeight(point, 1.0) +} + +public fun <T : Any> MutableHistogram<T, *>.put(vararg point: T): Unit = put(ArrayBuffer(point)) + +public fun MutableHistogram<Double, *>.put(vararg point: Number): Unit = + put(RealBuffer(point.map { it.toDouble() }.toDoubleArray())) + +public fun MutableHistogram<Double, *>.put(vararg point: Double): Unit = put(RealBuffer(point)) +public fun <T : Any> MutableHistogram<T, *>.fill(sequence: Iterable<Point<T>>): Unit = sequence.forEach { put(it) } + +/** + * Pass a sequence builder into histogram + */ +public fun <T : Any> MutableHistogram<T, *>.fill(block: suspend SequenceScope<Point<T>>.() -> Unit): Unit = + fill(sequence(block).asIterable()) diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt similarity index 56% rename from kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt rename to kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt index f05ae1694..c58952ab4 100644 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/RealHistogram.kt @@ -1,15 +1,18 @@ -package scientifik.kmath.histogram +package kscience.kmath.histogram -import scientifik.kmath.linear.Point -import scientifik.kmath.operations.SpaceOperations -import scientifik.kmath.operations.invoke -import scientifik.kmath.real.asVector -import scientifik.kmath.structures.* +import kscience.kmath.linear.Point +import kscience.kmath.operations.SpaceOperations +import kscience.kmath.operations.invoke +import kscience.kmath.real.asVector +import kscience.kmath.structures.* import kotlin.math.floor - -data class BinDef<T : Comparable<T>>(val space: SpaceOperations<Point<T>>, val center: Point<T>, val sizes: Point<T>) { - fun contains(vector: Point<out T>): Boolean { +public data class BinDef<T : Comparable<T>>( + public val space: SpaceOperations<Point<T>>, + public val center: Point<T>, + public val sizes: Point<T> +) { + public fun contains(vector: Point<out T>): Boolean { require(vector.size == center.size) { "Dimension mismatch for input vector. Expected ${center.size}, but found ${vector.size}" } val upper = space { center + sizes / 2.0 } val lower = space { center - sizes / 2.0 } @@ -18,21 +21,20 @@ data class BinDef<T : Comparable<T>>(val space: SpaceOperations<Point<T>>, val c } -class MultivariateBin<T : Comparable<T>>(val def: BinDef<T>, override val value: Number) : Bin<T> { - override operator fun contains(point: Point<T>): Boolean = def.contains(point) - - override val dimension: Int +public class MultivariateBin<T : Comparable<T>>(public val def: BinDef<T>, public override val value: Number) : Bin<T> { + public override val dimension: Int get() = def.center.size - override val center: Point<T> + public override val center: Point<T> get() = def.center + public override operator fun contains(point: Point<T>): Boolean = def.contains(point) } /** * Uniform multivariate histogram with fixed borders. Based on NDStructure implementation with complexity of m for bin search, where m is the number of dimensions. */ -class RealHistogram( +public class RealHistogram( private val lower: Buffer<Double>, private val upper: Buffer<Double>, private val binNums: IntArray = IntArray(lower.size) { 20 } @@ -40,7 +42,7 @@ class RealHistogram( private val strides = DefaultStrides(IntArray(binNums.size) { binNums[it] + 2 }) private val values: NDStructure<LongCounter> = NDStructure.auto(strides) { LongCounter() } private val weights: NDStructure<DoubleCounter> = NDStructure.auto(strides) { DoubleCounter() } - override val dimension: Int get() = lower.size + public override val dimension: Int get() = lower.size private val binSize = RealBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] } init { @@ -64,7 +66,7 @@ class RealHistogram( private fun getValue(index: IntArray): Long = values[index].sum() - fun getValue(point: Buffer<out Double>): Long = getValue(getIndex(point)) + public fun getValue(point: Buffer<out Double>): Long = getValue(getIndex(point)) private fun getDef(index: IntArray): BinDef<Double> { val center = index.mapIndexed { axis, i -> @@ -78,9 +80,9 @@ class RealHistogram( return BinDef(RealBufferFieldOperations, center, binSize) } - fun getDef(point: Buffer<out Double>): BinDef<Double> = getDef(getIndex(point)) + public fun getDef(point: Buffer<out Double>): BinDef<Double> = getDef(getIndex(point)) - override operator fun get(point: Buffer<out Double>): MultivariateBin<Double>? { + public override operator fun get(point: Buffer<out Double>): MultivariateBin<Double>? { val index = getIndex(point) return MultivariateBin(getDef(index), getValue(index)) } @@ -90,27 +92,27 @@ class RealHistogram( // values[index].increment() // } - override fun putWithWeight(point: Buffer<out Double>, weight: Double) { + public override fun putWithWeight(point: Buffer<out Double>, weight: Double) { val index = getIndex(point) values[index].increment() weights[index].add(weight) } - override operator fun iterator(): Iterator<MultivariateBin<Double>> = weights.elements().map { (index, value) -> - MultivariateBin(getDef(index), value.sum()) - }.iterator() + public override operator fun iterator(): Iterator<MultivariateBin<Double>> = + weights.elements().map { (index, value) -> MultivariateBin(getDef(index), value.sum()) } + .iterator() /** * Convert this histogram into NDStructure containing bin values but not bin descriptions */ - fun values(): NDStructure<Number> = NDStructure.auto(values.shape) { values[it].sum() } + public fun values(): NDStructure<Number> = NDStructure.auto(values.shape) { values[it].sum() } /** * Sum of weights */ - fun weights(): NDStructure<Double> = NDStructure.auto(weights.shape) { weights[it].sum() } + public fun weights(): NDStructure<Double> = NDStructure.auto(weights.shape) { weights[it].sum() } - companion object { + public companion object { /** * Use it like * ``` @@ -120,9 +122,9 @@ class RealHistogram( *) *``` */ - fun fromRanges(vararg ranges: ClosedFloatingPointRange<Double>): RealHistogram = RealHistogram( - ranges.map { it.start }.asVector(), - ranges.map { it.endInclusive }.asVector() + public fun fromRanges(vararg ranges: ClosedFloatingPointRange<Double>): RealHistogram = RealHistogram( + ranges.map(ClosedFloatingPointRange<Double>::start).asVector(), + ranges.map(ClosedFloatingPointRange<Double>::endInclusive).asVector() ) /** @@ -134,10 +136,21 @@ class RealHistogram( *) *``` */ - fun fromRanges(vararg ranges: Pair<ClosedFloatingPointRange<Double>, Int>): RealHistogram = RealHistogram( - ListBuffer(ranges.map { it.first.start }), - ListBuffer(ranges.map { it.first.endInclusive }), - ranges.map { it.second }.toIntArray() - ) + public fun fromRanges(vararg ranges: Pair<ClosedFloatingPointRange<Double>, Int>): RealHistogram = + RealHistogram( + ListBuffer( + ranges + .map(Pair<ClosedFloatingPointRange<Double>, Int>::first) + .map(ClosedFloatingPointRange<Double>::start) + ), + + ListBuffer( + ranges + .map(Pair<ClosedFloatingPointRange<Double>, Int>::first) + .map(ClosedFloatingPointRange<Double>::endInclusive) + ), + + ranges.map(Pair<ClosedFloatingPointRange<Double>, Int>::second).toIntArray() + ) } } diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt deleted file mode 100644 index 9c7de3303..000000000 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt +++ /dev/null @@ -1,20 +0,0 @@ -package scientifik.kmath.histogram - -/* - * Common representation for atomic counters - * TODO replace with atomics - */ - -expect class LongCounter() { - fun decrement() - fun increment() - fun reset() - fun sum(): Long - fun add(l: Long) -} - -expect class DoubleCounter() { - fun reset() - fun sum(): Double - fun add(d: Double) -} \ No newline at end of file diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt deleted file mode 100644 index 32b6444cc..000000000 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt +++ /dev/null @@ -1,56 +0,0 @@ -package scientifik.kmath.histogram - -import scientifik.kmath.domains.Domain -import scientifik.kmath.linear.Point -import scientifik.kmath.structures.ArrayBuffer -import scientifik.kmath.structures.RealBuffer - -/** - * The bin in the histogram. The histogram is by definition always done in the real space - */ -interface Bin<T : Any> : Domain<T> { - /** - * The value of this bin - */ - val value: Number - val center: Point<T> -} - -interface Histogram<T : Any, out B : Bin<T>> : Iterable<B> { - - /** - * Find existing bin, corresponding to given coordinates - */ - operator fun get(point: Point<out T>): B? - - /** - * Dimension of the histogram - */ - val dimension: Int - -} - -interface MutableHistogram<T : Any, out B : Bin<T>> : Histogram<T, B> { - - /** - * Increment appropriate bin - */ - fun putWithWeight(point: Point<out T>, weight: Double) - - fun put(point: Point<out T>): Unit = putWithWeight(point, 1.0) -} - -fun <T : Any> MutableHistogram<T, *>.put(vararg point: T): Unit = put(ArrayBuffer(point)) - -fun MutableHistogram<Double, *>.put(vararg point: Number): Unit = - put(RealBuffer(point.map { it.toDouble() }.toDoubleArray())) - -fun MutableHistogram<Double, *>.put(vararg point: Double): Unit = put(RealBuffer(point)) - -fun <T : Any> MutableHistogram<T, *>.fill(sequence: Iterable<Point<T>>): Unit = sequence.forEach { put(it) } - -/** - * Pass a sequence builder into histogram - */ -fun <T : Any> MutableHistogram<T, *>.fill(block: suspend SequenceScope<Point<T>>.() -> Unit): Unit = - fill(sequence(block).asIterable()) diff --git a/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt b/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt index 5edecb5a5..87292f17e 100644 --- a/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt +++ b/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt @@ -1,16 +1,16 @@ package scietifik.kmath.histogram -import scientifik.kmath.histogram.RealHistogram -import scientifik.kmath.histogram.fill -import scientifik.kmath.histogram.put -import scientifik.kmath.real.RealVector +import kscience.kmath.histogram.RealHistogram +import kscience.kmath.histogram.fill +import kscience.kmath.histogram.put +import kscience.kmath.real.RealVector import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue -class MultivariateHistogramTest { +internal class MultivariateHistogramTest { @Test fun testSinglePutHistogram() { val histogram = RealHistogram.fromRanges( diff --git a/kmath-histograms/src/jsMain/kotlin/kscience/kmath/histogram/Counters.kt b/kmath-histograms/src/jsMain/kotlin/kscience/kmath/histogram/Counters.kt new file mode 100644 index 000000000..d0fa1f4c2 --- /dev/null +++ b/kmath-histograms/src/jsMain/kotlin/kscience/kmath/histogram/Counters.kt @@ -0,0 +1,37 @@ +package kscience.kmath.histogram + +public actual class LongCounter { + private var sum: Long = 0L + + public actual fun decrement() { + sum-- + } + + public actual fun increment() { + sum++ + } + + public actual fun reset() { + sum = 0 + } + + public actual fun sum(): Long = sum + + public actual fun add(l: Long) { + sum += l + } +} + +public actual class DoubleCounter { + private var sum: Double = 0.0 + + public actual fun reset() { + sum = 0.0 + } + + public actual fun sum(): Double = sum + + public actual fun add(d: Double) { + sum += d + } +} diff --git a/kmath-histograms/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt b/kmath-histograms/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt deleted file mode 100644 index 3765220b9..000000000 --- a/kmath-histograms/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt +++ /dev/null @@ -1,33 +0,0 @@ -package scientifik.kmath.histogram - -actual class LongCounter { - private var sum: Long = 0 - actual fun decrement() { - sum-- - } - - actual fun increment() { - sum++ - } - - actual fun reset() { - sum = 0 - } - - actual fun sum(): Long = sum - actual fun add(l: Long) { - sum += l - } -} - -actual class DoubleCounter { - private var sum: Double = 0.0 - actual fun reset() { - sum = 0.0 - } - - actual fun sum(): Double = sum - actual fun add(d: Double) { - sum += d - } -} \ No newline at end of file diff --git a/kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/Counters.kt b/kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/Counters.kt new file mode 100644 index 000000000..efbd185ef --- /dev/null +++ b/kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/Counters.kt @@ -0,0 +1,7 @@ +package kscience.kmath.histogram + +import java.util.concurrent.atomic.DoubleAdder +import java.util.concurrent.atomic.LongAdder + +public actual typealias LongCounter = LongAdder +public actual typealias DoubleCounter = DoubleAdder diff --git a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt b/kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/UnivariateHistogram.kt similarity index 54% rename from kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt rename to kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/UnivariateHistogram.kt index e30a45f5a..5fada1302 100644 --- a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt +++ b/kmath-histograms/src/jvmMain/kotlin/kscience/kmath/histogram/UnivariateHistogram.kt @@ -1,32 +1,33 @@ -package scientifik.kmath.histogram +package kscience.kmath.histogram -import scientifik.kmath.real.RealVector -import scientifik.kmath.real.asVector -import scientifik.kmath.structures.Buffer +import kscience.kmath.real.RealVector +import kscience.kmath.real.asVector +import kscience.kmath.structures.Buffer import java.util.* import kotlin.math.floor //TODO move to common -class UnivariateBin(val position: Double, val size: Double, val counter: LongCounter = LongCounter()) : Bin<Double> { +public class UnivariateBin( + public val position: Double, + public val size: Double, + public val counter: LongCounter = LongCounter() +) : Bin<Double> { //TODO add weighting - override val value: Number get() = counter.sum() + public override val value: Number get() = counter.sum() - override val center: RealVector get() = doubleArrayOf(position).asVector() + public override val center: RealVector get() = doubleArrayOf(position).asVector() + public override val dimension: Int get() = 1 - operator fun contains(value: Double): Boolean = value in (position - size / 2)..(position + size / 2) - - override fun contains(point: Buffer<Double>): Boolean = contains(point[0]) - - internal operator fun inc() = this.also { counter.increment() } - - override val dimension: Int get() = 1 + public operator fun contains(value: Double): Boolean = value in (position - size / 2)..(position + size / 2) + public override fun contains(point: Buffer<Double>): Boolean = contains(point[0]) + internal operator fun inc(): UnivariateBin = this.also { counter.increment() } } /** * Univariate histogram with log(n) bin search speed */ -class UnivariateHistogram private constructor(private val factory: (Double) -> UnivariateBin) : +public class UnivariateHistogram private constructor(private val factory: (Double) -> UnivariateBin) : MutableHistogram<Double, UnivariateBin> { private val bins: TreeMap<Double, UnivariateBin> = TreeMap() @@ -43,19 +44,19 @@ class UnivariateHistogram private constructor(private val factory: (Double) -> U } private fun createBin(value: Double): UnivariateBin = factory(value).also { - synchronized(this) { bins.put(it.position, it) } + synchronized(this) { bins[it.position] = it } } - override operator fun get(point: Buffer<out Double>): UnivariateBin? = get(point[0]) + public override operator fun get(point: Buffer<out Double>): UnivariateBin? = get(point[0]) - override val dimension: Int get() = 1 + public override val dimension: Int get() = 1 - override operator fun iterator(): Iterator<UnivariateBin> = bins.values.iterator() + public override operator fun iterator(): Iterator<UnivariateBin> = bins.values.iterator() /** * Thread safe put operation */ - fun put(value: Double) { + public fun put(value: Double) { (get(value) ?: createBin(value)).inc() } @@ -64,13 +65,13 @@ class UnivariateHistogram private constructor(private val factory: (Double) -> U put(point[0]) } - companion object { - fun uniform(binSize: Double, start: Double = 0.0): UnivariateHistogram = UnivariateHistogram { value -> + public companion object { + public fun uniform(binSize: Double, start: Double = 0.0): UnivariateHistogram = UnivariateHistogram { value -> val center = start + binSize * floor((value - start) / binSize + 0.5) UnivariateBin(center, binSize) } - fun custom(borders: DoubleArray): UnivariateHistogram { + public fun custom(borders: DoubleArray): UnivariateHistogram { val sorted = borders.sortedArray() return UnivariateHistogram { value -> @@ -79,12 +80,14 @@ class UnivariateHistogram private constructor(private val factory: (Double) -> U Double.NEGATIVE_INFINITY, Double.MAX_VALUE ) + value > sorted.last() -> UnivariateBin( Double.POSITIVE_INFINITY, Double.MAX_VALUE ) + else -> { - val index = (0 until sorted.size).first { value > sorted[it] } + val index = sorted.indices.first { value > sorted[it] } val left = sorted[index] val right = sorted[index + 1] UnivariateBin((left + right) / 2, (right - left)) @@ -95,4 +98,4 @@ class UnivariateHistogram private constructor(private val factory: (Double) -> U } } -fun UnivariateHistogram.fill(sequence: Iterable<Double>) = sequence.forEach { put(it) } +public fun UnivariateHistogram.fill(sequence: Iterable<Double>): Unit = sequence.forEach(::put) diff --git a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/Counters.kt b/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/Counters.kt deleted file mode 100644 index bb3667f7d..000000000 --- a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/Counters.kt +++ /dev/null @@ -1,7 +0,0 @@ -package scientifik.kmath.histogram - -import java.util.concurrent.atomic.DoubleAdder -import java.util.concurrent.atomic.LongAdder - -actual typealias LongCounter = LongAdder -actual typealias DoubleCounter = DoubleAdder \ No newline at end of file diff --git a/kmath-memory/build.gradle.kts b/kmath-memory/build.gradle.kts index 1f34a4f17..94527a6a3 100644 --- a/kmath-memory/build.gradle.kts +++ b/kmath-memory/build.gradle.kts @@ -1,3 +1 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("ru.mipt.npm.mpp") } diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt index 177c6b46b..8de9a7a0c 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt @@ -1,4 +1,4 @@ -package scientifik.memory +package kscience.memory import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -6,84 +6,84 @@ import kotlin.contracts.contract /** * Represents a display of certain memory structure. */ -interface Memory { +public interface Memory { /** * The length of this memory in bytes. */ - val size: Int + public val size: Int /** * Get a projection of this memory (it reflects the changes in the parent memory block). */ - fun view(offset: Int, length: Int): Memory + public fun view(offset: Int, length: Int): Memory /** * Creates an independent copy of this memory. */ - fun copy(): Memory + public fun copy(): Memory /** * Gets or creates a reader of this memory. */ - fun reader(): MemoryReader + public fun reader(): MemoryReader /** * Gets or creates a writer of this memory. */ - fun writer(): MemoryWriter + public fun writer(): MemoryWriter - companion object + public companion object } /** * The interface to read primitive types in this memory. */ -interface MemoryReader { +public interface MemoryReader { /** * The underlying memory. */ - val memory: Memory + public val memory: Memory /** * Reads [Double] at certain [offset]. */ - fun readDouble(offset: Int): Double + public fun readDouble(offset: Int): Double /** * Reads [Float] at certain [offset]. */ - fun readFloat(offset: Int): Float + public fun readFloat(offset: Int): Float /** * Reads [Byte] at certain [offset]. */ - fun readByte(offset: Int): Byte + public fun readByte(offset: Int): Byte /** * Reads [Short] at certain [offset]. */ - fun readShort(offset: Int): Short + public fun readShort(offset: Int): Short /** * Reads [Int] at certain [offset]. */ - fun readInt(offset: Int): Int + public fun readInt(offset: Int): Int /** * Reads [Long] at certain [offset]. */ - fun readLong(offset: Int): Long + public fun readLong(offset: Int): Long /** * Disposes this reader if needed. */ - fun release() + public fun release() } /** * Uses the memory for read then releases the reader. */ -inline fun <R> Memory.read(block: MemoryReader.() -> R): R { +public inline fun <R> Memory.read(block: MemoryReader.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } val reader = reader() val result = reader.block() @@ -94,52 +94,52 @@ inline fun <R> Memory.read(block: MemoryReader.() -> R): R { /** * The interface to write primitive types into this memory. */ -interface MemoryWriter { +public interface MemoryWriter { /** * The underlying memory. */ - val memory: Memory + public val memory: Memory /** * Writes [Double] at certain [offset]. */ - fun writeDouble(offset: Int, value: Double) + public fun writeDouble(offset: Int, value: Double) /** * Writes [Float] at certain [offset]. */ - fun writeFloat(offset: Int, value: Float) + public fun writeFloat(offset: Int, value: Float) /** * Writes [Byte] at certain [offset]. */ - fun writeByte(offset: Int, value: Byte) + public fun writeByte(offset: Int, value: Byte) /** * Writes [Short] at certain [offset]. */ - fun writeShort(offset: Int, value: Short) + public fun writeShort(offset: Int, value: Short) /** * Writes [Int] at certain [offset]. */ - fun writeInt(offset: Int, value: Int) + public fun writeInt(offset: Int, value: Int) /** * Writes [Long] at certain [offset]. */ - fun writeLong(offset: Int, value: Long) + public fun writeLong(offset: Int, value: Long) /** * Disposes this writer if needed. */ - fun release() + public fun release() } /** * Uses the memory for write then releases the writer. */ -inline fun Memory.write(block: MemoryWriter.() -> Unit) { +public inline fun Memory.write(block: MemoryWriter.() -> Unit) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } writer().apply(block).release() } @@ -147,10 +147,10 @@ inline fun Memory.write(block: MemoryWriter.() -> Unit) { /** * Allocates the most effective platform-specific memory. */ -expect fun Memory.Companion.allocate(length: Int): Memory +public expect fun Memory.Companion.allocate(length: Int): Memory /** * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied * and could be mutated independently from the resulting [Memory]. */ -expect fun Memory.Companion.wrap(array: ByteArray): Memory +public expect fun Memory.Companion.wrap(array: ByteArray): Memory diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt index 1381afbec..d2cbb32fd 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt @@ -1,49 +1,49 @@ -package scientifik.memory +package kscience.memory /** * A specification to read or write custom objects with fixed size in bytes. * * @param T the type of object this spec manages. */ -interface MemorySpec<T : Any> { +public interface MemorySpec<T : Any> { /** * Size of [T] in bytes after serialization. */ - val objectSize: Int + public val objectSize: Int /** * Reads the object starting from [offset]. */ - fun MemoryReader.read(offset: Int): T + public fun MemoryReader.read(offset: Int): T // TODO consider thread safety /** * Writes the object [value] starting from [offset]. */ - fun MemoryWriter.write(offset: Int, value: T) + public fun MemoryWriter.write(offset: Int, value: T) } /** * Reads the object with [spec] starting from [offset]. */ -fun <T : Any> MemoryReader.read(spec: MemorySpec<T>, offset: Int): T = with(spec) { read(offset) } +public fun <T : Any> MemoryReader.read(spec: MemorySpec<T>, offset: Int): T = with(spec) { read(offset) } /** * Writes the object [value] with [spec] starting from [offset]. */ -fun <T : Any> MemoryWriter.write(spec: MemorySpec<T>, offset: Int, value: T): Unit = with(spec) { write(offset, value) } +public fun <T : Any> MemoryWriter.write(spec: MemorySpec<T>, offset: Int, value: T): Unit = with(spec) { write(offset, value) } /** * Reads array of [size] objects mapped by [spec] at certain [offset]. */ -inline fun <reified T : Any> MemoryReader.readArray(spec: MemorySpec<T>, offset: Int, size: Int): Array<T> = +public inline fun <reified T : Any> MemoryReader.readArray(spec: MemorySpec<T>, offset: Int, size: Int): Array<T> = Array(size) { i -> with(spec) { read(offset + i * objectSize) } } /** * Writes [array] of objects mapped by [spec] at certain [offset]. */ -fun <T : Any> MemoryWriter.writeArray(spec: MemorySpec<T>, offset: Int, array: Array<T>): Unit = +public fun <T : Any> MemoryWriter.writeArray(spec: MemorySpec<T>, offset: Int, array: Array<T>): Unit = with(spec) { array.indices.forEach { i -> write(offset + i * objectSize, array[i]) } } // TODO It is possible to add elastic MemorySpec with unknown object size diff --git a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt index 974750502..d6b8841e4 100644 --- a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt +++ b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt @@ -1,4 +1,4 @@ -package scientifik.memory +package kscience.memory import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.DataView @@ -83,7 +83,7 @@ private class DataViewMemory(val view: DataView) : Memory { /** * Allocates memory based on a [DataView]. */ -actual fun Memory.Companion.allocate(length: Int): Memory { +public actual fun Memory.Companion.allocate(length: Int): Memory { val buffer = ArrayBuffer(length) return DataViewMemory(DataView(buffer, 0, length)) } @@ -92,7 +92,7 @@ actual fun Memory.Companion.allocate(length: Int): Memory { * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied * and could be mutated independently from the resulting [Memory]. */ -actual fun Memory.Companion.wrap(array: ByteArray): Memory { +public actual fun Memory.Companion.wrap(array: ByteArray): Memory { @Suppress("CAST_NEVER_SUCCEEDS") val int8Array = array as Int8Array return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length)) } diff --git a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt index ac8609094..c912b28ff 100644 --- a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt +++ b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt @@ -1,4 +1,4 @@ -package scientifik.memory +package kscience.memory import java.io.IOException import java.nio.ByteBuffer @@ -93,14 +93,14 @@ internal class ByteBufferMemory( /** * Allocates memory based on a [ByteBuffer]. */ -actual fun Memory.Companion.allocate(length: Int): Memory = +public actual fun Memory.Companion.allocate(length: Int): Memory = ByteBufferMemory(checkNotNull(ByteBuffer.allocate(length))) /** * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied * and could be mutated independently from the resulting [Memory]. */ -actual fun Memory.Companion.wrap(array: ByteArray): Memory = ByteBufferMemory(checkNotNull(ByteBuffer.wrap(array))) +public actual fun Memory.Companion.wrap(array: ByteArray): Memory = ByteBufferMemory(checkNotNull(ByteBuffer.wrap(array))) /** * Wraps this [ByteBuffer] to [Memory] object. @@ -110,14 +110,14 @@ actual fun Memory.Companion.wrap(array: ByteArray): Memory = ByteBufferMemory(ch * @param size the size of memory to map. * @return the [Memory] object. */ -fun ByteBuffer.asMemory(startOffset: Int = 0, size: Int = limit()): Memory = +public fun ByteBuffer.asMemory(startOffset: Int = 0, size: Int = limit()): Memory = ByteBufferMemory(this, startOffset, size) /** * Uses direct memory-mapped buffer from file to read something and close it afterwards. */ @Throws(IOException::class) -inline fun <R> Path.readAsMemory(position: Long = 0, size: Long = Files.size(this), block: Memory.() -> R): R { +public inline fun <R> Path.readAsMemory(position: Long = 0, size: Long = Files.size(this), block: Memory.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return FileChannel diff --git a/kmath-prob/build.gradle.kts b/kmath-prob/build.gradle.kts index 6cdd301c2..4c9663e5f 100644 --- a/kmath-prob/build.gradle.kts +++ b/kmath-prob/build.gradle.kts @@ -1,6 +1,4 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("ru.mipt.npm.mpp") } kotlin.sourceSets { commonMain { @@ -8,8 +6,9 @@ kotlin.sourceSets { api(project(":kmath-coroutines")) } } - jvmMain{ - dependencies{ + + jvmMain { + dependencies { api("org.apache.commons:commons-rng-sampling:1.3") api("org.apache.commons:commons-rng-simple:1.3") } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt similarity index 61% rename from kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt rename to kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt index 3b874adaa..d55a52f56 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt @@ -1,23 +1,23 @@ -package scientifik.kmath.prob +package kscience.kmath.prob -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.collect -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.BufferFactory +import kscience.kmath.chains.Chain +import kscience.kmath.chains.collect +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.BufferFactory -interface Sampler<T : Any> { - fun sample(generator: RandomGenerator): Chain<T> +public interface Sampler<T : Any> { + public fun sample(generator: RandomGenerator): Chain<T> } /** * A distribution of typed objects */ -interface Distribution<T : Any> : Sampler<T> { +public interface Distribution<T : Any> : Sampler<T> { /** * A probability value for given argument [arg]. * For continuous distributions returns PDF */ - fun probability(arg: T): Double + public fun probability(arg: T): Double /** * Create a chain of samples from this distribution. @@ -28,20 +28,20 @@ interface Distribution<T : Any> : Sampler<T> { /** * An empty companion. Distribution factories should be written as its extensions */ - companion object + public companion object } -interface UnivariateDistribution<T : Comparable<T>> : Distribution<T> { +public interface UnivariateDistribution<T : Comparable<T>> : Distribution<T> { /** * Cumulative distribution for ordered parameter (CDF) */ - fun cumulative(arg: T): Double + public fun cumulative(arg: T): Double } /** * Compute probability integral in an interval */ -fun <T : Comparable<T>> UnivariateDistribution<T>.integral(from: T, to: T): Double { +public fun <T : Comparable<T>> UnivariateDistribution<T>.integral(from: T, to: T): Double { require(to > from) return cumulative(to) - cumulative(from) } @@ -49,7 +49,7 @@ fun <T : Comparable<T>> UnivariateDistribution<T>.integral(from: T, to: T): Doub /** * Sample a bunch of values */ -fun <T : Any> Sampler<T>.sampleBuffer( +public fun <T : Any> Sampler<T>.sampleBuffer( generator: RandomGenerator, size: Int, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing @@ -57,6 +57,7 @@ fun <T : Any> Sampler<T>.sampleBuffer( require(size > 1) //creating temporary storage once val tmp = ArrayList<T>(size) + return sample(generator).collect { chain -> //clear list from previous run tmp.clear() @@ -72,5 +73,5 @@ fun <T : Any> Sampler<T>.sampleBuffer( /** * Generate a bunch of samples from real distributions */ -fun Sampler<Double>.sampleBuffer(generator: RandomGenerator, size: Int) = - sampleBuffer(generator, size, Buffer.Companion::real) \ No newline at end of file +public fun Sampler<Double>.sampleBuffer(generator: RandomGenerator, size: Int): Chain<Buffer<Double>> = + sampleBuffer(generator, size, Buffer.Companion::real) diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt new file mode 100644 index 000000000..4d713fc4e --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt @@ -0,0 +1,43 @@ +package kscience.kmath.prob + +import kscience.kmath.chains.Chain +import kscience.kmath.chains.SimpleChain + +/** + * A multivariate distribution which takes a map of parameters + */ +public interface NamedDistribution<T> : Distribution<Map<String, T>> + +/** + * A multivariate distribution that has independent distributions for separate axis + */ +public class FactorizedDistribution<T>(public val distributions: Collection<NamedDistribution<T>>) : + NamedDistribution<T> { + override fun probability(arg: Map<String, T>): Double = + distributions.fold(1.0) { acc, distr -> acc * distr.probability(arg) } + + override fun sample(generator: RandomGenerator): Chain<Map<String, T>> { + val chains = distributions.map { it.sample(generator) } + return SimpleChain { chains.fold(emptyMap()) { acc, chain -> acc + chain.next() } } + } +} + +public class NamedDistributionWrapper<T : Any>(public val name: String, public val distribution: Distribution<T>) : + NamedDistribution<T> { + override fun probability(arg: Map<String, T>): Double = distribution.probability( + arg[name] ?: error("Argument with name $name not found in input parameters") + ) + + override fun sample(generator: RandomGenerator): Chain<Map<String, T>> { + val chain = distribution.sample(generator) + return SimpleChain { mapOf(name to chain.next()) } + } +} + +public class DistributionBuilder<T : Any> { + private val distributions = ArrayList<NamedDistribution<T>>() + + public infix fun String.to(distribution: Distribution<T>) { + distributions.add(NamedDistributionWrapper(this, distribution)) + } +} diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomChain.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomChain.kt new file mode 100644 index 000000000..b4a80f6c5 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomChain.kt @@ -0,0 +1,17 @@ +package kscience.kmath.prob + +import kscience.kmath.chains.Chain + +/** + * A possibly stateful chain producing random values. + */ +public class RandomChain<out R>( + public val generator: RandomGenerator, + private val gen: suspend RandomGenerator.() -> R +) : Chain<R> { + override suspend fun next(): R = generator.gen() + + override fun fork(): Chain<R> = RandomChain(generator.fork(), gen) +} + +public fun <R> RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain<R> = RandomChain(this, gen) diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt new file mode 100644 index 000000000..0d95d6f97 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt @@ -0,0 +1,48 @@ +package kscience.kmath.prob + +import kotlin.random.Random + +/** + * A basic generator + */ +public interface RandomGenerator { + public fun nextBoolean(): Boolean + public fun nextDouble(): Double + public fun nextInt(): Int + public fun nextInt(until: Int): Int + public fun nextLong(): Long + public fun nextLong(until: Long): Long + public fun fillBytes(array: ByteArray, fromIndex: Int = 0, toIndex: Int = array.size) + public fun nextBytes(size: Int): ByteArray = ByteArray(size).also { fillBytes(it) } + + /** + * Create a new generator which is independent from current generator (operations on new generator do not affect this one + * and vise versa). The statistical properties of new generator should be the same as for this one. + * For pseudo-random generator, the fork is keeping the same sequence of numbers for given call order for each run. + * + * The thread safety of this operation is not guaranteed since it could affect the state of the generator. + */ + public fun fork(): RandomGenerator + + public companion object { + public val default: DefaultGenerator by lazy { DefaultGenerator() } + + public fun default(seed: Long): DefaultGenerator = DefaultGenerator(Random(seed)) + } +} + +public inline class DefaultGenerator(public val random: Random = Random) : RandomGenerator { + public override fun nextBoolean(): Boolean = random.nextBoolean() + public override fun nextDouble(): Double = random.nextDouble() + public override fun nextInt(): Int = random.nextInt() + public override fun nextInt(until: Int): Int = random.nextInt(until) + public override fun nextLong(): Long = random.nextLong() + public override fun nextLong(until: Long): Long = random.nextLong(until) + + public override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { + random.nextBytes(array, fromIndex, toIndex) + } + + public override fun nextBytes(size: Int): ByteArray = random.nextBytes(size) + public override fun fork(): RandomGenerator = RandomGenerator.default(random.nextLong()) +} diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/SamplerAlgebra.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/SamplerAlgebra.kt new file mode 100644 index 000000000..e363ba30b --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/SamplerAlgebra.kt @@ -0,0 +1,31 @@ +package kscience.kmath.prob + +import kscience.kmath.chains.Chain +import kscience.kmath.chains.ConstantChain +import kscience.kmath.chains.map +import kscience.kmath.chains.zip +import kscience.kmath.operations.Space +import kscience.kmath.operations.invoke + +public class BasicSampler<T : Any>(public val chainBuilder: (RandomGenerator) -> Chain<T>) : Sampler<T> { + public override fun sample(generator: RandomGenerator): Chain<T> = chainBuilder(generator) +} + +public class ConstantSampler<T : Any>(public val value: T) : Sampler<T> { + public override fun sample(generator: RandomGenerator): Chain<T> = ConstantChain(value) +} + +/** + * A space for samplers. Allows to perform simple operations on distributions + */ +public class SamplerSpace<T : Any>(public val space: Space<T>) : Space<Sampler<T>> { + public override val zero: Sampler<T> = ConstantSampler(space.zero) + + public override fun add(a: Sampler<T>, b: Sampler<T>): Sampler<T> = BasicSampler { generator -> + a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> space { aValue + bValue } } + } + + public override fun multiply(a: Sampler<T>, k: Number): Sampler<T> = BasicSampler { generator -> + a.sample(generator).map { space { it * k.toDouble() } } + } +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Statistic.kt similarity index 52% rename from kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt rename to kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Statistic.kt index c82d262bf..6720a3d7f 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Statistic.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.prob +package kscience.kmath.prob import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers @@ -6,18 +6,18 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.scanReduce -import scientifik.kmath.coroutines.mapParallel -import scientifik.kmath.operations.* -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.asIterable -import scientifik.kmath.structures.asSequence +import kotlinx.coroutines.flow.runningReduce +import kscience.kmath.coroutines.mapParallel +import kscience.kmath.operations.* +import kscience.kmath.structures.Buffer +import kscience.kmath.structures.asIterable +import kscience.kmath.structures.asSequence /** * A function, that transforms a buffer of random quantities to some resulting value */ -interface Statistic<T, R> { - suspend operator fun invoke(data: Buffer<T>): R +public interface Statistic<T, R> { + public suspend operator fun invoke(data: Buffer<T>): R } /** @@ -26,17 +26,17 @@ interface Statistic<T, R> { * @param I - intermediate block type * @param R - result type */ -interface ComposableStatistic<T, I, R> : Statistic<T, R> { +public interface ComposableStatistic<T, I, R> : Statistic<T, R> { //compute statistic on a single block - suspend fun computeIntermediate(data: Buffer<T>): I + public suspend fun computeIntermediate(data: Buffer<T>): I //Compose two blocks - suspend fun composeIntermediate(first: I, second: I): I + public suspend fun composeIntermediate(first: I, second: I): I //Transform block to result - suspend fun toResult(intermediate: I): R + public suspend fun toResult(intermediate: I): R - override suspend fun invoke(data: Buffer<T>): R = toResult(computeIntermediate(data)) + public override suspend fun invoke(data: Buffer<T>): R = toResult(computeIntermediate(data)) } @FlowPreview @@ -46,7 +46,7 @@ private fun <T, I, R> ComposableStatistic<T, I, R>.flowIntermediate( dispatcher: CoroutineDispatcher = Dispatchers.Default ): Flow<I> = flow .mapParallel(dispatcher) { computeIntermediate(it) } - .scanReduce(::composeIntermediate) + .runningReduce(::composeIntermediate) /** @@ -57,7 +57,7 @@ private fun <T, I, R> ComposableStatistic<T, I, R>.flowIntermediate( */ @FlowPreview @ExperimentalCoroutinesApi -fun <T, I, R> ComposableStatistic<T, I, R>.flow( +public fun <T, I, R> ComposableStatistic<T, I, R>.flow( flow: Flow<Buffer<T>>, dispatcher: CoroutineDispatcher = Dispatchers.Default ): Flow<R> = flowIntermediate(flow, dispatcher).map(::toResult) @@ -65,32 +65,32 @@ fun <T, I, R> ComposableStatistic<T, I, R>.flow( /** * Arithmetic mean */ -class Mean<T>(val space: Space<T>) : ComposableStatistic<T, Pair<T, Int>, T> { - override suspend fun computeIntermediate(data: Buffer<T>): Pair<T, Int> = +public class Mean<T>(public val space: Space<T>) : ComposableStatistic<T, Pair<T, Int>, T> { + public override suspend fun computeIntermediate(data: Buffer<T>): Pair<T, Int> = space { sum(data.asIterable()) } to data.size - override suspend fun composeIntermediate(first: Pair<T, Int>, second: Pair<T, Int>): Pair<T, Int> = + public override suspend fun composeIntermediate(first: Pair<T, Int>, second: Pair<T, Int>): Pair<T, Int> = space { first.first + second.first } to (first.second + second.second) - override suspend fun toResult(intermediate: Pair<T, Int>): T = + public override suspend fun toResult(intermediate: Pair<T, Int>): T = space { intermediate.first / intermediate.second } - companion object { + public companion object { //TODO replace with optimized version which respects overflow - val real: Mean<Double> = Mean(RealField) - val int: Mean<Int> = Mean(IntRing) - val long: Mean<Long> = Mean(LongRing) + public val real: Mean<Double> = Mean(RealField) + public val int: Mean<Int> = Mean(IntRing) + public val long: Mean<Long> = Mean(LongRing) } } /** * Non-composable median */ -class Median<T>(private val comparator: Comparator<T>) : Statistic<T, T> { - override suspend fun invoke(data: Buffer<T>): T = +public class Median<T>(private val comparator: Comparator<T>) : Statistic<T, T> { + public override suspend fun invoke(data: Buffer<T>): T = data.asSequence().sortedWith(comparator).toList()[data.size / 2] //TODO check if this is correct - companion object { - val real: Median<Double> = Median(Comparator { a: Double, b: Double -> a.compareTo(b) }) + public companion object { + public val real: Median<Double> = Median { a: Double, b: Double -> a.compareTo(b) } } } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/UniformDistribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/UniformDistribution.kt new file mode 100644 index 000000000..8df2c01e1 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/UniformDistribution.kt @@ -0,0 +1,22 @@ +package kscience.kmath.prob + +import kscience.kmath.chains.Chain +import kscience.kmath.chains.SimpleChain + +public class UniformDistribution(public val range: ClosedFloatingPointRange<Double>) : UnivariateDistribution<Double> { + 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<Double> = + SimpleChain { range.start + generator.nextDouble() * length } + + override fun cumulative(arg: Double): Double = when { + arg < range.start -> 0.0 + arg >= range.endInclusive -> 1.0 + else -> (arg - range.start) / length + } +} + +public fun Distribution.Companion.uniform(range: ClosedFloatingPointRange<Double>): UniformDistribution = + UniformDistribution(range) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt deleted file mode 100644 index ea526c058..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt +++ /dev/null @@ -1,47 +0,0 @@ -package scientifik.kmath.prob - -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.SimpleChain - -/** - * A multivariate distribution which takes a map of parameters - */ -interface NamedDistribution<T> : Distribution<Map<String, T>> - -/** - * A multivariate distribution that has independent distributions for separate axis - */ -class FactorizedDistribution<T>(val distributions: Collection<NamedDistribution<T>>) : NamedDistribution<T> { - - override fun probability(arg: Map<String, T>): Double { - return distributions.fold(1.0) { acc, distr -> acc * distr.probability(arg) } - } - - override fun sample(generator: RandomGenerator): Chain<Map<String, T>> { - val chains = distributions.map { it.sample(generator) } - return SimpleChain<Map<String, T>> { - chains.fold(emptyMap()) { acc, chain -> acc + chain.next() } - } - } -} - -class NamedDistributionWrapper<T : Any>(val name: String, val distribution: Distribution<T>) : NamedDistribution<T> { - override fun probability(arg: Map<String, T>): Double = distribution.probability( - arg[name] ?: error("Argument with name $name not found in input parameters") - ) - - override fun sample(generator: RandomGenerator): Chain<Map<String, T>> { - val chain = distribution.sample(generator) - return SimpleChain { - mapOf(name to chain.next()) - } - } -} - -class DistributionBuilder<T: Any>{ - private val distributions = ArrayList<NamedDistribution<T>>() - - infix fun String.to(distribution: Distribution<T>){ - distributions.add(NamedDistributionWrapper(this,distribution)) - } -} \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt deleted file mode 100644 index 49163c701..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt +++ /dev/null @@ -1,14 +0,0 @@ -package scientifik.kmath.prob - -import scientifik.kmath.chains.Chain - -/** - * A possibly stateful chain producing random values. - */ -class RandomChain<out R>(val generator: RandomGenerator, private val gen: suspend RandomGenerator.() -> R) : Chain<R> { - override suspend fun next(): R = generator.gen() - - override fun fork(): Chain<R> = RandomChain(generator.fork(), gen) -} - -fun <R> RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain<R> = RandomChain(this, gen) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt deleted file mode 100644 index 2a225fe47..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt +++ /dev/null @@ -1,55 +0,0 @@ -package scientifik.kmath.prob - -import kotlin.random.Random - -/** - * A basic generator - */ -interface RandomGenerator { - fun nextBoolean(): Boolean - - fun nextDouble(): Double - fun nextInt(): Int - fun nextInt(until: Int): Int - fun nextLong(): Long - fun nextLong(until: Long): Long - - fun fillBytes(array: ByteArray, fromIndex: Int = 0, toIndex: Int = array.size) - fun nextBytes(size: Int): ByteArray = ByteArray(size).also { fillBytes(it) } - - /** - * Create a new generator which is independent from current generator (operations on new generator do not affect this one - * and vise versa). The statistical properties of new generator should be the same as for this one. - * For pseudo-random generator, the fork is keeping the same sequence of numbers for given call order for each run. - * - * The thread safety of this operation is not guaranteed since it could affect the state of the generator. - */ - fun fork(): RandomGenerator - - companion object { - val default by lazy { DefaultGenerator() } - - fun default(seed: Long) = DefaultGenerator(Random(seed)) - } -} - -inline class DefaultGenerator(val random: Random = Random) : RandomGenerator { - override fun nextBoolean(): Boolean = random.nextBoolean() - - override fun nextDouble(): Double = random.nextDouble() - - override fun nextInt(): Int = random.nextInt() - override fun nextInt(until: Int): Int = random.nextInt(until) - - override fun nextLong(): Long = random.nextLong() - - override fun nextLong(until: Long): Long = random.nextLong(until) - - override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { - random.nextBytes(array, fromIndex, toIndex) - } - - override fun nextBytes(size: Int): ByteArray = random.nextBytes(size) - - override fun fork(): RandomGenerator = RandomGenerator.default(random.nextLong()) -} \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt deleted file mode 100644 index 02f98439e..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt +++ /dev/null @@ -1,32 +0,0 @@ -package scientifik.kmath.prob - -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.ConstantChain -import scientifik.kmath.chains.map -import scientifik.kmath.chains.zip -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.invoke - -class BasicSampler<T : Any>(val chainBuilder: (RandomGenerator) -> Chain<T>) : Sampler<T> { - override fun sample(generator: RandomGenerator): Chain<T> = chainBuilder(generator) -} - -class ConstantSampler<T : Any>(val value: T) : Sampler<T> { - override fun sample(generator: RandomGenerator): Chain<T> = ConstantChain(value) -} - -/** - * A space for samplers. Allows to perform simple operations on distributions - */ -class SamplerSpace<T : Any>(val space: Space<T>) : Space<Sampler<T>> { - - override val zero: Sampler<T> = ConstantSampler(space.zero) - - override fun add(a: Sampler<T>, b: Sampler<T>): Sampler<T> = BasicSampler { generator -> - a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> space { aValue + bValue } } - } - - override fun multiply(a: Sampler<T>, k: Number): Sampler<T> = BasicSampler { generator -> - a.sample(generator).map { space { it * k.toDouble() } } - } -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/UniformDistribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/UniformDistribution.kt deleted file mode 100644 index 9d96bff59..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/UniformDistribution.kt +++ /dev/null @@ -1,34 +0,0 @@ -package scientifik.kmath.prob - -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.SimpleChain - -class UniformDistribution(val range: ClosedFloatingPointRange<Double>) : UnivariateDistribution<Double> { - - private val length = range.endInclusive - range.start - - override fun probability(arg: Double): Double { - return if (arg in range) { - return 1.0 / length - } else { - 0.0 - } - } - - override fun sample(generator: RandomGenerator): Chain<Double> { - return SimpleChain { - range.start + generator.nextDouble() * length - } - } - - override fun cumulative(arg: Double): Double { - return when { - arg < range.start -> 0.0 - arg >= range.endInclusive -> 1.0 - else -> (arg - range.start) / length - } - } -} - -fun Distribution.Companion.uniform(range: ClosedFloatingPointRange<Double>): UniformDistribution = - UniformDistribution(range) \ No newline at end of file diff --git a/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt new file mode 100644 index 000000000..18be6f019 --- /dev/null +++ b/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt @@ -0,0 +1,58 @@ +package kscience.kmath.prob + +import org.apache.commons.rng.UniformRandomProvider +import org.apache.commons.rng.simple.RandomSource + +public class RandomSourceGenerator(public val source: RandomSource, seed: Long?) : RandomGenerator { + internal val random: UniformRandomProvider = seed?.let { + RandomSource.create(source, seed) + } ?: RandomSource.create(source) + + public override fun nextBoolean(): Boolean = random.nextBoolean() + public override fun nextDouble(): Double = random.nextDouble() + public override fun nextInt(): Int = random.nextInt() + public override fun nextInt(until: Int): Int = random.nextInt(until) + public override fun nextLong(): Long = random.nextLong() + public override fun nextLong(until: Long): Long = random.nextLong(until) + + public override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { + require(toIndex > fromIndex) + random.nextBytes(array, fromIndex, toIndex - fromIndex) + } + + public override fun fork(): RandomGenerator = RandomSourceGenerator(source, nextLong()) +} + +public inline class RandomGeneratorProvider(public val generator: RandomGenerator) : UniformRandomProvider { + public override fun nextBoolean(): Boolean = generator.nextBoolean() + public override fun nextFloat(): Float = generator.nextDouble().toFloat() + + public override fun nextBytes(bytes: ByteArray) { + generator.fillBytes(bytes) + } + + public override fun nextBytes(bytes: ByteArray, start: Int, len: Int) { + generator.fillBytes(bytes, start, start + len) + } + + public override fun nextInt(): Int = generator.nextInt() + public override fun nextInt(n: Int): Int = generator.nextInt(n) + public override fun nextDouble(): Double = generator.nextDouble() + public override fun nextLong(): Long = generator.nextLong() + public override fun nextLong(n: Long): Long = generator.nextLong(n) +} + +/** + * Represent this [RandomGenerator] as commons-rng [UniformRandomProvider] preserving and mirroring its current state. + * Getting new value from one of those changes the state of another. + */ +public fun RandomGenerator.asUniformRandomProvider(): UniformRandomProvider = if (this is RandomSourceGenerator) + random +else + RandomGeneratorProvider(this) + +public fun RandomGenerator.Companion.fromSource(source: RandomSource, seed: Long? = null): RandomSourceGenerator = + RandomSourceGenerator(source, seed) + +public fun RandomGenerator.Companion.mersenneTwister(seed: Long? = null): RandomSourceGenerator = + fromSource(RandomSource.MT, seed) diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt b/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/distributions.kt similarity index 55% rename from kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt rename to kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/distributions.kt index 412454994..ff20572cc 100644 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt +++ b/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/distributions.kt @@ -1,47 +1,42 @@ -package scientifik.kmath.prob +package kscience.kmath.prob +import kscience.kmath.chains.BlockingIntChain +import kscience.kmath.chains.BlockingRealChain +import kscience.kmath.chains.Chain import org.apache.commons.rng.UniformRandomProvider import org.apache.commons.rng.sampling.distribution.* -import scientifik.kmath.chains.BlockingIntChain -import scientifik.kmath.chains.BlockingRealChain -import scientifik.kmath.chains.Chain -import java.util.* import kotlin.math.PI import kotlin.math.exp import kotlin.math.pow import kotlin.math.sqrt -abstract class ContinuousSamplerDistribution : Distribution<Double> { - +public abstract class ContinuousSamplerDistribution : Distribution<Double> { private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingRealChain() { private val sampler = buildCMSampler(generator) override fun nextDouble(): Double = sampler.sample() - override fun fork(): Chain<Double> = ContinuousSamplerChain(generator.fork()) } protected abstract fun buildCMSampler(generator: RandomGenerator): ContinuousSampler - override fun sample(generator: RandomGenerator): BlockingRealChain = ContinuousSamplerChain(generator) + public override fun sample(generator: RandomGenerator): BlockingRealChain = ContinuousSamplerChain(generator) } -abstract class DiscreteSamplerDistribution : Distribution<Int> { - +public abstract class DiscreteSamplerDistribution : Distribution<Int> { private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingIntChain() { private val sampler = buildSampler(generator) override fun nextInt(): Int = sampler.sample() - override fun fork(): Chain<Int> = ContinuousSamplerChain(generator.fork()) } protected abstract fun buildSampler(generator: RandomGenerator): DiscreteSampler - override fun sample(generator: RandomGenerator): BlockingIntChain = ContinuousSamplerChain(generator) + public override fun sample(generator: RandomGenerator): BlockingIntChain = ContinuousSamplerChain(generator) } -enum class NormalSamplerMethod { +public enum class NormalSamplerMethod { BoxMuller, Marsaglia, Ziggurat @@ -54,20 +49,18 @@ private fun normalSampler(method: NormalSamplerMethod, provider: UniformRandomPr NormalSamplerMethod.Ziggurat -> ZigguratNormalizedGaussianSampler(provider) } -fun Distribution.Companion.normal( +public fun Distribution.Companion.normal( method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat ): Distribution<Double> = object : ContinuousSamplerDistribution() { override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { - val provider: UniformRandomProvider = generator.asUniformRandomProvider() + val provider = generator.asUniformRandomProvider() return normalSampler(method, provider) } - override fun probability(arg: Double): Double { - return exp(-arg.pow(2) / 2) / sqrt(PI * 2) - } + override fun probability(arg: Double): Double = exp(-arg.pow(2) / 2) / sqrt(PI * 2) } -fun Distribution.Companion.normal( +public fun Distribution.Companion.normal( mean: Double, sigma: Double, method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat @@ -76,34 +69,27 @@ fun Distribution.Companion.normal( private val norm = sigma * sqrt(PI * 2) override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { - val provider: UniformRandomProvider = generator.asUniformRandomProvider() + val provider = generator.asUniformRandomProvider() val normalizedSampler = normalSampler(method, provider) return GaussianSampler(normalizedSampler, mean, sigma) } - override fun probability(arg: Double): Double { - return exp(-(arg - mean).pow(2) / 2 / sigma2) / norm - } + override fun probability(arg: Double): Double = exp(-(arg - mean).pow(2) / 2 / sigma2) / norm } -fun Distribution.Companion.poisson( - lambda: Double -): DiscreteSamplerDistribution = object : DiscreteSamplerDistribution() { +public fun Distribution.Companion.poisson(lambda: Double): DiscreteSamplerDistribution = + object : DiscreteSamplerDistribution() { + private val computedProb: MutableMap<Int, Double> = hashMapOf(0 to exp(-lambda)) - override fun buildSampler(generator: RandomGenerator): DiscreteSampler { - return PoissonSampler.of(generator.asUniformRandomProvider(), lambda) - } + override fun buildSampler(generator: RandomGenerator): DiscreteSampler = + PoissonSampler.of(generator.asUniformRandomProvider(), lambda) - private val computedProb: HashMap<Int, Double> = hashMapOf(0 to exp(-lambda)) + override fun probability(arg: Int): Double { + require(arg >= 0) { "The argument must be >= 0" } - override fun probability(arg: Int): Double { - require(arg >= 0) { "The argument must be >= 0" } - return if (arg > 40) { - exp(-(arg - lambda).pow(2) / 2 / lambda) / sqrt(2 * PI * lambda) - } else { - computedProb.getOrPut(arg) { - probability(arg - 1) * lambda / arg - } + return if (arg > 40) + exp(-(arg - lambda).pow(2) / 2 / lambda) / sqrt(2 * PI * lambda) + else + computedProb.getOrPut(arg) { probability(arg - 1) * lambda / arg } } } -} diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt deleted file mode 100644 index f5a73a08b..000000000 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt +++ /dev/null @@ -1,67 +0,0 @@ -package scientifik.kmath.prob - -import org.apache.commons.rng.UniformRandomProvider -import org.apache.commons.rng.simple.RandomSource - -class RandomSourceGenerator(val source: RandomSource, seed: Long?) : RandomGenerator { - internal val random: UniformRandomProvider = seed?.let { - RandomSource.create(source, seed) - } ?: RandomSource.create(source) - - override fun nextBoolean(): Boolean = random.nextBoolean() - - override fun nextDouble(): Double = random.nextDouble() - - override fun nextInt(): Int = random.nextInt() - override fun nextInt(until: Int): Int = random.nextInt(until) - - override fun nextLong(): Long = random.nextLong() - override fun nextLong(until: Long): Long = random.nextLong(until) - - override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { - require(toIndex > fromIndex) - random.nextBytes(array, fromIndex, toIndex - fromIndex) - } - - override fun fork(): RandomGenerator = RandomSourceGenerator(source, nextLong()) -} - -inline class RandomGeneratorProvider(val generator: RandomGenerator) : UniformRandomProvider { - override fun nextBoolean(): Boolean = generator.nextBoolean() - - override fun nextFloat(): Float = generator.nextDouble().toFloat() - - override fun nextBytes(bytes: ByteArray) { - generator.fillBytes(bytes) - } - - override fun nextBytes(bytes: ByteArray, start: Int, len: Int) { - generator.fillBytes(bytes, start, start + len) - } - - override fun nextInt(): Int = generator.nextInt() - - override fun nextInt(n: Int): Int = generator.nextInt(n) - - override fun nextDouble(): Double = generator.nextDouble() - - override fun nextLong(): Long = generator.nextLong() - - override fun nextLong(n: Long): Long = generator.nextLong(n) -} - -/** - * Represent this [RandomGenerator] as commons-rng [UniformRandomProvider] preserving and mirroring its current state. - * Getting new value from one of those changes the state of another. - */ -fun RandomGenerator.asUniformRandomProvider(): UniformRandomProvider = if (this is RandomSourceGenerator) { - random -} else { - RandomGeneratorProvider(this) -} - -fun RandomGenerator.Companion.fromSource(source: RandomSource, seed: Long? = null): RandomSourceGenerator = - RandomSourceGenerator(source, seed) - -fun RandomGenerator.Companion.mersenneTwister(seed: Long? = null): RandomSourceGenerator = - fromSource(RandomSource.MT, seed) diff --git a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/CommonsDistributionsTest.kt similarity index 91% rename from kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt rename to kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/CommonsDistributionsTest.kt index 7638c695e..12a00684b 100644 --- a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt +++ b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/CommonsDistributionsTest.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.prob +package kscience.kmath.prob import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.toList @@ -6,7 +6,7 @@ import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -class CommonsDistributionsTest { +internal class CommonsDistributionsTest { @Test fun testNormalDistributionSuspend() { val distribution = Distribution.normal(7.0, 2.0) @@ -24,5 +24,4 @@ class CommonsDistributionsTest { val sample = distribution.sample(generator).nextBlock(1000) Assertions.assertEquals(7.0, sample.average(), 0.1) } - -} \ No newline at end of file +} diff --git a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/SamplerTest.kt b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/SamplerTest.kt similarity index 92% rename from kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/SamplerTest.kt rename to kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/SamplerTest.kt index 1152f3057..3d8a4f531 100644 --- a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/SamplerTest.kt +++ b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/SamplerTest.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.prob +package kscience.kmath.prob import kotlinx.coroutines.runBlocking import kotlin.test.Test diff --git a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/StatisticTest.kt similarity index 81% rename from kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt rename to kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/StatisticTest.kt index 2613f71d5..22ca472a8 100644 --- a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt +++ b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/StatisticTest.kt @@ -1,18 +1,20 @@ -package scientifik.kmath.prob +package kscience.kmath.prob import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -import scientifik.kmath.streaming.chunked +import kscience.kmath.streaming.chunked import kotlin.test.Test -class StatisticTest { +internal class StatisticTest { //create a random number generator. val generator = RandomGenerator.default(1) + //Create a stateless chain from generator. val data = generator.chain { nextDouble() } - //Convert a chaint to Flow and break it into chunks. + + //Convert a chain to Flow and break it into chunks. val chunked = data.chunked(1000) @Test @@ -22,7 +24,8 @@ class StatisticTest { .flow(chunked) //create a flow with results .drop(99) // Skip first 99 values and use one with total data .first() //get 1e5 data samples average + println(average) } } -} \ No newline at end of file +} diff --git a/kmath-viktor/build.gradle.kts b/kmath-viktor/build.gradle.kts index 5ad0620e9..6fe8ad878 100644 --- a/kmath-viktor/build.gradle.kts +++ b/kmath-viktor/build.gradle.kts @@ -1,6 +1,4 @@ -plugins { - id("scientifik.jvm") -} +plugins { id("ru.mipt.npm.jvm") } description = "Binding for https://github.com/JetBrains-Research/viktor" diff --git a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorBuffer.kt b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorBuffer.kt new file mode 100644 index 000000000..5c9611758 --- /dev/null +++ b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorBuffer.kt @@ -0,0 +1,19 @@ +package kscience.kmath.viktor + +import kscience.kmath.structures.MutableBuffer +import org.jetbrains.bio.viktor.F64FlatArray + +@Suppress("NOTHING_TO_INLINE", "OVERRIDE_BY_INLINE") +public inline class ViktorBuffer(public val flatArray: F64FlatArray) : MutableBuffer<Double> { + public override val size: Int + get() = flatArray.size + + public override inline fun get(index: Int): Double = flatArray[index] + + public override inline fun set(index: Int, value: Double) { + flatArray[index] = value + } + + public override fun copy(): MutableBuffer<Double> = ViktorBuffer(flatArray.copy().flatten()) + public override operator fun iterator(): Iterator<Double> = flatArray.data.iterator() +} diff --git a/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt new file mode 100644 index 000000000..2471362cb --- /dev/null +++ b/kmath-viktor/src/main/kotlin/kscience/kmath/viktor/ViktorNDStructure.kt @@ -0,0 +1,88 @@ +package kscience.kmath.viktor + +import kscience.kmath.operations.RealField +import kscience.kmath.structures.DefaultStrides +import kscience.kmath.structures.MutableNDStructure +import kscience.kmath.structures.NDField +import kscience.kmath.structures.Strides +import org.jetbrains.bio.viktor.F64Array + +@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public inline class ViktorNDStructure(public val f64Buffer: F64Array) : MutableNDStructure<Double> { + public override val shape: IntArray get() = f64Buffer.shape + + public override inline fun get(index: IntArray): Double = f64Buffer.get(*index) + + public override inline fun set(index: IntArray, value: Double) { + f64Buffer.set(*index, value = value) + } + + public override fun elements(): Sequence<Pair<IntArray, Double>> = + DefaultStrides(shape).indices().map { it to get(it) } +} + +public fun F64Array.asStructure(): ViktorNDStructure = ViktorNDStructure(this) + +@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public class ViktorNDField(public override val shape: IntArray) : NDField<Double, RealField, ViktorNDStructure> { + public override val zero: ViktorNDStructure + get() = F64Array.full(init = 0.0, shape = shape).asStructure() + + public override val one: ViktorNDStructure + get() = F64Array.full(init = 1.0, shape = shape).asStructure() + + public val strides: Strides = DefaultStrides(shape) + + public override val elementContext: RealField get() = RealField + + public override fun produce(initializer: RealField.(IntArray) -> Double): ViktorNDStructure = + F64Array(*shape).apply { + this@ViktorNDField.strides.indices().forEach { index -> + set(value = RealField.initializer(index), indices = index) + } + }.asStructure() + + public override fun map(arg: ViktorNDStructure, transform: RealField.(Double) -> Double): ViktorNDStructure = + F64Array(*shape).apply { + this@ViktorNDField.strides.indices().forEach { index -> + set(value = RealField.transform(arg[index]), indices = index) + } + }.asStructure() + + public override fun mapIndexed( + arg: ViktorNDStructure, + transform: RealField.(index: IntArray, Double) -> Double + ): ViktorNDStructure = F64Array(*shape).apply { + this@ViktorNDField.strides.indices().forEach { index -> + set(value = RealField.transform(index, arg[index]), indices = index) + } + }.asStructure() + + public override fun combine( + a: ViktorNDStructure, + b: ViktorNDStructure, + transform: RealField.(Double, Double) -> Double + ): ViktorNDStructure = F64Array(*shape).apply { + this@ViktorNDField.strides.indices().forEach { index -> + set(value = RealField.transform(a[index], b[index]), indices = index) + } + }.asStructure() + + public override fun add(a: ViktorNDStructure, b: ViktorNDStructure): ViktorNDStructure = + (a.f64Buffer + b.f64Buffer).asStructure() + + public override fun multiply(a: ViktorNDStructure, k: Number): ViktorNDStructure = + (a.f64Buffer * k.toDouble()).asStructure() + + public override inline fun ViktorNDStructure.plus(b: ViktorNDStructure): ViktorNDStructure = + (f64Buffer + b.f64Buffer).asStructure() + + public override inline fun ViktorNDStructure.minus(b: ViktorNDStructure): ViktorNDStructure = + (f64Buffer - b.f64Buffer).asStructure() + + public override inline fun ViktorNDStructure.times(k: Number): ViktorNDStructure = + (f64Buffer * k.toDouble()).asStructure() + + public override inline fun ViktorNDStructure.plus(arg: Double): ViktorNDStructure = + (f64Buffer.plus(arg)).asStructure() +} \ No newline at end of file diff --git a/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt b/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt deleted file mode 100644 index 551b877a7..000000000 --- a/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt +++ /dev/null @@ -1,20 +0,0 @@ -package scientifik.kmath.viktor - -import org.jetbrains.bio.viktor.F64FlatArray -import scientifik.kmath.structures.MutableBuffer - -@Suppress("NOTHING_TO_INLINE", "OVERRIDE_BY_INLINE") -inline class ViktorBuffer(val flatArray: F64FlatArray) : MutableBuffer<Double> { - override val size: Int get() = flatArray.size - - override inline fun get(index: Int): Double = flatArray[index] - override inline fun set(index: Int, value: Double) { - flatArray[index] = value - } - - override fun copy(): MutableBuffer<Double> { - return ViktorBuffer(flatArray.copy().flatten()) - } - - override operator fun iterator(): Iterator<Double> = flatArray.data.iterator() -} diff --git a/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorNDStructure.kt b/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorNDStructure.kt deleted file mode 100644 index 84e927721..000000000 --- a/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorNDStructure.kt +++ /dev/null @@ -1,86 +0,0 @@ -package scientifik.kmath.viktor - -import org.jetbrains.bio.viktor.F64Array -import scientifik.kmath.operations.RealField -import scientifik.kmath.structures.DefaultStrides -import scientifik.kmath.structures.MutableNDStructure -import scientifik.kmath.structures.NDField - -@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -inline class ViktorNDStructure(val f64Buffer: F64Array) : MutableNDStructure<Double> { - - override val shape: IntArray get() = f64Buffer.shape - - override inline fun get(index: IntArray): Double = f64Buffer.get(*index) - - override inline fun set(index: IntArray, value: Double) { - f64Buffer.set(*index, value = value) - } - - override fun elements(): Sequence<Pair<IntArray, Double>> { - return DefaultStrides(shape).indices().map { it to get(it) } - } -} - -fun F64Array.asStructure(): ViktorNDStructure = ViktorNDStructure(this) - -@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -class ViktorNDField(override val shape: IntArray) : NDField<Double, RealField, ViktorNDStructure> { - override val zero: ViktorNDStructure - get() = F64Array.full(init = 0.0, shape = *shape).asStructure() - override val one: ViktorNDStructure - get() = F64Array.full(init = 1.0, shape = *shape).asStructure() - - val strides = DefaultStrides(shape) - - override val elementContext: RealField get() = RealField - - override fun produce(initializer: RealField.(IntArray) -> Double): ViktorNDStructure = F64Array(*shape).apply { - this@ViktorNDField.strides.indices().forEach { index -> - set(value = RealField.initializer(index), indices = *index) - } - }.asStructure() - - override fun map(arg: ViktorNDStructure, transform: RealField.(Double) -> Double): ViktorNDStructure = - F64Array(*shape).apply { - this@ViktorNDField.strides.indices().forEach { index -> - set(value = RealField.transform(arg[index]), indices = *index) - } - }.asStructure() - - override fun mapIndexed( - arg: ViktorNDStructure, - transform: RealField.(index: IntArray, Double) -> Double - ): ViktorNDStructure = F64Array(*shape).apply { - this@ViktorNDField.strides.indices().forEach { index -> - set(value = RealField.transform(index, arg[index]), indices = *index) - } - }.asStructure() - - override fun combine( - a: ViktorNDStructure, - b: ViktorNDStructure, - transform: RealField.(Double, Double) -> Double - ): ViktorNDStructure = F64Array(*shape).apply { - this@ViktorNDField.strides.indices().forEach { index -> - set(value = RealField.transform(a[index], b[index]), indices = *index) - } - }.asStructure() - - override fun add(a: ViktorNDStructure, b: ViktorNDStructure): ViktorNDStructure { - return (a.f64Buffer + b.f64Buffer).asStructure() - } - - override fun multiply(a: ViktorNDStructure, k: Number): ViktorNDStructure = - (a.f64Buffer * k.toDouble()).asStructure() - - override inline fun ViktorNDStructure.plus(b: ViktorNDStructure): ViktorNDStructure = - (f64Buffer + b.f64Buffer).asStructure() - - override inline fun ViktorNDStructure.minus(b: ViktorNDStructure): ViktorNDStructure = - (f64Buffer - b.f64Buffer).asStructure() - - override inline fun ViktorNDStructure.times(k: Number): ViktorNDStructure = (f64Buffer * k.toDouble()).asStructure() - - override inline fun ViktorNDStructure.plus(arg: Double): ViktorNDStructure = (f64Buffer.plus(arg)).asStructure() -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 9e69a4291..64d18d271 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,14 +1,13 @@ pluginManagement { - - val toolsVersion = "0.5.2" + val toolsVersion = "0.6.0" plugins { - id("kotlinx.benchmark") version "0.2.0-dev-8" - id("scientifik.mpp") version toolsVersion - id("scientifik.jvm") version toolsVersion - id("scientifik.atomic") version toolsVersion - id("scientifik.publish") version toolsVersion - kotlin("plugin.allopen") version "1.3.72" + id("kotlinx.benchmark") version "0.2.0-dev-20" + id("ru.mipt.npm.base") version toolsVersion + id("ru.mipt.npm.mpp") version toolsVersion + id("ru.mipt.npm.jvm") version toolsVersion + id("ru.mipt.npm.publish") version toolsVersion + kotlin("plugin.allopen") } repositories { @@ -16,9 +15,10 @@ pluginManagement { jcenter() gradlePluginPortal() maven("https://dl.bintray.com/kotlin/kotlin-eap") - maven("https://dl.bintray.com/mipt-npm/scientifik") + maven("https://dl.bintray.com/mipt-npm/kscience") maven("https://dl.bintray.com/mipt-npm/dev") maven("https://dl.bintray.com/kotlin/kotlinx") + maven("https://dl.bintray.com/kotlin/kotlin-dev/") } }