diff --git a/CHANGELOG.md b/CHANGELOG.md index 75833602c..c4b8c06cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added ### Changed +- Kotlin 1.7 +- `LazyStructure` `deffered` -> `async` to comply with coroutines code style ### Deprecated diff --git a/build.gradle.kts b/build.gradle.kts index 10f2385f5..d8c591799 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,13 +11,13 @@ allprojects { } group = "space.kscience" - version = "0.3.0" + version = "0.3.1-dev-1" } subprojects { if (name.startsWith("kmath")) apply() - plugins.withId("org.jetbrains.dokka"){ + plugins.withId("org.jetbrains.dokka") { tasks.withType { dependsOn(tasks["assemble"]) @@ -51,6 +51,18 @@ subprojects { } } } + + plugins.withId("org.jetbrains.kotlin.multiplatform") { + configure { + sourceSets { + val commonTest by getting { + dependencies { + implementation(projects.testUtils) + } + } + } + } + } } readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md") diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index fa5142538..20611e92d 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,7 +1,8 @@ plugins { + kotlin("jvm") version "1.7.0" `kotlin-dsl` `version-catalog` - alias(miptNpmLibs.plugins.kotlin.plugin.serialization) + alias(npmlibs.plugins.kotlin.plugin.serialization) } java.targetCompatibility = JavaVersion.VERSION_11 @@ -13,17 +14,18 @@ repositories { gradlePluginPortal() } -val toolsVersion: String by extra -val kotlinVersion = miptNpmLibs.versions.kotlin.asProvider().get() -val benchmarksVersion = miptNpmLibs.versions.kotlinx.benchmark.get() +val toolsVersion = npmlibs.versions.tools.get() +val kotlinVersion = npmlibs.versions.kotlin.asProvider().get() +val benchmarksVersion = npmlibs.versions.kotlinx.benchmark.get() dependencies { api("ru.mipt.npm:gradle-tools:$toolsVersion") + api(npmlibs.atomicfu.gradle) //plugins form benchmarks api("org.jetbrains.kotlinx:kotlinx-benchmark-plugin:$benchmarksVersion") api("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion") //to be used inside build-script only - implementation(miptNpmLibs.kotlinx.serialization.json) + implementation(npmlibs.kotlinx.serialization.json) } kotlin.sourceSets.all { diff --git a/buildSrc/gradle.properties b/buildSrc/gradle.properties deleted file mode 100644 index a0b05e812..000000000 --- a/buildSrc/gradle.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# Copyright 2018-2021 KMath contributors. -# Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. -# - -kotlin.code.style=official -toolsVersion=0.11.2-kotlin-1.6.10 diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 7c1df133d..bce265510 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -6,7 +6,17 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") dependencyResolutionManagement { - val toolsVersion: String by extra + val projectProperties = java.util.Properties() + file("../gradle.properties").inputStream().use { + projectProperties.load(it) + } + + projectProperties.forEach { key, value -> + extra.set(key.toString(), value) + } + + + val toolsVersion: String = projectProperties["toolsVersion"].toString() repositories { mavenLocal() @@ -16,7 +26,7 @@ dependencyResolutionManagement { } versionCatalogs { - create("miptNpmLibs") { + create("npmlibs") { from("ru.mipt.npm:version-catalog:$toolsVersion") } } diff --git a/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt b/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt index 7f8cb35b3..7c23d8ea0 100644 --- a/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt +++ b/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt @@ -319,7 +319,9 @@ public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra}, } else -> null - }?.let(type::cast) + }?.let{ + type.cast(it) + } } /** diff --git a/gradle.properties b/gradle.properties index 80108737e..5202289fa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,8 +6,10 @@ kotlin.code.style=official kotlin.jupyter.add.scanner=false kotlin.mpp.stability.nowarn=true kotlin.native.ignoreDisabledTargets=true -#kotlin.incremental.js.ir=true +//kotlin.incremental.js.ir=true org.gradle.configureondemand=true -org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G org.gradle.parallel=true +org.gradle.jvmargs=-Xmx4096m + +toolsVersion=0.11.7-kotlin-1.7.0 diff --git a/kmath-ast/README.md b/kmath-ast/README.md index 5d5630f46..553c60bb3 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -199,10 +199,7 @@ public fun main() { Result LaTeX: -
- -![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{12}+x^{2/3}) -
+$$\operatorname{exp}\\,\left(\sqrt{x}\right)-\frac{\frac{\operatorname{arcsin}\\,\left(2\\,x\right)}{2\times10^{10}+x^{3}}}{12}+x^{2/3}$$ Result MathML (can be used with MathJax or other renderers): diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 15b1d0900..f49c2767a 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -24,7 +24,7 @@ kotlin.sourceSets { commonMain { dependencies { - api("com.github.h0tk3y.betterParse:better-parse:0.4.2") + api("com.github.h0tk3y.betterParse:better-parse:0.4.4") api(project(":kmath-core")) } } @@ -57,7 +57,7 @@ tasks.dokkaHtml { if (System.getProperty("space.kscience.kmath.ast.dump.generated.classes") == "1") tasks.jvmTest { - jvmArgs = (jvmArgs ?: emptyList()) + listOf("-Dspace.kscience.kmath.ast.dump.generated.classes=1") + jvmArgs("-Dspace.kscience.kmath.ast.dump.generated.classes=1") } readme { diff --git a/kmath-ast/docs/README-TEMPLATE.md b/kmath-ast/docs/README-TEMPLATE.md index e9e22f4d4..96bbfbf5a 100644 --- a/kmath-ast/docs/README-TEMPLATE.md +++ b/kmath-ast/docs/README-TEMPLATE.md @@ -170,10 +170,7 @@ public fun main() { Result LaTeX: -
- -![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{12}+x^{2/3}) -
+$$\operatorname{exp}\\,\left(\sqrt{x}\right)-\frac{\frac{\operatorname{arcsin}\\,\left(2\\,x\right)}{2\times10^{10}+x^{3}}}{12}+x^{2/3}$$ Result MathML (can be used with MathJax or other renderers): diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt index a8b1aa2e1..240acf1d7 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt @@ -36,7 +36,7 @@ public fun MST.compileToExpression(algebra: Algebra): Expression ) } - return ESTreeBuilder { visit(typed) }.instance + return ESTreeBuilder { visit(typed) }.instance } /** diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/ESTreeBuilder.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/ESTreeBuilder.kt index 10a6c4a16..16a59d273 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/ESTreeBuilder.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/ESTreeBuilder.kt @@ -22,28 +22,20 @@ internal class ESTreeBuilder(val bodyCallback: ESTreeBuilder.() -> BaseExp } } + @Suppress("UNUSED_VARIABLE") val instance: Expression by lazy { val node = Program( sourceType = "script", - VariableDeclaration( - kind = "var", - VariableDeclarator( - id = Identifier("executable"), - init = FunctionExpression( - params = arrayOf(Identifier("constants"), Identifier("arguments")), - body = BlockStatement(ReturnStatement(bodyCallback())), - ), - ), - ), + ReturnStatement(bodyCallback()) ) - eval(generate(node)) - GeneratedExpression(js("executable"), constants.toTypedArray()) + val code = generate(node) + GeneratedExpression(js("new Function('constants', 'arguments_0', code)"), constants.toTypedArray()) } private val constants = mutableListOf() - fun constant(value: Any?) = when { + fun constant(value: Any?): BaseExpression = when { value == null || jsTypeOf(value) == "number" || jsTypeOf(value) == "string" || jsTypeOf(value) == "boolean" -> SimpleLiteral(value) @@ -61,7 +53,8 @@ internal class ESTreeBuilder(val bodyCallback: ESTreeBuilder.() -> BaseExp } } - fun variable(name: Symbol): BaseExpression = call(getOrFail, Identifier("arguments"), SimpleLiteral(name.identity)) + fun variable(name: Symbol): BaseExpression = + call(getOrFail, Identifier("arguments_0"), SimpleLiteral(name.identity)) fun call(function: Function, vararg args: BaseExpression): BaseExpression = SimpleCallExpression( optional = false, diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.extensions.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.extensions.kt index 3aa31f921..5b1ce914e 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.extensions.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.extensions.kt @@ -3,6 +3,8 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ +@file:Suppress("unused") + package space.kscience.kmath.internal.estree internal fun Program(sourceType: String, vararg body: dynamic) = object : Program { @@ -28,9 +30,10 @@ internal fun Identifier(name: String) = object : Identifier { override var name = name } -internal fun FunctionExpression(params: Array, body: BlockStatement) = object : FunctionExpression { +internal fun FunctionExpression(id: Identifier?, params: Array, body: BlockStatement) = object : FunctionExpression { override var params = params override var type = "FunctionExpression" + override var id: Identifier? = id override var body = body } diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt index aacb62f36..878919d8d 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt @@ -71,7 +71,7 @@ internal sealed class WasmBuilder>( protected open fun visitBinary(mst: TypedMst.Binary): ExpressionRef = error("Binary operation ${mst.operation} not defined in $this") - protected open fun createModule(): BinaryenModule = js("new \$module\$binaryen.Module()") + protected open fun createModule(): BinaryenModule = space.kscience.kmath.internal.binaryen.Module() protected fun visit(node: TypedMst): ExpressionRef = when (node) { is TypedMst.Constant -> visitNumber( diff --git a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/AsmBuilder.kt index a85079fc8..a76b47ecc 100644 --- a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/AsmBuilder.kt @@ -49,5 +49,7 @@ internal abstract class AsmBuilder { * ASM Type for [space.kscience.kmath.expressions.Symbol]. */ val SYMBOL_TYPE: Type = getObjectType("space/kscience/kmath/expressions/Symbol") + + const val ARGUMENTS_NAME = "args" } } diff --git a/kmath-complex/build.gradle.kts b/kmath-complex/build.gradle.kts index ea74df646..f0ce631a5 100644 --- a/kmath-complex/build.gradle.kts +++ b/kmath-complex/build.gradle.kts @@ -19,13 +19,15 @@ readme { feature( id = "complex", - description = "Complex Numbers", ref = "src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt" - ) + ){ + "Complex numbers operations" + } feature( id = "quaternion", - description = "Quaternions", ref = "src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt" - ) + ){ + "Quaternions and their composition" + } } diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt index 3ef3428c6..359b66b20 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt @@ -16,52 +16,130 @@ import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableMemoryBuffer import kotlin.math.* +/** + * Represents `double`-based quaternion. + * + * @property w The first component. + * @property x The second component. + * @property y The third component. + * @property z The fourth component. + */ +public class Quaternion( + public val w: Double, + public val x: Double, + public val y: Double, + public val z: Double, +) : Buffer { + init { + require(!w.isNaN()) { "w-component of quaternion is not-a-number" } + require(!x.isNaN()) { "x-component of quaternion is not-a-number" } + require(!y.isNaN()) { "y-component of quaternion is not-a-number" } + require(!z.isNaN()) { "z-component of quaternion is not-a-number" } + } + + /** + * Returns a string representation of this quaternion. + */ + override fun toString(): String = "($w + $x * i + $y * j + $z * k)" + + override val size: Int get() = 4 + + override fun get(index: Int): Double = when (index) { + 0 -> w + 1 -> x + 2 -> y + 3 -> z + else -> error("Index $index out of bounds [0,3]") + } + + override fun iterator(): Iterator = listOf(w, x, y, z).iterator() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Quaternion + + if (w != other.w) return false + if (x != other.x) return false + if (y != other.y) return false + if (z != other.z) return false + + return true + } + + override fun hashCode(): Int { + var result = w.hashCode() + result = 31 * result + x.hashCode() + result = 31 * result + y.hashCode() + result = 31 * result + z.hashCode() + return result + } + + public companion object : MemorySpec { + override val objectSize: Int get() = 32 + + override fun MemoryReader.read(offset: Int): Quaternion = Quaternion( + readDouble(offset), + readDouble(offset + 8), + readDouble(offset + 16), + readDouble(offset + 24) + ) + + override fun MemoryWriter.write(offset: Int, value: Quaternion) { + writeDouble(offset, value.w) + writeDouble(offset + 8, value.x) + writeDouble(offset + 16, value.y) + writeDouble(offset + 24, value.z) + } + } +} + +public fun Quaternion(w: Number, x: Number = 0.0, y: Number = 0.0, z: Number = 0.0): Quaternion = Quaternion( + w.toDouble(), + x.toDouble(), + y.toDouble(), + z.toDouble(), +) + /** * This quaternion's conjugate. */ public val Quaternion.conjugate: Quaternion - get() = QuaternionField { z - x * i - y * j - z * k } + get() = Quaternion(w, -x, -y, -z) /** * This quaternion's reciprocal. */ public val Quaternion.reciprocal: Quaternion get() { - QuaternionField { - val n = norm(this@reciprocal) - return conjugate / (n * n) - } + val norm2 = (w * w + x * x + y * y + z * z) + return Quaternion(w / norm2, -x / norm2, -y / norm2, -z / norm2) } -/** - * Absolute value of the quaternion. - */ -public val Quaternion.r: Double - get() = sqrt(w * w + x * x + y * y + z * z) - /** * A field of [Quaternion]. */ @OptIn(UnstableKMathAPI::class) -public object QuaternionField : Field, Norm, PowerOperations, +public object QuaternionField : Field, Norm, PowerOperations, ExponentialOperations, NumbersAddOps, ScaleOperations { - override val zero: Quaternion = 0.toQuaternion() - override val one: Quaternion = 1.toQuaternion() + override val zero: Quaternion = Quaternion(0.0) + override val one: Quaternion = Quaternion(1.0) /** * The `i` quaternion unit. */ - public val i: Quaternion = Quaternion(0, 1) + public val i: Quaternion = Quaternion(0.0, 1.0, 0.0, 0.0) /** * The `j` quaternion unit. */ - public val j: Quaternion = Quaternion(0, 0, 1) + public val j: Quaternion = Quaternion(0.0, 0.0, 1.0, 0.0) /** * The `k` quaternion unit. */ - public val k: Quaternion = Quaternion(0, 0, 0, 1) + public val k: Quaternion = Quaternion(0.0, 0.0, 0.0, 1.0) override fun add(left: Quaternion, right: Quaternion): Quaternion = Quaternion(left.w + right.w, left.x + right.x, left.y + right.y, left.z + right.z) @@ -133,7 +211,7 @@ public object QuaternionField : Field, Norm, override fun exp(arg: Quaternion): Quaternion { val un = arg.x * arg.x + arg.y * arg.y + arg.z * arg.z - if (un == 0.0) return exp(arg.w).toQuaternion() + if (un == 0.0) return Quaternion(exp(arg.w)) val n1 = sqrt(un) val ea = exp(arg.w) val n2 = ea * sin(n1) / n1 @@ -158,7 +236,8 @@ public object QuaternionField : Field, Norm, return Quaternion(ln(n), th * arg.x, th * arg.y, th * arg.z) } - override operator fun Number.plus(other: Quaternion): Quaternion = Quaternion(toDouble() + other.w, other.x, other.y, other.z) + override operator fun Number.plus(other: Quaternion): Quaternion = + Quaternion(toDouble() + other.w, other.x, other.y, other.z) override operator fun Number.minus(other: Quaternion): Quaternion = Quaternion(toDouble() - other.w, -other.x, -other.y, -other.z) @@ -170,7 +249,12 @@ public object QuaternionField : Field, Norm, Quaternion(toDouble() * arg.w, toDouble() * arg.x, toDouble() * arg.y, toDouble() * arg.z) override fun Quaternion.unaryMinus(): Quaternion = Quaternion(-w, -x, -y, -z) - override fun norm(arg: Quaternion): Quaternion = sqrt(arg.conjugate * arg) + override fun norm(arg: Quaternion): Double = sqrt( + arg.w.pow(2) + + arg.x.pow(2) + + arg.y.pow(2) + + arg.z.pow(2) + ) override fun bindSymbolOrNull(value: String): Quaternion? = when (value) { "i" -> i @@ -179,7 +263,7 @@ public object QuaternionField : Field, Norm, else -> null } - override fun number(value: Number): Quaternion = value.toQuaternion() + override fun number(value: Number): Quaternion = Quaternion(value) override fun sinh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / 2.0 override fun cosh(arg: Quaternion): Quaternion = (exp(arg) + exp(-arg)) / 2.0 @@ -189,76 +273,6 @@ public object QuaternionField : Field, Norm, override fun atanh(arg: Quaternion): Quaternion = (ln(arg + one) - ln(one - arg)) / 2.0 } -/** - * Represents `double`-based quaternion. - * - * @property w The first component. - * @property x The second component. - * @property y The third component. - * @property z The fourth component. - */ -@OptIn(UnstableKMathAPI::class) -public data class Quaternion( - val w: Double, val x: Double, val y: Double, val z: Double, -) { - public constructor(w: Number, x: Number, y: Number, z: Number) : this( - w.toDouble(), - x.toDouble(), - y.toDouble(), - z.toDouble(), - ) - - public constructor(w: Number, x: Number, y: Number) : this(w.toDouble(), x.toDouble(), y.toDouble(), 0.0) - public constructor(w: Number, x: Number) : this(w.toDouble(), x.toDouble(), 0.0, 0.0) - public constructor(w: Number) : this(w.toDouble(), 0.0, 0.0, 0.0) - public constructor(wx: Complex, yz: Complex) : this(wx.re, wx.im, yz.re, yz.im) - public constructor(wx: Complex) : this(wx.re, wx.im, 0, 0) - - init { - require(!w.isNaN()) { "w-component of quaternion is not-a-number" } - require(!x.isNaN()) { "x-component of quaternion is not-a-number" } - require(!y.isNaN()) { "x-component of quaternion is not-a-number" } - require(!z.isNaN()) { "x-component of quaternion is not-a-number" } - } - - /** - * Returns a string representation of this quaternion. - */ - override fun toString(): String = "($w + $x * i + $y * j + $z * k)" - - public companion object : MemorySpec { - override val objectSize: Int - get() = 32 - - override fun MemoryReader.read(offset: Int): Quaternion = - Quaternion(readDouble(offset), readDouble(offset + 8), readDouble(offset + 16), readDouble(offset + 24)) - - override fun MemoryWriter.write(offset: Int, value: Quaternion) { - writeDouble(offset, value.w) - writeDouble(offset + 8, value.x) - writeDouble(offset + 16, value.y) - writeDouble(offset + 24, value.z) - } - } -} - -/** - * Creates a quaternion with real part equal to this real. - * - * @receiver the real part. - * @return a new quaternion. - */ -public fun Number.toQuaternion(): Quaternion = Quaternion(this) - -/** - * Creates a quaternion with `w`-component equal to `re`-component of given complex and `x`-component equal to - * `im`-component of given complex. - * - * @receiver the complex number. - * @return a new quaternion. - */ -public fun Complex.toQuaternion(): Quaternion = Quaternion(this) - /** * Creates a new buffer of quaternions with the specified [size], where each element is calculated by calling the * specified [init] function. diff --git a/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionFieldTest.kt b/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionTest.kt similarity index 84% rename from kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionFieldTest.kt rename to kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionTest.kt index 6784f3516..fd4df736c 100644 --- a/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionFieldTest.kt +++ b/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionTest.kt @@ -6,10 +6,23 @@ package space.kscience.kmath.complex import space.kscience.kmath.operations.invoke +import space.kscience.kmath.testutils.assertBufferEquals import kotlin.test.Test import kotlin.test.assertEquals -internal class QuaternionFieldTest { +internal class QuaternionTest { + + @Test + fun testNorm() { + assertEquals(2.0, QuaternionField.norm(Quaternion(1.0, 1.0, 1.0, 1.0))) + } + + @Test + fun testInverse() = QuaternionField { + val q = Quaternion(1.0, 2.0, -3.0, 4.0) + assertBufferEquals(one, q * q.reciprocal, 1e-4) + } + @Test fun testAddition() { assertEquals(Quaternion(42, 42), QuaternionField { Quaternion(16, 16) + Quaternion(26, 26) }) diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 4a35a54fb..052924ce8 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -1,8 +1,6 @@ plugins { - kotlin("multiplatform") - id("ru.mipt.npm.gradle.common") + id("ru.mipt.npm.gradle.mpp") id("ru.mipt.npm.gradle.native") -// id("com.xcporter.metaview") version "0.0.5" } kotlin.sourceSets { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt index a9712e870..ad291cf7f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt @@ -10,25 +10,6 @@ import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.* import kotlin.reflect.KClass -/** - * An exception is thrown when the expected and actual shape of NDArray differ. - * - * @property expected the expected shape. - * @property actual the actual shape. - */ -public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) : - RuntimeException("Shape ${actual.contentToString()} doesn't fit in expected shape ${expected.contentToString()}.") - -public typealias Shape = IntArray - -public fun Shape(shapeFirst: Int, vararg shapeRest: Int): Shape = intArrayOf(shapeFirst, *shapeRest) - -public interface WithShape { - public val shape: Shape - - public val indices: ShapeIndexer get() = DefaultStrides(shape) -} - /** * The base interface for all ND-algebra implementations. * diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt index b09344d12..68e8ebe90 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferAlgebraND.kt @@ -47,7 +47,7 @@ public interface BufferAlgebraND> : AlgebraND { zipInline(left.toBufferND(), right.toBufferND(), transform) public companion object { - public val defaultIndexerBuilder: (IntArray) -> ShapeIndexer = DefaultStrides.Companion::invoke + public val defaultIndexerBuilder: (IntArray) -> ShapeIndexer = ::Strides } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Shape.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Shape.kt new file mode 100644 index 000000000..d682d5e69 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Shape.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.nd + +/** + * An exception is thrown when the expected and actual shape of NDArray differ. + * + * @property expected the expected shape. + * @property actual the actual shape. + */ +public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) : + RuntimeException("Shape ${actual.contentToString()} doesn't fit in expected shape ${expected.contentToString()}.") + +public class IndexOutOfShapeException(public val shape: Shape, public val index: IntArray) : + RuntimeException("Index ${index.contentToString()} is out of shape ${shape.contentToString()}") + +public typealias Shape = IntArray + +public fun Shape(shapeFirst: Int, vararg shapeRest: Int): Shape = intArrayOf(shapeFirst, *shapeRest) + +public interface WithShape { + public val shape: Shape + + public val indices: ShapeIndexer get() = DefaultStrides(shape) +} + +internal fun requireIndexInShape(index: IntArray, shape: Shape) { + if (index.size != shape.size) throw IndexOutOfShapeException(index, shape) + shape.forEachIndexed { axis, axisShape -> + if (index[axis] !in 0 until axisShape) throw IndexOutOfShapeException(index, shape) + } +} diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndexer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndexer.kt index c6ff79587..29701425f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndexer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShapeIndexer.kt @@ -10,7 +10,7 @@ import kotlin.native.concurrent.ThreadLocal /** * A converter from linear index to multivariate index */ -public interface ShapeIndexer: Iterable{ +public interface ShapeIndexer : Iterable { public val shape: Shape /** @@ -44,7 +44,7 @@ public interface ShapeIndexer: Iterable{ /** * Linear transformation of indexes */ -public abstract class Strides: ShapeIndexer { +public abstract class Strides : ShapeIndexer { /** * Array strides */ @@ -66,7 +66,7 @@ public abstract class Strides: ShapeIndexer { /** * Simple implementation of [Strides]. */ -public class DefaultStrides private constructor(override val shape: IntArray) : Strides() { +public class DefaultStrides(override val shape: IntArray) : Strides() { override val linearSize: Int get() = strides[shape.size] /** @@ -112,10 +112,16 @@ public class DefaultStrides private constructor(override val shape: IntArray) : /** * Cached builder for default strides */ + @Deprecated("Replace by Strides(shape)") public operator fun invoke(shape: IntArray): Strides = defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } } } @ThreadLocal -private val defaultStridesCache = HashMap() \ No newline at end of file +private val defaultStridesCache = HashMap() + +/** + * Cached builder for default strides + */ +public fun Strides(shape: IntArray): Strides = defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt index d948cf36f..e934c6370 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt @@ -101,8 +101,8 @@ public interface StructureND : Featured, WithShape { val bufferRepr: String = when (structure.shape.size) { 1 -> (0 until structure.shape[0]).map { structure[it] } .joinToString(prefix = "[", postfix = "]", separator = ", ") - 2 -> (0 until structure.shape[0]).joinToString(prefix = "[", postfix = "]", separator = ", ") { i -> - (0 until structure.shape[1]).joinToString(prefix = "[", postfix = "]", separator = ", ") { j -> + 2 -> (0 until structure.shape[0]).joinToString(prefix = "[\n", postfix = "\n]", separator = ",\n") { i -> + (0 until structure.shape[1]).joinToString(prefix = " [", postfix = "]", separator = ", ") { j -> structure[i, j].toString() } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/VirtualStructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/VirtualStructureND.kt new file mode 100644 index 000000000..7e86ea73d --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/VirtualStructureND.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.nd + +import space.kscience.kmath.misc.UnstableKMathAPI + +public open class VirtualStructureND( + override val shape: Shape, + public val producer: (IntArray) -> T, +) : StructureND { + override fun get(index: IntArray): T { + requireIndexInShape(index, shape) + return producer(index) + } +} + +@UnstableKMathAPI +public class VirtualDoubleStructureND( + shape: Shape, + producer: (IntArray) -> Double, +) : VirtualStructureND(shape, producer) + +@UnstableKMathAPI +public class VirtualIntStructureND( + shape: Shape, + producer: (IntArray) -> Int, +) : VirtualStructureND(shape, producer) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/operationsND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/operationsND.kt new file mode 100644 index 000000000..c9624dc4e --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/operationsND.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.nd + +public fun StructureND.roll(axis: Int, step: Int = 1): StructureND { + require(axis in shape.indices) { "Axis $axis is outside of shape dimensions: [0, ${shape.size})" } + return VirtualStructureND(shape) { index -> + val newIndex: IntArray = IntArray(index.size) { indexAxis -> + if (indexAxis == axis) { + (index[indexAxis] + step).mod(shape[indexAxis]) + } else { + index[indexAxis] + } + } + get(newIndex) + } +} + +public fun StructureND.roll(pair: Pair, vararg others: Pair): StructureND { + val axisMap: Map = mapOf(pair, *others) + require(axisMap.keys.all { it in shape.indices }) { "Some of axes ${axisMap.keys} is outside of shape dimensions: [0, ${shape.size})" } + return VirtualStructureND(shape) { index -> + val newIndex: IntArray = IntArray(index.size) { indexAxis -> + val offset = axisMap[indexAxis] ?: 0 + (index[indexAxis] + offset).mod(shape[indexAxis]) + } + get(newIndex) + } +} \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/NdOperationsTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/NdOperationsTest.kt new file mode 100644 index 000000000..25b062b44 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/NdOperationsTest.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.nd + +import space.kscience.kmath.operations.DoubleField +import kotlin.test.Test +import kotlin.test.assertEquals + +class NdOperationsTest { + @Test + fun roll() { + val structure = DoubleField.ndAlgebra.structureND(5, 5) { index -> + index.sumOf { it.toDouble() } + } + + println(StructureND.toString(structure)) + + val rolled = structure.roll(0,-1) + + println(StructureND.toString(rolled)) + + assertEquals(4.0, rolled[0, 0]) + } + +} \ No newline at end of file diff --git a/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt b/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt index ac9eb773a..2b9265843 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/space/kscience/kmath/structures/LazyStructureND.kt @@ -18,12 +18,12 @@ public class LazyStructureND( ) : StructureND { private val cache: MutableMap> = HashMap() - public fun deferred(index: IntArray): Deferred = cache.getOrPut(index) { + public fun async(index: IntArray): Deferred = cache.getOrPut(index) { scope.async(context = Dispatchers.Math) { function(index) } } - public suspend fun await(index: IntArray): T = deferred(index).await() - override operator fun get(index: IntArray): T = runBlocking { deferred(index).await() } + public suspend fun await(index: IntArray): T = async(index).await() + override operator fun get(index: IntArray): T = runBlocking { async(index).await() } @OptIn(PerformancePitfall::class) override fun elements(): Sequence> { @@ -33,8 +33,8 @@ public class LazyStructureND( } } -public fun StructureND.deferred(index: IntArray): Deferred = - if (this is LazyStructureND) deferred(index) else CompletableDeferred(get(index)) +public fun StructureND.async(index: IntArray): Deferred = + if (this is LazyStructureND) this@async.async(index) else CompletableDeferred(get(index)) public suspend fun StructureND.await(index: IntArray): T = if (this is LazyStructureND) await(index) else get(index) diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt index dce739dc2..aac327a84 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt @@ -271,7 +271,9 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace null - }?.let(type::cast) + }?.let{ + type.cast(it) + } } /** @@ -505,7 +507,9 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace null - }?.let(type::cast) + }?.let{ + type.cast(it) + } } /** @@ -734,7 +738,9 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace null - }?.let(type::cast) + }?.let{ + type.cast(it) + } } /** @@ -963,7 +969,9 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace null - }?.let(type::cast) + }?.let{ + type.cast(it) + } } /** diff --git a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt index 209bb5b27..af6284e5e 100644 --- a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt +++ b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt @@ -3,6 +3,8 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ +@file:OptIn(PerformancePitfall::class) + package space.kscience.kmath.ejml import org.ejml.data.DMatrixRMaj @@ -18,11 +20,11 @@ import kotlin.random.Random import kotlin.random.asJavaRandom import kotlin.test.* -@OptIn(PerformancePitfall::class) -fun assertMatrixEquals(expected: StructureND, actual: StructureND) { +internal fun assertMatrixEquals(expected: StructureND, actual: StructureND) { assertTrue { StructureND.contentEquals(expected, actual) } } +@OptIn(UnstableKMathAPI::class) internal class EjmlMatrixTest { private val random = Random(0) diff --git a/kmath-geometry/build.gradle.kts b/kmath-geometry/build.gradle.kts index 7eb814683..bfe2e32a2 100644 --- a/kmath-geometry/build.gradle.kts +++ b/kmath-geometry/build.gradle.kts @@ -6,7 +6,7 @@ plugins { kotlin.sourceSets.commonMain { dependencies { - api(project(":kmath-core")) + api(projects.kmath.kmathComplex) } } diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt index d00575bcc..a83cb3ac7 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt @@ -6,12 +6,10 @@ package space.kscience.kmath.geometry import space.kscience.kmath.linear.Point -import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.invoke import kotlin.math.sqrt -@OptIn(UnstableKMathAPI::class) public interface Vector2D : Point, Vector { public val x: Double public val y: Double @@ -29,7 +27,6 @@ public interface Vector2D : Point, Vector { public val Vector2D.r: Double get() = Euclidean2DSpace { norm() } -@Suppress("FunctionName") public fun Vector2D(x: Double, y: Double): Vector2D = Vector2DImpl(x, y) private data class Vector2DImpl( diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt index e12563b46..c1fc74bf1 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt @@ -6,12 +6,11 @@ package space.kscience.kmath.geometry import space.kscience.kmath.linear.Point -import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.Buffer import kotlin.math.sqrt -@OptIn(UnstableKMathAPI::class) public interface Vector3D : Point, Vector { public val x: Double public val y: Double @@ -31,6 +30,19 @@ public interface Vector3D : Point, Vector { @Suppress("FunctionName") public fun Vector3D(x: Double, y: Double, z: Double): Vector3D = Vector3DImpl(x, y, z) +public fun Buffer.asVector3D(): Vector3D = object : Vector3D { + init { + require(this@asVector3D.size == 3) { "Buffer of size 3 is required for Vector3D" } + } + + override val x: Double get() = this@asVector3D[0] + override val y: Double get() = this@asVector3D[1] + override val z: Double get() = this@asVector3D[2] + + override fun toString(): String = this@asVector3D.toString() + +} + public val Vector3D.r: Double get() = Euclidean3DSpace { norm() } private data class Vector3DImpl( diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Projections.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/projections.kt similarity index 97% rename from kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Projections.kt rename to kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/projections.kt index 5e299f450..6ffc43739 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Projections.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/projections.kt @@ -5,6 +5,8 @@ package space.kscience.kmath.geometry +//TODO move vector to receiver + /** * Project vector onto a line. * @param vector to project diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt new file mode 100644 index 000000000..374315610 --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.geometry + +import space.kscience.kmath.complex.Quaternion +import space.kscience.kmath.complex.QuaternionField +import space.kscience.kmath.complex.reciprocal +import space.kscience.kmath.linear.LinearSpace +import space.kscience.kmath.linear.Matrix +import space.kscience.kmath.linear.linearSpace +import space.kscience.kmath.linear.matrix +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.invoke +import kotlin.math.pow +import kotlin.math.sqrt + +internal fun Vector3D.toQuaternion(): Quaternion = Quaternion(0.0, x, y, z) + +/** + * Angle in radians denoted by this quaternion rotation + */ +public val Quaternion.theta: Double get() = kotlin.math.acos(w) * 2 + +/** + * An axis of quaternion rotation + */ +public val Quaternion.vector: Vector3D + get() { + val sint2 = sqrt(1 - w * w) + + return object : Vector3D { + override val x: Double get() = this@vector.x/sint2 + override val y: Double get() = this@vector.y/sint2 + override val z: Double get() = this@vector.z/sint2 + override fun toString(): String = listOf(x, y, z).toString() + } + } + +/** + * Rotate a vector in a [Euclidean3DSpace] + */ +public fun Euclidean3DSpace.rotate(vector: Vector3D, q: Quaternion): Vector3D = with(QuaternionField) { + val p = vector.toQuaternion() + (q * p * q.reciprocal).vector +} + +/** + * Use a composition of quaternions to create a rotation + */ +public fun Euclidean3DSpace.rotate(vector: Vector3D, composition: QuaternionField.() -> Quaternion): Vector3D = + rotate(vector, QuaternionField.composition()) + +public fun Euclidean3DSpace.rotate(vector: Vector3D, matrix: Matrix): Vector3D { + require(matrix.colNum == 3 && matrix.rowNum == 3) { "Square 3x3 rotation matrix is required" } + return with(DoubleField.linearSpace) { matrix.dot(vector).asVector3D() } +} + +/** + * Convert a [Quaternion] to a rotation matrix + */ +@OptIn(UnstableKMathAPI::class) +public fun Quaternion.toRotationMatrix( + linearSpace: LinearSpace = DoubleField.linearSpace, +): Matrix { + val s = QuaternionField.norm(this).pow(-2) + return linearSpace.matrix(3, 3)( + 1.0 - 2 * s * (y * y + z * z), 2 * s * (x * y - z * w), 2 * s * (x * z + y * w), + 2 * s * (x * y + z * w), 1.0 - 2 * s * (x * x + z * z), 2 * s * (y * z - x * w), + 2 * s * (x * z - y * w), 2 * s * (y * z + x * w), 1.0 - 2 * s * (x * x + y * y) + ) +} + +/** + * taken from https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf + */ +public fun Quaternion.Companion.fromRotationMatrix(matrix: Matrix): Quaternion { + val t: Double + val q = if (matrix[2, 2] < 0) { + if (matrix[0, 0] > matrix[1, 1]) { + t = 1 + matrix[0, 0] - matrix[1, 1] - matrix[2, 2] + Quaternion(t, matrix[0, 1] + matrix[1, 0], matrix[2, 0] + matrix[0, 2], matrix[1, 2] - matrix[2, 1]) + } else { + t = 1 - matrix[0, 0] + matrix[1, 1] - matrix[2, 2] + Quaternion(matrix[0, 1] + matrix[1, 0], t, matrix[1, 2] + matrix[2, 1], matrix[2, 0] - matrix[0, 2]) + } + } else { + if (matrix[0, 0] < -matrix[1, 1]) { + t = 1 - matrix[0, 0] - matrix[1, 1] + matrix[2, 2] + Quaternion(matrix[2, 0] + matrix[0, 2], matrix[1, 2] + matrix[2, 1], t, matrix[0, 1] - matrix[1, 0]) + } else { + t = 1 + matrix[0, 0] + matrix[1, 1] + matrix[2, 2] + Quaternion(matrix[1, 2] - matrix[2, 1], matrix[2, 0] - matrix[0, 2], matrix[0, 1] - matrix[1, 0], t) + } + } + return QuaternionField.invoke { q * (0.5 / sqrt(t)) } +} \ No newline at end of file diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt new file mode 100644 index 000000000..abe26d398 --- /dev/null +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.geometry + +import space.kscience.kmath.complex.Quaternion +import space.kscience.kmath.testutils.assertBufferEquals +import kotlin.test.Ignore +import kotlin.test.Test +import kotlin.test.assertEquals + +class RotationTest { + + @Test + fun rotations() = with(Euclidean3DSpace) { + val vector = Vector3D(1.0, 1.0, 1.0) + val q = Quaternion(1.0, 2.0, -3.0, 4.0) + val rotatedByQ = rotate(vector, q) + val matrix = q.toRotationMatrix() + val rotatedByM = rotate(vector,matrix) + + assertBufferEquals(rotatedByQ, rotatedByM, 1e-4) + } + + @Test + @Ignore + fun rotationConversion() { + + val q = Quaternion(1.0, 2.0, -3.0, 4.0) + + val matrix = q.toRotationMatrix() + + assertEquals(q, Quaternion.fromRotationMatrix(matrix)) + } +} \ No newline at end of file diff --git a/kmath-histograms/build.gradle.kts b/kmath-histograms/build.gradle.kts index 51271bb0a..786086c9f 100644 --- a/kmath-histograms/build.gradle.kts +++ b/kmath-histograms/build.gradle.kts @@ -1,17 +1,15 @@ plugins { - kotlin("multiplatform") - id("ru.mipt.npm.gradle.common") + id("ru.mipt.npm.gradle.mpp") id("ru.mipt.npm.gradle.native") } -kscience { - useAtomic() -} +//apply(plugin = "kotlinx-atomicfu") kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-core")) + api(npmlibs.atomicfu) } } commonTest { diff --git a/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt b/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt index c4eeb53cc..2c56a88a7 100644 --- a/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt +++ b/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt @@ -6,12 +6,14 @@ package space.kscience.kmath.histogram import org.junit.jupiter.api.Test +import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.real.step import kotlin.random.Random import kotlin.test.assertEquals import kotlin.test.assertTrue +@OptIn(UnstableKMathAPI::class) class TreeHistogramTest { @Test diff --git a/kmath-memory/build.gradle.kts b/kmath-memory/build.gradle.kts index 4478e5b80..2bd6dd723 100644 --- a/kmath-memory/build.gradle.kts +++ b/kmath-memory/build.gradle.kts @@ -1,6 +1,5 @@ plugins { - kotlin("multiplatform") - id("ru.mipt.npm.gradle.common") + id("ru.mipt.npm.gradle.mpp") id("ru.mipt.npm.gradle.native") } diff --git a/kmath-optimization/build.gradle.kts b/kmath-optimization/build.gradle.kts index b920b9267..2e5bf6005 100644 --- a/kmath-optimization/build.gradle.kts +++ b/kmath-optimization/build.gradle.kts @@ -3,10 +3,6 @@ plugins { id("ru.mipt.npm.gradle.native") } -kscience { - useAtomic() -} - kotlin.sourceSets { all { languageSettings.optIn("space.kscience.kmath.misc.UnstableKMathAPI") @@ -15,6 +11,7 @@ kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-coroutines")) + api(npmlibs.atomicfu) } } } diff --git a/kmath-stat/build.gradle.kts b/kmath-stat/build.gradle.kts index 41a1666f8..b458135b3 100644 --- a/kmath-stat/build.gradle.kts +++ b/kmath-stat/build.gradle.kts @@ -3,14 +3,11 @@ plugins { id("ru.mipt.npm.gradle.native") } -kscience { - useAtomic() -} - kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-coroutines")) + implementation(npmlibs.atomicfu) } } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Sampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Sampler.kt index c96ecdc8c..a88f3e437 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Sampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Sampler.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.stat import kotlinx.coroutines.flow.first import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.combine +import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.DoubleBuffer @@ -30,6 +31,7 @@ public fun interface Sampler { /** * Sample a bunch of values */ +@OptIn(UnstableKMathAPI::class) public fun Sampler.sampleBuffer( generator: RandomGenerator, size: Int, diff --git a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/MCScopeTest.kt b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/MCScopeTest.kt index 075d7f3e5..cca645809 100644 --- a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/MCScopeTest.kt +++ b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/MCScopeTest.kt @@ -66,7 +66,7 @@ class MCScopeTest { } - @OptIn(ObsoleteCoroutinesApi::class) + @OptIn(DelicateCoroutinesApi::class) fun compareResult(test: ATest) { val res1 = runBlocking(Dispatchers.Default) { test() } val res2 = runBlocking(newSingleThreadContext("test")) { test() } diff --git a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt index ecfd8d098..ad7a978a0 100644 --- a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt +++ b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt @@ -7,6 +7,7 @@ import org.tensorflow.op.core.Constant import org.tensorflow.types.TFloat64 import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.misc.PerformancePitfall +import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.DefaultStrides import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.StructureND @@ -74,6 +75,7 @@ public class DoubleTensorFlowAlgebra internal constructor( * * The resulting tensor is available outside of scope */ +@UnstableKMathAPI public fun DoubleField.produceWithTF( block: DoubleTensorFlowAlgebra.() -> StructureND, ): StructureND = Graph().use { graph -> diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt index 1d4d6cebd..cecbc35b8 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt @@ -117,6 +117,7 @@ public open class ViktorFieldOpsND : public val DoubleField.viktorAlgebra: ViktorFieldOpsND get() = ViktorFieldOpsND +@OptIn(UnstableKMathAPI::class) public open class ViktorFieldND( override val shape: Shape, ) : ViktorFieldOpsND(), FieldND, NumbersAddOps> { diff --git a/settings.gradle.kts b/settings.gradle.kts index b3c275810..e3c621e9a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,26 @@ rootProject.name = "kmath" enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +dependencyResolutionManagement { + val toolsVersion: String by extra + + repositories { + mavenLocal() + maven("https://repo.kotlin.link") + mavenCentral() + gradlePluginPortal() + } + + versionCatalogs { + create("npmlibs") { + from("ru.mipt.npm:version-catalog:$toolsVersion") + } + } +} + include( + ":test-utils", ":kmath-memory", ":kmath-complex", ":kmath-core", @@ -27,4 +46,4 @@ include( ":kmath-jafama", ":examples", ":benchmarks", -) +) \ No newline at end of file diff --git a/test-utils/build.gradle.kts b/test-utils/build.gradle.kts new file mode 100644 index 000000000..88c7ea9d0 --- /dev/null +++ b/test-utils/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("ru.mipt.npm.gradle.mpp") + id("ru.mipt.npm.gradle.native") +} + +kotlin.sourceSets { + commonMain { + dependencies { + api(projects.kmath.kmathCore) + api(kotlin("test")) + } + } +} diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/AlgebraicVerifier.kt b/test-utils/src/commonMain/kotlin/AlgebraicVerifier.kt similarity index 100% rename from kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/AlgebraicVerifier.kt rename to test-utils/src/commonMain/kotlin/AlgebraicVerifier.kt diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/FieldVerifier.kt b/test-utils/src/commonMain/kotlin/FieldVerifier.kt similarity index 96% rename from kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/FieldVerifier.kt rename to test-utils/src/commonMain/kotlin/FieldVerifier.kt index 20a7b6a72..f4ca7506b 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/FieldVerifier.kt +++ b/test-utils/src/commonMain/kotlin/FieldVerifier.kt @@ -10,7 +10,7 @@ import space.kscience.kmath.operations.invoke import kotlin.test.assertEquals import kotlin.test.assertNotEquals -internal class FieldVerifier>( +public class FieldVerifier>( algebra: A, a: T, b: T, c: T, x: Number, ) : RingVerifier(algebra, a, b, c, x) { diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/RingVerifier.kt b/test-utils/src/commonMain/kotlin/RingVerifier.kt similarity index 95% rename from kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/RingVerifier.kt rename to test-utils/src/commonMain/kotlin/RingVerifier.kt index daf18834a..14606cb2c 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/RingVerifier.kt +++ b/test-utils/src/commonMain/kotlin/RingVerifier.kt @@ -10,7 +10,7 @@ import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.invoke import kotlin.test.assertEquals -internal open class RingVerifier(algebra: A, a: T, b: T, c: T, x: Number) : +public open class RingVerifier(algebra: A, a: T, b: T, c: T, x: Number) : SpaceVerifier(algebra, a, b, c, x) where A : Ring, A : ScaleOperations { override fun verify() { diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/SpaceVerifier.kt b/test-utils/src/commonMain/kotlin/SpaceVerifier.kt similarity index 93% rename from kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/SpaceVerifier.kt rename to test-utils/src/commonMain/kotlin/SpaceVerifier.kt index 951197fc6..d761a3775 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/testutils/SpaceVerifier.kt +++ b/test-utils/src/commonMain/kotlin/SpaceVerifier.kt @@ -11,12 +11,12 @@ import space.kscience.kmath.operations.invoke import kotlin.test.assertEquals import kotlin.test.assertNotEquals -internal open class SpaceVerifier( +public open class SpaceVerifier( override val algebra: S, - val a: T, - val b: T, - val c: T, - val x: Number, + public val a: T, + public val b: T, + public val c: T, + public val x: Number, ) : AlgebraicVerifier> where S : Ring, S : ScaleOperations { override fun verify() { algebra { diff --git a/test-utils/src/commonMain/kotlin/asserts.kt b/test-utils/src/commonMain/kotlin/asserts.kt new file mode 100644 index 000000000..8e7d1ae23 --- /dev/null +++ b/test-utils/src/commonMain/kotlin/asserts.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.testutils + +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.indices +import kotlin.test.assertEquals +import kotlin.test.fail + +public fun assertBufferEquals(expected: Buffer, result: Buffer, tolerance: Double = 1e-4) { + if (expected.size != result.size) { + fail("Expected size is ${expected.size}, but the result size is ${result.size}") + } + expected.indices.forEach { + assertEquals(expected[it], result[it], tolerance) + } +} \ No newline at end of file