diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f39e12a12..9a9f04621 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,9 +13,11 @@ jobs: - name: Checkout the repo uses: actions/checkout@v2 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: DeLaGuardo/setup-graalvm@4.0 with: - java-version: 11 + graalvm: 21.1.0 + java: java11 + arch: amd64 - name: Add msys to path if: matrix.os == 'windows-latest' run: SETX PATH "%PATH%;C:\msys64\mingw64\bin" diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 110537a10..86fdac6a6 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -16,9 +16,7 @@ jobs: with: java-version: 11 - name: Build - run: | - ./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace - mv build/dokka/htmlMultiModule/-modules.html build/dokka/htmlMultiModule/index.html + run: ./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace - name: Deploy to GitHub Pages uses: JamesIves/github-pages-deploy-action@4.1.0 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ca374574e..c5c110e89 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,9 +18,11 @@ jobs: - name: Checkout the repo uses: actions/checkout@v2 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: DeLaGuardo/setup-graalvm@4.0 with: - java-version: 11 + graalvm: 21.1.0 + java: java11 + arch: amd64 - name: Add msys to path if: matrix.os == 'windows-latest' run: SETX PATH "%PATH%;C:\msys64\mingw64\bin" diff --git a/.gitignore b/.gitignore index 2a13b9e3c..d6c4af4e3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ out/ .idea/ !.idea/copyright/ +!.idea/scopes/ .vscode/ diff --git a/.idea/copyright/kmath.xml b/.idea/copyright/kmath.xml index 6fe438777..17e44e4d0 100644 --- a/.idea/copyright/kmath.xml +++ b/.idea/copyright/kmath.xml @@ -1,6 +1,6 @@ - - - \ No newline at end of file + + + diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml index 6cc25cb4a..b538bdf41 100644 --- a/.idea/copyright/profiles_settings.xml +++ b/.idea/copyright/profiles_settings.xml @@ -1,7 +1,21 @@ - + + + + + + + + + \ No newline at end of file diff --git a/.idea/scopes/Apply_copyright.xml b/.idea/scopes/Apply_copyright.xml new file mode 100644 index 000000000..0eb589133 --- /dev/null +++ b/.idea/scopes/Apply_copyright.xml @@ -0,0 +1,4 @@ + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index c3bd2641a..286511327 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - Blocking chains and Statistics - Multiplatform integration - Integration for any Field element +- Extended operations for ND4J fields +- Jupyter Notebook integration module (kmath-jupyter) ### Changed - Exponential operations merged with hyperbolic functions @@ -22,6 +24,8 @@ - Redesign advanced Chain API - Redesign MST. Remove MSTExpression. - Move MST to core +- Separated benchmarks and examples +- Rewritten EJML module without ejml-simple ### Deprecated @@ -30,6 +34,8 @@ - Number multiplication and division in main Algebra chain - `contentEquals` from Buffer. It moved to the companion. - MSTExpression +- Expression algebra builders +- Comples and Quaternion no longer are elements. ### Fixed - Ring inherits RingOperations, not GroupOperations diff --git a/README.md b/README.md index 0210b4caf..c8da21b64 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,12 @@ KMath is a modular library. Different modules provide different features with di
+* ### [benchmarks](benchmarks) +> +> +> **Maturity**: EXPERIMENTAL +
+ * ### [examples](examples) > > @@ -85,15 +91,13 @@ KMath is a modular library. Different modules provide different features with di * ### [kmath-ast](kmath-ast) > > -> **Maturity**: PROTOTYPE +> **Maturity**: EXPERIMENTAL > > **Features:** -> - [expression-language](kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser -> - [mst](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt) : MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation -> - [mst-building](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure -> - [mst-interpreter](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt) : MST interpreter +> - [expression-language](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser > - [mst-jvm-codegen](kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler > - [mst-js-codegen](kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler +> - [rendering](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt) : Extendable MST rendering
@@ -150,9 +154,9 @@ performance calculations to code generation. > **Maturity**: PROTOTYPE > > **Features:** -> - [ejml-vector](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : The Point implementation using SimpleMatrix. -> - [ejml-matrix](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : The Matrix implementation using SimpleMatrix. -> - [ejml-linear-space](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : The LinearSpace implementation using SimpleMatrix. +> - [ejml-vector](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : Point implementations. +> - [ejml-matrix](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : Matrix implementation. +> - [ejml-linear-space](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : LinearSpace implementations.
@@ -196,6 +200,12 @@ One can still use generic algebras though. > **Maturity**: PROTOTYPE
+* ### [kmath-jupyter](kmath-jupyter) +> +> +> **Maturity**: PROTOTYPE +
+ * ### [kmath-kotlingrad](kmath-kotlingrad) > > @@ -226,6 +236,18 @@ One can still use generic algebras though. > **Maturity**: EXPERIMENTAL
+* ### [kmath-tensors](kmath-tensors) +> +> +> **Maturity**: PROTOTYPE +> +> **Features:** +> - [tensor algebra](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt) : Basic linear algebra operations on tensors (plus, dot, etc.) +> - [tensor algebra with broadcasting](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/BroadcastDoubleTensorAlgebra.kt) : Basic linear algebra operations implemented with broadcasting. +> - [linear algebra operations](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt) : Advanced linear algebra operations like LU decomposition, SVD, etc. + +
+ * ### [kmath-viktor](kmath-viktor) > > @@ -266,8 +288,8 @@ repositories { } dependencies { - api("space.kscience:kmath-core:0.3.0-dev-6") - // api("space.kscience:kmath-core-jvm:0.3.0-dev-6") for jvm-specific version + api("space.kscience:kmath-core:0.3.0-dev-8") + // api("space.kscience:kmath-core-jvm:0.3.0-dev-8") for jvm-specific version } ``` diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts new file mode 100644 index 000000000..98ffc5a96 --- /dev/null +++ b/benchmarks/build.gradle.kts @@ -0,0 +1,126 @@ +plugins { + kotlin("multiplatform") + kotlin("plugin.allopen") + id("org.jetbrains.kotlinx.benchmark") +} + +allOpen.annotation("org.openjdk.jmh.annotations.State") +sourceSets.register("benchmarks") + +repositories { + mavenCentral() + maven("https://repo.kotlin.link") + maven("https://clojars.org/repo") + maven("https://jitpack.io") + maven("http://logicrunch.research.it.uu.se/maven") { + isAllowInsecureProtocol = true + } +} + +kotlin { + jvm() + + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":kmath-ast")) + implementation(project(":kmath-core")) + implementation(project(":kmath-coroutines")) + implementation(project(":kmath-complex")) + implementation(project(":kmath-stat")) + implementation(project(":kmath-dimensions")) + implementation(project(":kmath-for-real")) + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.0") + } + } + + val jvmMain by getting { + dependencies { + implementation(project(":kmath-commons")) + implementation(project(":kmath-ejml")) + implementation(project(":kmath-nd4j")) + implementation(project(":kmath-kotlingrad")) + implementation(project(":kmath-viktor")) + implementation("org.nd4j:nd4j-native:1.0.0-beta7") + + // uncomment if your system supports AVX2 + // val os = System.getProperty("os.name") + // + // if (System.getProperty("os.arch") in arrayOf("x86_64", "amd64")) when { + // os.startsWith("Windows") -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:windows-x86_64-avx2") + // os == "Linux" -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:linux-x86_64-avx2") + // os == "Mac OS X" -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:macosx-x86_64-avx2") + // } else + // implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") + } + } + } +} + +// Configure benchmark +benchmark { + // Setup configurations + targets { + register("jvm") + } + + fun kotlinx.benchmark.gradle.BenchmarkConfiguration.commonConfiguration() { + warmups = 1 + iterations = 5 + iterationTime = 1000 + iterationTimeUnit = "ms" + } + + configurations.register("buffer") { + commonConfiguration() + include("BufferBenchmark") + } + + configurations.register("dot") { + commonConfiguration() + include("DotBenchmark") + } + + configurations.register("expressions") { + commonConfiguration() + include("ExpressionsInterpretersBenchmark") + } + + configurations.register("matrixInverse") { + commonConfiguration() + include("MatrixInverseBenchmark") + } + + configurations.register("bigInt") { + commonConfiguration() + include("BigIntBenchmark") + } +} + +// Fix kotlinx-benchmarks bug +afterEvaluate { + val jvmBenchmarkJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } +} + + +kotlin.sourceSets.all { + with(languageSettings) { + useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") + useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes") + useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI") + } +} + +tasks.withType { + kotlinOptions { + jvmTarget = "11" + freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" + } +} + + +readme { + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL +} diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ArrayBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ArrayBenchmark.kt similarity index 100% rename from examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ArrayBenchmark.kt rename to benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ArrayBenchmark.kt diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt similarity index 58% rename from examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt rename to benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt index 672efd5c2..2076aedc7 100644 --- a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt @@ -5,19 +5,26 @@ package space.kscience.kmath.benchmarks + import kotlinx.benchmark.Blackhole import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State +import space.kscience.kmath.operations.BigInt import space.kscience.kmath.operations.BigIntField import space.kscience.kmath.operations.JBigIntegerField import space.kscience.kmath.operations.invoke +private fun BigInt.pow(power: Int): BigInt = modPow(BigIntField.number(power), BigInt.ZERO) + @State(Scope.Benchmark) internal class BigIntBenchmark { val kmNumber = BigIntField.number(Int.MAX_VALUE) val jvmNumber = JBigIntegerField.number(Int.MAX_VALUE) + val largeKmNumber = BigIntField { number(11).pow(100_000) } + val largeJvmNumber = JBigIntegerField { number(11).pow(100_000) } + val bigExponent = 50_000 @Benchmark fun kmAdd(blackhole: Blackhole) = BigIntField { @@ -34,8 +41,28 @@ internal class BigIntBenchmark { blackhole.consume(kmNumber * kmNumber * kmNumber) } + @Benchmark + fun kmMultiplyLarge(blackhole: Blackhole) = BigIntField { + blackhole.consume(largeKmNumber*largeKmNumber) + } + @Benchmark fun jvmMultiply(blackhole: Blackhole) = JBigIntegerField { blackhole.consume(jvmNumber * jvmNumber * jvmNumber) } -} \ No newline at end of file + + @Benchmark + fun jvmMultiplyLarge(blackhole: Blackhole) = JBigIntegerField { + blackhole.consume(largeJvmNumber*largeJvmNumber) + } + +// @Benchmark +// fun kmPower(blackhole: Blackhole) = BigIntField { +// blackhole.consume(kmNumber.pow(bigExponent)) +// } +// +// @Benchmark +// fun jvmPower(blackhole: Blackhole) = JBigIntegerField { +// blackhole.consume(jvmNumber.pow(bigExponent)) +// } +} diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt similarity index 100% rename from examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt rename to benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt similarity index 88% rename from examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt rename to benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt index 23e73cb5f..2c5a03a97 100644 --- a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt @@ -10,7 +10,7 @@ import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Scope import kotlinx.benchmark.State import space.kscience.kmath.commons.linear.CMLinearSpace -import space.kscience.kmath.ejml.EjmlLinearSpace +import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.invoke import space.kscience.kmath.operations.DoubleField @@ -29,8 +29,8 @@ internal class DotBenchmark { val cmMatrix1 = CMLinearSpace { matrix1.toCM() } val cmMatrix2 = CMLinearSpace { matrix2.toCM() } - val ejmlMatrix1 = EjmlLinearSpace { matrix1.toEjml() } - val ejmlMatrix2 = EjmlLinearSpace { matrix2.toEjml() } + val ejmlMatrix1 = EjmlLinearSpaceDDRM { matrix1.toEjml() } + val ejmlMatrix2 = EjmlLinearSpaceDDRM { matrix2.toEjml() } } @Benchmark @@ -42,14 +42,14 @@ internal class DotBenchmark { @Benchmark fun ejmlDot(blackhole: Blackhole) { - EjmlLinearSpace { + EjmlLinearSpaceDDRM { blackhole.consume(ejmlMatrix1 dot ejmlMatrix2) } } @Benchmark fun ejmlDotWithConversion(blackhole: Blackhole) { - EjmlLinearSpace { + EjmlLinearSpaceDDRM { blackhole.consume(matrix1 dot matrix2) } } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt new file mode 100644 index 000000000..942fba308 --- /dev/null +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt @@ -0,0 +1,66 @@ +/* + * 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.benchmarks + +import kotlinx.benchmark.Benchmark +import kotlinx.benchmark.Blackhole +import kotlinx.benchmark.Scope +import kotlinx.benchmark.State +import space.kscience.kmath.asm.compileToExpression +import space.kscience.kmath.expressions.* +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.symbol +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.bindSymbol +import space.kscience.kmath.operations.invoke +import kotlin.random.Random + +@State(Scope.Benchmark) +internal class ExpressionsInterpretersBenchmark { + @Benchmark + fun functionalExpression(blackhole: Blackhole) = invokeAndSum(functional, blackhole) + + @Benchmark + fun mstExpression(blackhole: Blackhole) = invokeAndSum(mst, blackhole) + + @Benchmark + fun asmExpression(blackhole: Blackhole) = invokeAndSum(asm, blackhole) + + @Benchmark + fun rawExpression(blackhole: Blackhole) = invokeAndSum(raw, blackhole) + + private fun invokeAndSum(expr: Expression, blackhole: Blackhole) { + val random = Random(0) + var sum = 0.0 + + repeat(times) { + sum += expr(x to random.nextDouble()) + } + + blackhole.consume(sum) + } + + private companion object { + private val x: Symbol by symbol + private val algebra: DoubleField = DoubleField + private const val times = 1_000_000 + + private val functional: Expression = DoubleField.expressionInExtendedField { + bindSymbol(x) * number(2.0) + number(2.0) / bindSymbol(x) - number(16.0) / sin(bindSymbol(x)) + } + + private val node = MstExtendedField { + bindSymbol(x) * 2.0 + number(2.0) / bindSymbol(x) - number(16.0) / sin(bindSymbol(x)) + } + + private val mst: Expression = node.toExpression(DoubleField) + private val asm: Expression = node.compileToExpression(DoubleField) + + private val raw: Expression = Expression { args -> + args.getValue(x) * 2.0 + 2.0 / args.getValue(x) - 16.0 / kotlin.math.sin(args.getValue(x)) + } + } +} diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt similarity index 63% rename from examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt rename to benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt index d1803e389..7bb32af28 100644 --- a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt @@ -11,25 +11,26 @@ import kotlinx.benchmark.Scope import kotlinx.benchmark.State import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.commons.linear.inverse -import space.kscience.kmath.ejml.EjmlLinearSpace -import space.kscience.kmath.ejml.inverse +import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM +import space.kscience.kmath.linear.InverseMatrixFeature import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.inverseWithLup import space.kscience.kmath.linear.invoke +import space.kscience.kmath.nd.getFeature import kotlin.random.Random @State(Scope.Benchmark) internal class MatrixInverseBenchmark { - companion object { - val random = Random(1224) - const val dim = 100 + private companion object { + private val random = Random(1224) + private const val dim = 100 private val space = LinearSpace.real //creating invertible matrix - val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } - val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 } - val matrix = space { l dot u } + private val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } + private val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 } + private val matrix = space { l dot u } } @Benchmark @@ -46,8 +47,8 @@ internal class MatrixInverseBenchmark { @Benchmark fun ejmlInverse(blackhole: Blackhole) { - with(EjmlLinearSpace) { - blackhole.consume(inverse(matrix)) + with(EjmlLinearSpaceDDRM) { + blackhole.consume(matrix.getFeature>()?.inverse) } } } diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt similarity index 100% rename from examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt rename to benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt similarity index 100% rename from examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt rename to benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt similarity index 100% rename from examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt rename to benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt diff --git a/build.gradle.kts b/build.gradle.kts index 9b2200cb4..406a46810 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,53 +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. - */ - -import org.jetbrains.dokka.gradle.DokkaTask -import java.net.URL - plugins { id("ru.mipt.npm.gradle.project") + kotlin("jupyter.api") apply false } allprojects { repositories { - jcenter() maven("https://clojars.org/repo") - maven("https://dl.bintray.com/egor-bogomolov/astminer/") - maven("https://dl.bintray.com/hotkeytlt/maven") maven("https://jitpack.io") - maven{ - setUrl("http://logicrunch.research.it.uu.se/maven/") + maven("http://logicrunch.research.it.uu.se/maven") { isAllowInsecureProtocol = true } + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") mavenCentral() } group = "space.kscience" - version = "0.3.0-dev-6" + version = "0.3.0-dev-8" } subprojects { if (name.startsWith("kmath")) apply() afterEvaluate { - tasks.withType { - dokkaSourceSets.all { - val readmeFile = File(this@subprojects.projectDir, "./README.md") - if (readmeFile.exists()) - includes.setFrom(includes + readmeFile.absolutePath) + tasks.withType { + dependsOn(tasks.getByName("assemble")) - arrayOf( - "http://ejml.org/javadoc/", - "https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/", - "https://deeplearning4j.org/api/latest/" - ).map { URL("${it}package-list") to URL(it) }.forEach { (a, b) -> - externalDocumentationLink { - packageListUrl.set(a) - url.set(b) - } - } + dokkaSourceSets.all { + val readmeFile = File(this@subprojects.projectDir, "README.md") + if (readmeFile.exists()) includes.setFrom(includes + readmeFile.absolutePath) + externalDocumentationLink("http://ejml.org/javadoc/") + externalDocumentationLink("https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/") + externalDocumentationLink("https://deeplearning4j.org/api/latest/") + externalDocumentationLink("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/") } } } diff --git a/docs/templates/ARTIFACT-TEMPLATE.md b/docs/templates/ARTIFACT-TEMPLATE.md index 01d9c51da..1bac2a8ff 100644 --- a/docs/templates/ARTIFACT-TEMPLATE.md +++ b/docs/templates/ARTIFACT-TEMPLATE.md @@ -6,8 +6,7 @@ The Maven coordinates of this project are `${group}:${name}:${version}`. ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { @@ -18,8 +17,7 @@ dependencies { ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 98d7b7073..1c7caf1b9 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -1,36 +1,16 @@ -/* - * 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. - */ - -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("jvm") - kotlin("plugin.allopen") - id("org.jetbrains.kotlinx.benchmark") } -allOpen.annotation("org.openjdk.jmh.annotations.State") -sourceSets.register("benchmarks") - repositories { - jcenter() + mavenCentral() maven("https://repo.kotlin.link") maven("https://clojars.org/repo") - maven("https://dl.bintray.com/egor-bogomolov/astminer/") - maven("https://dl.bintray.com/hotkeytlt/maven") - maven("https://dl.bintray.com/kotlin/kotlin-eap") - maven("https://dl.bintray.com/kotlin/kotlinx") - maven("https://dl.bintray.com/mipt-npm/dev") - maven("https://dl.bintray.com/mipt-npm/kscience") maven("https://jitpack.io") - maven{ - setUrl("http://logicrunch.research.it.uu.se/maven/") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers") + maven("http://logicrunch.research.it.uu.se/maven") { isAllowInsecureProtocol = true } - mavenCentral() } dependencies { @@ -45,10 +25,10 @@ dependencies { implementation(project(":kmath-dimensions")) implementation(project(":kmath-ejml")) implementation(project(":kmath-nd4j")) + implementation(project(":kmath-tensors")) implementation(project(":kmath-for-real")) - implementation("org.deeplearning4j:deeplearning4j-core:1.0.0-beta7") implementation("org.nd4j:nd4j-native:1.0.0-beta7") // uncomment if your system supports AVX2 @@ -61,62 +41,9 @@ dependencies { // } else implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") - implementation("org.jetbrains.kotlinx:kotlinx-io:0.2.0-npm-dev-11") - implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.0") implementation("org.slf4j:slf4j-simple:1.7.30") - // plotting - implementation("kscience.plotlykt:plotlykt-server:0.3.1-dev") - - "benchmarksImplementation"("org.jetbrains.kotlinx:kotlinx.benchmark.runtime-jvm:0.2.0-dev-20") - "benchmarksImplementation"(sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath) -} - -// Configure benchmark -benchmark { - // Setup configurations - targets.register("benchmarks") - // This one matches sourceSet name above - - configurations.register("buffer") { - warmups = 1 // 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 - include("BufferBenchmark") - } - - configurations.register("dot") { - warmups = 1 // 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 - include("DotBenchmark") - } - - configurations.register("expressions") { - warmups = 1 // 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 - include("ExpressionsInterpretersBenchmark") - } - - configurations.register("matrixInverse") { - warmups = 1 // 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 - include("MatrixInverseBenchmark") - } - - configurations.register("bigInt") { - warmups = 1 // 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 - include("BigIntBenchmark") - } + implementation("space.kscience:plotlykt-server:0.4.0-dev-2") } kotlin.sourceSets.all { @@ -127,7 +54,7 @@ kotlin.sourceSets.all { } } -tasks.withType { +tasks.withType { kotlinOptions{ jvmTarget = "11" freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" @@ -135,5 +62,5 @@ tasks.withType { } readme { - maturity = Maturity.EXPERIMENTAL + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL } diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt b/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt deleted file mode 100644 index d6fde8398..000000000 --- a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt +++ /dev/null @@ -1,77 +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. - */ - -package space.kscience.kmath.benchmarks - -import kotlinx.benchmark.Benchmark -import kotlinx.benchmark.Blackhole -import kotlinx.benchmark.Scope -import kotlinx.benchmark.State -import space.kscience.kmath.asm.compileToExpression -import space.kscience.kmath.expressions.* -import space.kscience.kmath.misc.symbol -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.bindSymbol -import space.kscience.kmath.operations.invoke -import kotlin.random.Random - -@State(Scope.Benchmark) -internal class ExpressionsInterpretersBenchmark { - @Benchmark - fun functionalExpression(blackhole: Blackhole) { - val expr = algebra.expressionInField { - val x = bindSymbol(x) - x * const(2.0) + const(2.0) / x - const(16.0) - } - - invokeAndSum(expr, blackhole) - } - - @Benchmark - fun mstExpression(blackhole: Blackhole) { - val expr = MstField { - val x = bindSymbol(x) - x * 2.0 + number(2.0) / x - 16.0 - }.toExpression(algebra) - - invokeAndSum(expr, blackhole) - } - - @Benchmark - fun asmExpression(blackhole: Blackhole) { - val expr = MstField { - val x = bindSymbol(x) - x * 2.0 + number(2.0) / x - 16.0 - }.compileToExpression(algebra) - - invokeAndSum(expr, blackhole) - } - - @Benchmark - fun rawExpression(blackhole: Blackhole) { - val expr = Expression { args -> - val x = args.getValue(x) - x * 2.0 + 2.0 / x - 16.0 - } - - invokeAndSum(expr, blackhole) - } - - private fun invokeAndSum(expr: Expression, blackhole: Blackhole) { - val random = Random(0) - var sum = 0.0 - - repeat(1000000) { - sum += expr(x to random.nextDouble()) - } - - blackhole.consume(sum) - } - - private companion object { - private val algebra = DoubleField - private val x by symbol - } -} diff --git a/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt b/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt index 4a31f33a3..25f42f5a9 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt @@ -25,5 +25,5 @@ fun main() { val expectedDerivative = "2*x-4".parseMath().compileToExpression(DoubleField) - assert(actualDerivative("x" to 123.0) == expectedDerivative("x" to 123.0)) + assert(actualDerivative(x to 123.0) == expectedDerivative(x to 123.0)) } diff --git a/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt b/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt index be4dc461b..028985260 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt @@ -7,9 +7,6 @@ package space.kscience.kmath.commons.fit import kotlinx.html.br import kotlinx.html.h3 -import kscience.plotly.* -import kscience.plotly.models.ScatterMode -import kscience.plotly.models.TraceValues import space.kscience.kmath.commons.optimization.chiSquared import space.kscience.kmath.commons.optimization.minimize import space.kscience.kmath.distributions.NormalDistribution @@ -22,6 +19,9 @@ import space.kscience.kmath.real.step import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.structures.asIterable import space.kscience.kmath.structures.toList +import space.kscience.plotly.* +import space.kscience.plotly.models.ScatterMode +import space.kscience.plotly.models.TraceValues import kotlin.math.pow import kotlin.math.sqrt diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt index 90542adf4..6990e8c8f 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt @@ -1,3 +1,8 @@ +/* + * 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.functions import space.kscience.kmath.integration.integrate diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt index bd431c22c..8020df8f6 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/matrixIntegration.kt @@ -1,3 +1,8 @@ +/* + * 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.functions import space.kscience.kmath.integration.integrate diff --git a/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt b/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt index f7b284e89..a01ea7fe2 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt @@ -22,8 +22,8 @@ fun main() { return DoubleBuffer(x.size) { i -> val h = sigma[i] / 5 val dVector = DoubleBuffer(x.size) { if (it == i) h else 0.0 } - val f1 = invoke(x + dVector / 2) - val f0 = invoke(x - dVector / 2) + val f1 = this(x + dVector / 2) + val f0 = this(x - dVector / 2) (f1 - f0) / h } } diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt index cc1f5f680..501bf98db 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.structures +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import org.nd4j.linalg.factory.Nd4j import space.kscience.kmath.nd.* @@ -22,6 +23,7 @@ internal inline fun measureAndPrint(title: String, block: () -> Unit) { println("$title completed in $time millis") } +@OptIn(DelicateCoroutinesApi::class) fun main() { // initializing Nd4j Nd4j.zeros(0) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/DataSetNormalization.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/DataSetNormalization.kt new file mode 100644 index 000000000..6fbf16a91 --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/DataSetNormalization.kt @@ -0,0 +1,46 @@ +/* + * 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.tensors + +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra + + +// Dataset normalization + +fun main() { + + // work in context with broadcast methods + BroadcastDoubleTensorAlgebra { + // take dataset of 5-element vectors from normal distribution + val dataset = randomNormal(intArrayOf(100, 5)) * 1.5 // all elements from N(0, 1.5) + + dataset += fromArray( + intArrayOf(5), + doubleArrayOf(0.0, 1.0, 1.5, 3.0, 5.0) // rows means + ) + + + // find out mean and standard deviation of each column + val mean = dataset.mean(0, false) + val std = dataset.std(0, false) + + println("Mean:\n$mean") + println("Standard deviation:\n$std") + + // also we can calculate other statistic as minimum and maximum of rows + println("Minimum:\n${dataset.min(0, false)}") + println("Maximum:\n${dataset.max(0, false)}") + + // now we can scale dataset with mean normalization + val datasetScaled = (dataset - mean) / std + + // find out mean and std of scaled dataset + + println("Mean of scaled:\n${datasetScaled.mean(0, false)}") + println("Mean of scaled:\n${datasetScaled.std(0, false)}") + } +} \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LinearSystemSolvingWithLUP.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LinearSystemSolvingWithLUP.kt new file mode 100644 index 000000000..78370b517 --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LinearSystemSolvingWithLUP.kt @@ -0,0 +1,97 @@ +/* + * 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.tensors + +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.DoubleTensor +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra + +// solving linear system with LUP decomposition + +fun main () { + + // work in context with linear operations + BroadcastDoubleTensorAlgebra { + + // set true value of x + val trueX = fromArray( + intArrayOf(4), + doubleArrayOf(-2.0, 1.5, 6.8, -2.4) + ) + + // and A matrix + val a = fromArray( + intArrayOf(4, 4), + doubleArrayOf( + 0.5, 10.5, 4.5, 1.0, + 8.5, 0.9, 12.8, 0.1, + 5.56, 9.19, 7.62, 5.45, + 1.0, 2.0, -3.0, -2.5 + ) + ) + + // calculate y value + val b = a dot trueX + + // check out A and b + println("A:\n$a") + println("b:\n$b") + + // solve `Ax = b` system using LUP decomposition + + // get P, L, U such that PA = LU + val (p, l, u) = a.lu() + + // check that P is permutation matrix + println("P:\n$p") + // L is lower triangular matrix and U is upper triangular matrix + println("L:\n$l") + println("U:\n$u") + // and PA = LU + println("PA:\n${p dot a}") + println("LU:\n${l dot u}") + + /* Ax = b; + PAx = Pb; + LUx = Pb; + let y = Ux, then + Ly = Pb -- this system can be easily solved, since the matrix L is lower triangular; + Ux = y can be solved the same way, since the matrix L is upper triangular + */ + + + + // this function returns solution x of a system lx = b, l should be lower triangular + fun solveLT(l: DoubleTensor, b: DoubleTensor): DoubleTensor { + val n = l.shape[0] + val x = zeros(intArrayOf(n)) + for (i in 0 until n){ + x[intArrayOf(i)] = (b[intArrayOf(i)] - l[i].dot(x).value()) / l[intArrayOf(i, i)] + } + return x + } + + val y = solveLT(l, p dot b) + + // solveLT(l, b) function can be easily adapted for upper triangular matrix by the permutation matrix revMat + // create it by placing ones on side diagonal + val revMat = u.zeroesLike() + val n = revMat.shape[0] + for (i in 0 until n) { + revMat[intArrayOf(i, n - 1 - i)] = 1.0 + } + + // solution of system ux = b, u should be upper triangular + fun solveUT(u: DoubleTensor, b: DoubleTensor): DoubleTensor = revMat dot solveLT( + revMat dot u dot revMat, revMat dot b + ) + + val x = solveUT(u, y) + + println("True x:\n$trueX") + println("x founded with LU method:\n$x") + } +} \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/NeuralNetwork.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/NeuralNetwork.kt new file mode 100644 index 000000000..5b3c2e1cd --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/NeuralNetwork.kt @@ -0,0 +1,241 @@ +/* + * 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.tensors + +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra +import space.kscience.kmath.tensors.core.DoubleTensor +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.tensors.core.toDoubleArray +import kotlin.math.sqrt + +const val seed = 100500L + +// Simple feedforward neural network with backpropagation training + +// interface of network layer +interface Layer { + fun forward(input: DoubleTensor): DoubleTensor + fun backward(input: DoubleTensor, outputError: DoubleTensor): DoubleTensor +} + +// activation layer +open class Activation( + val activation: (DoubleTensor) -> DoubleTensor, + val activationDer: (DoubleTensor) -> DoubleTensor +) : Layer { + override fun forward(input: DoubleTensor): DoubleTensor { + return activation(input) + } + + override fun backward(input: DoubleTensor, outputError: DoubleTensor): DoubleTensor { + return DoubleTensorAlgebra { outputError * activationDer(input) } + } +} + +fun relu(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra { + x.map { if (it > 0) it else 0.0 } +} + +fun reluDer(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra { + x.map { if (it > 0) 1.0 else 0.0 } +} + +// activation layer with relu activator +class ReLU : Activation(::relu, ::reluDer) + +fun sigmoid(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra { + 1.0 / (1.0 + (-x).exp()) +} + +fun sigmoidDer(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra { + sigmoid(x) * (1.0 - sigmoid(x)) +} + +// activation layer with sigmoid activator +class Sigmoid : Activation(::sigmoid, ::sigmoidDer) + +// dense layer +class Dense( + private val inputUnits: Int, + private val outputUnits: Int, + private val learningRate: Double = 0.1 +) : Layer { + + private val weights: DoubleTensor = DoubleTensorAlgebra { + randomNormal( + intArrayOf(inputUnits, outputUnits), + seed + ) * sqrt(2.0 / (inputUnits + outputUnits)) + } + + private val bias: DoubleTensor = DoubleTensorAlgebra { zeros(intArrayOf(outputUnits)) } + + override fun forward(input: DoubleTensor): DoubleTensor { + return BroadcastDoubleTensorAlgebra { (input dot weights) + bias } + } + + override fun backward(input: DoubleTensor, outputError: DoubleTensor): DoubleTensor = DoubleTensorAlgebra { + val gradInput = outputError dot weights.transpose() + + val gradW = input.transpose() dot outputError + val gradBias = outputError.mean(dim = 0, keepDim = false) * input.shape[0].toDouble() + + weights -= learningRate * gradW + bias -= learningRate * gradBias + + gradInput + } + +} + +// simple accuracy equal to the proportion of correct answers +fun accuracy(yPred: DoubleTensor, yTrue: DoubleTensor): Double { + check(yPred.shape contentEquals yTrue.shape) + val n = yPred.shape[0] + var correctCnt = 0 + for (i in 0 until n) { + if (yPred[intArrayOf(i, 0)] == yTrue[intArrayOf(i, 0)]) { + correctCnt += 1 + } + } + return correctCnt.toDouble() / n.toDouble() +} + +// neural network class +@OptIn(ExperimentalStdlibApi::class) +class NeuralNetwork(private val layers: List) { + private fun softMaxLoss(yPred: DoubleTensor, yTrue: DoubleTensor): DoubleTensor = BroadcastDoubleTensorAlgebra { + + val onesForAnswers = yPred.zeroesLike() + yTrue.toDoubleArray().forEachIndexed { index, labelDouble -> + val label = labelDouble.toInt() + onesForAnswers[intArrayOf(index, label)] = 1.0 + } + + val softmaxValue = yPred.exp() / yPred.exp().sum(dim = 1, keepDim = true) + + (-onesForAnswers + softmaxValue) / (yPred.shape[0].toDouble()) + } + + + private fun forward(x: DoubleTensor): List { + var input = x + + return buildList { + layers.forEach { layer -> + val output = layer.forward(input) + add(output) + input = output + } + } + } + + private fun train(xTrain: DoubleTensor, yTrain: DoubleTensor) { + val layerInputs = buildList { + add(xTrain) + addAll(forward(xTrain)) + } + + var lossGrad = softMaxLoss(layerInputs.last(), yTrain) + + layers.zip(layerInputs).reversed().forEach { (layer, input) -> + lossGrad = layer.backward(input, lossGrad) + } + } + + fun fit(xTrain: DoubleTensor, yTrain: DoubleTensor, batchSize: Int, epochs: Int) = DoubleTensorAlgebra { + fun iterBatch(x: DoubleTensor, y: DoubleTensor): Sequence> = sequence { + val n = x.shape[0] + val shuffledIndices = (0 until n).shuffled() + for (i in 0 until n step batchSize) { + val excerptIndices = shuffledIndices.drop(i).take(batchSize).toIntArray() + val batch = x.rowsByIndices(excerptIndices) to y.rowsByIndices(excerptIndices) + yield(batch) + } + } + + for (epoch in 0 until epochs) { + println("Epoch ${epoch + 1}/$epochs") + for ((xBatch, yBatch) in iterBatch(xTrain, yTrain)) { + train(xBatch, yBatch) + } + println("Accuracy:${accuracy(yTrain, predict(xTrain).argMax(1, true))}") + } + } + + fun predict(x: DoubleTensor): DoubleTensor { + return forward(x).last() + } + +} + + +@OptIn(ExperimentalStdlibApi::class) +fun main() { + BroadcastDoubleTensorAlgebra { + val features = 5 + val sampleSize = 250 + val trainSize = 180 + //val testSize = sampleSize - trainSize + + // take sample of features from normal distribution + val x = randomNormal(intArrayOf(sampleSize, features), seed) * 2.5 + + x += fromArray( + intArrayOf(5), + doubleArrayOf(0.0, -1.0, -2.5, -3.0, 5.5) // rows means + ) + + + // define class like '1' if the sum of features > 0 and '0' otherwise + val y = fromArray( + intArrayOf(sampleSize, 1), + DoubleArray(sampleSize) { i -> + if (x[i].sum() > 0.0) { + 1.0 + } else { + 0.0 + } + } + ) + + // split train ans test + val trainIndices = (0 until trainSize).toList().toIntArray() + val testIndices = (trainSize until sampleSize).toList().toIntArray() + + val xTrain = x.rowsByIndices(trainIndices) + val yTrain = y.rowsByIndices(trainIndices) + + val xTest = x.rowsByIndices(testIndices) + val yTest = y.rowsByIndices(testIndices) + + // build model + val layers = buildList { + add(Dense(features, 64)) + add(ReLU()) + add(Dense(64, 16)) + add(ReLU()) + add(Dense(16, 2)) + add(Sigmoid()) + } + val model = NeuralNetwork(layers) + + // fit it with train data + model.fit(xTrain, yTrain, batchSize = 20, epochs = 10) + + // make prediction + val prediction = model.predict(xTest) + + // process raw prediction via argMax + val predictionLabels = prediction.argMax(1, true) + + // find out accuracy + val acc = accuracy(yTest, predictionLabels) + println("Test accuracy:$acc") + + } +} diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/OLSWithSVD.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/OLSWithSVD.kt new file mode 100644 index 000000000..b42602988 --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/OLSWithSVD.kt @@ -0,0 +1,68 @@ +/* + * 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.tensors + +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.DoubleTensor +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra + +import kotlin.math.abs + +// OLS estimator using SVD + +fun main() { + //seed for random + val randSeed = 100500L + + // work in context with linear operations + DoubleTensorAlgebra { + // take coefficient vector from normal distribution + val alpha = randomNormal( + intArrayOf(5), + randSeed + ) + fromArray( + intArrayOf(5), + doubleArrayOf(1.0, 2.5, 3.4, 5.0, 10.1) + ) + + println("Real alpha:\n$alpha") + + // also take sample of size 20 from normal distribution for x + val x = randomNormal( + intArrayOf(20, 5), + randSeed + ) + + // calculate y and add gaussian noise (N(0, 0.05)) + val y = x dot alpha + y += y.randomNormalLike(randSeed) * 0.05 + + // now restore the coefficient vector with OSL estimator with SVD + val (u, singValues, v) = x.svd() + + // we have to make sure the singular values of the matrix are not close to zero + println("Singular values:\n$singValues") + + + // inverse Sigma matrix can be restored from singular values with diagonalEmbedding function + val sigma = diagonalEmbedding(singValues.map{ if (abs(it) < 1e-3) 0.0 else 1.0/it }) + + val alphaOLS = v dot sigma dot u.transpose() dot y + println("Estimated alpha:\n" + + "$alphaOLS") + + // figure out MSE of approximation + fun mse(yTrue: DoubleTensor, yPred: DoubleTensor): Double { + require(yTrue.shape.size == 1) + require(yTrue.shape contentEquals yPred.shape) + + val diff = yTrue - yPred + return diff.dot(diff).sqrt().value() + } + + println("MSE: ${mse(alpha, alphaOLS)}") + } +} \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/PCA.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/PCA.kt new file mode 100644 index 000000000..f8ac13d3f --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/PCA.kt @@ -0,0 +1,78 @@ +/* + * 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.tensors + +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra + + +// simple PCA + +fun main(){ + val seed = 100500L + + // work in context with broadcast methods + BroadcastDoubleTensorAlgebra { + + // assume x is range from 0 until 10 + val x = fromArray( + intArrayOf(10), + (0 until 10).toList().map { it.toDouble() }.toDoubleArray() + ) + + // take y dependent on x with noise + val y = 2.0 * x + (3.0 + x.randomNormalLike(seed) * 1.5) + + println("x:\n$x") + println("y:\n$y") + + // stack them into single dataset + val dataset = stack(listOf(x, y)).transpose() + + // normalize both x and y + val xMean = x.mean() + val yMean = y.mean() + + val xStd = x.std() + val yStd = y.std() + + val xScaled = (x - xMean) / xStd + val yScaled = (y - yMean) / yStd + + // save means ans standard deviations for further recovery + val mean = fromArray( + intArrayOf(2), + doubleArrayOf(xMean, yMean) + ) + println("Means:\n$mean") + + val std = fromArray( + intArrayOf(2), + doubleArrayOf(xStd, yStd) + ) + println("Standard deviations:\n$std") + + // calculate the covariance matrix of scaled x and y + val covMatrix = cov(listOf(xScaled, yScaled)) + println("Covariance matrix:\n$covMatrix") + + // and find out eigenvector of it + val (_, evecs) = covMatrix.symEig() + val v = evecs[0] + println("Eigenvector:\n$v") + + // reduce dimension of dataset + val datasetReduced = v dot stack(listOf(xScaled, yScaled)) + println("Reduced data:\n$datasetReduced") + + // we can restore original data from reduced data. + // for example, find 7th element of dataset + val n = 7 + val restored = (datasetReduced[n] dot v.view(intArrayOf(1, 2))) * std + mean + println("Original value:\n${dataset[n]}") + println("Restored value:\n$restored") + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 442d9132e..f371643ee 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.8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 6ad9eb930..4f906e0c8 100755 --- a/gradlew +++ b/gradlew @@ -1,8 +1,19 @@ #!/usr/bin/env sh # -# 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. +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. # ############################################################################## diff --git a/kmath-ast/README.md b/kmath-ast/README.md index b1ef9c7d3..646ab4306 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -1,41 +1,37 @@ # Module kmath-ast -Abstract syntax tree expression representation and related optimizations. +Performance and visualization extensions to MST API. - - [expression-language](src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser - - [mst](src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt) : MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation - - [mst-building](src/commonMain/kotlin/space/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure - - [mst-interpreter](src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt) : MST interpreter + - [expression-language](src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser - [mst-jvm-codegen](src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler - [mst-js-codegen](src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler + - [rendering](src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt) : Extendable MST rendering ## Artifact: -The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-6`. +The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-8`. **Gradle:** ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { - implementation 'space.kscience:kmath-ast:0.3.0-dev-6' + implementation 'space.kscience:kmath-ast:0.3.0-dev-8' } ``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { - implementation("space.kscience:kmath-ast:0.3.0-dev-6") + implementation("space.kscience:kmath-ast:0.3.0-dev-8") } ``` @@ -43,21 +39,26 @@ dependencies { ### On JVM -`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds -a special implementation of `Expression` with implemented `invoke` function. +`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a +special implementation of `Expression` with implemented `invoke` function. For example, the following builder: ```kotlin -DoubleField.mstInField { symbol("x") + 2 }.compile() +import space.kscience.kmath.expressions.* +import space.kscience.kmath.operations.* +import space.kscience.kmath.asm.* + +MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) ``` -… leads to generation of bytecode, which can be decompiled to the following Java class: +... leads to generation of bytecode, which can be decompiled to the following Java class: ```java package space.kscience.kmath.asm.generated; import java.util.Map; + import kotlin.jvm.functions.Function2; import space.kscience.kmath.asm.internal.MapIntrinsics; import space.kscience.kmath.expressions.Expression; @@ -67,7 +68,7 @@ public final class AsmCompiledExpression_45045_0 implements Expression { private final Object[] constants; public final Double invoke(Map arguments) { - return (Double)((Function2)this.constants[0]).invoke((Double)MapIntrinsics.getOrFail(arguments, "x"), 2); + return (Double) ((Function2) this.constants[0]).invoke((Double) MapIntrinsics.getOrFail(arguments, "x"), 2); } public AsmCompiledExpression_45045_0(Object[] constants) { @@ -77,19 +78,10 @@ public final class AsmCompiledExpression_45045_0 implements Expression { ``` -### Example Usage - -This API extends MST and MstExpression, so you may optimize as both of them: - -```kotlin -DoubleField.mstInField { symbol("x") + 2 }.compile() -DoubleField.expression("x+2".parseMath()) -``` - #### Known issues -- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid - class loading overhead. +- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid class + loading overhead. - This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders. ### On JS @@ -97,33 +89,62 @@ DoubleField.expression("x+2".parseMath()) A similar feature is also available on JS. ```kotlin -DoubleField.mstInField { symbol("x") + 2 }.compile() +import space.kscience.kmath.expressions.* +import space.kscience.kmath.operations.* +import space.kscience.kmath.estree.* + +MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) ``` The code above returns expression implemented with such a JS function: ```js var executable = function (constants, arguments) { - return constants[1](constants[0](arguments, "x"), 2); + return constants[1](constants[0](arguments, "x"), 2); }; ``` +JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation. +Currently, only expressions inside `DoubleField` and `IntRing` are supported. + +```kotlin +import space.kscience.kmath.expressions.* +import space.kscience.kmath.operations.* +import space.kscience.kmath.wasm.* + +MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) +``` + +An example of emitted Wasm IR in the form of WAT: + +```lisp +(func $executable (param $0 f64) (result f64) + (f64.add + (local.get $0) + (f64.const 2) + ) +) +``` + #### Known issues -- This feature uses `eval` which can be unavailable in several environments. +- ESTree expression compilation uses `eval` which can be unavailable in several environments. +- WebAssembly isn't supported by old versions of browsers (see https://webassembly.org/roadmap/). ## Rendering expressions -kmath-ast also includes an extensible engine to display expressions in LaTeX or MathML syntax. +kmath-ast also includes an extensible engine to display expressions in LaTeX or MathML syntax. Example usage: ```kotlin import space.kscience.kmath.ast.* import space.kscience.kmath.ast.rendering.* +import space.kscience.kmath.misc.* +@OptIn(UnstableKMathAPI::class) public fun main() { - val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath() + val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(12)+x^(2/3)".parseMath() val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst) val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax) println("LaTeX:") @@ -135,15 +156,80 @@ public fun main() { } ``` -Result LaTeX: +Result LaTeX: -![](http://chart.googleapis.com/chart?cht=tx&chl=e%5E%7B%5Csqrt%7Bx%7D%7D-%5Cfrac%7B%5Cfrac%7B%5Coperatorname%7Bsin%7D%5E%7B-1%7D%5C,%5Cleft(2%5C,x%5Cright)%7D%7B2%5Ctimes10%5E%7B10%7D%2Bx%5E%7B3%7D%7D%7D%7B-12%7D) +![](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}) -Result MathML (embedding MathML is not allowed by GitHub Markdown): +Result MathML (can be used with MathJax or other renderers): + +
```html -ex-sin-12x2×1010+x3-12 + + + exp + + + + x + + + - + + + + + arcsin + + + 2 + + x + + + + 2 + × + + + 10 + + + 10 + + + + + + + x + + + 3 + + + + + + + 12 + + + + + + + x + + + 2 + / + 3 + + + + ``` -It is also possible to create custom algorithms of render, and even add support of other markup languages +
+ +It is also possible to create custom algorithms of render, and even add support of other markup languages (see API reference). diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 3f309b67c..508374d82 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -1,10 +1,3 @@ -/* - * 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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") @@ -25,8 +18,13 @@ kotlin.js { } kotlin.sourceSets { + filter { it.name.contains("test", true) } + .map(org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet::languageSettings) + .forEach { it.useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI") } + commonMain { dependencies { + api("com.github.h0tk3y.betterParse:better-parse:0.4.2") api(project(":kmath-core")) } } @@ -39,13 +37,15 @@ kotlin.sourceSets { jsMain { dependencies { - implementation(npm("astring", "1.7.0")) + implementation(npm("astring", "1.7.4")) + implementation(npm("binaryen", "100.0")) + implementation(npm("js-base64", "3.6.0")) + implementation(npm("webassembly", "0.11.0")) } } jvmMain { dependencies { - api("com.github.h0tk3y.betterParse:better-parse:0.4.1") implementation("org.ow2.asm:asm:9.1") implementation("org.ow2.asm:asm-commons:9.1") } @@ -58,31 +58,13 @@ tasks.dokkaHtml { } readme { - maturity = Maturity.PROTOTYPE + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) feature( id = "expression-language", description = "Expression language and its parser", - ref = "src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt" - ) - - feature( - id = "mst", - description = "MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation", - ref = "src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt" - ) - - feature( - id = "mst-building", - description = "MST building algebraic structure", - ref = "src/commonMain/kotlin/space/kscience/kmath/ast/MstAlgebra.kt" - ) - - feature( - id = "mst-interpreter", - description = "MST interpreter", - ref = "src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt" + ref = "src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt" ) feature( @@ -96,4 +78,10 @@ readme { description = "Dynamic MST to JS compiler", ref = "src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt" ) + + feature( + id = "rendering", + description = "Extendable MST rendering", + ref = "src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt" + ) } diff --git a/kmath-ast/docs/README-TEMPLATE.md b/kmath-ast/docs/README-TEMPLATE.md index 9ed44d584..80ea31642 100644 --- a/kmath-ast/docs/README-TEMPLATE.md +++ b/kmath-ast/docs/README-TEMPLATE.md @@ -1,6 +1,6 @@ # Module kmath-ast -Abstract syntax tree expression representation and related optimizations. +Performance and visualization extensions to MST API. ${features} @@ -10,21 +10,26 @@ ${artifact} ### On JVM -`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds -a special implementation of `Expression` with implemented `invoke` function. +`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a +special implementation of `Expression` with implemented `invoke` function. For example, the following builder: ```kotlin -DoubleField.mstInField { symbol("x") + 2 }.compile() +import space.kscience.kmath.expressions.* +import space.kscience.kmath.operations.* +import space.kscience.kmath.asm.* + +MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) ``` -… leads to generation of bytecode, which can be decompiled to the following Java class: +... leads to generation of bytecode, which can be decompiled to the following Java class: ```java package space.kscience.kmath.asm.generated; import java.util.Map; + import kotlin.jvm.functions.Function2; import space.kscience.kmath.asm.internal.MapIntrinsics; import space.kscience.kmath.expressions.Expression; @@ -34,7 +39,7 @@ public final class AsmCompiledExpression_45045_0 implements Expression { private final Object[] constants; public final Double invoke(Map arguments) { - return (Double)((Function2)this.constants[0]).invoke((Double)MapIntrinsics.getOrFail(arguments, "x"), 2); + return (Double) ((Function2) this.constants[0]).invoke((Double) MapIntrinsics.getOrFail(arguments, "x"), 2); } public AsmCompiledExpression_45045_0(Object[] constants) { @@ -44,19 +49,10 @@ public final class AsmCompiledExpression_45045_0 implements Expression { ``` -### Example Usage - -This API extends MST and MstExpression, so you may optimize as both of them: - -```kotlin -DoubleField.mstInField { symbol("x") + 2 }.compile() -DoubleField.expression("x+2".parseMath()) -``` - #### Known issues -- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid - class loading overhead. +- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid class + loading overhead. - This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders. ### On JS @@ -64,33 +60,62 @@ DoubleField.expression("x+2".parseMath()) A similar feature is also available on JS. ```kotlin -DoubleField.mstInField { symbol("x") + 2 }.compile() +import space.kscience.kmath.expressions.* +import space.kscience.kmath.operations.* +import space.kscience.kmath.estree.* + +MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) ``` The code above returns expression implemented with such a JS function: ```js var executable = function (constants, arguments) { - return constants[1](constants[0](arguments, "x"), 2); + return constants[1](constants[0](arguments, "x"), 2); }; ``` +JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation. +Currently, only expressions inside `DoubleField` and `IntRing` are supported. + +```kotlin +import space.kscience.kmath.expressions.* +import space.kscience.kmath.operations.* +import space.kscience.kmath.wasm.* + +MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) +``` + +An example of emitted Wasm IR in the form of WAT: + +```lisp +(func \$executable (param \$0 f64) (result f64) + (f64.add + (local.get \$0) + (f64.const 2) + ) +) +``` + #### Known issues -- This feature uses `eval` which can be unavailable in several environments. +- ESTree expression compilation uses `eval` which can be unavailable in several environments. +- WebAssembly isn't supported by old versions of browsers (see https://webassembly.org/roadmap/). ## Rendering expressions -kmath-ast also includes an extensible engine to display expressions in LaTeX or MathML syntax. +kmath-ast also includes an extensible engine to display expressions in LaTeX or MathML syntax. Example usage: ```kotlin import space.kscience.kmath.ast.* import space.kscience.kmath.ast.rendering.* +import space.kscience.kmath.misc.* +@OptIn(UnstableKMathAPI::class) public fun main() { - val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath() + val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(12)+x^(2/3)".parseMath() val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst) val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax) println("LaTeX:") @@ -102,15 +127,80 @@ public fun main() { } ``` -Result LaTeX: +Result LaTeX: -![](http://chart.googleapis.com/chart?cht=tx&chl=e%5E%7B%5Csqrt%7Bx%7D%7D-%5Cfrac%7B%5Cfrac%7B%5Coperatorname%7Bsin%7D%5E%7B-1%7D%5C,%5Cleft(2%5C,x%5Cright)%7D%7B2%5Ctimes10%5E%7B10%7D%2Bx%5E%7B3%7D%7D%7D%7B-12%7D) +![](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}) -Result MathML (embedding MathML is not allowed by GitHub Markdown): +Result MathML (can be used with MathJax or other renderers): + +
```html -ex-sin-12x2×1010+x3-12 + + + exp + + + + x + + + - + + + + + arcsin + + + 2 + + x + + + + 2 + × + + + 10 + + + 10 + + + + + + + x + + + 3 + + + + + + + 12 + + + + + + + x + + + 2 + / + 3 + + + + ``` -It is also possible to create custom algorithms of render, and even add support of other markup languages +
+ +It is also possible to create custom algorithms of render, and even add support of other markup languages (see API reference). diff --git a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt similarity index 96% rename from kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt rename to kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt index 025f4984c..246625d29 100644 --- a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt @@ -3,8 +3,6 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -// TODO move to common when https://github.com/h0tk3y/better-parse/pull/37 is merged - package space.kscience.kmath.ast import com.github.h0tk3y.betterParse.combinators.* @@ -31,7 +29,6 @@ import space.kscience.kmath.operations.RingOperations * @author Iaroslav Postovalov */ public object ArithmeticsEvaluator : Grammar() { - // TODO replace with "...".toRegex() when better-parse 0.4.1 is released private val num: Token by regexToken("[\\d.]+(?:[eE][-+]?\\d+)?".toRegex()) private val id: Token by regexToken("[a-z_A-Z][\\da-z_A-Z]*".toRegex()) private val lpar: Token by literalToken("(") diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/LatexSyntaxRenderer.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/LatexSyntaxRenderer.kt index 34230f3c8..01717b0f9 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/LatexSyntaxRenderer.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/LatexSyntaxRenderer.kt @@ -5,6 +5,8 @@ package space.kscience.kmath.ast.rendering +import space.kscience.kmath.misc.UnstableKMathAPI + /** * [SyntaxRenderer] implementation for LaTeX. * @@ -23,6 +25,7 @@ package space.kscience.kmath.ast.rendering * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public object LatexSyntaxRenderer : SyntaxRenderer { public override fun render(node: MathSyntax, output: Appendable): Unit = output.run { fun render(syntax: MathSyntax) = render(syntax, output) @@ -71,6 +74,15 @@ public object LatexSyntaxRenderer : SyntaxRenderer { append('}') } + is ExponentSyntax -> if (node.useOperatorForm) { + append("\\operatorname{exp}\\,") + render(node.operand) + } else { + append("e^{") + render(node.operand) + append('}') + } + is SuperscriptSyntax -> { render(node.left) append("^{") @@ -106,7 +118,11 @@ public object LatexSyntaxRenderer : SyntaxRenderer { render(node.right) } - is FractionSyntax -> { + is FractionSyntax -> if (node.infix) { + render(node.left) + append('/') + render(node.right) + } else { append("\\frac{") render(node.left) append("}{") diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathMLSyntaxRenderer.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathMLSyntaxRenderer.kt index f7487a356..cda8e2322 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathMLSyntaxRenderer.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathMLSyntaxRenderer.kt @@ -5,6 +5,8 @@ package space.kscience.kmath.ast.rendering +import space.kscience.kmath.misc.UnstableKMathAPI + /** * [SyntaxRenderer] implementation for MathML. * @@ -12,14 +14,18 @@ package space.kscience.kmath.ast.rendering * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public object MathMLSyntaxRenderer : SyntaxRenderer { public override fun render(node: MathSyntax, output: Appendable) { - output.append("") - render0(node, output) + output.append("") + renderPart(node, output) output.append("") } - private fun render0(node: MathSyntax, output: Appendable): Unit = output.run { + /** + * Renders a part of syntax returning a correct MathML tag not the whole MathML instance. + */ + public fun renderPart(node: MathSyntax, output: Appendable): Unit = output.run { fun tag(tagName: String, vararg attr: Pair, block: () -> Unit = {}) { append('<') append(tagName) @@ -44,7 +50,7 @@ public object MathMLSyntaxRenderer : SyntaxRenderer { append('>') } - fun render(syntax: MathSyntax) = render0(syntax, output) + fun render(syntax: MathSyntax) = renderPart(syntax, output) when (node) { is NumberSyntax -> tag("mn") { append(node.string) } @@ -82,6 +88,19 @@ public object MathMLSyntaxRenderer : SyntaxRenderer { is RadicalSyntax -> tag("msqrt") { render(node.operand) } + is ExponentSyntax -> if (node.useOperatorForm) { + tag("mo") { append("exp") } + tag("mspace", "width" to "0.167em") + render(node.operand) + } else { + tag("msup") { + tag("mrow") { + tag("mi") { append("e") } + } + tag("mrow") { render(node.operand) } + } + } + is SuperscriptSyntax -> tag("msup") { tag("mrow") { render(node.left) } tag("mrow") { render(node.right) } @@ -114,14 +133,13 @@ public object MathMLSyntaxRenderer : SyntaxRenderer { render(node.right) } - is FractionSyntax -> tag("mfrac") { - tag("mrow") { - render(node.left) - } - - tag("mrow") { - render(node.right) - } + is FractionSyntax -> if (node.infix) { + render(node.left) + tag("mo") { append('/') } + render(node.right) + } else tag("mfrac") { + tag("mrow") { render(node.left) } + tag("mrow") { render(node.right) } } is RadicalWithIndexSyntax -> tag("mroot") { diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt index 891e8f05b..c33f95483 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt @@ -6,12 +6,14 @@ package space.kscience.kmath.ast.rendering import space.kscience.kmath.expressions.MST +import space.kscience.kmath.misc.UnstableKMathAPI /** * Renders [MST] to [MathSyntax]. * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public fun interface MathRenderer { /** * Renders [MST] to [MathSyntax]. @@ -25,6 +27,7 @@ public fun interface MathRenderer { * @property features The applied features. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public open class FeaturedMathRenderer(public val features: List) : MathRenderer { public override fun render(mst: MST): MathSyntax { for (feature in features) feature.render(this, mst)?.let { return it } @@ -48,6 +51,7 @@ public open class FeaturedMathRenderer(public val features: List) * @property stages The applied stages. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public open class FeaturedMathRendererWithPostProcess( features: List, public val stages: List, @@ -83,8 +87,9 @@ public open class FeaturedMathRendererWithPostProcess( Fraction.Default, Power.Default, SquareRoot.Default, - Exponential.Default, + Exponent.Default, InverseTrigonometricOperations.Default, + InverseHyperbolicOperations.Default, // Fallback option for unknown operations - printing them as operator BinaryOperator.Default, @@ -100,6 +105,8 @@ public open class FeaturedMathRendererWithPostProcess( PrintSymbolic, ), listOf( + BetterExponent, + BetterFraction, SimplifyParentheses.Default, BetterMultiplication, ), diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathSyntax.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathSyntax.kt index 069e56c71..a71985fbc 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathSyntax.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathSyntax.kt @@ -5,11 +5,14 @@ package space.kscience.kmath.ast.rendering +import space.kscience.kmath.misc.UnstableKMathAPI + /** * Mathematical typography syntax node. * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public sealed class MathSyntax { /** * The parent node of this syntax node. @@ -22,6 +25,7 @@ public sealed class MathSyntax { * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public sealed class TerminalSyntax : MathSyntax() /** @@ -29,6 +33,7 @@ public sealed class TerminalSyntax : MathSyntax() * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public sealed class OperationSyntax : MathSyntax() { /** * The operation token. @@ -41,6 +46,7 @@ public sealed class OperationSyntax : MathSyntax() { * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public sealed class UnarySyntax : OperationSyntax() { /** * The operand of this node. @@ -53,6 +59,7 @@ public sealed class UnarySyntax : OperationSyntax() { * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public sealed class BinarySyntax : OperationSyntax() { /** * The left-hand side operand. @@ -71,6 +78,7 @@ public sealed class BinarySyntax : OperationSyntax() { * @property string The digits of number. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class NumberSyntax(public var string: String) : TerminalSyntax() /** @@ -79,6 +87,7 @@ public data class NumberSyntax(public var string: String) : TerminalSyntax() * @property string The symbol. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class SymbolSyntax(public var string: String) : TerminalSyntax() /** @@ -89,14 +98,16 @@ public data class SymbolSyntax(public var string: String) : TerminalSyntax() * @see UnaryOperatorSyntax * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class OperatorNameSyntax(public var name: String) : TerminalSyntax() /** - * Represents a usage of special symbols. + * Represents a usage of special symbols (e.g., *∞*). * * @property kind The kind of symbol. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class SpecialSymbolSyntax(public var kind: Kind) : TerminalSyntax() { /** * The kind of symbol. @@ -121,6 +132,7 @@ public data class SpecialSymbolSyntax(public var kind: Kind) : TerminalSyntax() * @property parentheses Whether the operand should be wrapped with parentheses. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class OperandSyntax( public val operand: MathSyntax, public var parentheses: Boolean, @@ -131,11 +143,12 @@ public data class OperandSyntax( } /** - * Represents unary, prefix operator syntax (like f x). + * Represents unary, prefix operator syntax (like *f(x)*). * * @property prefix The prefix. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class UnaryOperatorSyntax( public override val operation: String, public var prefix: MathSyntax, @@ -147,10 +160,11 @@ public data class UnaryOperatorSyntax( } /** - * Represents prefix, unary plus operator. + * Represents prefix, unary plus operator (*+x*). * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class UnaryPlusSyntax( public override val operation: String, public override val operand: OperandSyntax, @@ -161,10 +175,11 @@ public data class UnaryPlusSyntax( } /** - * Represents prefix, unary minus operator. + * Represents prefix, unary minus operator (*-x*). * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class UnaryMinusSyntax( public override val operation: String, public override val operand: OperandSyntax, @@ -175,11 +190,12 @@ public data class UnaryMinusSyntax( } /** - * Represents radical with a node inside it. + * Represents radical with a node inside it (*√x*). * * @property operand The radicand. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class RadicalSyntax( public override val operation: String, public override val operand: MathSyntax, @@ -190,12 +206,32 @@ public data class RadicalSyntax( } /** - * Represents a syntax node with superscript (usually, for exponentiation). + * Represents exponential function. + * + * @property operand The argument of function. + * @property useOperatorForm `true` if operator form is used (*exp (x)*), `false` if exponentiation form is used + * (*ex*). + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public data class ExponentSyntax( + public override val operation: String, + public override val operand: OperandSyntax, + public var useOperatorForm: Boolean, +) : UnarySyntax() { + init { + operand.parent = this + } +} + +/** + * Represents a syntax node with superscript (*x2*). * * @property left The node. * @property right The superscript. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class SuperscriptSyntax( public override val operation: String, public override val left: MathSyntax, @@ -208,12 +244,13 @@ public data class SuperscriptSyntax( } /** - * Represents a syntax node with subscript. + * Represents a syntax node with subscript (*xi*). * * @property left The node. * @property right The subscript. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class SubscriptSyntax( public override val operation: String, public override val left: MathSyntax, @@ -226,11 +263,12 @@ public data class SubscriptSyntax( } /** - * Represents binary, prefix operator syntax (like f(a, b)). + * Represents binary, prefix operator syntax (like *f(a, b)*). * * @property prefix The prefix. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class BinaryOperatorSyntax( public override val operation: String, public var prefix: MathSyntax, @@ -244,12 +282,13 @@ public data class BinaryOperatorSyntax( } /** - * Represents binary, infix addition. + * Represents binary, infix addition (*42 + 42*). * * @param left The augend. * @param right The addend. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class BinaryPlusSyntax( public override val operation: String, public override val left: OperandSyntax, @@ -262,12 +301,13 @@ public data class BinaryPlusSyntax( } /** - * Represents binary, infix subtraction. + * Represents binary, infix subtraction (*42 - 42*). * * @param left The minuend. * @param right The subtrahend. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class BinaryMinusSyntax( public override val operation: String, public override val left: OperandSyntax, @@ -284,12 +324,15 @@ public data class BinaryMinusSyntax( * * @property left The numerator. * @property right The denominator. + * @property infix Whether infix (*1 / 2*) or normal (*½*) fraction should be made. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class FractionSyntax( public override val operation: String, - public override val left: MathSyntax, - public override val right: MathSyntax, + public override val left: OperandSyntax, + public override val right: OperandSyntax, + public var infix: Boolean, ) : BinarySyntax() { init { left.parent = this @@ -298,12 +341,13 @@ public data class FractionSyntax( } /** - * Represents radical syntax with index. + * Represents radical syntax with index (*3√x*). * * @property left The index. * @property right The radicand. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class RadicalWithIndexSyntax( public override val operation: String, public override val left: MathSyntax, @@ -316,13 +360,14 @@ public data class RadicalWithIndexSyntax( } /** - * Represents binary, infix multiplication in the form of coefficient (2 x) or with operator (x×2). + * Represents binary, infix multiplication in the form of coefficient (*2 x*) or with operator (*x × 2*). * * @property left The multiplicand. * @property right The multiplier. - * @property times whether the times (×) symbol should be used. + * @property times Whether the times (×) symbol should be used. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public data class MultiplicationSyntax( public override val operation: String, public override val left: OperandSyntax, diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/SyntaxRenderer.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/SyntaxRenderer.kt index 7fa91e158..fb2b3b66f 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/SyntaxRenderer.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/SyntaxRenderer.kt @@ -5,12 +5,15 @@ package space.kscience.kmath.ast.rendering +import space.kscience.kmath.misc.UnstableKMathAPI + /** * Abstraction of writing [MathSyntax] as a string of an actual markup language. Typical implementation should * involve traversal of MathSyntax with handling each its subtype. * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public fun interface SyntaxRenderer { /** * Renders the [MathSyntax] to [output]. @@ -23,6 +26,7 @@ public fun interface SyntaxRenderer { * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public fun SyntaxRenderer.renderWithStringBuilder(node: MathSyntax): String { val sb = StringBuilder() render(node, sb) diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/features.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/features.kt index 5d6c7bb0a..ac716f9ff 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/features.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/features.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.ast.rendering import space.kscience.kmath.ast.rendering.FeaturedMathRenderer.RenderFeature import space.kscience.kmath.expressions.MST +import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.* import kotlin.reflect.KClass @@ -15,11 +16,12 @@ import kotlin.reflect.KClass * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public object PrintSymbolic : RenderFeature { - public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? { - if (node !is MST.Symbolic) return null - return SymbolSyntax(string = node.value) - } + public override fun render(renderer: FeaturedMathRenderer, node: MST): SymbolSyntax? = + if (node !is MST.Symbolic) null + else + SymbolSyntax(string = node.value) } /** @@ -27,39 +29,46 @@ public object PrintSymbolic : RenderFeature { * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public object PrintNumeric : RenderFeature { - public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? { - if (node !is MST.Numeric) return null - return NumberSyntax(string = node.value.toString()) - } + public override fun render(renderer: FeaturedMathRenderer, node: MST): NumberSyntax? = if (node !is MST.Numeric) + null + else + NumberSyntax(string = node.value.toString()) } -private fun printSignedNumberString(s: String): MathSyntax { - if (s.startsWith('-')) - return UnaryMinusSyntax( - operation = GroupOperations.MINUS_OPERATION, - operand = OperandSyntax( - operand = NumberSyntax(string = s.removePrefix("-")), - parentheses = true, - ), - ) - - return NumberSyntax(string = s) -} +@UnstableKMathAPI +private fun printSignedNumberString(s: String): MathSyntax = if (s.startsWith('-')) + UnaryMinusSyntax( + operation = GroupOperations.MINUS_OPERATION, + operand = OperandSyntax( + operand = NumberSyntax(string = s.removePrefix("-")), + parentheses = true, + ), + ) +else + NumberSyntax(string = s) /** * Special printing for numeric types which are printed in form of * *('-'? (DIGIT+ ('.' DIGIT+)? ('E' '-'? DIGIT+)? | 'Infinity')) | 'NaN'*. * * @property types The suitable types. + * @author Iaroslav Postovalov */ +@UnstableKMathAPI public class PrettyPrintFloats(public val types: Set>) : RenderFeature { public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? { if (node !is MST.Numeric || node.value::class !in types) return null - val toString = node.value.toString().removeSuffix(".0") - if ('E' in toString) { - val (beforeE, afterE) = toString.split('E') + val toString = when (val v = node.value) { + is Float -> v.multiplatformToString() + is Double -> v.multiplatformToString() + else -> v.toString() + }.removeSuffix(".0") + + if (toString.contains('E', ignoreCase = true)) { + val (beforeE, afterE) = toString.split('E', ignoreCase = true) val significand = beforeE.toDouble().toString().removeSuffix(".0") val exponent = afterE.toDouble().toString().removeSuffix(".0") @@ -105,14 +114,15 @@ public class PrettyPrintFloats(public val types: Set>) : Rend * Special printing for numeric types which are printed in form of *'-'? DIGIT+*. * * @property types The suitable types. + * @author Iaroslav Postovalov */ +@UnstableKMathAPI public class PrettyPrintIntegers(public val types: Set>) : RenderFeature { - public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? { + public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? = if (node !is MST.Numeric || node.value::class !in types) - return null - - return printSignedNumberString(node.value.toString()) - } + null + else + printSignedNumberString(node.value.toString()) public companion object { /** @@ -127,12 +137,15 @@ public class PrettyPrintIntegers(public val types: Set>) : Re * Special printing for symbols meaning Pi. * * @property symbols The allowed symbols. + * @author Iaroslav Postovalov */ +@UnstableKMathAPI public class PrettyPrintPi(public val symbols: Set) : RenderFeature { - public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? { - if (node !is MST.Symbolic || node.value !in symbols) return null - return SpecialSymbolSyntax(kind = SpecialSymbolSyntax.Kind.SMALL_PI) - } + public override fun render(renderer: FeaturedMathRenderer, node: MST): SpecialSymbolSyntax? = + if (node !is MST.Symbolic || node.value !in symbols) + null + else + SpecialSymbolSyntax(kind = SpecialSymbolSyntax.Kind.SMALL_PI) public companion object { /** @@ -147,17 +160,20 @@ public class PrettyPrintPi(public val symbols: Set) : RenderFeature { * not [MST.Unary]. * * @param operations the allowed operations. If `null`, any operation is accepted. + * @author Iaroslav Postovalov */ +@UnstableKMathAPI public abstract class Unary(public val operations: Collection?) : RenderFeature { /** - * The actual render function. + * The actual render function specialized for [MST.Unary]. */ - protected abstract fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax? + protected abstract fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax? - public final override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? { - if (node !is MST.Unary || operations != null && node.operation !in operations) return null - return render0(renderer, node) - } + public final override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? = + if (node !is MST.Unary || operations != null && node.operation !in operations) + null + else + renderUnary(renderer, node) } /** @@ -165,169 +181,301 @@ public abstract class Unary(public val operations: Collection?) : Render * not [MST.Binary]. * * @property operations the allowed operations. If `null`, any operation is accepted. + * @author Iaroslav Postovalov */ +@UnstableKMathAPI public abstract class Binary(public val operations: Collection?) : RenderFeature { /** - * The actual render function. + * The actual render function specialized for [MST.Binary]. */ - protected abstract fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax? + protected abstract fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax? public final override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? { if (node !is MST.Binary || operations != null && node.operation !in operations) return null - return render0(renderer, node) + return renderBinary(renderer, node) } } +/** + * Handles binary nodes by producing [BinaryPlusSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI public class BinaryPlus(operations: Collection?) : Binary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = BinaryPlusSyntax( - operation = node.operation, - left = OperandSyntax(parent.render(node.left), true), - right = OperandSyntax(parent.render(node.right), true), - ) + public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): BinaryPlusSyntax = + BinaryPlusSyntax( + operation = node.operation, + left = OperandSyntax(parent.render(node.left), true), + right = OperandSyntax(parent.render(node.right), true), + ) public companion object { + /** + * The default instance configured with [GroupOperations.PLUS_OPERATION]. + */ public val Default: BinaryPlus = BinaryPlus(setOf(GroupOperations.PLUS_OPERATION)) } } +/** + * Handles binary nodes by producing [BinaryMinusSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI public class BinaryMinus(operations: Collection?) : Binary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = BinaryMinusSyntax( - operation = node.operation, - left = OperandSyntax(operand = parent.render(node.left), parentheses = true), - right = OperandSyntax(operand = parent.render(node.right), parentheses = true), - ) + public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): BinaryMinusSyntax = + BinaryMinusSyntax( + operation = node.operation, + left = OperandSyntax(operand = parent.render(node.left), parentheses = true), + right = OperandSyntax(operand = parent.render(node.right), parentheses = true), + ) public companion object { + /** + * The default instance configured with [GroupOperations.MINUS_OPERATION]. + */ public val Default: BinaryMinus = BinaryMinus(setOf(GroupOperations.MINUS_OPERATION)) } } +/** + * Handles unary nodes by producing [UnaryPlusSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI public class UnaryPlus(operations: Collection?) : Unary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryPlusSyntax( + public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryPlusSyntax = UnaryPlusSyntax( operation = node.operation, operand = OperandSyntax(operand = parent.render(node.value), parentheses = true), ) public companion object { + /** + * The default instance configured with [GroupOperations.PLUS_OPERATION]. + */ public val Default: UnaryPlus = UnaryPlus(setOf(GroupOperations.PLUS_OPERATION)) } } +/** + * Handles binary nodes by producing [UnaryMinusSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI public class UnaryMinus(operations: Collection?) : Unary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryMinusSyntax( + public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryMinusSyntax = UnaryMinusSyntax( operation = node.operation, operand = OperandSyntax(operand = parent.render(node.value), parentheses = true), ) public companion object { + /** + * The default instance configured with [GroupOperations.MINUS_OPERATION]. + */ public val Default: UnaryMinus = UnaryMinus(setOf(GroupOperations.MINUS_OPERATION)) } } +/** + * Handles binary nodes by producing [FractionSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI public class Fraction(operations: Collection?) : Binary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = FractionSyntax( + public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): FractionSyntax = FractionSyntax( operation = node.operation, - left = parent.render(node.left), - right = parent.render(node.right), + left = OperandSyntax(operand = parent.render(node.left), parentheses = true), + right = OperandSyntax(operand = parent.render(node.right), parentheses = true), + infix = true, ) public companion object { + /** + * The default instance configured with [FieldOperations.DIV_OPERATION]. + */ public val Default: Fraction = Fraction(setOf(FieldOperations.DIV_OPERATION)) } } +/** + * Handles binary nodes by producing [BinaryOperatorSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI public class BinaryOperator(operations: Collection?) : Binary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = BinaryOperatorSyntax( - operation = node.operation, - prefix = OperatorNameSyntax(name = node.operation), - left = parent.render(node.left), - right = parent.render(node.right), - ) + public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): BinaryOperatorSyntax = + BinaryOperatorSyntax( + operation = node.operation, + prefix = OperatorNameSyntax(name = node.operation), + left = parent.render(node.left), + right = parent.render(node.right), + ) public companion object { + /** + * The default instance configured with `null`. + */ public val Default: BinaryOperator = BinaryOperator(null) } } +/** + * Handles unary nodes by producing [UnaryOperatorSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI public class UnaryOperator(operations: Collection?) : Unary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryOperatorSyntax( - operation = node.operation, - prefix = OperatorNameSyntax(node.operation), - operand = OperandSyntax(parent.render(node.value), true), - ) + public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryOperatorSyntax = + UnaryOperatorSyntax( + operation = node.operation, + prefix = OperatorNameSyntax(node.operation), + operand = OperandSyntax(parent.render(node.value), true), + ) public companion object { + /** + * The default instance configured with `null`. + */ public val Default: UnaryOperator = UnaryOperator(null) } } +/** + * Handles binary nodes by producing [SuperscriptSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI public class Power(operations: Collection?) : Binary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = SuperscriptSyntax( - operation = node.operation, - left = OperandSyntax(parent.render(node.left), true), - right = OperandSyntax(parent.render(node.right), true), - ) + public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): SuperscriptSyntax = + SuperscriptSyntax( + operation = node.operation, + left = OperandSyntax(parent.render(node.left), true), + right = OperandSyntax(parent.render(node.right), true), + ) public companion object { + /** + * The default instance configured with [PowerOperations.POW_OPERATION]. + */ public val Default: Power = Power(setOf(PowerOperations.POW_OPERATION)) } } +/** + * Handles binary nodes by producing [RadicalSyntax] with no index. + */ +@UnstableKMathAPI public class SquareRoot(operations: Collection?) : Unary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = + public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): RadicalSyntax = RadicalSyntax(operation = node.operation, operand = parent.render(node.value)) public companion object { + /** + * The default instance configured with [PowerOperations.SQRT_OPERATION]. + */ public val Default: SquareRoot = SquareRoot(setOf(PowerOperations.SQRT_OPERATION)) } } -public class Exponential(operations: Collection?) : Unary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = SuperscriptSyntax( +/** + * Handles unary nodes by producing [ExponentSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public class Exponent(operations: Collection?) : Unary(operations) { + public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): ExponentSyntax = ExponentSyntax( operation = node.operation, - left = SymbolSyntax(string = "e"), - right = parent.render(node.value), - ) - - public companion object { - public val Default: Exponential = Exponential(setOf(ExponentialOperations.EXP_OPERATION)) - } -} - -public class Multiplication(operations: Collection?) : Binary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = MultiplicationSyntax( - operation = node.operation, - left = OperandSyntax(operand = parent.render(node.left), parentheses = true), - right = OperandSyntax(operand = parent.render(node.right), parentheses = true), - times = true, - ) - - public companion object { - public val Default: Multiplication = Multiplication(setOf( - RingOperations.TIMES_OPERATION, - )) - } -} - -public class InverseTrigonometricOperations(operations: Collection?) : Unary(operations) { - public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryOperatorSyntax( - operation = node.operation, - prefix = SuperscriptSyntax( - operation = PowerOperations.POW_OPERATION, - left = OperatorNameSyntax(name = node.operation.removePrefix("a")), - right = UnaryMinusSyntax( - operation = GroupOperations.MINUS_OPERATION, - operand = OperandSyntax(operand = NumberSyntax(string = "1"), parentheses = true), - ), - ), operand = OperandSyntax(operand = parent.render(node.value), parentheses = true), + useOperatorForm = true, ) public companion object { + /** + * The default instance configured with [ExponentialOperations.EXP_OPERATION]. + */ + public val Default: Exponent = Exponent(setOf(ExponentialOperations.EXP_OPERATION)) + } +} + +/** + * Handles binary nodes by producing [MultiplicationSyntax]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public class Multiplication(operations: Collection?) : Binary(operations) { + public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MultiplicationSyntax = + MultiplicationSyntax( + operation = node.operation, + left = OperandSyntax(operand = parent.render(node.left), parentheses = true), + right = OperandSyntax(operand = parent.render(node.right), parentheses = true), + times = true, + ) + + public companion object { + /** + * The default instance configured with [RingOperations.TIMES_OPERATION]. + */ + public val Default: Multiplication = Multiplication(setOf(RingOperations.TIMES_OPERATION)) + } +} + +/** + * Handles binary nodes by producing inverse [UnaryOperatorSyntax] with *arc* prefix instead of *a*. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public class InverseTrigonometricOperations(operations: Collection?) : Unary(operations) { + public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryOperatorSyntax = + UnaryOperatorSyntax( + operation = node.operation, + prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "arc")), + operand = OperandSyntax(operand = parent.render(node.value), parentheses = true), + ) + + public companion object { + /** + * The default instance configured with [TrigonometricOperations.ACOS_OPERATION], + * [TrigonometricOperations.ASIN_OPERATION], [TrigonometricOperations.ATAN_OPERATION]. + */ public val Default: InverseTrigonometricOperations = InverseTrigonometricOperations(setOf( TrigonometricOperations.ACOS_OPERATION, TrigonometricOperations.ASIN_OPERATION, TrigonometricOperations.ATAN_OPERATION, + )) + } +} + +/** + * Handles binary nodes by producing inverse [UnaryOperatorSyntax] with *ar* prefix instead of *a*. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public class InverseHyperbolicOperations(operations: Collection?) : Unary(operations) { + public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryOperatorSyntax = + UnaryOperatorSyntax( + operation = node.operation, + prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "ar")), + operand = OperandSyntax(operand = parent.render(node.value), parentheses = true), + ) + + public companion object { + /** + * The default instance configured with [ExponentialOperations.ACOSH_OPERATION], + * [ExponentialOperations.ASINH_OPERATION], and [ExponentialOperations.ATANH_OPERATION]. + */ + public val Default: InverseHyperbolicOperations = InverseHyperbolicOperations(setOf( ExponentialOperations.ACOSH_OPERATION, ExponentialOperations.ASINH_OPERATION, ExponentialOperations.ATANH_OPERATION, diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/multiplatformToString.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/multiplatformToString.kt new file mode 100644 index 000000000..291399cee --- /dev/null +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/multiplatformToString.kt @@ -0,0 +1,9 @@ +/* + * 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.ast.rendering + +internal expect fun Double.multiplatformToString(): String +internal expect fun Float.multiplatformToString(): String diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/stages.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/stages.kt index 0ebb41b28..1f31af853 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/stages.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/stages.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.ast.rendering +import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.FieldOperations import space.kscience.kmath.operations.GroupOperations import space.kscience.kmath.operations.PowerOperations @@ -15,70 +16,182 @@ import space.kscience.kmath.operations.RingOperations * * @author Iaroslav Postovalov */ +@UnstableKMathAPI public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostProcessStage { - public override fun perform(node: MathSyntax) { - when (node) { - is NumberSyntax -> Unit - is SymbolSyntax -> Unit - is OperatorNameSyntax -> Unit - is SpecialSymbolSyntax -> Unit - is OperandSyntax -> perform(node.operand) + public override fun perform(node: MathSyntax): Unit = when (node) { + is NumberSyntax -> Unit + is SymbolSyntax -> Unit + is OperatorNameSyntax -> Unit + is SpecialSymbolSyntax -> Unit + is OperandSyntax -> perform(node.operand) - is UnaryOperatorSyntax -> { - perform(node.prefix) - perform(node.operand) - } - - is UnaryPlusSyntax -> perform(node.operand) - is UnaryMinusSyntax -> perform(node.operand) - is RadicalSyntax -> perform(node.operand) - - is SuperscriptSyntax -> { - perform(node.left) - perform(node.right) - } - - is SubscriptSyntax -> { - perform(node.left) - perform(node.right) - } - - is BinaryOperatorSyntax -> { - perform(node.prefix) - perform(node.left) - perform(node.right) - } - - is BinaryPlusSyntax -> { - perform(node.left) - perform(node.right) - } - - is BinaryMinusSyntax -> { - perform(node.left) - perform(node.right) - } - - is FractionSyntax -> { - perform(node.left) - perform(node.right) - } - - is RadicalWithIndexSyntax -> { - perform(node.left) - perform(node.right) - } - - is MultiplicationSyntax -> { - node.times = node.right.operand is NumberSyntax && !node.right.parentheses - || node.left.operand is NumberSyntax && node.right.operand is FractionSyntax - || node.left.operand is NumberSyntax && node.right.operand is NumberSyntax - || node.left.operand is NumberSyntax && node.right.operand is SuperscriptSyntax && node.right.operand.left is NumberSyntax - - perform(node.left) - perform(node.right) - } + is UnaryOperatorSyntax -> { + perform(node.prefix) + perform(node.operand) } + + is UnaryPlusSyntax -> perform(node.operand) + is UnaryMinusSyntax -> perform(node.operand) + is RadicalSyntax -> perform(node.operand) + is ExponentSyntax -> perform(node.operand) + + is SuperscriptSyntax -> { + perform(node.left) + perform(node.right) + } + + is SubscriptSyntax -> { + perform(node.left) + perform(node.right) + } + + is BinaryOperatorSyntax -> { + perform(node.prefix) + perform(node.left) + perform(node.right) + } + + is BinaryPlusSyntax -> { + perform(node.left) + perform(node.right) + } + + is BinaryMinusSyntax -> { + perform(node.left) + perform(node.right) + } + + is FractionSyntax -> { + perform(node.left) + perform(node.right) + } + + is RadicalWithIndexSyntax -> { + perform(node.left) + perform(node.right) + } + + is MultiplicationSyntax -> { + node.times = node.right.operand is NumberSyntax && !node.right.parentheses + || node.left.operand is NumberSyntax && node.right.operand is FractionSyntax + || node.left.operand is NumberSyntax && node.right.operand is NumberSyntax + || node.left.operand is NumberSyntax && node.right.operand is SuperscriptSyntax && node.right.operand.left is NumberSyntax + + perform(node.left) + perform(node.right) + } + } +} + +/** + * Chooses [FractionSyntax.infix] depending on the context. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public object BetterFraction : FeaturedMathRendererWithPostProcess.PostProcessStage { + private fun perform0(node: MathSyntax, infix: Boolean = false): Unit = when (node) { + is NumberSyntax -> Unit + is SymbolSyntax -> Unit + is OperatorNameSyntax -> Unit + is SpecialSymbolSyntax -> Unit + is OperandSyntax -> perform0(node.operand, infix) + + is UnaryOperatorSyntax -> { + perform0(node.prefix, infix) + perform0(node.operand, infix) + } + + is UnaryPlusSyntax -> perform0(node.operand, infix) + is UnaryMinusSyntax -> perform0(node.operand, infix) + is RadicalSyntax -> perform0(node.operand, infix) + is ExponentSyntax -> perform0(node.operand, infix) + + is SuperscriptSyntax -> { + perform0(node.left, true) + perform0(node.right, true) + } + + is SubscriptSyntax -> { + perform0(node.left, true) + perform0(node.right, true) + } + + is BinaryOperatorSyntax -> { + perform0(node.prefix, infix) + perform0(node.left, infix) + perform0(node.right, infix) + } + + is BinaryPlusSyntax -> { + perform0(node.left, infix) + perform0(node.right, infix) + } + + is BinaryMinusSyntax -> { + perform0(node.left, infix) + perform0(node.right, infix) + } + + is FractionSyntax -> { + node.infix = infix + perform0(node.left, infix) + perform0(node.right, infix) + } + + is RadicalWithIndexSyntax -> { + perform0(node.left, true) + perform0(node.right, true) + } + + is MultiplicationSyntax -> { + perform0(node.left, infix) + perform0(node.right, infix) + } + } + + public override fun perform(node: MathSyntax): Unit = perform0(node) +} + +/** + * Applies [ExponentSyntax.useOperatorForm] to [ExponentSyntax] when the operand contains a fraction, a + * superscript or a subscript to improve readability. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public object BetterExponent : FeaturedMathRendererWithPostProcess.PostProcessStage { + private fun perform0(node: MathSyntax): Boolean { + return when (node) { + is NumberSyntax -> false + is SymbolSyntax -> false + is OperatorNameSyntax -> false + is SpecialSymbolSyntax -> false + is OperandSyntax -> perform0(node.operand) + is UnaryOperatorSyntax -> perform0(node.prefix) || perform0(node.operand) + is UnaryPlusSyntax -> perform0(node.operand) + is UnaryMinusSyntax -> perform0(node.operand) + is RadicalSyntax -> true + + is ExponentSyntax -> { + val r = perform0(node.operand) + node.useOperatorForm = r + r + } + + is SuperscriptSyntax -> true + is SubscriptSyntax -> true + is BinaryOperatorSyntax -> perform0(node.prefix) || perform0(node.left) || perform0(node.right) + is BinaryPlusSyntax -> perform0(node.left) || perform0(node.right) + is BinaryMinusSyntax -> perform0(node.left) || perform0(node.right) + is FractionSyntax -> true + is RadicalWithIndexSyntax -> true + is MultiplicationSyntax -> perform0(node.left) || perform0(node.right) + } + } + + public override fun perform(node: MathSyntax) { + perform0(node) } } @@ -88,91 +201,95 @@ public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostPro * @property precedenceFunction Returns the precedence number for syntax node. Higher number is lower priority. * @author Iaroslav Postovalov */ +@UnstableKMathAPI public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) -> Int) : FeaturedMathRendererWithPostProcess.PostProcessStage { - public override fun perform(node: MathSyntax) { - when (node) { - is NumberSyntax -> Unit - is SymbolSyntax -> Unit - is OperatorNameSyntax -> Unit - is SpecialSymbolSyntax -> Unit + public override fun perform(node: MathSyntax): Unit = when (node) { + is NumberSyntax -> Unit + is SymbolSyntax -> Unit + is OperatorNameSyntax -> Unit + is SpecialSymbolSyntax -> Unit - is OperandSyntax -> { - val isRightOfSuperscript = - (node.parent is SuperscriptSyntax) && (node.parent as SuperscriptSyntax).right === node + is OperandSyntax -> { + val isRightOfSuperscript = + (node.parent is SuperscriptSyntax) && (node.parent as SuperscriptSyntax).right === node - val precedence = precedenceFunction(node.operand) + val precedence = precedenceFunction(node.operand) - val needParenthesesByPrecedence = when (val parent = node.parent) { - null -> false + val needParenthesesByPrecedence = when (val parent = node.parent) { + null -> false - is BinarySyntax -> { - val parentPrecedence = precedenceFunction(parent) + is BinarySyntax -> { + val parentPrecedence = precedenceFunction(parent) - parentPrecedence < precedence || - parentPrecedence == precedence && parentPrecedence != 0 && node === parent.right - } - - else -> precedence > precedenceFunction(parent) + parentPrecedence < precedence || + parentPrecedence == precedence && parentPrecedence != 0 && node === parent.right } - node.parentheses = !isRightOfSuperscript - && (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax) - - perform(node.operand) + else -> precedence > precedenceFunction(parent) } - is UnaryOperatorSyntax -> { - perform(node.prefix) - perform(node.operand) - } + val isInsideExpOperator = + node.parent is ExponentSyntax && (node.parent as ExponentSyntax).useOperatorForm - is UnaryPlusSyntax -> perform(node.operand) - is UnaryMinusSyntax -> { - perform(node.operand) - } - is RadicalSyntax -> perform(node.operand) + val isOnOrUnderNormalFraction = node.parent is FractionSyntax && !((node.parent as FractionSyntax).infix) - is SuperscriptSyntax -> { - perform(node.left) - perform(node.right) - } + node.parentheses = !isRightOfSuperscript + && (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax || isInsideExpOperator) + && !isOnOrUnderNormalFraction - is SubscriptSyntax -> { - perform(node.left) - perform(node.right) - } + perform(node.operand) + } - is BinaryOperatorSyntax -> { - perform(node.prefix) - perform(node.left) - perform(node.right) - } + is UnaryOperatorSyntax -> { + perform(node.prefix) + perform(node.operand) + } - is BinaryPlusSyntax -> { - perform(node.left) - perform(node.right) - } + is UnaryPlusSyntax -> perform(node.operand) + is UnaryMinusSyntax -> perform(node.operand) + is RadicalSyntax -> perform(node.operand) + is ExponentSyntax -> perform(node.operand) - is BinaryMinusSyntax -> { - perform(node.left) - perform(node.right) - } + is SuperscriptSyntax -> { + perform(node.left) + perform(node.right) + } - is FractionSyntax -> { - perform(node.left) - perform(node.right) - } + is SubscriptSyntax -> { + perform(node.left) + perform(node.right) + } - is MultiplicationSyntax -> { - perform(node.left) - perform(node.right) - } + is BinaryOperatorSyntax -> { + perform(node.prefix) + perform(node.left) + perform(node.right) + } - is RadicalWithIndexSyntax -> { - perform(node.left) - perform(node.right) - } + is BinaryPlusSyntax -> { + perform(node.left) + perform(node.right) + } + + is BinaryMinusSyntax -> { + perform(node.left) + perform(node.right) + } + + is FractionSyntax -> { + perform(node.left) + perform(node.right) + } + + is MultiplicationSyntax -> { + perform(node.left) + perform(node.right) + } + + is RadicalWithIndexSyntax -> { + perform(node.left) + perform(node.right) } } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerConsistencyWithInterpreter.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerConsistencyWithInterpreter.kt new file mode 100644 index 000000000..0d018070c --- /dev/null +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerConsistencyWithInterpreter.kt @@ -0,0 +1,56 @@ +/* + * 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.ast + +import space.kscience.kmath.expressions.MstField +import space.kscience.kmath.expressions.MstRing +import space.kscience.kmath.expressions.interpret +import space.kscience.kmath.misc.Symbol.Companion.x +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.bindSymbol +import space.kscience.kmath.operations.invoke +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class TestCompilerConsistencyWithInterpreter { + @Test + fun intRing() = runCompilerTest { + val mst = MstRing { + binaryOperationFunction("+")( + unaryOperationFunction("+")( + (bindSymbol(x) - (2.toByte() + (scale( + add(number(1), number(1)), + 2.0, + ) + 1.toByte()))) * 3.0 - 1.toByte() + ), + + number(1), + ) * number(2) + } + + assertEquals( + mst.interpret(IntRing, x to 3), + mst.compile(IntRing, x to 3), + ) + } + + @Test + fun doubleField() = runCompilerTest { + val mst = MstField { + +(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( + (3.0 - (bindSymbol(x) + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0 + + number(1), + number(1) / 2 + number(2.0) * one, + ) + zero + } + + assertEquals( + mst.interpret(DoubleField, x to 2.0), + mst.compile(DoubleField, x to 2.0), + ) + } +} diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerOperations.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerOperations.kt new file mode 100644 index 000000000..7d2af31c2 --- /dev/null +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerOperations.kt @@ -0,0 +1,65 @@ +/* + * 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.ast + +import space.kscience.kmath.expressions.MstExtendedField +import space.kscience.kmath.expressions.invoke +import space.kscience.kmath.misc.Symbol.Companion.x +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.bindSymbol +import space.kscience.kmath.operations.invoke +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class TestCompilerOperations { + @Test + fun testUnaryPlus() = runCompilerTest { + val expr = MstExtendedField { +bindSymbol(x) }.compileToExpression(DoubleField) + assertEquals(2.0, expr(x to 2.0)) + } + + @Test + fun testUnaryMinus() = runCompilerTest { + val expr = MstExtendedField { -bindSymbol(x) }.compileToExpression(DoubleField) + assertEquals(-2.0, expr(x to 2.0)) + } + + @Test + fun testAdd() = runCompilerTest { + val expr = MstExtendedField { bindSymbol(x) + bindSymbol(x) }.compileToExpression(DoubleField) + assertEquals(4.0, expr(x to 2.0)) + } + + @Test + fun testSine() = runCompilerTest { + val expr = MstExtendedField { sin(bindSymbol(x)) }.compileToExpression(DoubleField) + assertEquals(0.0, expr(x to 0.0)) + } + + @Test + fun testCosine() = runCompilerTest { + val expr = MstExtendedField { cos(bindSymbol(x)) }.compileToExpression(DoubleField) + assertEquals(1.0, expr(x to 0.0)) + } + + @Test + fun testSubtract() = runCompilerTest { + val expr = MstExtendedField { bindSymbol(x) - bindSymbol(x) }.compileToExpression(DoubleField) + assertEquals(0.0, expr(x to 2.0)) + } + + @Test + fun testDivide() = runCompilerTest { + val expr = MstExtendedField { bindSymbol(x) / bindSymbol(x) }.compileToExpression(DoubleField) + assertEquals(1.0, expr(x to 2.0)) + } + + @Test + fun testPower() = runCompilerTest { + val expr = MstExtendedField { bindSymbol(x) pow 2 }.compileToExpression(DoubleField) + assertEquals(4.0, expr(x to 2.0)) + } +} diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerVariables.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerVariables.kt new file mode 100644 index 000000000..ecf8ed367 --- /dev/null +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerVariables.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.ast + +import space.kscience.kmath.expressions.MstRing +import space.kscience.kmath.expressions.invoke +import space.kscience.kmath.misc.Symbol.Companion.x +import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.bindSymbol +import space.kscience.kmath.operations.invoke +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +internal class TestCompilerVariables { + @Test + fun testVariable() = runCompilerTest { + val expr = MstRing { bindSymbol(x) }.compileToExpression(IntRing) + assertEquals(1, expr(x to 1)) + } + + @Test + fun testUndefinedVariableFails() = runCompilerTest { + val expr = MstRing { bindSymbol(x) }.compileToExpression(IntRing) + assertFailsWith { expr() } + } +} diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/ParserTest.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt similarity index 75% rename from kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/ParserTest.kt rename to kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt index 53afaa674..b838245e1 100644 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/ParserTest.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt @@ -7,31 +7,22 @@ package space.kscience.kmath.ast import space.kscience.kmath.complex.Complex import space.kscience.kmath.complex.ComplexField -import space.kscience.kmath.expressions.MstField import space.kscience.kmath.expressions.evaluate -import space.kscience.kmath.expressions.interpret import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals -internal class ParserTest { +internal class TestParser { @Test - fun `evaluate MST`() { + fun evaluateParsedMst() { val mst = "2+2*(2+2)".parseMath() val res = ComplexField.evaluate(mst) assertEquals(Complex(10.0, 0.0), res) } @Test - fun `evaluate MSTExpression`() { - val res = MstField.invoke { number(2) + number(2) * (number(2) + number(2)) }.interpret(ComplexField) - assertEquals(Complex(10.0, 0.0), res) - } - - @Test - fun `evaluate MST with singular`() { + fun evaluateMstSymbol() { val mst = "i".parseMath() val res = ComplexField.evaluate(mst) assertEquals(ComplexField.i, res) @@ -39,14 +30,14 @@ internal class ParserTest { @Test - fun `evaluate MST with unary function`() { + fun evaluateMstUnary() { val mst = "sin(0)".parseMath() val res = DoubleField.evaluate(mst) assertEquals(0.0, res) } @Test - fun `evaluate MST with binary function`() { + fun evaluateMstBinary() { val magicalAlgebra = object : Algebra { override fun bindSymbolOrNull(value: String): String = value diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/ParserPrecedenceTest.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParserPrecedence.kt similarity index 89% rename from kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/ParserPrecedenceTest.kt rename to kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParserPrecedence.kt index 6273eff27..bb6bb3ce1 100644 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/ParserPrecedenceTest.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParserPrecedence.kt @@ -7,13 +7,10 @@ package space.kscience.kmath.ast import space.kscience.kmath.expressions.evaluate import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.Field import kotlin.test.Test import kotlin.test.assertEquals -internal class ParserPrecedenceTest { - private val f: Field = DoubleField - +internal class TestParserPrecedence { @Test fun test1(): Unit = assertEquals(6.0, f.evaluate("2*2+2".parseMath())) @@ -37,4 +34,8 @@ internal class ParserPrecedenceTest { @Test fun test8(): Unit = assertEquals(18.0, f.evaluate("2*2^3+2".parseMath())) + + private companion object { + private val f = DoubleField + } } diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestFeatures.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestFeatures.kt similarity index 68% rename from kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestFeatures.kt rename to kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestFeatures.kt index 1584293ce..a40c785b9 100644 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestFeatures.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestFeatures.kt @@ -42,6 +42,22 @@ internal class TestFeatures { testLatex(Numeric(1.1e-10), "1.1\\times10^{-10}") testLatex(Numeric(-1.1e-10), "-1.1\\times10^{-10}") testLatex(Numeric(-1.1e10), "-1.1\\times10^{10}") + testLatex(Numeric(0.001), "0.001") + testLatex(Numeric(0.0000001), "1\\times10^{-7}") + + testLatex(Numeric(Float.NaN), "NaN") + testLatex(Numeric(Float.POSITIVE_INFINITY), "\\infty") + testLatex(Numeric(Float.NEGATIVE_INFINITY), "-\\infty") + testLatex(Numeric(1.0f), "1") + testLatex(Numeric(-1.0f), "-1") + testLatex(Numeric(1.42f), "1.42") + testLatex(Numeric(-1.42f), "-1.42") + testLatex(Numeric(1e10f), "1\\times10^{10}") + testLatex(Numeric(1e-10f), "1\\times10^{-10}") + testLatex(Numeric(-1e-10f), "-1\\times10^{-10}") + testLatex(Numeric(-1e10f), "-1\\times10^{10}") + testLatex(Numeric(0.001f), "0.001") + testLatex(Numeric(0.0000001f), "1\\times10^{-7}") } @Test @@ -83,13 +99,17 @@ internal class TestFeatures { fun multiplication() = testLatex("x*1", "x\\times1") @Test - fun inverseTrigonometry() { - testLatex("asin(x)", "\\operatorname{sin}^{-1}\\,\\left(x\\right)") - testLatex("asinh(x)", "\\operatorname{sinh}^{-1}\\,\\left(x\\right)") - testLatex("acos(x)", "\\operatorname{cos}^{-1}\\,\\left(x\\right)") - testLatex("acosh(x)", "\\operatorname{cosh}^{-1}\\,\\left(x\\right)") - testLatex("atan(x)", "\\operatorname{tan}^{-1}\\,\\left(x\\right)") - testLatex("atanh(x)", "\\operatorname{tanh}^{-1}\\,\\left(x\\right)") + fun inverseTrigonometric() { + testLatex("asin(x)", "\\operatorname{arcsin}\\,\\left(x\\right)") + testLatex("acos(x)", "\\operatorname{arccos}\\,\\left(x\\right)") + testLatex("atan(x)", "\\operatorname{arctan}\\,\\left(x\\right)") + } + + @Test + fun inverseHyperbolic() { + testLatex("asinh(x)", "\\operatorname{arsinh}\\,\\left(x\\right)") + testLatex("acosh(x)", "\\operatorname{arcosh}\\,\\left(x\\right)") + testLatex("atanh(x)", "\\operatorname{artanh}\\,\\left(x\\right)") } // @Test diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestLatex.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestLatex.kt similarity index 100% rename from kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestLatex.kt rename to kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestLatex.kt diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestMathML.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestMathML.kt similarity index 100% rename from kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestMathML.kt rename to kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestMathML.kt diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestStages.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestStages.kt similarity index 74% rename from kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestStages.kt rename to kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestStages.kt index a4017fdb4..09ec127c7 100644 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestStages.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestStages.kt @@ -30,4 +30,17 @@ internal class TestStages { testLatex("(x+x)^x+x*x", "\\left(x+x\\right)^{x}+x\\,x") testLatex("x^(x+x)", "x^{x+x}") } + + @Test + fun exponent() { + testLatex("exp(x)", "e^{x}") + testLatex("exp(x/2)", "\\operatorname{exp}\\,\\left(\\frac{x}{2}\\right)") + testLatex("exp(x^2)", "\\operatorname{exp}\\,\\left(x^{2}\\right)") + } + + @Test + fun fraction() { + testLatex("x/y", "\\frac{x}{y}") + testLatex("x^(x/y)", "x^{x/y}") + } } diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestUtils.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestUtils.kt similarity index 82% rename from kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestUtils.kt rename to kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestUtils.kt index 7c9400532..bf87b6fd0 100644 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/rendering/TestUtils.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/rendering/TestUtils.kt @@ -30,17 +30,17 @@ internal object TestUtils { ) internal fun testMathML(mst: MST, expectedMathML: String) = assertEquals( - expected = "$expectedMathML", + expected = "$expectedMathML", actual = mathML(mst), ) internal fun testMathML(expression: String, expectedMathML: String) = assertEquals( - expected = "$expectedMathML", + expected = "$expectedMathML", actual = mathML(expression.parseMath()), ) internal fun testMathML(expression: MathSyntax, expectedMathML: String) = assertEquals( - expected = "$expectedMathML", + expected = "$expectedMathML", actual = MathMLSyntaxRenderer.renderWithStringBuilder(expression), ) } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt new file mode 100644 index 000000000..abeaed0f8 --- /dev/null +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -0,0 +1,25 @@ +/* + * 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.ast + +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.expressions.MST +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.IntRing + +internal interface CompilerTestContext { + fun MST.compileToExpression(algebra: IntRing): Expression + fun MST.compile(algebra: IntRing, arguments: Map): Int + fun MST.compile(algebra: IntRing, vararg arguments: Pair): Int = compile(algebra, mapOf(*arguments)) + fun MST.compileToExpression(algebra: DoubleField): Expression + fun MST.compile(algebra: DoubleField, arguments: Map): Double + + fun MST.compile(algebra: DoubleField, vararg arguments: Pair): Double = + compile(algebra, mapOf(*arguments)) +} + +internal expect inline fun runCompilerTest(action: CompilerTestContext.() -> Unit) diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/ast/rendering/multiplatformToString.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/ast/rendering/multiplatformToString.kt new file mode 100644 index 000000000..521907d2c --- /dev/null +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/ast/rendering/multiplatformToString.kt @@ -0,0 +1,18 @@ +/* + * 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.ast.rendering + +internal actual fun Double.multiplatformToString(): String { + val d = this + if (d >= 1e7 || d <= -1e7) return js("d.toExponential()") as String + return toString() +} + +internal actual fun Float.multiplatformToString(): String { + val d = this + if (d >= 1e7f || d <= -1e7f) return js("d.toExponential()") as String + return toString() +} 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 83914f3ec..40468f5ab 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 @@ -6,11 +6,11 @@ package space.kscience.kmath.estree import space.kscience.kmath.estree.internal.ESTreeBuilder -import space.kscience.kmath.estree.internal.estree.BaseExpression import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.MST import space.kscience.kmath.expressions.MST.* import space.kscience.kmath.expressions.invoke +import space.kscience.kmath.internal.estree.BaseExpression import space.kscience.kmath.misc.Symbol import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.NumericAlgebra 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 6f917a24c..ac20484a4 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 @@ -5,9 +5,14 @@ package space.kscience.kmath.estree.internal -import space.kscience.kmath.estree.internal.astring.generate -import space.kscience.kmath.estree.internal.estree.* import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.internal.astring.generate +import space.kscience.kmath.internal.estree.* +import space.kscience.kmath.internal.estree.BaseExpression +import space.kscience.kmath.internal.estree.BlockStatement +import space.kscience.kmath.internal.estree.Program +import space.kscience.kmath.internal.estree.VariableDeclaration +import space.kscience.kmath.internal.estree.VariableDeclarator import space.kscience.kmath.misc.Symbol internal class ESTreeBuilder(val bodyCallback: ESTreeBuilder.() -> BaseExpression) { diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/astring/astring.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/astring/astring.kt similarity index 90% rename from kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/astring/astring.kt rename to kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/astring/astring.kt index 354757b83..cca2d83af 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/astring/astring.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/astring/astring.kt @@ -6,9 +6,9 @@ @file:JsModule("astring") @file:JsNonModule -package space.kscience.kmath.estree.internal.astring +package space.kscience.kmath.internal.astring -import space.kscience.kmath.estree.internal.estree.BaseNode +import space.kscience.kmath.internal.estree.BaseNode internal external interface Options { var indent: String? diff --git a/license/COPYRIGHT_HEADER.txt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/astring/astring.typealises.kt similarity index 65% rename from license/COPYRIGHT_HEADER.txt rename to kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/astring/astring.typealises.kt index 3e7d28489..93b4f6ce6 100644 --- a/license/COPYRIGHT_HEADER.txt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/astring/astring.typealises.kt @@ -1,4 +1,8 @@ /* * 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. - */ \ No newline at end of file + */ + +package space.kscience.kmath.internal.astring + +internal typealias Generator = Any diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/base64/base64.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/base64/base64.kt new file mode 100644 index 000000000..86e0cede7 --- /dev/null +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/base64/base64.kt @@ -0,0 +1,54 @@ +/* + * 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. + */ + +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", + "OVERRIDING_FINAL_MEMBER", + "RETURN_TYPE_MISMATCH_ON_OVERRIDE", + "CONFLICTING_OVERLOADS", + "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", + "ObjectPropertyName", + "ClassName", +) +@file:JsNonModule +@file:JsModule("js-base64") + +package space.kscience.kmath.internal.base64 + +import org.khronos.webgl.Uint8Array + +internal external var version: Any + +internal external var VERSION: Any + +internal external var btoaPolyfill: (bin: String) -> String + +internal external var _btoa: (bin: String) -> String + +internal external var fromUint8Array: (u8a: Uint8Array, urlsafe: Boolean) -> String + +internal external var utob: (u: String) -> String + +internal external var encode: (src: String, urlsafe: Boolean) -> String + +internal external var encodeURI: (src: String) -> String + +internal external var btou: (b: String) -> String + +internal external var atobPolyfill: (asc: String) -> String + +internal external var _atob: (asc: String) -> String + +internal external var toUint8Array: (a: String) -> Uint8Array + +internal external var decode: (src: String) -> String + +internal external var isValid: (src: Any) -> Boolean + +internal external var extendString: () -> Unit + +internal external var extendUint8Array: () -> Unit + +internal external var extendBuiltins: () -> Unit diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.kt new file mode 100644 index 000000000..42b6ac7d8 --- /dev/null +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.kt @@ -0,0 +1,2239 @@ +/* + * 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. + */ + +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", + "OVERRIDING_FINAL_MEMBER", + "RETURN_TYPE_MISMATCH_ON_OVERRIDE", + "CONFLICTING_OVERLOADS", + "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", + "PropertyName", + "ClassName", +) + +@file:JsModule("binaryen") +@file:JsNonModule + +package space.kscience.kmath.internal.binaryen + +import org.khronos.webgl.Uint8Array +import kotlin.js.Promise + +internal external var isReady: Boolean + +internal external var ready: Promise + +internal external var none: Type + +internal external var i32: Type + +internal external var i64: Type + +internal external var f32: Type + +internal external var f64: Type + +internal external var v128: Type + +internal external var funcref: Type + +internal external var anyref: Type + +internal external var nullref: Type + +internal external var exnref: Type + +internal external var unreachable: Type + +internal external var auto: Type + +internal external fun createType(types: Array): Type + +internal external fun expandType(type: Type): Array + +internal external enum class ExpressionIds { + Invalid, + Block, + If, + Loop, + Break, + Switch, + Call, + CallIndirect, + LocalGet, + LocalSet, + GlobalGet, + GlobalSet, + Load, + Store, + Const, + Unary, + Binary, + Select, + Drop, + Return, + Host, + Nop, + Unreachable, + AtomicCmpxchg, + AtomicRMW, + AtomicWait, + AtomicNotify, + AtomicFence, + SIMDExtract, + SIMDReplace, + SIMDShuffle, + SIMDTernary, + SIMDShift, + SIMDLoad, + MemoryInit, + DataDrop, + MemoryCopy, + MemoryFill, + RefNull, + RefIsNull, + RefFunc, + Try, + Throw, + Rethrow, + BrOnExn, + TupleMake, + TupleExtract, + Push, + Pop +} + +internal external var InvalidId: ExpressionIds + +internal external var BlockId: ExpressionIds + +internal external var IfId: ExpressionIds + +internal external var LoopId: ExpressionIds + +internal external var BreakId: ExpressionIds + +internal external var SwitchId: ExpressionIds + +internal external var CallId: ExpressionIds + +internal external var CallIndirectId: ExpressionIds + +internal external var LocalGetId: ExpressionIds + +internal external var LocalSetId: ExpressionIds + +internal external var GlobalGetId: ExpressionIds + +internal external var GlobalSetId: ExpressionIds + +internal external var LoadId: ExpressionIds + +internal external var StoreId: ExpressionIds + +internal external var ConstId: ExpressionIds + +internal external var UnaryId: ExpressionIds + +internal external var BinaryId: ExpressionIds + +internal external var SelectId: ExpressionIds + +internal external var DropId: ExpressionIds + +internal external var ReturnId: ExpressionIds + +internal external var HostId: ExpressionIds + +internal external var NopId: ExpressionIds + +internal external var UnreachableId: ExpressionIds + +internal external var AtomicCmpxchgId: ExpressionIds + +internal external var AtomicRMWId: ExpressionIds + +internal external var AtomicWaitId: ExpressionIds + +internal external var AtomicNotifyId: ExpressionIds + +internal external var AtomicFenceId: ExpressionIds + +internal external var SIMDExtractId: ExpressionIds + +internal external var SIMDReplaceId: ExpressionIds + +internal external var SIMDShuffleId: ExpressionIds + +internal external var SIMDTernaryId: ExpressionIds + +internal external var SIMDShiftId: ExpressionIds + +internal external var SIMDLoadId: ExpressionIds + +internal external var MemoryInitId: ExpressionIds + +internal external var DataDropId: ExpressionIds + +internal external var MemoryCopyId: ExpressionIds + +internal external var MemoryFillId: ExpressionIds + +internal external var RefNullId: ExpressionIds + +internal external var RefIsNullId: ExpressionIds + +internal external var RefFuncId: ExpressionIds + +internal external var TryId: ExpressionIds + +internal external var ThrowId: ExpressionIds + +internal external var RethrowId: ExpressionIds + +internal external var BrOnExnId: ExpressionIds + +internal external var TupleMakeId: ExpressionIds + +internal external var TupleExtractId: ExpressionIds + +internal external var PushId: ExpressionIds + +internal external var PopId: ExpressionIds + +internal external enum class ExternalKinds { + Function, + Table, + Memory, + Global, + Event +} + +internal external var ExternalFunction: ExternalKinds + +internal external var ExternalTable: ExternalKinds + +internal external var ExternalMemory: ExternalKinds + +internal external var ExternalGlobal: ExternalKinds + +internal external var ExternalEvent: ExternalKinds + +internal external enum class Features { + MVP, + Atomics, + MutableGlobals, + TruncSat, + SIMD, + BulkMemory, + SignExt, + ExceptionHandling, + TailCall, + ReferenceTypes, + Multivalue, + GC, + Memory64, + All +} + +internal external enum class Operations { + ClzInt32, + CtzInt32, + PopcntInt32, + NegFloat32, + AbsFloat32, + CeilFloat32, + FloorFloat32, + TruncFloat32, + NearestFloat32, + SqrtFloat32, + EqZInt32, + ClzInt64, + CtzInt64, + PopcntInt64, + NegFloat64, + AbsFloat64, + CeilFloat64, + FloorFloat64, + TruncFloat64, + NearestFloat64, + SqrtFloat64, + EqZInt64, + ExtendSInt32, + ExtendUInt32, + WrapInt64, + TruncSFloat32ToInt32, + TruncSFloat32ToInt64, + TruncUFloat32ToInt32, + TruncUFloat32ToInt64, + TruncSFloat64ToInt32, + TruncSFloat64ToInt64, + TruncUFloat64ToInt32, + TruncUFloat64ToInt64, + TruncSatSFloat32ToInt32, + TruncSatSFloat32ToInt64, + TruncSatUFloat32ToInt32, + TruncSatUFloat32ToInt64, + TruncSatSFloat64ToInt32, + TruncSatSFloat64ToInt64, + TruncSatUFloat64ToInt32, + TruncSatUFloat64ToInt64, + ReinterpretFloat32, + ReinterpretFloat64, + ConvertSInt32ToFloat32, + ConvertSInt32ToFloat64, + ConvertUInt32ToFloat32, + ConvertUInt32ToFloat64, + ConvertSInt64ToFloat32, + ConvertSInt64ToFloat64, + ConvertUInt64ToFloat32, + ConvertUInt64ToFloat64, + PromoteFloat32, + DemoteFloat64, + ReinterpretInt32, + ReinterpretInt64, + ExtendS8Int32, + ExtendS16Int32, + ExtendS8Int64, + ExtendS16Int64, + ExtendS32Int64, + AddInt32, + SubInt32, + MulInt32, + DivSInt32, + DivUInt32, + RemSInt32, + RemUInt32, + AndInt32, + OrInt32, + XorInt32, + ShlInt32, + ShrUInt32, + ShrSInt32, + RotLInt32, + RotRInt32, + EqInt32, + NeInt32, + LtSInt32, + LtUInt32, + LeSInt32, + LeUInt32, + GtSInt32, + GtUInt32, + GeSInt32, + GeUInt32, + AddInt64, + SubInt64, + MulInt64, + DivSInt64, + DivUInt64, + RemSInt64, + RemUInt64, + AndInt64, + OrInt64, + XorInt64, + ShlInt64, + ShrUInt64, + ShrSInt64, + RotLInt64, + RotRInt64, + EqInt64, + NeInt64, + LtSInt64, + LtUInt64, + LeSInt64, + LeUInt64, + GtSInt64, + GtUInt64, + GeSInt64, + GeUInt64, + AddFloat32, + SubFloat32, + MulFloat32, + DivFloat32, + CopySignFloat32, + MinFloat32, + MaxFloat32, + EqFloat32, + NeFloat32, + LtFloat32, + LeFloat32, + GtFloat32, + GeFloat32, + AddFloat64, + SubFloat64, + MulFloat64, + DivFloat64, + CopySignFloat64, + MinFloat64, + MaxFloat64, + EqFloat64, + NeFloat64, + LtFloat64, + LeFloat64, + GtFloat64, + GeFloat64, + MemorySize, + MemoryGrow, + AtomicRMWAdd, + AtomicRMWSub, + AtomicRMWAnd, + AtomicRMWOr, + AtomicRMWXor, + AtomicRMWXchg, + SplatVecI8x16, + ExtractLaneSVecI8x16, + ExtractLaneUVecI8x16, + ReplaceLaneVecI8x16, + SplatVecI16x8, + ExtractLaneSVecI16x8, + ExtractLaneUVecI16x8, + ReplaceLaneVecI16x8, + SplatVecI32x4, + ExtractLaneVecI32x4, + ReplaceLaneVecI32x4, + SplatVecI64x2, + ExtractLaneVecI64x2, + ReplaceLaneVecI64x2, + SplatVecF32x4, + ExtractLaneVecF32x4, + ReplaceLaneVecF32x4, + SplatVecF64x2, + ExtractLaneVecF64x2, + ReplaceLaneVecF64x2, + EqVecI8x16, + NeVecI8x16, + LtSVecI8x16, + LtUVecI8x16, + GtSVecI8x16, + GtUVecI8x16, + LeSVecI8x16, + LeUVecI8x16, + GeSVecI8x16, + GeUVecI8x16, + EqVecI16x8, + NeVecI16x8, + LtSVecI16x8, + LtUVecI16x8, + GtSVecI16x8, + GtUVecI16x8, + LeSVecI16x8, + LeUVecI16x8, + GeSVecI16x8, + GeUVecI16x8, + EqVecI32x4, + NeVecI32x4, + LtSVecI32x4, + LtUVecI32x4, + GtSVecI32x4, + GtUVecI32x4, + LeSVecI32x4, + LeUVecI32x4, + GeSVecI32x4, + GeUVecI32x4, + EqVecF32x4, + NeVecF32x4, + LtVecF32x4, + GtVecF32x4, + LeVecF32x4, + GeVecF32x4, + EqVecF64x2, + NeVecF64x2, + LtVecF64x2, + GtVecF64x2, + LeVecF64x2, + GeVecF64x2, + NotVec128, + AndVec128, + OrVec128, + XorVec128, + AndNotVec128, + BitselectVec128, + NegVecI8x16, + AnyTrueVecI8x16, + AllTrueVecI8x16, + ShlVecI8x16, + ShrSVecI8x16, + ShrUVecI8x16, + AddVecI8x16, + AddSatSVecI8x16, + AddSatUVecI8x16, + SubVecI8x16, + SubSatSVecI8x16, + SubSatUVecI8x16, + MulVecI8x16, + MinSVecI8x16, + MinUVecI8x16, + MaxSVecI8x16, + MaxUVecI8x16, + NegVecI16x8, + AnyTrueVecI16x8, + AllTrueVecI16x8, + ShlVecI16x8, + ShrSVecI16x8, + ShrUVecI16x8, + AddVecI16x8, + AddSatSVecI16x8, + AddSatUVecI16x8, + SubVecI16x8, + SubSatSVecI16x8, + SubSatUVecI16x8, + MulVecI16x8, + MinSVecI16x8, + MinUVecI16x8, + MaxSVecI16x8, + MaxUVecI16x8, + DotSVecI16x8ToVecI32x4, + NegVecI32x4, + AnyTrueVecI32x4, + AllTrueVecI32x4, + ShlVecI32x4, + ShrSVecI32x4, + ShrUVecI32x4, + AddVecI32x4, + SubVecI32x4, + MulVecI32x4, + MinSVecI32x4, + MinUVecI32x4, + MaxSVecI32x4, + MaxUVecI32x4, + NegVecI64x2, + AnyTrueVecI64x2, + AllTrueVecI64x2, + ShlVecI64x2, + ShrSVecI64x2, + ShrUVecI64x2, + AddVecI64x2, + SubVecI64x2, + AbsVecF32x4, + NegVecF32x4, + SqrtVecF32x4, + QFMAVecF32x4, + QFMSVecF32x4, + AddVecF32x4, + SubVecF32x4, + MulVecF32x4, + DivVecF32x4, + MinVecF32x4, + MaxVecF32x4, + AbsVecF64x2, + NegVecF64x2, + SqrtVecF64x2, + QFMAVecF64x2, + QFMSVecF64x2, + AddVecF64x2, + SubVecF64x2, + MulVecF64x2, + DivVecF64x2, + MinVecF64x2, + MaxVecF64x2, + TruncSatSVecF32x4ToVecI32x4, + TruncSatUVecF32x4ToVecI32x4, + TruncSatSVecF64x2ToVecI64x2, + TruncSatUVecF64x2ToVecI64x2, + ConvertSVecI32x4ToVecF32x4, + ConvertUVecI32x4ToVecF32x4, + ConvertSVecI64x2ToVecF64x2, + ConvertUVecI64x2ToVecF64x2, + LoadSplatVec8x16, + LoadSplatVec16x8, + LoadSplatVec32x4, + LoadSplatVec64x2, + LoadExtSVec8x8ToVecI16x8, + LoadExtUVec8x8ToVecI16x8, + LoadExtSVec16x4ToVecI32x4, + LoadExtUVec16x4ToVecI32x4, + LoadExtSVec32x2ToVecI64x2, + LoadExtUVec32x2ToVecI64x2, + NarrowSVecI16x8ToVecI8x16, + NarrowUVecI16x8ToVecI8x16, + NarrowSVecI32x4ToVecI16x8, + NarrowUVecI32x4ToVecI16x8, + WidenLowSVecI8x16ToVecI16x8, + WidenHighSVecI8x16ToVecI16x8, + WidenLowUVecI8x16ToVecI16x8, + WidenHighUVecI8x16ToVecI16x8, + WidenLowSVecI16x8ToVecI32x4, + WidenHighSVecI16x8ToVecI32x4, + WidenLowUVecI16x8ToVecI32x4, + WidenHighUVecI16x8ToVecI32x4, + SwizzleVec8x16 +} + +internal external var ClzInt32: Operations + +internal external var CtzInt32: Operations + +internal external var PopcntInt32: Operations + +internal external var NegFloat32: Operations + +internal external var AbsFloat32: Operations + +internal external var CeilFloat32: Operations + +internal external var FloorFloat32: Operations + +internal external var TruncFloat32: Operations + +internal external var NearestFloat32: Operations + +internal external var SqrtFloat32: Operations + +internal external var EqZInt32: Operations + +internal external var ClzInt64: Operations + +internal external var CtzInt64: Operations + +internal external var PopcntInt64: Operations + +internal external var NegFloat64: Operations + +internal external var AbsFloat64: Operations + +internal external var CeilFloat64: Operations + +internal external var FloorFloat64: Operations + +internal external var TruncFloat64: Operations + +internal external var NearestFloat64: Operations + +internal external var SqrtFloat64: Operations + +internal external var EqZInt64: Operations + +internal external var ExtendSInt32: Operations + +internal external var ExtendUInt32: Operations + +internal external var WrapInt64: Operations + +internal external var TruncSFloat32ToInt32: Operations + +internal external var TruncSFloat32ToInt64: Operations + +internal external var TruncUFloat32ToInt32: Operations + +internal external var TruncUFloat32ToInt64: Operations + +internal external var TruncSFloat64ToInt32: Operations + +internal external var TruncSFloat64ToInt64: Operations + +internal external var TruncUFloat64ToInt32: Operations + +internal external var TruncUFloat64ToInt64: Operations + +internal external var TruncSatSFloat32ToInt32: Operations + +internal external var TruncSatSFloat32ToInt64: Operations + +internal external var TruncSatUFloat32ToInt32: Operations + +internal external var TruncSatUFloat32ToInt64: Operations + +internal external var TruncSatSFloat64ToInt32: Operations + +internal external var TruncSatSFloat64ToInt64: Operations + +internal external var TruncSatUFloat64ToInt32: Operations + +internal external var TruncSatUFloat64ToInt64: Operations + +internal external var ReinterpretFloat32: Operations + +internal external var ReinterpretFloat64: Operations + +internal external var ConvertSInt32ToFloat32: Operations + +internal external var ConvertSInt32ToFloat64: Operations + +internal external var ConvertUInt32ToFloat32: Operations + +internal external var ConvertUInt32ToFloat64: Operations + +internal external var ConvertSInt64ToFloat32: Operations + +internal external var ConvertSInt64ToFloat64: Operations + +internal external var ConvertUInt64ToFloat32: Operations + +internal external var ConvertUInt64ToFloat64: Operations + +internal external var PromoteFloat32: Operations + +internal external var DemoteFloat64: Operations + +internal external var ReinterpretInt32: Operations + +internal external var ReinterpretInt64: Operations + +internal external var ExtendS8Int32: Operations + +internal external var ExtendS16Int32: Operations + +internal external var ExtendS8Int64: Operations + +internal external var ExtendS16Int64: Operations + +internal external var ExtendS32Int64: Operations + +internal external var AddInt32: Operations + +internal external var SubInt32: Operations + +internal external var MulInt32: Operations + +internal external var DivSInt32: Operations + +internal external var DivUInt32: Operations + +internal external var RemSInt32: Operations + +internal external var RemUInt32: Operations + +internal external var AndInt32: Operations + +internal external var OrInt32: Operations + +internal external var XorInt32: Operations + +internal external var ShlInt32: Operations + +internal external var ShrUInt32: Operations + +internal external var ShrSInt32: Operations + +internal external var RotLInt32: Operations + +internal external var RotRInt32: Operations + +internal external var EqInt32: Operations + +internal external var NeInt32: Operations + +internal external var LtSInt32: Operations + +internal external var LtUInt32: Operations + +internal external var LeSInt32: Operations + +internal external var LeUInt32: Operations + +internal external var GtSInt32: Operations + +internal external var GtUInt32: Operations + +internal external var GeSInt32: Operations + +internal external var GeUInt32: Operations + +internal external var AddInt64: Operations + +internal external var SubInt64: Operations + +internal external var MulInt64: Operations + +internal external var DivSInt64: Operations + +internal external var DivUInt64: Operations + +internal external var RemSInt64: Operations + +internal external var RemUInt64: Operations + +internal external var AndInt64: Operations + +internal external var OrInt64: Operations + +internal external var XorInt64: Operations + +internal external var ShlInt64: Operations + +internal external var ShrUInt64: Operations + +internal external var ShrSInt64: Operations + +internal external var RotLInt64: Operations + +internal external var RotRInt64: Operations + +internal external var EqInt64: Operations + +internal external var NeInt64: Operations + +internal external var LtSInt64: Operations + +internal external var LtUInt64: Operations + +internal external var LeSInt64: Operations + +internal external var LeUInt64: Operations + +internal external var GtSInt64: Operations + +internal external var GtUInt64: Operations + +internal external var GeSInt64: Operations + +internal external var GeUInt64: Operations + +internal external var AddFloat32: Operations + +internal external var SubFloat32: Operations + +internal external var MulFloat32: Operations + +internal external var DivFloat32: Operations + +internal external var CopySignFloat32: Operations + +internal external var MinFloat32: Operations + +internal external var MaxFloat32: Operations + +internal external var EqFloat32: Operations + +internal external var NeFloat32: Operations + +internal external var LtFloat32: Operations + +internal external var LeFloat32: Operations + +internal external var GtFloat32: Operations + +internal external var GeFloat32: Operations + +internal external var AddFloat64: Operations + +internal external var SubFloat64: Operations + +internal external var MulFloat64: Operations + +internal external var DivFloat64: Operations + +internal external var CopySignFloat64: Operations + +internal external var MinFloat64: Operations + +internal external var MaxFloat64: Operations + +internal external var EqFloat64: Operations + +internal external var NeFloat64: Operations + +internal external var LtFloat64: Operations + +internal external var LeFloat64: Operations + +internal external var GtFloat64: Operations + +internal external var GeFloat64: Operations + +internal external var MemorySize: Operations + +internal external var MemoryGrow: Operations + +internal external var AtomicRMWAdd: Operations + +internal external var AtomicRMWSub: Operations + +internal external var AtomicRMWAnd: Operations + +internal external var AtomicRMWOr: Operations + +internal external var AtomicRMWXor: Operations + +internal external var AtomicRMWXchg: Operations + +internal external var SplatVecI8x16: Operations + +internal external var ExtractLaneSVecI8x16: Operations + +internal external var ExtractLaneUVecI8x16: Operations + +internal external var ReplaceLaneVecI8x16: Operations + +internal external var SplatVecI16x8: Operations + +internal external var ExtractLaneSVecI16x8: Operations + +internal external var ExtractLaneUVecI16x8: Operations + +internal external var ReplaceLaneVecI16x8: Operations + +internal external var SplatVecI32x4: Operations + +internal external var ExtractLaneVecI32x4: Operations + +internal external var ReplaceLaneVecI32x4: Operations + +internal external var SplatVecI64x2: Operations + +internal external var ExtractLaneVecI64x2: Operations + +internal external var ReplaceLaneVecI64x2: Operations + +internal external var SplatVecF32x4: Operations + +internal external var ExtractLaneVecF32x4: Operations + +internal external var ReplaceLaneVecF32x4: Operations + +internal external var SplatVecF64x2: Operations + +internal external var ExtractLaneVecF64x2: Operations + +internal external var ReplaceLaneVecF64x2: Operations + +internal external var EqVecI8x16: Operations + +internal external var NeVecI8x16: Operations + +internal external var LtSVecI8x16: Operations + +internal external var LtUVecI8x16: Operations + +internal external var GtSVecI8x16: Operations + +internal external var GtUVecI8x16: Operations + +internal external var LeSVecI8x16: Operations + +internal external var LeUVecI8x16: Operations + +internal external var GeSVecI8x16: Operations + +internal external var GeUVecI8x16: Operations + +internal external var EqVecI16x8: Operations + +internal external var NeVecI16x8: Operations + +internal external var LtSVecI16x8: Operations + +internal external var LtUVecI16x8: Operations + +internal external var GtSVecI16x8: Operations + +internal external var GtUVecI16x8: Operations + +internal external var LeSVecI16x8: Operations + +internal external var LeUVecI16x8: Operations + +internal external var GeSVecI16x8: Operations + +internal external var GeUVecI16x8: Operations + +internal external var EqVecI32x4: Operations + +internal external var NeVecI32x4: Operations + +internal external var LtSVecI32x4: Operations + +internal external var LtUVecI32x4: Operations + +internal external var GtSVecI32x4: Operations + +internal external var GtUVecI32x4: Operations + +internal external var LeSVecI32x4: Operations + +internal external var LeUVecI32x4: Operations + +internal external var GeSVecI32x4: Operations + +internal external var GeUVecI32x4: Operations + +internal external var EqVecF32x4: Operations + +internal external var NeVecF32x4: Operations + +internal external var LtVecF32x4: Operations + +internal external var GtVecF32x4: Operations + +internal external var LeVecF32x4: Operations + +internal external var GeVecF32x4: Operations + +internal external var EqVecF64x2: Operations + +internal external var NeVecF64x2: Operations + +internal external var LtVecF64x2: Operations + +internal external var GtVecF64x2: Operations + +internal external var LeVecF64x2: Operations + +internal external var GeVecF64x2: Operations + +internal external var NotVec128: Operations + +internal external var AndVec128: Operations + +internal external var OrVec128: Operations + +internal external var XorVec128: Operations + +internal external var AndNotVec128: Operations + +internal external var BitselectVec128: Operations + +internal external var NegVecI8x16: Operations + +internal external var AnyTrueVecI8x16: Operations + +internal external var AllTrueVecI8x16: Operations + +internal external var ShlVecI8x16: Operations + +internal external var ShrSVecI8x16: Operations + +internal external var ShrUVecI8x16: Operations + +internal external var AddVecI8x16: Operations + +internal external var AddSatSVecI8x16: Operations + +internal external var AddSatUVecI8x16: Operations + +internal external var SubVecI8x16: Operations + +internal external var SubSatSVecI8x16: Operations + +internal external var SubSatUVecI8x16: Operations + +internal external var MulVecI8x16: Operations + +internal external var MinSVecI8x16: Operations + +internal external var MinUVecI8x16: Operations + +internal external var MaxSVecI8x16: Operations + +internal external var MaxUVecI8x16: Operations + +internal external var NegVecI16x8: Operations + +internal external var AnyTrueVecI16x8: Operations + +internal external var AllTrueVecI16x8: Operations + +internal external var ShlVecI16x8: Operations + +internal external var ShrSVecI16x8: Operations + +internal external var ShrUVecI16x8: Operations + +internal external var AddVecI16x8: Operations + +internal external var AddSatSVecI16x8: Operations + +internal external var AddSatUVecI16x8: Operations + +internal external var SubVecI16x8: Operations + +internal external var SubSatSVecI16x8: Operations + +internal external var SubSatUVecI16x8: Operations + +internal external var MulVecI16x8: Operations + +internal external var MinSVecI16x8: Operations + +internal external var MinUVecI16x8: Operations + +internal external var MaxSVecI16x8: Operations + +internal external var MaxUVecI16x8: Operations + +internal external var DotSVecI16x8ToVecI32x4: Operations + +internal external var NegVecI32x4: Operations + +internal external var AnyTrueVecI32x4: Operations + +internal external var AllTrueVecI32x4: Operations + +internal external var ShlVecI32x4: Operations + +internal external var ShrSVecI32x4: Operations + +internal external var ShrUVecI32x4: Operations + +internal external var AddVecI32x4: Operations + +internal external var SubVecI32x4: Operations + +internal external var MulVecI32x4: Operations + +internal external var MinSVecI32x4: Operations + +internal external var MinUVecI32x4: Operations + +internal external var MaxSVecI32x4: Operations + +internal external var MaxUVecI32x4: Operations + +internal external var NegVecI64x2: Operations + +internal external var AnyTrueVecI64x2: Operations + +internal external var AllTrueVecI64x2: Operations + +internal external var ShlVecI64x2: Operations + +internal external var ShrSVecI64x2: Operations + +internal external var ShrUVecI64x2: Operations + +internal external var AddVecI64x2: Operations + +internal external var SubVecI64x2: Operations + +internal external var AbsVecF32x4: Operations + +internal external var NegVecF32x4: Operations + +internal external var SqrtVecF32x4: Operations + +internal external var QFMAVecF32x4: Operations + +internal external var QFMSVecF32x4: Operations + +internal external var AddVecF32x4: Operations + +internal external var SubVecF32x4: Operations + +internal external var MulVecF32x4: Operations + +internal external var DivVecF32x4: Operations + +internal external var MinVecF32x4: Operations + +internal external var MaxVecF32x4: Operations + +internal external var AbsVecF64x2: Operations + +internal external var NegVecF64x2: Operations + +internal external var SqrtVecF64x2: Operations + +internal external var QFMAVecF64x2: Operations + +internal external var QFMSVecF64x2: Operations + +internal external var AddVecF64x2: Operations + +internal external var SubVecF64x2: Operations + +internal external var MulVecF64x2: Operations + +internal external var DivVecF64x2: Operations + +internal external var MinVecF64x2: Operations + +internal external var MaxVecF64x2: Operations + +internal external var TruncSatSVecF32x4ToVecI32x4: Operations + +internal external var TruncSatUVecF32x4ToVecI32x4: Operations + +internal external var TruncSatSVecF64x2ToVecI64x2: Operations + +internal external var TruncSatUVecF64x2ToVecI64x2: Operations + +internal external var ConvertSVecI32x4ToVecF32x4: Operations + +internal external var ConvertUVecI32x4ToVecF32x4: Operations + +internal external var ConvertSVecI64x2ToVecF64x2: Operations + +internal external var ConvertUVecI64x2ToVecF64x2: Operations + +internal external var LoadSplatVec8x16: Operations + +internal external var LoadSplatVec16x8: Operations + +internal external var LoadSplatVec32x4: Operations + +internal external var LoadSplatVec64x2: Operations + +internal external var LoadExtSVec8x8ToVecI16x8: Operations + +internal external var LoadExtUVec8x8ToVecI16x8: Operations + +internal external var LoadExtSVec16x4ToVecI32x4: Operations + +internal external var LoadExtUVec16x4ToVecI32x4: Operations + +internal external var LoadExtSVec32x2ToVecI64x2: Operations + +internal external var LoadExtUVec32x2ToVecI64x2: Operations + +internal external var NarrowSVecI16x8ToVecI8x16: Operations + +internal external var NarrowUVecI16x8ToVecI8x16: Operations + +internal external var NarrowSVecI32x4ToVecI16x8: Operations + +internal external var NarrowUVecI32x4ToVecI16x8: Operations + +internal external var WidenLowSVecI8x16ToVecI16x8: Operations + +internal external var WidenHighSVecI8x16ToVecI16x8: Operations + +internal external var WidenLowUVecI8x16ToVecI16x8: Operations + +internal external var WidenHighUVecI8x16ToVecI16x8: Operations + +internal external var WidenLowSVecI16x8ToVecI32x4: Operations + +internal external var WidenHighSVecI16x8ToVecI32x4: Operations + +internal external var WidenLowUVecI16x8ToVecI32x4: Operations + +internal external var WidenHighUVecI16x8ToVecI32x4: Operations + +internal external var SwizzleVec8x16: Operations + +internal external interface `T$2` { + fun get(index: Number, type: Type): ExpressionRef + fun set(index: Number, value: ExpressionRef): ExpressionRef + fun tee(index: Number, value: ExpressionRef, type: Type): ExpressionRef +} + +internal external interface `T$3` { + fun get(name: String, type: Type): ExpressionRef + fun set(name: String, value: ExpressionRef): ExpressionRef +} + +internal external interface `T$4` { + fun size(): ExpressionRef + fun grow(value: ExpressionRef): ExpressionRef + fun init(segment: Number, dest: ExpressionRef, offset: ExpressionRef, size: ExpressionRef): ExpressionRef + fun copy(dest: ExpressionRef, source: ExpressionRef, size: ExpressionRef): ExpressionRef + fun fill(dest: ExpressionRef, value: ExpressionRef, size: ExpressionRef): ExpressionRef +} + +internal external interface `T$5` { + fun drop(segment: Number): ExpressionRef +} + +internal external interface `T$6` { + fun f32(value: ExpressionRef): ExpressionRef + fun f64(value: ExpressionRef): ExpressionRef +} + +internal external interface `T$7` { + fun add(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun sub(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun and(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun or(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun xor(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun xchg(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun cmpxchg(offset: Number, ptr: ExpressionRef, expected: ExpressionRef, replacement: ExpressionRef): ExpressionRef +} + +internal external interface `T$8` { + fun load(offset: Number, ptr: ExpressionRef): ExpressionRef + fun load8_u(offset: Number, ptr: ExpressionRef): ExpressionRef + fun load16_u(offset: Number, ptr: ExpressionRef): ExpressionRef + fun store(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store8(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store16(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + var rmw: `T$7` + var rmw8_u: `T$7` + var rmw16_u: `T$7` + fun wait(ptr: ExpressionRef, expected: ExpressionRef, timeout: ExpressionRef): ExpressionRef +} + +internal external interface `T$9` { + fun load(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load8_s(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load8_u(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load16_s(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load16_u(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun store(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store8(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store16(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun const(value: Number): ExpressionRef + fun clz(value: ExpressionRef): ExpressionRef + fun ctz(value: ExpressionRef): ExpressionRef + fun popcnt(value: ExpressionRef): ExpressionRef + fun eqz(value: ExpressionRef): ExpressionRef + var trunc_s: `T$6` + var trunc_u: `T$6` + var trunc_s_sat: `T$6` + var trunc_u_sat: `T$6` + fun reinterpret(value: ExpressionRef): ExpressionRef + fun extend8_s(value: ExpressionRef): ExpressionRef + fun extend16_s(value: ExpressionRef): ExpressionRef + fun wrap(value: ExpressionRef): ExpressionRef + fun add(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun mul(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun div_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun div_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun rem_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun rem_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun and(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun or(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun xor(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun shl(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun shr_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun shr_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun rotl(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun rotr(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun eq(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ne(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + var atomic: `T$8` + fun pop(): ExpressionRef +} + +internal external interface `T$10` { + fun load(offset: Number, ptr: ExpressionRef): ExpressionRef + fun load8_u(offset: Number, ptr: ExpressionRef): ExpressionRef + fun load16_u(offset: Number, ptr: ExpressionRef): ExpressionRef + fun load32_u(offset: Number, ptr: ExpressionRef): ExpressionRef + fun store(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store8(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store16(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store32(offset: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + var rmw: `T$7` + var rmw8_u: `T$7` + var rmw16_u: `T$7` + var rmw32_u: `T$7` + fun wait(ptr: ExpressionRef, expected: ExpressionRef, timeout: ExpressionRef): ExpressionRef +} + +internal external interface `T$11` { + fun load(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load8_s(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load8_u(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load16_s(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load16_u(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load32_s(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load32_u(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun store(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store8(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store16(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun store32(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun const(low: Number, high: Number): ExpressionRef + fun clz(value: ExpressionRef): ExpressionRef + fun ctz(value: ExpressionRef): ExpressionRef + fun popcnt(value: ExpressionRef): ExpressionRef + fun eqz(value: ExpressionRef): ExpressionRef + var trunc_s: `T$6` + var trunc_u: `T$6` + var trunc_s_sat: `T$6` + var trunc_u_sat: `T$6` + fun reinterpret(value: ExpressionRef): ExpressionRef + fun extend8_s(value: ExpressionRef): ExpressionRef + fun extend16_s(value: ExpressionRef): ExpressionRef + fun extend32_s(value: ExpressionRef): ExpressionRef + fun extend_s(value: ExpressionRef): ExpressionRef + fun extend_u(value: ExpressionRef): ExpressionRef + fun add(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun mul(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun div_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun div_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun rem_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun rem_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun and(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun or(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun xor(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun shl(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun shr_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun shr_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun rotl(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun rotr(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun eq(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ne(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + var atomic: `T$10` + fun pop(): ExpressionRef +} + +internal external interface `T$12` { + fun load(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun store(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun const(value: Number): ExpressionRef + fun const_bits(value: Number): ExpressionRef + fun neg(value: ExpressionRef): ExpressionRef + fun abs(value: ExpressionRef): ExpressionRef + fun ceil(value: ExpressionRef): ExpressionRef + fun floor(value: ExpressionRef): ExpressionRef + fun trunc(value: ExpressionRef): ExpressionRef + fun nearest(value: ExpressionRef): ExpressionRef + fun sqrt(value: ExpressionRef): ExpressionRef + fun reinterpret(value: ExpressionRef): ExpressionRef + var convert_s: `T$6` + var convert_u: `T$6` + fun demote(value: ExpressionRef): ExpressionRef + fun add(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun mul(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun div(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun copysign(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun min(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun max(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun eq(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ne(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun pop(): ExpressionRef +} + +internal external interface `T$13` { + fun load(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun store(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun const(value: Number): ExpressionRef + fun const_bits(low: Number, high: Number): ExpressionRef + fun neg(value: ExpressionRef): ExpressionRef + fun abs(value: ExpressionRef): ExpressionRef + fun ceil(value: ExpressionRef): ExpressionRef + fun floor(value: ExpressionRef): ExpressionRef + fun trunc(value: ExpressionRef): ExpressionRef + fun nearest(value: ExpressionRef): ExpressionRef + fun sqrt(value: ExpressionRef): ExpressionRef + fun reinterpret(value: ExpressionRef): ExpressionRef + var convert_s: `T$6` + var convert_u: `T$6` + fun promote(value: ExpressionRef): ExpressionRef + fun add(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun mul(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun div(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun copysign(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun min(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun max(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun eq(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ne(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun pop(): ExpressionRef +} + +internal external interface `T$14` { + fun load(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun store(offset: Number, align: Number, ptr: ExpressionRef, value: ExpressionRef): ExpressionRef + fun const(value: Number): ExpressionRef + fun not(value: ExpressionRef): ExpressionRef + fun and(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun or(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun xor(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun andnot(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun bitselect(left: ExpressionRef, right: ExpressionRef, cond: ExpressionRef): ExpressionRef + fun pop(): ExpressionRef +} + +internal external interface `T$15` { + fun splat(value: ExpressionRef): ExpressionRef + fun extract_lane_s(vec: ExpressionRef, index: ExpressionRef): ExpressionRef + fun extract_lane_u(vec: ExpressionRef, index: ExpressionRef): ExpressionRef + fun replace_lane(vec: ExpressionRef, index: ExpressionRef, value: ExpressionRef): ExpressionRef + fun eq(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ne(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun neg(value: ExpressionRef): ExpressionRef + fun any_true(value: ExpressionRef): ExpressionRef + fun all_true(value: ExpressionRef): ExpressionRef + fun shl(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun shr_s(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun shr_u(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun add(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun add_saturate_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun add_saturate_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub_saturate_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub_saturate_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun mul(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun min_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun min_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun max_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun max_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun avgr_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun narrow_i16x8_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun narrow_i16x8_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef +} + +internal external interface `T$16` { + fun splat(value: ExpressionRef): ExpressionRef + fun extract_lane_s(vec: ExpressionRef, index: ExpressionRef): ExpressionRef + fun extract_lane_u(vec: ExpressionRef, index: ExpressionRef): ExpressionRef + fun replace_lane(vec: ExpressionRef, index: ExpressionRef, value: ExpressionRef): ExpressionRef + fun eq(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ne(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun neg(value: ExpressionRef): ExpressionRef + fun any_true(value: ExpressionRef): ExpressionRef + fun all_true(value: ExpressionRef): ExpressionRef + fun shl(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun shr_s(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun shr_u(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun add(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun add_saturate_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun add_saturate_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub_saturate_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub_saturate_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun mul(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun min_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun min_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun max_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun max_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun avgr_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun narrow_i32x4_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun narrow_i32x4_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun widen_low_i8x16_s(value: ExpressionRef): ExpressionRef + fun widen_high_i8x16_s(value: ExpressionRef): ExpressionRef + fun widen_low_i8x16_u(value: ExpressionRef): ExpressionRef + fun widen_high_i8x16_u(value: ExpressionRef): ExpressionRef + fun load8x8_s(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load8x8_u(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef +} + +internal external interface `T$17` { + fun splat(value: ExpressionRef): ExpressionRef + fun extract_lane(vec: ExpressionRef, index: ExpressionRef): ExpressionRef + fun replace_lane(vec: ExpressionRef, index: ExpressionRef, value: ExpressionRef): ExpressionRef + fun eq(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ne(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_s(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge_u(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun neg(value: ExpressionRef): ExpressionRef + fun any_true(value: ExpressionRef): ExpressionRef + fun all_true(value: ExpressionRef): ExpressionRef + fun shl(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun shr_s(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun shr_u(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun add(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun mul(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun trunc_sat_f32x4_s(value: ExpressionRef): ExpressionRef + fun trunc_sat_f32x4_u(value: ExpressionRef): ExpressionRef + fun widen_low_i16x8_s(value: ExpressionRef): ExpressionRef + fun widen_high_i16x8_s(value: ExpressionRef): ExpressionRef + fun widen_low_i16x8_u(value: ExpressionRef): ExpressionRef + fun widen_high_i16x8_u(value: ExpressionRef): ExpressionRef + fun load16x4_s(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load16x4_u(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef +} + +internal external interface `T$18` { + fun splat(value: ExpressionRef): ExpressionRef + fun extract_lane(vec: ExpressionRef, index: ExpressionRef): ExpressionRef + fun replace_lane(vec: ExpressionRef, index: ExpressionRef, value: ExpressionRef): ExpressionRef + fun neg(value: ExpressionRef): ExpressionRef + fun any_true(value: ExpressionRef): ExpressionRef + fun all_true(value: ExpressionRef): ExpressionRef + fun shl(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun shr_s(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun shr_u(vec: ExpressionRef, shift: ExpressionRef): ExpressionRef + fun add(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun trunc_sat_f64x2_s(value: ExpressionRef): ExpressionRef + fun trunc_sat_f64x2_u(value: ExpressionRef): ExpressionRef + fun load32x2_s(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef + fun load32x2_u(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef +} + +internal external interface `T$19` { + fun splat(value: ExpressionRef): ExpressionRef + fun extract_lane(vec: ExpressionRef, index: ExpressionRef): ExpressionRef + fun replace_lane(vec: ExpressionRef, index: ExpressionRef, value: ExpressionRef): ExpressionRef + fun eq(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ne(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun lt(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun gt(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun le(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun ge(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun abs(value: ExpressionRef): ExpressionRef + fun neg(value: ExpressionRef): ExpressionRef + fun sqrt(value: ExpressionRef): ExpressionRef + fun qfma(a: ExpressionRef, b: ExpressionRef, c: ExpressionRef): ExpressionRef + fun qfms(a: ExpressionRef, b: ExpressionRef, c: ExpressionRef): ExpressionRef + fun add(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun sub(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun mul(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun div(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun min(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun max(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun convert_i32x4_s(value: ExpressionRef): ExpressionRef + fun convert_i32x4_u(value: ExpressionRef): ExpressionRef +} + +internal external interface `T$20` { + fun shuffle(left: ExpressionRef, right: ExpressionRef, mask: Array): ExpressionRef + fun swizzle(left: ExpressionRef, right: ExpressionRef): ExpressionRef + fun load_splat(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef +} + +internal external interface `T$21` { + fun load_splat(offset: Number, align: Number, ptr: ExpressionRef): ExpressionRef +} + +internal external interface `T$22` { + fun pop(): ExpressionRef +} + +internal external interface `T$23` { + fun `null`(): ExpressionRef + fun is_null(value: ExpressionRef): ExpressionRef + fun func(name: String): ExpressionRef +} + +internal external interface `T$24` { + fun notify(ptr: ExpressionRef, notifyCount: ExpressionRef): ExpressionRef + fun fence(): ExpressionRef +} + +internal external interface `T$25` { + fun make(elements: Array): ExpressionRef + fun extract(tuple: ExpressionRef, index: Number): ExpressionRef +} + +internal external interface `T$26` { + var imported: Boolean + var segments: Array +} + +internal external interface `T$27` { + var binary: Uint8Array + var sourceMap: String? +} + +internal open external class Module { + open var ptr: Number + open fun block(label: String, children: Array, resultType: Type = definedExternally): ExpressionRef + open fun `if`( + condition: ExpressionRef, + ifTrue: ExpressionRef, + ifFalse: ExpressionRef = definedExternally + ): ExpressionRef + + open fun loop(label: String, body: ExpressionRef): ExpressionRef + open fun br( + label: String, + condition: ExpressionRef = definedExternally, + value: ExpressionRef = definedExternally + ): ExpressionRef + + open fun br_if( + label: String, + condition: ExpressionRef = definedExternally, + value: ExpressionRef = definedExternally + ): ExpressionRef + + open fun switch( + labels: Array, + defaultLabel: String, + condition: ExpressionRef, + value: ExpressionRef = definedExternally + ): ExpressionRef + + open fun call(name: String, operands: Array, returnType: Type): ExpressionRef + open fun return_call(name: String, operands: Array, returnType: Type): ExpressionRef + open fun call_indirect( + target: ExpressionRef, + operands: Array, + params: Type, + results: Type + ): ExpressionRef + + open fun return_call_indirect( + target: ExpressionRef, + operands: Array, + params: Type, + results: Type + ): ExpressionRef + + open var local: `T$2` + open var global: `T$3` + open var memory: `T$4` + open var data: `T$5` + open var i32: `T$9` + open var i64: `T$11` + open var f32: `T$12` + open var f64: `T$13` + open var v128: `T$14` + open var i8x16: `T$15` + open var i16x8: `T$16` + open var i32x4: `T$17` + open var i64x2: `T$18` + open var f32x4: `T$19` + open var f64x2: `T$19` + open var v8x16: `T$20` + open var v16x8: `T$21` + open var v32x4: `T$21` + open var v64x2: `T$21` + open var funcref: `T$22` + open var anyref: `T$22` + open var nullref: `T$22` + open var exnref: `T$22` + open var ref: `T$23` + open var atomic: `T$24` + open var tuple: `T$25` + open fun `try`(body: ExpressionRef, catchBody: ExpressionRef): ExpressionRef + open fun `throw`(event: String, operands: Array): ExpressionRef + open fun rethrow(exnref: ExpressionRef): ExpressionRef + open fun br_on_exn(label: String, event: String, exnref: ExpressionRef): ExpressionRef + open fun push(value: ExpressionRef): ExpressionRef + open fun select( + condition: ExpressionRef, + ifTrue: ExpressionRef, + ifFalse: ExpressionRef, + type: Type = definedExternally + ): ExpressionRef + + open fun drop(value: ExpressionRef): ExpressionRef + open fun `return`(value: ExpressionRef = definedExternally): ExpressionRef + open fun host(op: Operations, name: String, operands: Array): ExpressionRef + open fun nop(): ExpressionRef + open fun unreachable(): ExpressionRef + open fun addFunction(name: String, params: Type, results: Type, vars: Array, body: ExpressionRef): FunctionRef + open fun getFunction(name: String): FunctionRef + open fun removeFunction(name: String) + open fun getNumFunctions(): Number + open fun getFunctionByIndex(index: Number): FunctionRef + open fun addGlobal(name: String, type: Type, mutable: Boolean, init: ExpressionRef): GlobalRef + open fun getGlobal(name: String): GlobalRef + open fun removeGlobal(name: String) + open fun addEvent(name: String, attribute: Number, params: Type, results: Type): EventRef + open fun getEvent(name: String): EventRef + open fun removeEvent(name: String) + open fun addFunctionImport( + internalName: String, + externalModuleName: String, + externalBaseName: String, + params: Type, + results: Type + ) + + open fun addTableImport(internalName: String, externalModuleName: String, externalBaseName: String) + open fun addMemoryImport(internalName: String, externalModuleName: String, externalBaseName: String) + open fun addGlobalImport( + internalName: String, + externalModuleName: String, + externalBaseName: String, + globalType: Type + ) + + open fun addEventImport( + internalName: String, + externalModuleName: String, + externalBaseName: String, + attribute: Number, + params: Type, + results: Type + ) + + open fun addFunctionExport(internalName: String, externalName: String): ExportRef + open fun addTableExport(internalName: String, externalName: String): ExportRef + open fun addMemoryExport(internalName: String, externalName: String): ExportRef + open fun addGlobalExport(internalName: String, externalName: String): ExportRef + open fun removeExport(externalName: String) + open fun getNumExports(): Number + open fun getExportByIndex(index: Number): ExportRef + open fun setFunctionTable( + initial: Number, + maximum: Number, + funcNames: Array, + offset: ExpressionRef = definedExternally + ) + + open fun getFunctionTable(): `T$26` + open fun setMemory( + initial: Number, + maximum: Number, + exportName: String? = definedExternally, + segments: Array? = definedExternally, + flags: Array? = definedExternally, + shared: Boolean = definedExternally + ) + + open fun getNumMemorySegments(): Number + open fun getMemorySegmentInfoByIndex(index: Number): MemorySegmentInfo + open fun setStart(start: FunctionRef) + open fun getFeatures(): Features + open fun setFeatures(features: Features) + open fun addCustomSection(name: String, contents: Uint8Array) + open fun emitText(): String + open fun emitStackIR(optimize: Boolean = definedExternally): String + open fun emitAsmjs(): String + open fun validate(): Number + open fun optimize() + open fun optimizeFunction(func: String) + open fun optimizeFunction(func: FunctionRef) + open fun runPasses(passes: Array) + open fun runPassesOnFunction(func: String, passes: Array) + open fun runPassesOnFunction(func: FunctionRef, passes: Array) + open fun autoDrop() + open fun dispose() + open fun emitBinary(): Uint8Array + open fun emitBinary(sourceMapUrl: String?): `T$27` + open fun interpret() + open fun addDebugInfoFileName(filename: String): Number + open fun getDebugInfoFileName(index: Number): String? + open fun setDebugLocation( + func: FunctionRef, + expr: ExpressionRef, + fileIndex: Number, + lineNumber: Number, + columnNumber: Number + ) + + open fun copyExpression(expr: ExpressionRef): ExpressionRef +} + +internal external interface MemorySegment { + var offset: ExpressionRef + var data: Uint8Array + var passive: Boolean? + get() = definedExternally + set(value) = definedExternally +} + +internal external interface TableElement { + var offset: ExpressionRef + var names: Array +} + +internal external fun wrapModule(ptr: Number): Module + +internal external fun getExpressionId(expression: ExpressionRef): Number + +internal external fun getExpressionType(expression: ExpressionRef): Type + +internal external fun getExpressionInfo(expression: ExpressionRef): ExpressionInfo + +internal external interface MemorySegmentInfo { + var offset: ExpressionRef + var data: Uint8Array + var passive: Boolean +} + +internal external interface ExpressionInfo { + var id: ExpressionIds + var type: Type +} + +internal external interface BlockInfo : ExpressionInfo { + var name: String + var children: Array +} + +internal external interface IfInfo : ExpressionInfo { + var condition: ExpressionRef + var ifTrue: ExpressionRef + var ifFalse: ExpressionRef +} + +internal external interface LoopInfo : ExpressionInfo { + var name: String + var body: ExpressionRef +} + +internal external interface BreakInfo : ExpressionInfo { + var name: String + var condition: ExpressionRef + var value: ExpressionRef +} + +internal external interface SwitchInfo : ExpressionInfo { + var names: Array + var defaultName: String? + var condition: ExpressionRef + var value: ExpressionRef +} + +internal external interface CallInfo : ExpressionInfo { + var isReturn: Boolean + var target: String + var operands: Array +} + +internal external interface CallIndirectInfo : ExpressionInfo { + var isReturn: Boolean + var target: ExpressionRef + var operands: Array +} + +internal external interface LocalGetInfo : ExpressionInfo { + var index: Number +} + +internal external interface LocalSetInfo : ExpressionInfo { + var isTee: Boolean + var index: Number + var value: ExpressionRef +} + +internal external interface GlobalGetInfo : ExpressionInfo { + var name: String +} + +internal external interface GlobalSetInfo : ExpressionInfo { + var name: String + var value: ExpressionRef +} + +internal external interface LoadInfo : ExpressionInfo { + var isAtomic: Boolean + var isSigned: Boolean + var offset: Number + var bytes: Number + var align: Number + var ptr: ExpressionRef +} + +internal external interface StoreInfo : ExpressionInfo { + var isAtomic: Boolean + var offset: Number + var bytes: Number + var align: Number + var ptr: ExpressionRef + var value: ExpressionRef +} + +internal external interface `T$28` { + var low: Number + var high: Number +} + +internal external interface ConstInfo : ExpressionInfo { + var value: dynamic /* Number | `T$28` */ + get() = definedExternally + set(value) = definedExternally +} + +internal external interface UnaryInfo : ExpressionInfo { + var op: Operations + var value: ExpressionRef +} + +internal external interface BinaryInfo : ExpressionInfo { + var op: Operations + var left: ExpressionRef + var right: ExpressionRef +} + +internal external interface SelectInfo : ExpressionInfo { + var ifTrue: ExpressionRef + var ifFalse: ExpressionRef + var condition: ExpressionRef +} + +internal external interface DropInfo : ExpressionInfo { + var value: ExpressionRef +} + +internal external interface ReturnInfo : ExpressionInfo { + var value: ExpressionRef +} + +internal external interface NopInfo : ExpressionInfo + +internal external interface UnreachableInfo : ExpressionInfo + +internal external interface HostInfo : ExpressionInfo { + var op: Operations + var nameOperand: String? + var operands: Array +} + +internal external interface AtomicRMWInfo : ExpressionInfo { + var op: Operations + var bytes: Number + var offset: Number + var ptr: ExpressionRef + var value: ExpressionRef +} + +internal external interface AtomicCmpxchgInfo : ExpressionInfo { + var bytes: Number + var offset: Number + var ptr: ExpressionRef + var expected: ExpressionRef + var replacement: ExpressionRef +} + +internal external interface AtomicWaitInfo : ExpressionInfo { + var ptr: ExpressionRef + var expected: ExpressionRef + var timeout: ExpressionRef + var expectedType: Type +} + +internal external interface AtomicNotifyInfo : ExpressionInfo { + var ptr: ExpressionRef + var notifyCount: ExpressionRef +} + +internal external interface AtomicFenceInfo : ExpressionInfo { + var order: Number +} + +internal external interface SIMDExtractInfo : ExpressionInfo { + var op: Operations + var vec: ExpressionRef + var index: ExpressionRef +} + +internal external interface SIMDReplaceInfo : ExpressionInfo { + var op: Operations + var vec: ExpressionRef + var index: ExpressionRef + var value: ExpressionRef +} + +internal external interface SIMDShuffleInfo : ExpressionInfo { + var left: ExpressionRef + var right: ExpressionRef + var mask: Array +} + +internal external interface SIMDTernaryInfo : ExpressionInfo { + var op: Operations + var a: ExpressionRef + var b: ExpressionRef + var c: ExpressionRef +} + +internal external interface SIMDShiftInfo : ExpressionInfo { + var op: Operations + var vec: ExpressionRef + var shift: ExpressionRef +} + +internal external interface SIMDLoadInfo : ExpressionInfo { + var op: Operations + var offset: Number + var align: Number + var ptr: ExpressionRef +} + +internal external interface MemoryInitInfo : ExpressionInfo { + var segment: Number + var dest: ExpressionRef + var offset: ExpressionRef + var size: ExpressionRef +} + +internal external interface MemoryDropInfo : ExpressionInfo { + var segment: Number +} + +internal external interface MemoryCopyInfo : ExpressionInfo { + var dest: ExpressionRef + var source: ExpressionRef + var size: ExpressionRef +} + +internal external interface MemoryFillInfo : ExpressionInfo { + var dest: ExpressionRef + var value: ExpressionRef + var size: ExpressionRef +} + +internal external interface RefNullInfo : ExpressionInfo + +internal external interface RefIsNullInfo : ExpressionInfo { + var value: ExpressionRef +} + +internal external interface RefFuncInfo : ExpressionInfo { + var func: String +} + +internal external interface TryInfo : ExpressionInfo { + var body: ExpressionRef + var catchBody: ExpressionRef +} + +internal external interface ThrowInfo : ExpressionInfo { + var event: String + var operands: Array +} + +internal external interface RethrowInfo : ExpressionInfo { + var exnref: ExpressionRef +} + +internal external interface BrOnExnInfo : ExpressionInfo { + var name: String + var event: String + var exnref: ExpressionRef +} + +internal external interface PopInfo : ExpressionInfo + +internal external interface PushInfo : ExpressionInfo { + var value: ExpressionRef +} + +internal external fun getFunctionInfo(func: FunctionRef): FunctionInfo + +internal external interface FunctionInfo { + var name: String + var module: String? + var base: String? + var params: Type + var results: Type + var vars: Array + var body: ExpressionRef +} + +internal external fun getGlobalInfo(global: GlobalRef): GlobalInfo + +internal external interface GlobalInfo { + var name: String + var module: String? + var base: String? + var type: Type + var mutable: Boolean + var init: ExpressionRef +} + +internal external fun getExportInfo(export_: ExportRef): ExportInfo + +internal external interface ExportInfo { + var kind: ExternalKinds + var name: String + var value: String +} + +internal external fun getEventInfo(event: EventRef): EventInfo + +internal external interface EventInfo { + var name: String + var module: String? + var base: String? + var attribute: Number + var params: Type + var results: Type +} + +internal external fun getSideEffects(expr: ExpressionRef, features: Features): SideEffects + +internal external enum class SideEffects { + None, + Branches, + Calls, + ReadsLocal, + WritesLocal, + ReadsGlobal, + WritesGlobal, + ReadsMemory, + WritesMemory, + ImplicitTrap, + IsAtomic, + Throws, + Any +} + +internal external fun emitText(expression: ExpressionRef): String + +internal external fun emitText(expression: Module): String + +internal external fun readBinary(data: Uint8Array): Module + +internal external fun parseText(text: String): Module + +internal external fun getOptimizeLevel(): Number + +internal external fun setOptimizeLevel(level: Number): Number + +internal external fun getShrinkLevel(): Number + +internal external fun setShrinkLevel(level: Number): Number + +internal external fun getDebugInfo(): Boolean + +internal external fun setDebugInfo(on: Boolean) + +internal external fun getLowMemoryUnused(): Boolean + +internal external fun setLowMemoryUnused(on: Boolean) + +internal external fun getPassArgument(key: String): String? + +internal external fun setPassArgument(key: String, value: String?) + +internal external fun clearPassArguments() + +internal external fun getAlwaysInlineMaxSize(): Number + +internal external fun setAlwaysInlineMaxSize(size: Number) + +internal external fun getFlexibleInlineMaxSize(): Number + +internal external fun setFlexibleInlineMaxSize(size: Number) + +internal external fun getOneCallerInlineMaxSize(): Number + +internal external fun setOneCallerInlineMaxSize(size: Number) + +internal external fun exit(status: Number) + +internal open external class Relooper(module: Module) { + open fun addBlock(expression: ExpressionRef): RelooperBlockRef + open fun addBranch(from: RelooperBlockRef, to: RelooperBlockRef, condition: ExpressionRef, code: ExpressionRef) + open fun addBlockWithSwitch(code: ExpressionRef, condition: ExpressionRef): RelooperBlockRef + open fun addBranchForSwitch( + from: RelooperBlockRef, + to: RelooperBlockRef, + indexes: Array, + code: ExpressionRef + ) + + open fun renderAndDispose(entry: RelooperBlockRef, labelHelper: Number): ExpressionRef +} diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.typealiases.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.typealiases.kt new file mode 100644 index 000000000..523b13b40 --- /dev/null +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/binaryen/index.binaryen.typealiases.kt @@ -0,0 +1,16 @@ +/* + * 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. + */ + +@file:Suppress("PackageDirectoryMismatch", "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", "KDocMissingDocumentation") + +package space.kscience.kmath.internal.binaryen + +internal typealias Type = Number +internal typealias ExpressionRef = Number +internal typealias FunctionRef = Number +internal typealias GlobalRef = Number +internal typealias ExportRef = Number +internal typealias EventRef = Number +internal typealias RelooperBlockRef = Number diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/emitter/emitter.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/emitter/emitter.kt similarity index 91% rename from kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/emitter/emitter.kt rename to kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/emitter/emitter.kt index 7707f53a2..1f7b09af8 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/emitter/emitter.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/emitter/emitter.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package space.kscience.kmath.estree.internal.emitter +package space.kscience.kmath.internal.emitter internal open external class Emitter { constructor(obj: Any) diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/estree/estree.extensions.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.extensions.kt similarity index 97% rename from kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/estree/estree.extensions.kt rename to kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.extensions.kt index 9ba11e085..3aa31f921 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/estree/estree.extensions.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.extensions.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package space.kscience.kmath.estree.internal.estree +package space.kscience.kmath.internal.estree internal fun Program(sourceType: String, vararg body: dynamic) = object : Program { override var type = "Program" diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/estree/estree.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.kt similarity index 99% rename from kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/estree/estree.kt rename to kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.kt index a0e42db5d..e5254013e 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/estree/estree.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/estree/estree.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package space.kscience.kmath.estree.internal.estree +package space.kscience.kmath.internal.estree import kotlin.js.RegExp diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/stream/stream.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/stream/stream.kt similarity index 70% rename from kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/stream/stream.kt rename to kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/stream/stream.kt index 4bdeeea0b..52be5530f 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/stream/stream.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/stream/stream.kt @@ -3,9 +3,9 @@ * 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.estree.internal.stream +package space.kscience.kmath.internal.stream -import space.kscience.kmath.estree.internal.emitter.Emitter +import space.kscience.kmath.internal.emitter.Emitter internal open external class Stream : Emitter { open fun pipe(dest: Any, options: Any): Any diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/tsstdlib/lib.es2015.iterable.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/tsstdlib/lib.es2015.iterable.kt similarity index 94% rename from kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/tsstdlib/lib.es2015.iterable.kt rename to kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/tsstdlib/lib.es2015.iterable.kt index a3c721ed4..9c012e3a3 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/tsstdlib/lib.es2015.iterable.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/tsstdlib/lib.es2015.iterable.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package space.kscience.kmath.estree.internal.tsstdlib +package space.kscience.kmath.internal.tsstdlib internal external interface IteratorYieldResult { var done: Boolean? diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/tsstdlib/lib.es5.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/tsstdlib/lib.es5.kt similarity index 88% rename from kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/tsstdlib/lib.es5.kt rename to kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/tsstdlib/lib.es5.kt index d2413b3e3..0cd395f2c 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/tsstdlib/lib.es5.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/tsstdlib/lib.es5.kt @@ -5,7 +5,7 @@ @file:Suppress("UNUSED_TYPEALIAS_PARAMETER", "DEPRECATION") -package space.kscience.kmath.estree.internal.tsstdlib +package space.kscience.kmath.internal.tsstdlib import kotlin.js.RegExp @@ -38,6 +38,8 @@ internal external interface RegExpConstructor { var lastMatch: String } +internal typealias Record = Any + internal external interface ConcatArray { var length: Number @@ -85,3 +87,10 @@ internal external interface ArrayLike { } internal typealias Extract = Any + +internal external interface PromiseLike { + fun then( + onfulfilled: ((value: T) -> Any?)? = definedExternally, + onrejected: ((reason: Any) -> Any?)? = definedExternally + ): PromiseLike +} diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/lib.dom.WebAssembly.module_dukat.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/lib.dom.WebAssembly.module_dukat.kt new file mode 100644 index 000000000..3754c3eff --- /dev/null +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/lib.dom.WebAssembly.module_dukat.kt @@ -0,0 +1,236 @@ +/* + * 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. + */ + +@file:JsQualifier("WebAssembly") + +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", + "OVERRIDING_FINAL_MEMBER", + "RETURN_TYPE_MISMATCH_ON_OVERRIDE", + "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", + "ClassName", +) + +package space.kscience.kmath.internal.webassembly + +import space.kscience.kmath.internal.tsstdlib.PromiseLike +import org.khronos.webgl.ArrayBuffer +import org.khronos.webgl.ArrayBufferView +import org.khronos.webgl.Uint8Array +import org.w3c.fetch.Response +import kotlin.js.Promise + +@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") +internal external interface CompileError { + companion object { + var prototype: CompileError + } +} + +@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") +internal external interface Global { + var value: Any + fun valueOf(): Any + + companion object { + var prototype: Global + } +} + +@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") +@JsName("Instance") +internal external interface Instance1 { + var exports: Exports + + companion object { + var prototype: Instance + } +} + +@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") +internal external interface LinkError { + companion object { + var prototype: LinkError + } +} + +@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") +internal external interface Memory { + var buffer: ArrayBuffer + fun grow(delta: Number): Number + + companion object { + var prototype: Memory + } +} + +@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") +@JsName("Module") +internal external interface Module1 { + companion object { + var prototype: Module + fun customSections(moduleObject: Module, sectionName: String): Array + fun exports(moduleObject: Module): Array + fun imports(moduleObject: Module): Array + } +} + +@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") +internal external interface RuntimeError { + companion object { + var prototype: RuntimeError + } +} + +@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") +internal external interface Table { + var length: Number + fun get(index: Number): Function<*>? + fun grow(delta: Number): Number + fun set(index: Number, value: Function<*>?) + + companion object { + var prototype: Table + } +} + +internal external interface GlobalDescriptor { + var mutable: Boolean? + get() = definedExternally + set(value) = definedExternally + var value: String /* "f32" | "f64" | "i32" | "i64" */ +} + +internal external interface MemoryDescriptor { + var initial: Number + var maximum: Number? + get() = definedExternally + set(value) = definedExternally +} + +internal external interface ModuleExportDescriptor { + var kind: String /* "function" | "global" | "memory" | "table" */ + var name: String +} + +internal external interface ModuleImportDescriptor { + var kind: String /* "function" | "global" | "memory" | "table" */ + var module: String + var name: String +} + +internal external interface TableDescriptor { + var element: String /* "anyfunc" */ + var initial: Number + var maximum: Number? + get() = definedExternally + set(value) = definedExternally +} + +internal external interface WebAssemblyInstantiatedSource { + var instance: Instance + var module: Module +} + +internal external fun compile(bytes: ArrayBufferView): Promise + +internal external fun compile(bytes: ArrayBuffer): Promise + +internal external fun compileStreaming(source: Response): Promise + +internal external fun compileStreaming(source: Promise): Promise + +internal external fun instantiate( + bytes: ArrayBufferView, + importObject: Imports = definedExternally, +): Promise + +internal external fun instantiate(bytes: ArrayBufferView): Promise + +internal external fun instantiate( + bytes: ArrayBuffer, + importObject: Imports = definedExternally, +): dynamic /* Promise | Promise */ + +internal external fun instantiate(bytes: ArrayBuffer): dynamic /* Promise | Promise */ + +internal external fun instantiate(moduleObject: Module, importObject: Imports = definedExternally): Promise + +internal external fun instantiate(moduleObject: Module): Promise + +internal external fun instantiateStreaming( + response: Response, + importObject: Imports = definedExternally, +): Promise + +internal external fun instantiateStreaming(response: Response): Promise + +internal external fun instantiateStreaming( + response: PromiseLike, + importObject: Imports = definedExternally, +): Promise + +internal external fun instantiateStreaming(response: PromiseLike): Promise + +internal external fun validate(bytes: ArrayBufferView): Boolean + +internal external fun validate(bytes: ArrayBuffer): Boolean + +internal external interface `T$0` { + var name: String + var kind: String +} + +internal external interface `T$1` { + var module: String + var name: String + var kind: String +} + +internal open external class Module { + constructor(bufferSource: ArrayBuffer) + constructor(bufferSource: Uint8Array) + + companion object { + fun customSections(module: Module, sectionName: String): Array + fun exports(module: Module): Array<`T$0`> + fun imports(module: Module): Array<`T$1`> + } +} + +@JsName("Instance") +internal open external class Instance(module: Module, importObject: Any = definedExternally) { + open var exports: Any +} + +@JsName("Memory") +internal open external class Memory1(memoryDescriptor: MemoryDescriptor) { + open var buffer: ArrayBuffer + open fun grow(numPages: Number): Number +} + +@JsName("Table") +internal open external class Table1(tableDescriptor: TableDescriptor) { + open var length: Number + open fun get(index: Number): Function<*> + open fun grow(numElements: Number): Number + open fun set(index: Number, value: Function<*>) +} + +internal external fun compile(bufferSource: Uint8Array): Promise + +internal external interface ResultObject { + var module: Module + var instance: Instance +} + +internal external fun instantiate( + bufferSource: Uint8Array, + importObject: Any = definedExternally, +): Promise + +internal external fun instantiate(bufferSource: Uint8Array): Promise + +internal external fun validate(bufferSource: Uint8Array): Boolean diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/nonDeclarations.WebAssembly.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/nonDeclarations.WebAssembly.kt new file mode 100644 index 000000000..c5023c384 --- /dev/null +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/internal/webassembly/nonDeclarations.WebAssembly.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. + */ + +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", + "OVERRIDING_FINAL_MEMBER", + "RETURN_TYPE_MISMATCH_ON_OVERRIDE", + "CONFLICTING_OVERLOADS", + "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING", +) + +package space.kscience.kmath.internal.webassembly + +import space.kscience.kmath.internal.tsstdlib.Record + +internal typealias Exports = Record | Global | Memory | Table */> + +internal typealias ModuleImports = Record | Global | Memory | Table | Number */> + +internal typealias Imports = Record + +internal typealias CompileError1 = Error + +internal typealias LinkError1 = Error + +internal typealias RuntimeError1 = Error 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 new file mode 100644 index 000000000..8fd3c9fb9 --- /dev/null +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt @@ -0,0 +1,160 @@ +/* + * 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.wasm.internal + +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.expressions.MST +import space.kscience.kmath.expressions.MST.* +import space.kscience.kmath.internal.binaryen.* +import space.kscience.kmath.internal.webassembly.Instance +import space.kscience.kmath.misc.StringSymbol +import space.kscience.kmath.operations.* +import space.kscience.kmath.internal.binaryen.Module as BinaryenModule +import space.kscience.kmath.internal.webassembly.Module as WasmModule + +private val spreader = eval("(obj, args) => obj(...args)") + +@Suppress("UnsafeCastFromDynamic") +internal sealed class WasmBuilder( + val binaryenType: Type, + val algebra: Algebra, + val target: MST, +) where T : Number { + val keys: MutableList = mutableListOf() + lateinit var ctx: BinaryenModule + + open fun visitSymbolic(mst: Symbolic): ExpressionRef { + try { + algebra.bindSymbol(mst.value) + } catch (ignored: Throwable) { + null + }?.let { return visitNumeric(Numeric(it)) } + + var idx = keys.indexOf(mst.value) + + if (idx == -1) { + keys += mst.value + idx = keys.lastIndex + } + + return ctx.local.get(idx, binaryenType) + } + + abstract fun visitNumeric(mst: Numeric): ExpressionRef + + open fun visitUnary(mst: Unary): ExpressionRef = + error("Unary operation ${mst.operation} not defined in $this") + + open fun visitBinary(mst: Binary): ExpressionRef = + error("Binary operation ${mst.operation} not defined in $this") + + open fun createModule(): BinaryenModule = js("new \$module\$binaryen.Module()") + + fun visit(mst: MST): ExpressionRef = when (mst) { + is Symbolic -> visitSymbolic(mst) + is Numeric -> visitNumeric(mst) + + is Unary -> when { + algebra is NumericAlgebra && mst.value is Numeric -> visitNumeric( + Numeric(algebra.unaryOperationFunction(mst.operation)(algebra.number((mst.value as Numeric).value)))) + + else -> visitUnary(mst) + } + + is Binary -> when { + algebra is NumericAlgebra && mst.left is Numeric && mst.right is Numeric -> visitNumeric(Numeric( + algebra.binaryOperationFunction(mst.operation) + .invoke(algebra.number((mst.left as Numeric).value), algebra.number((mst.right as Numeric).value)) + )) + + else -> visitBinary(mst) + } + } + + val instance by lazy { + val c = WasmModule(with(createModule()) { + ctx = this + val expr = visit(target) + + addFunction( + "executable", + createType(Array(keys.size) { binaryenType }), + binaryenType, + arrayOf(), + expr + ) + + setOptimizeLevel(3) + optimizeFunction("executable") + addFunctionExport("executable", "executable") + val res = emitBinary() + dispose() + res + }) + + val i = Instance(c, js("{}") as Any) + val symbols = keys.map(::StringSymbol) + keys.clear() + + Expression { args -> + val params = symbols.map(args::getValue).toTypedArray() + spreader(i.exports.asDynamic().executable, params) as T + } + } +} + +internal class DoubleWasmBuilder(target: MST) : WasmBuilder(f64, DoubleField, target) { + override fun createModule(): BinaryenModule = readBinary(f64StandardFunctions) + + override fun visitNumeric(mst: Numeric): ExpressionRef = ctx.f64.const(mst.value) + + override fun visitUnary(mst: Unary): ExpressionRef = when (mst.operation) { + GroupOperations.MINUS_OPERATION -> ctx.f64.neg(visit(mst.value)) + GroupOperations.PLUS_OPERATION -> visit(mst.value) + PowerOperations.SQRT_OPERATION -> ctx.f64.sqrt(visit(mst.value)) + TrigonometricOperations.SIN_OPERATION -> ctx.call("sin", arrayOf(visit(mst.value)), f64) + TrigonometricOperations.COS_OPERATION -> ctx.call("cos", arrayOf(visit(mst.value)), f64) + TrigonometricOperations.TAN_OPERATION -> ctx.call("tan", arrayOf(visit(mst.value)), f64) + TrigonometricOperations.ASIN_OPERATION -> ctx.call("asin", arrayOf(visit(mst.value)), f64) + TrigonometricOperations.ACOS_OPERATION -> ctx.call("acos", arrayOf(visit(mst.value)), f64) + TrigonometricOperations.ATAN_OPERATION -> ctx.call("atan", arrayOf(visit(mst.value)), f64) + ExponentialOperations.SINH_OPERATION -> ctx.call("sinh", arrayOf(visit(mst.value)), f64) + ExponentialOperations.COSH_OPERATION -> ctx.call("cosh", arrayOf(visit(mst.value)), f64) + ExponentialOperations.TANH_OPERATION -> ctx.call("tanh", arrayOf(visit(mst.value)), f64) + ExponentialOperations.ASINH_OPERATION -> ctx.call("asinh", arrayOf(visit(mst.value)), f64) + ExponentialOperations.ACOSH_OPERATION -> ctx.call("acosh", arrayOf(visit(mst.value)), f64) + ExponentialOperations.ATANH_OPERATION -> ctx.call("atanh", arrayOf(visit(mst.value)), f64) + ExponentialOperations.EXP_OPERATION -> ctx.call("exp", arrayOf(visit(mst.value)), f64) + ExponentialOperations.LN_OPERATION -> ctx.call("log", arrayOf(visit(mst.value)), f64) + else -> super.visitUnary(mst) + } + + override fun visitBinary(mst: Binary): ExpressionRef = when (mst.operation) { + GroupOperations.PLUS_OPERATION -> ctx.f64.add(visit(mst.left), visit(mst.right)) + GroupOperations.MINUS_OPERATION -> ctx.f64.sub(visit(mst.left), visit(mst.right)) + RingOperations.TIMES_OPERATION -> ctx.f64.mul(visit(mst.left), visit(mst.right)) + FieldOperations.DIV_OPERATION -> ctx.f64.div(visit(mst.left), visit(mst.right)) + PowerOperations.POW_OPERATION -> ctx.call("pow", arrayOf(visit(mst.left), visit(mst.right)), f64) + else -> super.visitBinary(mst) + } +} + +internal class IntWasmBuilder(target: MST) : WasmBuilder(i32, IntRing, target) { + override fun visitNumeric(mst: Numeric): ExpressionRef = ctx.i32.const(mst.value) + + override fun visitUnary(mst: Unary): ExpressionRef = when (mst.operation) { + GroupOperations.MINUS_OPERATION -> ctx.i32.sub(ctx.i32.const(0), visit(mst.value)) + GroupOperations.PLUS_OPERATION -> visit(mst.value) + else -> super.visitUnary(mst) + } + + override fun visitBinary(mst: Binary): ExpressionRef = when (mst.operation) { + GroupOperations.PLUS_OPERATION -> ctx.i32.add(visit(mst.left), visit(mst.right)) + GroupOperations.MINUS_OPERATION -> ctx.i32.sub(visit(mst.left), visit(mst.right)) + RingOperations.TIMES_OPERATION -> ctx.i32.mul(visit(mst.left), visit(mst.right)) + else -> super.visitBinary(mst) + } +} diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/f64StandardFunctions.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/f64StandardFunctions.kt new file mode 100644 index 000000000..21a88b5d0 --- /dev/null +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/f64StandardFunctions.kt @@ -0,0 +1,13 @@ +/* + * 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.wasm.internal + +import space.kscience.kmath.internal.base64.toUint8Array + +internal val f64StandardFunctions by lazy { toUint8Array(B) } + +private const val B = + "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" diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt new file mode 100644 index 000000000..394a0567e --- /dev/null +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt @@ -0,0 +1,91 @@ +/* + * 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.wasm + +import space.kscience.kmath.estree.compileWith +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.expressions.MST +import space.kscience.kmath.expressions.invoke +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.wasm.internal.DoubleWasmBuilder +import space.kscience.kmath.wasm.internal.IntWasmBuilder + +/** + * Compiles an [MST] to WASM in the context of reals. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public fun DoubleField.expression(mst: MST): Expression = + DoubleWasmBuilder(mst).instance + +/** + * Compiles an [MST] to WASM in the context of integers. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public fun IntRing.expression(mst: MST): Expression = + IntWasmBuilder(mst).instance + +/** + * Create a compiled expression with given [MST] and given [algebra]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public fun MST.compileToExpression(algebra: IntRing): Expression = compileWith(algebra) + + +/** + * Compile given MST to expression and evaluate it against [arguments]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public fun MST.compile(algebra: IntRing, arguments: Map): Int = + compileToExpression(algebra).invoke(arguments) + + +/** + * Compile given MST to expression and evaluate it against [arguments]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public fun MST.compile(algebra: IntRing, vararg arguments: Pair): Int = + compileToExpression(algebra)(*arguments) + +/** + * Create a compiled expression with given [MST] and given [algebra]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public fun MST.compileToExpression(algebra: DoubleField): Expression = compileWith(algebra) + + +/** + * Compile given MST to expression and evaluate it against [arguments]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public fun MST.compile(algebra: DoubleField, arguments: Map): Double = + compileToExpression(algebra).invoke(arguments) + + +/** + * Compile given MST to expression and evaluate it against [arguments]. + * + * @author Iaroslav Postovalov + */ +@UnstableKMathAPI +public fun MST.compile(algebra: DoubleField, vararg arguments: Pair): Double = + compileToExpression(algebra).invoke(*arguments) diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/TestExecutionTime.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/TestExecutionTime.kt new file mode 100644 index 000000000..01746ddb6 --- /dev/null +++ b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/TestExecutionTime.kt @@ -0,0 +1,72 @@ +/* + * 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.ast + +import space.kscience.kmath.expressions.* +import space.kscience.kmath.misc.symbol +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.ExtendedField +import space.kscience.kmath.operations.bindSymbol +import space.kscience.kmath.operations.invoke +import kotlin.math.sin +import kotlin.random.Random +import kotlin.test.Test +import kotlin.time.measureTime +import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression +import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression + +internal class TestExecutionTime { + private companion object { + private const val times = 1_000_000 + private val x by symbol + private val algebra: ExtendedField = DoubleField + + private val functional = DoubleField.expressionInExtendedField { + bindSymbol(x) * const(2.0) + const(2.0) / bindSymbol(x) - const(16.0) / sin(bindSymbol(x)) + } + + private val node = MstExtendedField { + bindSymbol(x) * number(2.0) + number(2.0) / bindSymbol(x) - number(16.0) / sin(bindSymbol(x)) + } + + private val mst = node.toExpression(DoubleField) + private val wasm = node.wasmCompileToExpression(DoubleField) + private val estree = node.estreeCompileToExpression(DoubleField) + + // In JavaScript, the expression below is implemented like + // _no_name_provided__125.prototype.invoke_178 = function (args) { + // var tmp = getValue(args, raw$_get_x__3(this._$x$delegate_2)) * 2.0 + 2.0 / getValue(args, raw$_get_x__3(this._$x$delegate_2)); + // var tmp0_sin_0_5 = getValue(args, raw$_get_x__3(this._$x$delegate_2)); + // return tmp - 16.0 / Math.sin(tmp0_sin_0_5); + // }; + + private val raw = Expression { args -> + args.getValue(x) * 2.0 + 2.0 / args.getValue(x) - 16.0 / sin(args.getValue(x)) + } + } + + private fun invokeAndSum(name: String, expr: Expression) { + println(name) + val rng = Random(0) + var sum = 0.0 + measureTime { repeat(times) { sum += expr(x to rng.nextDouble()) } }.also(::println) + } + + @Test + fun functionalExpression() = invokeAndSum("functional", functional) + + @Test + fun mstExpression() = invokeAndSum("mst", mst) + + @Test + fun wasmExpression() = invokeAndSum("wasm", wasm) + + @Test + fun estreeExpression() = invokeAndSum("estree", wasm) + + @Test + fun rawExpression() = invokeAndSum("raw", raw) +} diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt new file mode 100644 index 000000000..6b5b1b83d --- /dev/null +++ b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -0,0 +1,39 @@ +/* + * 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.ast + +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.expressions.MST +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.estree.compile as estreeCompile +import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression +import space.kscience.kmath.wasm.compile as wasmCompile +import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression + +private object WasmCompilerTestContext : CompilerTestContext { + override fun MST.compileToExpression(algebra: IntRing): Expression = wasmCompileToExpression(algebra) + override fun MST.compile(algebra: IntRing, arguments: Map): Int = wasmCompile(algebra, arguments) + override fun MST.compileToExpression(algebra: DoubleField): Expression = wasmCompileToExpression(algebra) + + override fun MST.compile(algebra: DoubleField, arguments: Map): Double = + wasmCompile(algebra, arguments) +} + +private object ESTreeCompilerTestContext : CompilerTestContext { + override fun MST.compileToExpression(algebra: IntRing): Expression = estreeCompileToExpression(algebra) + override fun MST.compile(algebra: IntRing, arguments: Map): Int = estreeCompile(algebra, arguments) + override fun MST.compileToExpression(algebra: DoubleField): Expression = estreeCompileToExpression(algebra) + + override fun MST.compile(algebra: DoubleField, arguments: Map): Double = + estreeCompile(algebra, arguments) +} + +internal actual inline fun runCompilerTest(action: CompilerTestContext.() -> Unit) { + action(WasmCompilerTestContext) + action(ESTreeCompilerTestContext) +} diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeConsistencyWithInterpreter.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeConsistencyWithInterpreter.kt deleted file mode 100644 index 5823518ce..000000000 --- a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeConsistencyWithInterpreter.kt +++ /dev/null @@ -1,94 +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. - */ - -package space.kscience.kmath.estree - -import space.kscience.kmath.complex.ComplexField -import space.kscience.kmath.complex.toComplex -import space.kscience.kmath.expressions.* -import space.kscience.kmath.misc.Symbol -import space.kscience.kmath.operations.ByteRing -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.invoke -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class TestESTreeConsistencyWithInterpreter { - - @Test - fun mstSpace() { - - val mst = MstGroup { - binaryOperationFunction("+")( - unaryOperationFunction("+")( - number(3.toByte()) - (number(2.toByte()) + (scale( - add(number(1), number(1)), - 2.0 - ) + number(1.toByte()) * 3.toByte() - number(1.toByte()))) - ), - - number(1) - ) + bindSymbol("x") + zero - } - - assertEquals( - mst.interpret(MstGroup, Symbol.x to MST.Numeric(2)), - mst.compile(MstGroup, Symbol.x to MST.Numeric(2)) - ) - } - - @Test - fun byteRing() { - val mst = MstRing { - binaryOperationFunction("+")( - unaryOperationFunction("+")( - (bindSymbol("x") - (2.toByte() + (scale( - add(number(1), number(1)), - 2.0 - ) + 1.toByte()))) * 3.0 - 1.toByte() - ), - - number(1) - ) * number(2) - } - - assertEquals( - mst.interpret(ByteRing, Symbol.x to 3.toByte()), - mst.compile(ByteRing, Symbol.x to 3.toByte()) - ) - } - - @Test - fun realField() { - val mst = MstField { - +(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( - (3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0 - + number(1), - number(1) / 2 + number(2.0) * one - ) + zero - } - - assertEquals( - mst.interpret(DoubleField, Symbol.x to 2.0), - mst.compile(DoubleField, Symbol.x to 2.0) - ) - } - - @Test - fun complexField() { - val mst = MstField { - +(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( - (3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0 - + number(1), - number(1) / 2 + number(2.0) * one - ) + zero - } - - assertEquals( - mst.interpret(ComplexField, Symbol.x to 2.0.toComplex()), - mst.compile(ComplexField, Symbol.x to 2.0.toComplex()) - ) - } -} diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeOperationsSupport.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeOperationsSupport.kt deleted file mode 100644 index a1bff92d0..000000000 --- a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeOperationsSupport.kt +++ /dev/null @@ -1,47 +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. - */ - -package space.kscience.kmath.estree - -import space.kscience.kmath.expressions.MstExtendedField -import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.invoke -import kotlin.random.Random -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class TestESTreeOperationsSupport { - @Test - fun testUnaryOperationInvocation() { - val expression = MstExtendedField { -bindSymbol("x") }.compileToExpression(DoubleField) - val res = expression("x" to 2.0) - assertEquals(-2.0, res) - } - - @Test - fun testBinaryOperationInvocation() { - val expression = MstExtendedField { -bindSymbol("x") + number(1.0) }.compileToExpression(DoubleField) - val res = expression("x" to 2.0) - assertEquals(-1.0, res) - } - - @Test - fun testConstProductInvocation() { - val res = MstExtendedField { bindSymbol("x") * 2 }.compileToExpression(DoubleField)("x" to 2.0) - assertEquals(4.0, res) - } - - @Test - fun testMultipleCalls() { - val e = - MstExtendedField { sin(bindSymbol("x")).pow(4) - 6 * bindSymbol("x") / tanh(bindSymbol("x")) } - .compileToExpression(DoubleField) - val r = Random(0) - var s = 0.0 - repeat(1000000) { s += e("x" to r.nextDouble()) } - println(s) - } -} diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeSpecialization.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeSpecialization.kt deleted file mode 100644 index b5ae1ca3f..000000000 --- a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeSpecialization.kt +++ /dev/null @@ -1,69 +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. - */ - -package space.kscience.kmath.estree - -import space.kscience.kmath.expressions.MstExtendedField -import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.invoke -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class TestESTreeSpecialization { - @Test - fun testUnaryPlus() { - val expr = MstExtendedField { unaryOperationFunction("+")(bindSymbol("x")) }.compileToExpression(DoubleField) - assertEquals(2.0, expr("x" to 2.0)) - } - - @Test - fun testUnaryMinus() { - val expr = MstExtendedField { unaryOperationFunction("-")(bindSymbol("x")) }.compileToExpression(DoubleField) - assertEquals(-2.0, expr("x" to 2.0)) - } - - @Test - fun testAdd() { - val expr = MstExtendedField { - binaryOperationFunction("+")(bindSymbol("x"), - bindSymbol("x")) - }.compileToExpression(DoubleField) - assertEquals(4.0, expr("x" to 2.0)) - } - - @Test - fun testSine() { - val expr = MstExtendedField { unaryOperationFunction("sin")(bindSymbol("x")) }.compileToExpression(DoubleField) - assertEquals(0.0, expr("x" to 0.0)) - } - - @Test - fun testMinus() { - val expr = MstExtendedField { - binaryOperationFunction("-")(bindSymbol("x"), - bindSymbol("x")) - }.compileToExpression(DoubleField) - assertEquals(0.0, expr("x" to 2.0)) - } - - @Test - fun testDivide() { - val expr = MstExtendedField { - binaryOperationFunction("/")(bindSymbol("x"), - bindSymbol("x")) - }.compileToExpression(DoubleField) - assertEquals(1.0, expr("x" to 2.0)) - } - - @Test - fun testPower() { - val expr = MstExtendedField { - binaryOperationFunction("pow")(bindSymbol("x"), number(2)) - }.compileToExpression(DoubleField) - - assertEquals(4.0, expr("x" to 2.0)) - } -} diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeVariables.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeVariables.kt deleted file mode 100644 index 1effe14e1..000000000 --- a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/estree/TestESTreeVariables.kt +++ /dev/null @@ -1,28 +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. - */ - -package space.kscience.kmath.estree - -import space.kscience.kmath.expressions.MstRing -import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.operations.ByteRing -import space.kscience.kmath.operations.invoke -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -internal class TestESTreeVariables { - @Test - fun testVariable() { - val expr = MstRing{ bindSymbol("x") }.compileToExpression(ByteRing) - assertEquals(1.toByte(), expr("x" to 1.toByte())) - } - - @Test - fun testUndefinedVariableFails() { - val expr = MstRing { bindSymbol("x") }.compileToExpression(ByteRing) - assertFailsWith { expr() } - } -} diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/wasm/TestWasmSpecific.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/wasm/TestWasmSpecific.kt new file mode 100644 index 000000000..dd5452d04 --- /dev/null +++ b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/wasm/TestWasmSpecific.kt @@ -0,0 +1,53 @@ +/* + * 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.wasm + +import space.kscience.kmath.expressions.MstExtendedField +import space.kscience.kmath.expressions.MstRing +import space.kscience.kmath.expressions.invoke +import space.kscience.kmath.misc.symbol +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.bindSymbol +import space.kscience.kmath.operations.invoke +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class TestWasmSpecific { + @Test + fun int() { + val res = MstRing { number(100000000) + number(10000000) }.compile(IntRing) + assertEquals(110000000, res) + } + + @Test + fun real() { + val res = MstExtendedField { number(100000000) + number(2).pow(10) }.compile(DoubleField) + assertEquals(100001024.0, res) + } + + @Test + fun argsPassing() { + val res = MstExtendedField { bindSymbol(y) + bindSymbol(x).pow(10) }.compile( + DoubleField, + x to 2.0, + y to 100000000.0, + ) + + assertEquals(100001024.0, res) + } + + @Test + fun powFunction() { + val expr = MstExtendedField { bindSymbol(x).pow(1.0 / 6.0) }.compileToExpression(DoubleField) + assertEquals(0.9730585187140817, expr(x to 0.8488554755054833)) + } + + private companion object { + private val x by symbol + private val y by symbol + } +} 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 bdd8f52b6..39ebf049d 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 @@ -342,8 +342,8 @@ internal class AsmBuilder( val MAP_INTRINSICS_TYPE: Type by lazy { getObjectType("space/kscience/kmath/asm/internal/MapIntrinsics") } /** - * ASM Type for [kscience.kmath.expressions.Symbol]. + * ASM Type for [space.kscience.kmath.misc.Symbol]. */ - val SYMBOL_TYPE: Type by lazy { getObjectType("space/kscience/kmath/expressions/Symbol") } + val SYMBOL_TYPE: Type by lazy { getObjectType("space/kscience/kmath/misc/Symbol") } } } diff --git a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/codegenUtils.kt b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/codegenUtils.kt index 560780f99..cfac59847 100644 --- a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/codegenUtils.kt +++ b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/codegenUtils.kt @@ -52,7 +52,7 @@ internal inline fun MethodVisitor.instructionAdapter(block: InstructionAdapter.( * * @author Iaroslav Postovalov */ -internal fun MethodVisitor.label(): Label = Label().also { visitLabel(it) } +internal fun MethodVisitor.label(): Label = Label().also(::visitLabel) /** * Creates a class name for [Expression] subclassed to implement [mst] provided. diff --git a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/ast/rendering/multiplatformToString.kt b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/ast/rendering/multiplatformToString.kt new file mode 100644 index 000000000..556adbe7d --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/ast/rendering/multiplatformToString.kt @@ -0,0 +1,9 @@ +/* + * 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.ast.rendering + +internal actual fun Double.multiplatformToString(): String = toString() +internal actual fun Float.multiplatformToString(): String = toString() diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmConsistencyWithInterpreter.kt b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmConsistencyWithInterpreter.kt deleted file mode 100644 index 77cfb2241..000000000 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmConsistencyWithInterpreter.kt +++ /dev/null @@ -1,94 +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. - */ - -package space.kscience.kmath.asm - -import space.kscience.kmath.complex.ComplexField -import space.kscience.kmath.complex.toComplex -import space.kscience.kmath.expressions.* -import space.kscience.kmath.misc.Symbol.Companion.x -import space.kscience.kmath.operations.ByteRing -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.invoke -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class TestAsmConsistencyWithInterpreter { - - @Test - fun mstSpace() { - - val mst = MstGroup { - binaryOperationFunction("+")( - unaryOperationFunction("+")( - number(3.toByte()) - (number(2.toByte()) + (scale( - add(number(1), number(1)), - 2.0 - ) + number(1.toByte()) * 3.toByte() - number(1.toByte()))) - ), - - number(1) - ) + bindSymbol("x") + zero - } - - assertEquals( - mst.interpret(MstGroup, x to MST.Numeric(2)), - mst.compile(MstGroup, x to MST.Numeric(2)) - ) - } - - @Test - fun byteRing() { - val mst = MstRing { - binaryOperationFunction("+")( - unaryOperationFunction("+")( - (bindSymbol("x") - (2.toByte() + (scale( - add(number(1), number(1)), - 2.0 - ) + 1.toByte()))) * 3.0 - 1.toByte() - ), - - number(1) - ) * number(2) - } - - assertEquals( - mst.interpret(ByteRing, x to 3.toByte()), - mst.compile(ByteRing, x to 3.toByte()) - ) - } - - @Test - fun realField() { - val mst = MstField { - +(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( - (3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0 - + number(1), - number(1) / 2 + number(2.0) * one - ) + zero - } - - assertEquals( - mst.interpret(DoubleField, x to 2.0), - mst.compile(DoubleField, x to 2.0) - ) - } - - @Test - fun complexField() { - val mst = MstField { - +(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( - (3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0 - + number(1), - number(1) / 2 + number(2.0) * one - ) + zero - } - - assertEquals( - mst.interpret(ComplexField, x to 2.0.toComplex()), - mst.compile(ComplexField, x to 2.0.toComplex()) - ) - } -} diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmOperationsSupport.kt b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmOperationsSupport.kt deleted file mode 100644 index 757235fb7..000000000 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmOperationsSupport.kt +++ /dev/null @@ -1,49 +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. - */ - -package space.kscience.kmath.asm - -import space.kscience.kmath.expressions.MstExtendedField -import space.kscience.kmath.expressions.MstField -import space.kscience.kmath.expressions.MstGroup -import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.invoke -import kotlin.random.Random -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class TestAsmOperationsSupport { - @Test - fun testUnaryOperationInvocation() { - val expression = MstGroup { -bindSymbol("x") }.compileToExpression(DoubleField) - val res = expression("x" to 2.0) - assertEquals(-2.0, res) - } - - @Test - fun testBinaryOperationInvocation() { - val expression = MstGroup { -bindSymbol("x") + number(1.0) }.compileToExpression(DoubleField) - val res = expression("x" to 2.0) - assertEquals(-1.0, res) - } - - @Test - fun testConstProductInvocation() { - val res = MstField { bindSymbol("x") * 2 }.compileToExpression(DoubleField)("x" to 2.0) - assertEquals(4.0, res) - } - - @Test - fun testMultipleCalls() { - val e = - MstExtendedField { sin(bindSymbol("x")).pow(4) - 6 * bindSymbol("x") / tanh(bindSymbol("x")) } - .compileToExpression(DoubleField) - val r = Random(0) - var s = 0.0 - repeat(1000000) { s += e("x" to r.nextDouble()) } - println(s) - } -} diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmSpecialization.kt b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmSpecialization.kt deleted file mode 100644 index b09d79515..000000000 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmSpecialization.kt +++ /dev/null @@ -1,69 +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. - */ - -package space.kscience.kmath.asm - -import space.kscience.kmath.expressions.MstExtendedField -import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.invoke -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class TestAsmSpecialization { - @Test - fun testUnaryPlus() { - val expr = MstExtendedField { unaryOperationFunction("+")(bindSymbol("x")) }.compileToExpression(DoubleField) - assertEquals(2.0, expr("x" to 2.0)) - } - - @Test - fun testUnaryMinus() { - val expr = MstExtendedField { unaryOperationFunction("-")(bindSymbol("x")) }.compileToExpression(DoubleField) - assertEquals(-2.0, expr("x" to 2.0)) - } - - @Test - fun testAdd() { - val expr = MstExtendedField { - binaryOperationFunction("+")(bindSymbol("x"), - bindSymbol("x")) - }.compileToExpression(DoubleField) - assertEquals(4.0, expr("x" to 2.0)) - } - - @Test - fun testSine() { - val expr = MstExtendedField { unaryOperationFunction("sin")(bindSymbol("x")) }.compileToExpression(DoubleField) - assertEquals(0.0, expr("x" to 0.0)) - } - - @Test - fun testMinus() { - val expr = MstExtendedField { - binaryOperationFunction("-")(bindSymbol("x"), - bindSymbol("x")) - }.compileToExpression(DoubleField) - assertEquals(0.0, expr("x" to 2.0)) - } - - @Test - fun testDivide() { - val expr = MstExtendedField { - binaryOperationFunction("/")(bindSymbol("x"), - bindSymbol("x")) - }.compileToExpression(DoubleField) - assertEquals(1.0, expr("x" to 2.0)) - } - - @Test - fun testPower() { - val expr = MstExtendedField { - binaryOperationFunction("pow")(bindSymbol("x"), number(2)) - }.compileToExpression(DoubleField) - - assertEquals(4.0, expr("x" to 2.0)) - } -} diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmVariables.kt b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmVariables.kt deleted file mode 100644 index 740326a59..000000000 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/asm/TestAsmVariables.kt +++ /dev/null @@ -1,28 +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. - */ - -package space.kscience.kmath.asm - -import space.kscience.kmath.expressions.MstRing -import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.operations.ByteRing -import space.kscience.kmath.operations.invoke -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -internal class TestAsmVariables { - @Test - fun testVariable() { - val expr = MstRing { bindSymbol("x") }.compileToExpression(ByteRing) - assertEquals(1.toByte(), expr("x" to 1.toByte())) - } - - @Test - fun testUndefinedVariableFails() { - val expr = MstRing { bindSymbol("x") }.compileToExpression(ByteRing) - assertFailsWith { expr() } - } -} diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt new file mode 100644 index 000000000..607c5fdd6 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -0,0 +1,25 @@ +/* + * 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.ast + +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.expressions.MST +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.asm.compile as asmCompile +import space.kscience.kmath.asm.compileToExpression as asmCompileToExpression + +private object AsmCompilerTestContext : CompilerTestContext { + override fun MST.compileToExpression(algebra: IntRing): Expression = asmCompileToExpression(algebra) + override fun MST.compile(algebra: IntRing, arguments: Map): Int = asmCompile(algebra, arguments) + override fun MST.compileToExpression(algebra: DoubleField): Expression = asmCompileToExpression(algebra) + + override fun MST.compile(algebra: DoubleField, arguments: Map): Double = + asmCompile(algebra, arguments) +} + +internal actual inline fun runCompilerTest(action: CompilerTestContext.() -> Unit) = action(AsmCompilerTestContext) diff --git a/kmath-commons/build.gradle.kts b/kmath-commons/build.gradle.kts index 570ac2b74..a208c956c 100644 --- a/kmath-commons/build.gradle.kts +++ b/kmath-commons/build.gradle.kts @@ -1,12 +1,8 @@ -/* - * 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. - */ - plugins { kotlin("jvm") id("ru.mipt.npm.gradle.common") } + description = "Commons math binding for kmath" dependencies { diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt index 535c6b39e..92bf86128 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt @@ -28,7 +28,7 @@ public class CMIntegrator( val integrator = integratorBuilder(integrand) val maxCalls = integrand.getFeature()?.maxCalls ?: defaultMaxCalls val remainingCalls = maxCalls - integrand.calls - val range = integrand.getFeature>()?.range + val range = integrand.getFeature()?.range ?: error("Integration range is not provided") val res = integrator.integrate(remainingCalls, integrand.function, range.start, range.endInclusive) diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/GaussRuleIntegrator.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/GaussRuleIntegrator.kt index 071bac315..1c9915563 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/GaussRuleIntegrator.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/GaussRuleIntegrator.kt @@ -17,7 +17,7 @@ public class GaussRuleIntegrator( ) : UnivariateIntegrator { override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { - val range = integrand.getFeature>()?.range + val range = integrand.getFeature()?.range ?: error("Integration range is not provided") val integrator: GaussIntegrator = getIntegrator(range) //TODO check performance diff --git a/kmath-complex/README.md b/kmath-complex/README.md index 3a05c3d6d..ee5ad416f 100644 --- a/kmath-complex/README.md +++ b/kmath-complex/README.md @@ -8,29 +8,27 @@ Complex and hypercomplex number systems in KMath. ## Artifact: -The Maven coordinates of this project are `space.kscience:kmath-complex:0.3.0-dev-6`. +The Maven coordinates of this project are `space.kscience:kmath-complex:0.3.0-dev-8`. **Gradle:** ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { - implementation 'space.kscience:kmath-complex:0.3.0-dev-6' + implementation 'space.kscience:kmath-complex:0.3.0-dev-8' } ``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { - implementation("space.kscience:kmath-complex:0.3.0-dev-6") + implementation("space.kscience:kmath-complex:0.3.0-dev-8") } ``` diff --git a/kmath-complex/build.gradle.kts b/kmath-complex/build.gradle.kts index 43911e70d..ea74df646 100644 --- a/kmath-complex/build.gradle.kts +++ b/kmath-complex/build.gradle.kts @@ -1,10 +1,3 @@ -import ru.mipt.npm.gradle.Maturity - -/* - * 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. - */ - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") @@ -12,10 +5,6 @@ plugins { } kotlin.sourceSets { - all { - languageSettings.useExperimentalAnnotation("kscience.kmath.misc.UnstableKMathAPI") - } - commonMain { dependencies { api(project(":kmath-core")) @@ -25,7 +14,7 @@ kotlin.sourceSets { readme { description = "Complex numbers and quaternions." - maturity = Maturity.PROTOTYPE + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) feature( diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt index 8d626c17d..8e28337f6 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt @@ -9,7 +9,10 @@ import space.kscience.kmath.memory.MemoryReader import space.kscience.kmath.memory.MemorySpec import space.kscience.kmath.memory.MemoryWriter import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.operations.* +import space.kscience.kmath.operations.ExtendedField +import space.kscience.kmath.operations.Norm +import space.kscience.kmath.operations.NumbersAddOperations +import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.MemoryBuffer import space.kscience.kmath.structures.MutableBuffer @@ -180,12 +183,10 @@ public object ComplexField : ExtendedField, Norm, Num * @property im The imaginary part. */ @OptIn(UnstableKMathAPI::class) -public data class Complex(val re: Double, val im: Double) : FieldElement { +public data class Complex(val re: Double, val im: Double) { public constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble()) public constructor(re: Number) : this(re.toDouble(), 0.0) - public override val context: ComplexField get() = ComplexField - public override fun toString(): String = "($re + i*$im)" public companion object : MemorySpec { 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 382b5cc05..c59aabdcb 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 @@ -27,8 +27,10 @@ public val Quaternion.conjugate: Quaternion */ public val Quaternion.reciprocal: Quaternion get() { - val n = QuaternionField { norm(this@reciprocal) } - return conjugate / (n * n) + QuaternionField { + val n = norm(this@reciprocal) + return conjugate / (n * n) + } } /** @@ -198,7 +200,7 @@ public object QuaternionField : Field, Norm, @OptIn(UnstableKMathAPI::class) public data class Quaternion( val w: Double, val x: Double, val y: Double, val z: Double, -) : FieldElement { +) { public constructor(w: Number, x: Number, y: Number, z: Number) : this( w.toDouble(), x.toDouble(), @@ -219,8 +221,6 @@ public data class Quaternion( require(!z.isNaN()) { "x-component of quaternion is not-a-number" } } - public override val context: QuaternionField get() = QuaternionField - /** * Returns a string representation of this quaternion. */ diff --git a/kmath-core/README.md b/kmath-core/README.md index b83fb13d0..7283a18ce 100644 --- a/kmath-core/README.md +++ b/kmath-core/README.md @@ -15,29 +15,27 @@ performance calculations to code generation. ## Artifact: -The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0-dev-6`. +The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0-dev-8`. **Gradle:** ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { - implementation 'space.kscience:kmath-core:0.3.0-dev-6' + implementation 'space.kscience:kmath-core:0.3.0-dev-8' } ``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { - implementation("space.kscience:kmath-core:0.3.0-dev-6") + implementation("space.kscience:kmath-core:0.3.0-dev-8") } ``` diff --git a/kmath-core/api/kmath-core.api b/kmath-core/api/kmath-core.api index 6b300123c..7dd459104 100644 --- a/kmath-core/api/kmath-core.api +++ b/kmath-core/api/kmath-core.api @@ -45,13 +45,6 @@ public abstract interface class space/kscience/kmath/expressions/ExpressionAlgeb public abstract fun const (Ljava/lang/Object;)Ljava/lang/Object; } -public final class space/kscience/kmath/expressions/ExpressionBuildersKt { - public static final fun extendedFieldExpression (Lspace/kscience/kmath/operations/ExtendedField;Lkotlin/jvm/functions/Function1;)Lspace/kscience/kmath/expressions/Expression; - public static final fun fieldExpression (Lspace/kscience/kmath/operations/Field;Lkotlin/jvm/functions/Function1;)Lspace/kscience/kmath/expressions/Expression; - public static final fun ringExpression (Lspace/kscience/kmath/operations/Ring;Lkotlin/jvm/functions/Function1;)Lspace/kscience/kmath/expressions/Expression; - public static final fun spaceExpression (Lspace/kscience/kmath/operations/Ring;Lkotlin/jvm/functions/Function1;)Lspace/kscience/kmath/expressions/Expression; -} - public final class space/kscience/kmath/expressions/ExpressionKt { public static final fun binding (Lspace/kscience/kmath/expressions/ExpressionAlgebra;)Lkotlin/properties/ReadOnlyProperty; public static final fun callByString (Lspace/kscience/kmath/expressions/Expression;[Lkotlin/Pair;)Ljava/lang/Object; @@ -272,6 +265,8 @@ public final class space/kscience/kmath/expressions/MstExtendedField : space/ksc public fun sin (Lspace/kscience/kmath/expressions/MST;)Lspace/kscience/kmath/expressions/MST$Unary; public synthetic fun sinh (Ljava/lang/Object;)Ljava/lang/Object; public fun sinh (Lspace/kscience/kmath/expressions/MST;)Lspace/kscience/kmath/expressions/MST$Unary; + public synthetic fun sqrt (Ljava/lang/Object;)Ljava/lang/Object; + public fun sqrt (Lspace/kscience/kmath/expressions/MST;)Lspace/kscience/kmath/expressions/MST; public synthetic fun tan (Ljava/lang/Object;)Ljava/lang/Object; public fun tan (Lspace/kscience/kmath/expressions/MST;)Lspace/kscience/kmath/expressions/MST$Unary; public synthetic fun tanh (Ljava/lang/Object;)Ljava/lang/Object; @@ -705,7 +700,11 @@ public final class space/kscience/kmath/misc/Symbol$Companion { } public final class space/kscience/kmath/misc/SymbolKt { + public static final fun get (Ljava/util/Map;Ljava/lang/String;)Ljava/lang/Object; + public static final fun get (Ljava/util/Map;Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; public static final fun getSymbol ()Lkotlin/properties/ReadOnlyProperty; + public static final fun set (Ljava/util/Map;Ljava/lang/String;Ljava/lang/Object;)V + public static final fun set (Ljava/util/Map;Lspace/kscience/kmath/misc/Symbol;Ljava/lang/Object;)V } public abstract interface annotation class space/kscience/kmath/misc/UnstableKMathAPI : java/lang/annotation/Annotation { @@ -748,7 +747,7 @@ public final class space/kscience/kmath/nd/BufferAlgebraNDKt { public static final fun ring (Lspace/kscience/kmath/nd/AlgebraND$Companion;Lspace/kscience/kmath/operations/Ring;Lkotlin/jvm/functions/Function2;[I)Lspace/kscience/kmath/nd/BufferedRingND; } -public final class space/kscience/kmath/nd/BufferND : space/kscience/kmath/nd/StructureND { +public class space/kscience/kmath/nd/BufferND : space/kscience/kmath/nd/StructureND { public fun (Lspace/kscience/kmath/nd/Strides;Lspace/kscience/kmath/structures/Buffer;)V public fun elements ()Lkotlin/sequences/Sequence; public fun get ([I)Ljava/lang/Object; @@ -789,10 +788,9 @@ public final class space/kscience/kmath/nd/DefaultStrides : space/kscience/kmath public fun equals (Ljava/lang/Object;)Z public fun getLinearSize ()I public fun getShape ()[I - public fun getStrides ()Ljava/util/List; + public fun getStrides ()[I public fun hashCode ()I public fun index (I)[I - public fun offset ([I)I } public final class space/kscience/kmath/nd/DefaultStrides$Companion { @@ -876,6 +874,22 @@ public abstract interface class space/kscience/kmath/nd/GroupND : space/kscience public final class space/kscience/kmath/nd/GroupND$Companion { } +public final class space/kscience/kmath/nd/MutableBufferND : space/kscience/kmath/nd/BufferND, space/kscience/kmath/nd/MutableStructureND { + public fun (Lspace/kscience/kmath/nd/Strides;Lspace/kscience/kmath/structures/MutableBuffer;)V + public final fun getMutableBuffer ()Lspace/kscience/kmath/structures/MutableBuffer; + public fun set ([ILjava/lang/Object;)V +} + +public abstract interface class space/kscience/kmath/nd/MutableStructure1D : space/kscience/kmath/nd/MutableStructureND, space/kscience/kmath/nd/Structure1D, space/kscience/kmath/structures/MutableBuffer { + public fun set ([ILjava/lang/Object;)V +} + +public abstract interface class space/kscience/kmath/nd/MutableStructure2D : space/kscience/kmath/nd/MutableStructureND, space/kscience/kmath/nd/Structure2D { + public fun getColumns ()Ljava/util/List; + public fun getRows ()Ljava/util/List; + public abstract fun set (IILjava/lang/Object;)V +} + public abstract interface class space/kscience/kmath/nd/MutableStructureND : space/kscience/kmath/nd/StructureND { public abstract fun set ([ILjava/lang/Object;)V } @@ -915,10 +929,10 @@ public final class space/kscience/kmath/nd/ShortRingNDKt { public abstract interface class space/kscience/kmath/nd/Strides { public abstract fun getLinearSize ()I public abstract fun getShape ()[I - public abstract fun getStrides ()Ljava/util/List; + public abstract fun getStrides ()[I public abstract fun index (I)[I public fun indices ()Lkotlin/sequences/Sequence; - public abstract fun offset ([I)I + public fun offset ([I)I } public abstract interface class space/kscience/kmath/nd/Structure1D : space/kscience/kmath/nd/StructureND, space/kscience/kmath/structures/Buffer { @@ -932,6 +946,7 @@ public final class space/kscience/kmath/nd/Structure1D$Companion { } public final class space/kscience/kmath/nd/Structure1DKt { + public static final fun as1D (Lspace/kscience/kmath/nd/MutableStructureND;)Lspace/kscience/kmath/nd/MutableStructure1D; public static final fun as1D (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/nd/Structure1D; public static final fun asND (Lspace/kscience/kmath/structures/Buffer;)Lspace/kscience/kmath/nd/Structure1D; } @@ -952,6 +967,7 @@ public final class space/kscience/kmath/nd/Structure2D$Companion { } public final class space/kscience/kmath/nd/Structure2DKt { + public static final fun as2D (Lspace/kscience/kmath/nd/MutableStructureND;)Lspace/kscience/kmath/nd/MutableStructure2D; public static final fun as2D (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/nd/Structure2D; } @@ -991,14 +1007,7 @@ public abstract interface class space/kscience/kmath/operations/Algebra { public fun unaryOperationFunction (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; } -public abstract interface class space/kscience/kmath/operations/AlgebraElement { - public abstract fun getContext ()Lspace/kscience/kmath/operations/Algebra; -} - public final class space/kscience/kmath/operations/AlgebraElementsKt { - public static final fun div (Lspace/kscience/kmath/operations/AlgebraElement;Lspace/kscience/kmath/operations/AlgebraElement;)Lspace/kscience/kmath/operations/AlgebraElement; - public static final fun plus (Lspace/kscience/kmath/operations/AlgebraElement;Lspace/kscience/kmath/operations/AlgebraElement;)Lspace/kscience/kmath/operations/AlgebraElement; - public static final fun times (Lspace/kscience/kmath/operations/AlgebraElement;Lspace/kscience/kmath/operations/AlgebraElement;)Lspace/kscience/kmath/operations/AlgebraElement; } public final class space/kscience/kmath/operations/AlgebraExtensionsKt { @@ -1209,7 +1218,6 @@ public abstract interface class space/kscience/kmath/operations/ExtendedField : public fun acosh (Ljava/lang/Object;)Ljava/lang/Object; public fun asinh (Ljava/lang/Object;)Ljava/lang/Object; public fun atanh (Ljava/lang/Object;)Ljava/lang/Object; - public fun bindSymbol (Ljava/lang/String;)Ljava/lang/Object; public fun cosh (Ljava/lang/Object;)Ljava/lang/Object; public fun rightSideNumberOperationFunction (Ljava/lang/String;)Lkotlin/jvm/functions/Function2; public fun sinh (Ljava/lang/Object;)Ljava/lang/Object; diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index df70aa40b..92a5f419d 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -1,10 +1,3 @@ -/* - * 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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") @@ -21,7 +14,7 @@ kotlin.sourceSets { readme { description = "Core classes, algebra definitions, basic linear algebra" - maturity = Maturity.DEVELOPMENT + maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) feature( diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt index 32a7efc1e..53124b777 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/MstAlgebra.kt @@ -135,6 +135,7 @@ public object MstExtendedField : ExtendedField, NumericAlgebra { public override fun acosh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ACOSH_OPERATION)(arg) public override fun atanh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ATANH_OPERATION)(arg) public override fun add(a: MST, b: MST): MST.Binary = MstField.add(a, b) + public override fun sqrt(arg: MST): MST = unaryOperationFunction(PowerOperations.SQRT_OPERATION)(arg) public override fun scale(a: MST, value: Double): MST = binaryOperation(GroupOperations.PLUS_OPERATION, a, number(value)) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt index 886008983..738156975 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt @@ -33,12 +33,12 @@ public interface SymbolIndexer { public operator fun DoubleArray.get(symbol: Symbol): Double { require(size == symbols.size) { "The input array size for indexer should be ${symbols.size} but $size found" } - return get(this@SymbolIndexer.indexOf(symbol)) + return get(indexOf(symbol)) } public operator fun Point.get(symbol: Symbol): T { require(size == symbols.size) { "The input buffer size for indexer should be ${symbols.size} but $size found" } - return get(this@SymbolIndexer.indexOf(symbol)) + return get(indexOf(symbol)) } public fun DoubleArray.toMap(): Map { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/expressionBuilders.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/expressionBuilders.kt deleted file mode 100644 index 142194070..000000000 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/expressionBuilders.kt +++ /dev/null @@ -1,45 +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. - */ - -package space.kscience.kmath.expressions - -import space.kscience.kmath.operations.ExtendedField -import space.kscience.kmath.operations.Field -import space.kscience.kmath.operations.Ring -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - - -/** - * Creates a functional expression with this [Ring]. - */ -public inline fun Ring.spaceExpression(block: FunctionalExpressionGroup>.() -> Expression): Expression { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return FunctionalExpressionGroup(this).block() -} - -/** - * Creates a functional expression with this [Ring]. - */ -public inline fun Ring.ringExpression(block: FunctionalExpressionRing>.() -> Expression): Expression { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return FunctionalExpressionRing(this).block() -} - -/** - * Creates a functional expression with this [Field]. - */ -public inline fun Field.fieldExpression(block: FunctionalExpressionField>.() -> Expression): Expression { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return FunctionalExpressionField(this).block() -} - -/** - * Creates a functional expression with this [ExtendedField]. - */ -public inline fun ExtendedField.extendedFieldExpression(block: FunctionalExpressionExtendedField>.() -> Expression): Expression { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return FunctionalExpressionExtendedField(this).block() -} diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt index 0798e8763..ec073ac48 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt @@ -19,6 +19,7 @@ import kotlin.reflect.KClass * @param T the type of items. */ public typealias Matrix = Structure2D +public typealias MutableMatrix = MutableStructure2D /** * Alias or using [Buffer] as a point/vector in a many-dimensional space. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Symbol.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Symbol.kt index 737acc025..b9fb6879a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Symbol.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Symbol.kt @@ -32,10 +32,33 @@ public value class StringSymbol(override val identity: String) : Symbol { override fun toString(): String = identity } - /** * A delegate to create a symbol with a string identity in this scope */ public val symbol: ReadOnlyProperty = ReadOnlyProperty { _, property -> StringSymbol(property.name) } + +/** + * Ger a value from a [String]-keyed map by a [Symbol] + */ +public operator fun Map.get(symbol: Symbol): T? = get(symbol.identity) + +/** + * Set a value of [String]-keyed map by a [Symbol] + */ +public operator fun MutableMap.set(symbol: Symbol, value: T){ + set(symbol.identity, value) +} + +/** + * Get a value from a [Symbol]-keyed map by a [String] + */ +public operator fun Map.get(string: String): T? = get(StringSymbol(string)) + +/** + * Set a value of [String]-keyed map by a [Symbol] + */ +public operator fun MutableMap.set(string: String, value: T){ + set(StringSymbol(string), value) +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt index 23d961a7e..1f608f478 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/BufferND.kt @@ -7,6 +7,8 @@ package space.kscience.kmath.nd import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory +import space.kscience.kmath.structures.MutableBuffer +import space.kscience.kmath.structures.MutableBufferFactory /** * Represents [StructureND] over [Buffer]. @@ -15,7 +17,7 @@ import space.kscience.kmath.structures.BufferFactory * @param strides The strides to access elements of [Buffer] by linear indices. * @param buffer The underlying buffer. */ -public class BufferND( +public open class BufferND( public val strides: Strides, public val buffer: Buffer, ) : StructureND { @@ -50,4 +52,35 @@ public inline fun StructureND.mapToBuffer( val strides = DefaultStrides(shape) BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) } +} + +/** + * Represents [MutableStructureND] over [MutableBuffer]. + * + * @param T the type of items. + * @param strides The strides to access elements of [MutableBuffer] by linear indices. + * @param mutableBuffer The underlying buffer. + */ +public class MutableBufferND( + strides: Strides, + public val mutableBuffer: MutableBuffer, +) : MutableStructureND, BufferND(strides, mutableBuffer) { + override fun set(index: IntArray, value: T) { + mutableBuffer[strides.offset(index)] = value + } +} + +/** + * Transform structure to a new structure using provided [MutableBufferFactory] and optimizing if argument is [MutableBufferND] + */ +public inline fun MutableStructureND.mapToMutableBuffer( + factory: MutableBufferFactory = MutableBuffer.Companion::auto, + crossinline transform: (T) -> R, +): MutableBufferND { + return if (this is MutableBufferND) + MutableBufferND(this.strides, factory.invoke(strides.linearSize) { transform(mutableBuffer[it]) }) + else { + val strides = DefaultStrides(shape) + MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) }) + } } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt index 0f0588fda..8ea6d0f02 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt @@ -6,6 +6,8 @@ package space.kscience.kmath.nd import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.MutableBuffer +import space.kscience.kmath.structures.asMutableBuffer import space.kscience.kmath.structures.asSequence import kotlin.jvm.JvmInline @@ -25,6 +27,16 @@ public interface Structure1D : StructureND, Buffer { public companion object } +/** + * A mutable structure that is guaranteed to be one-dimensional + */ +public interface MutableStructure1D : Structure1D, MutableStructureND, MutableBuffer { + public override operator fun set(index: IntArray, value: T) { + require(index.size == 1) { "Index dimension mismatch. Expected 1 but found ${index.size}" } + set(index[0], value) + } +} + /** * A 1D wrapper for nd-structure */ @@ -37,6 +49,23 @@ private value class Structure1DWrapper(val structure: StructureND) : Struc override fun elements(): Sequence> = structure.elements() } +/** + * A 1D wrapper for a mutable nd-structure + */ +private class MutableStructure1DWrapper(val structure: MutableStructureND) : MutableStructure1D { + override val shape: IntArray get() = structure.shape + override val size: Int get() = structure.shape[0] + override fun elements(): Sequence> = structure.elements() + + override fun get(index: Int): T = structure[index] + override fun set(index: Int, value: T) { + structure[intArrayOf(index)] = value + } + + override fun copy(): MutableBuffer = + structure.elements().map { it.second }.toMutableList().asMutableBuffer() +} + /** * A structure wrapper for buffer @@ -52,6 +81,21 @@ private value class Buffer1DWrapper(val buffer: Buffer) : Structure1D { override operator fun get(index: Int): T = buffer[index] } +internal class MutableBuffer1DWrapper(val buffer: MutableBuffer) : MutableStructure1D { + override val shape: IntArray get() = intArrayOf(buffer.size) + override val size: Int get() = buffer.size + + override fun elements(): Sequence> = + buffer.asSequence().mapIndexed { index, value -> intArrayOf(index) to value } + + override operator fun get(index: Int): T = buffer[index] + override fun set(index: Int, value: T) { + buffer[index] = value + } + + override fun copy(): MutableBuffer = buffer.copy() +} + /** * Represent a [StructureND] as [Structure1D]. Throw error in case of dimension mismatch */ @@ -62,6 +106,11 @@ public fun StructureND.as1D(): Structure1D = this as? Structure1D ? } } else error("Can't create 1d-structure from ${shape.size}d-structure") +public fun MutableStructureND.as1D(): MutableStructure1D = + this as? MutableStructure1D ?: if (shape.size == 1) { + MutableStructure1DWrapper(this) + } else error("Can't create 1d-structure from ${shape.size}d-structure") + /** * Represent this buffer as 1D structure */ @@ -75,3 +124,4 @@ internal fun Structure1D.unwrap(): Buffer = when { this is Structure1DWrapper && structure is BufferND -> structure.buffer else -> this } + diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt index 3eee41832..28ae07a3c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.nd import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.VirtualBuffer +import space.kscience.kmath.structures.MutableListBuffer import kotlin.jvm.JvmInline import kotlin.reflect.KClass @@ -63,6 +64,32 @@ public interface Structure2D : StructureND { public companion object } +/** + * Represents mutable [Structure2D]. + */ +public interface MutableStructure2D : Structure2D, MutableStructureND { + /** + * Inserts an item at the specified indices. + * + * @param i the first index. + * @param j the second index. + * @param value the value. + */ + public operator fun set(i: Int, j: Int, value: T) + + /** + * The buffer of rows of this structure. It gets elements from the structure dynamically. + */ + override val rows: List> + get() = List(rowNum) { i -> MutableBuffer1DWrapper(MutableListBuffer(colNum) { j -> get(i, j) })} + + /** + * The buffer of columns of this structure. It gets elements from the structure dynamically. + */ + override val columns: List> + get() = List(colNum) { j -> MutableBuffer1DWrapper(MutableListBuffer(rowNum) { i -> get(i, j) }) } +} + /** * A 2D wrapper for nd-structure */ @@ -81,6 +108,33 @@ private value class Structure2DWrapper(val structure: StructureND) : Struc override fun elements(): Sequence> = structure.elements() } +/** + * A 2D wrapper for a mutable nd-structure + */ +private class MutableStructure2DWrapper(val structure: MutableStructureND): MutableStructure2D +{ + override val shape: IntArray get() = structure.shape + + override val rowNum: Int get() = shape[0] + override val colNum: Int get() = shape[1] + + override operator fun get(i: Int, j: Int): T = structure[i, j] + + override fun set(index: IntArray, value: T) { + structure[index] = value + } + + override operator fun set(i: Int, j: Int, value: T){ + structure[intArrayOf(i, j)] = value + } + + override fun elements(): Sequence> = structure.elements() + + override fun equals(other: Any?): Boolean = false + + override fun hashCode(): Int = 0 +} + /** * Represent a [StructureND] as [Structure1D]. Throw error in case of dimension mismatch */ @@ -89,9 +143,18 @@ public fun StructureND.as2D(): Structure2D = this as? Structure2D ? else -> error("Can't create 2d-structure from ${shape.size}d-structure") } +public fun MutableStructureND.as2D(): MutableStructure2D = this as? MutableStructure2D ?: when (shape.size) { + 2 -> MutableStructure2DWrapper(this) + else -> error("Can't create 2d-structure from ${shape.size}d-structure") +} + /** * Expose inner [StructureND] if possible */ internal fun Structure2D.unwrap(): StructureND = if (this is Structure2DWrapper) structure - else this \ No newline at end of file + else this + +internal fun MutableStructure2D.unwrap(): MutableStructureND = + if (this is MutableStructure2DWrapper) structure else this + 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 0656b1f7f..a3331d71a 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 @@ -184,12 +184,15 @@ public interface Strides { /** * Array strides */ - public val strides: List + public val strides: IntArray /** * Get linear index from multidimensional index */ - public fun offset(index: IntArray): Int + public fun offset(index: IntArray): Int = index.mapIndexed { i, value -> + if (value < 0 || value >= shape[i]) throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${this.shape[i]})") + value * strides[i] + }.sum() /** * Get multidimensional from linear @@ -221,7 +224,7 @@ public class DefaultStrides private constructor(override val shape: IntArray) : /** * Strides for memory access */ - override val strides: List by lazy { + override val strides: IntArray by lazy { sequence { var current = 1 yield(1) @@ -230,14 +233,9 @@ public class DefaultStrides private constructor(override val shape: IntArray) : current *= it yield(current) } - }.toList() + }.toList().toIntArray() } - override fun offset(index: IntArray): Int = index.mapIndexed { i, value -> - if (value < 0 || value >= shape[i]) throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${this.shape[i]})") - value * strides[i] - }.sum() - override fun index(offset: Int): IntArray { val res = IntArray(shape.size) var current = offset diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt index 0334e0466..c994580bd 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt @@ -44,9 +44,9 @@ public interface Algebra { /** * Dynamically dispatches an unary operation with the certain name. * - * This function must follow two properties: + * This function must has two features: * - * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. + * 1. In case operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. * 2. This function is symmetric with second `unaryOperation` overload: * i.e. `unaryOperationFunction(a)(b) == unaryOperation(a, b)`. * diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/AlgebraElements.kt index d7c87f213..cc058d3fc 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/AlgebraElements.kt @@ -13,6 +13,8 @@ import space.kscience.kmath.misc.UnstableKMathAPI * @param C the type of mathematical context for this element. * @param T the type wrapped by this wrapper. */ +@UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public interface AlgebraElement> { /** * The context this element belongs to. @@ -45,6 +47,7 @@ public interface AlgebraElement> { * @return the difference. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public operator fun , S : NumbersAddOperations> T.minus(b: T): T = context.add(this, context.run { -b }) @@ -55,6 +58,8 @@ public operator fun , S : NumbersAddOperations> T.mi * @param b the addend. * @return the sum. */ +@UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public operator fun , S : Ring> T.plus(b: T): T = context.add(this, b) @@ -71,6 +76,8 @@ public operator fun , S : Ring> T.plus(b: T): T = * @param b the multiplier. * @return the product. */ +@UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public operator fun , R : Ring> T.times(b: T): T = context.multiply(this, b) @@ -81,6 +88,8 @@ public operator fun , R : Ring> T.times(b: T): T = * @param b the divisor. * @return the quotient. */ +@UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public operator fun , F : Field> T.div(b: T): T = context.divide(this, b) @@ -93,6 +102,7 @@ public operator fun , F : Field> T.div(b: T): T = * @param S the type of space. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public interface GroupElement, S : Group> : AlgebraElement /** @@ -103,6 +113,7 @@ public interface GroupElement, S : Group> : AlgebraEle * @param R the type of ring. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public interface RingElement, R : Ring> : GroupElement /** @@ -113,4 +124,5 @@ public interface RingElement, R : Ring> : GroupElement< * @param F the type of field. */ @UnstableKMathAPI -public interface FieldElement, F : Field> : RingElement \ No newline at end of file +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") +public interface FieldElement, F : Field> : RingElement diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt index 6a35828ee..deeb07e0e 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt @@ -97,14 +97,12 @@ public interface NumericAlgebra : Algebra { /** * The π mathematical constant. */ -public val NumericAlgebra.pi: T - get() = bindSymbolOrNull("pi") ?: number(PI) +public val NumericAlgebra.pi: T get() = bindSymbolOrNull("pi") ?: number(PI) /** * The *e* mathematical constant. */ -public val NumericAlgebra.e: T - get() = number(E) +public val NumericAlgebra.e: T get() = number(E) /** * Scale by scalar operations diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/OptionalOperations.kt index 8e3e6c777..979e65396 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/OptionalOperations.kt @@ -80,36 +80,42 @@ public interface TrigonometricOperations : Algebra { * Computes the sine of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> sin(arg: T): T = arg.context.sin(arg) /** * Computes the cosine of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> cos(arg: T): T = arg.context.cos(arg) /** * Computes the tangent of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> tan(arg: T): T = arg.context.tan(arg) /** * Computes the inverse sine of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> asin(arg: T): T = arg.context.asin(arg) /** * Computes the inverse cosine of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> acos(arg: T): T = arg.context.acos(arg) /** * Computes the inverse tangent of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> atan(arg: T): T = arg.context.atan(arg) /** @@ -154,18 +160,21 @@ public interface PowerOperations : Algebra { * @return the base raised to the power. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public infix fun >> T.pow(power: Double): T = context.power(this, power) /** * Computes the square root of the value [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> sqrt(arg: T): T = arg pow 0.5 /** * Computes the square of the value [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> sqr(arg: T): T = arg pow 2.0 /** @@ -261,12 +270,14 @@ public interface ExponentialOperations : Algebra { * The identifier of exponential function. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> exp(arg: T): T = arg.context.exp(arg) /** * The identifier of natural logarithm. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> ln(arg: T): T = arg.context.ln(arg) @@ -280,30 +291,35 @@ public fun >> sinh(arg: T): T * Computes the hyperbolic cosine of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> cosh(arg: T): T = arg.context.cosh(arg) /** * Computes the hyperbolic tangent of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> tanh(arg: T): T = arg.context.tanh(arg) /** * Computes the inverse hyperbolic sine of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> asinh(arg: T): T = arg.context.asinh(arg) /** * Computes the inverse hyperbolic cosine of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> acosh(arg: T): T = arg.context.acosh(arg) /** * Computes the inverse hyperbolic tangent of [arg]. */ @UnstableKMathAPI +@Deprecated("AlgebraElements are considered odd and will be removed in future releases.") public fun >> atanh(arg: T): T = arg.context.atanh(arg) /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt index 3b0abf657..0d6d9e98d 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt @@ -49,12 +49,6 @@ public interface ExtendedField : ExtendedFieldOperations, Field, Numeri 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.0 - public override fun bindSymbol(value: String): T = when (value) { - "pi" -> pi - "e" -> e - else -> super.bindSymbol(value) - } - public override fun rightSideNumberOperationFunction(operation: String): (left: T, right: Number) -> T = when (operation) { PowerOperations.POW_OPERATION -> ::power diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt index d187beab1..be5dfb359 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt @@ -232,7 +232,7 @@ public value class MutableListBuffer(public val list: MutableList) : Mutab } /** - * Returns an [ListBuffer] that wraps the original list. + * Returns an [MutableListBuffer] that wraps the original list. */ public fun MutableList.asMutableBuffer(): MutableListBuffer = MutableListBuffer(this) diff --git a/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/InterpretTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/InterpretTest.kt similarity index 57% rename from kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/InterpretTest.kt rename to kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/InterpretTest.kt index 93fde5aab..980819364 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/InterpretTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/InterpretTest.kt @@ -3,25 +3,21 @@ * 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.kscisnce.kmath.ast +package space.kscience.kmath.expressions -import space.kscience.kmath.expressions.MstField -import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.expressions.toExpression -import space.kscience.kmath.misc.Symbol.Companion.x +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.operations.invoke import kotlin.test.Test -class InterpretTest { - +internal class InterpretTest { @Test - fun interpretation(){ + fun interpretation() { val expr = MstField { - val x = bindSymbol(x) + val x = bindSymbol(Symbol.x) x * 2.0 + number(2.0) / x - 16.0 }.toExpression(DoubleField) - expr(x to 2.2) + expr(Symbol.x to 2.2) } -} \ No newline at end of file +} diff --git a/kmath-coroutines/build.gradle.kts b/kmath-coroutines/build.gradle.kts index 30f9ce1f9..1546e7d96 100644 --- a/kmath-coroutines/build.gradle.kts +++ b/kmath-coroutines/build.gradle.kts @@ -1,10 +1,3 @@ -/* - * 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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") @@ -29,5 +22,5 @@ kotlin.sourceSets { } readme { - maturity = Maturity.EXPERIMENTAL + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL } \ No newline at end of file diff --git a/kmath-dimensions/build.gradle.kts b/kmath-dimensions/build.gradle.kts index a9a9177c0..885f3c227 100644 --- a/kmath-dimensions/build.gradle.kts +++ b/kmath-dimensions/build.gradle.kts @@ -1,8 +1,3 @@ -/* - * 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. - */ - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") diff --git a/kmath-ejml/README.md b/kmath-ejml/README.md index cae11724d..cb12ef98d 100644 --- a/kmath-ejml/README.md +++ b/kmath-ejml/README.md @@ -2,36 +2,34 @@ EJML based linear algebra implementation. - - [ejml-vector](src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : The Point implementation using SimpleMatrix. - - [ejml-matrix](src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : The Matrix implementation using SimpleMatrix. - - [ejml-linear-space](src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : The LinearSpace implementation using SimpleMatrix. + - [ejml-vector](src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : Point implementations. + - [ejml-matrix](src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : Matrix implementation. + - [ejml-linear-space](src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : LinearSpace implementations. ## Artifact: -The Maven coordinates of this project are `space.kscience:kmath-ejml:0.3.0-dev-6`. +The Maven coordinates of this project are `space.kscience:kmath-ejml:0.3.0-dev-8`. **Gradle:** ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { - implementation 'space.kscience:kmath-ejml:0.3.0-dev-6' + implementation 'space.kscience:kmath-ejml:0.3.0-dev-8' } ``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { - implementation("space.kscience:kmath-ejml:0.3.0-dev-6") + implementation("space.kscience:kmath-ejml:0.3.0-dev-8") } ``` diff --git a/kmath-ejml/build.gradle.kts b/kmath-ejml/build.gradle.kts index 6ae0dcec6..c8e2ecd8b 100644 --- a/kmath-ejml/build.gradle.kts +++ b/kmath-ejml/build.gradle.kts @@ -1,39 +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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("jvm") id("ru.mipt.npm.gradle.common") } dependencies { - api("org.ejml:ejml-simple:0.40") + api("org.ejml:ejml-ddense:0.40") api(project(":kmath-core")) } readme { - maturity = Maturity.PROTOTYPE + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) feature( id = "ejml-vector", - description = "The Point implementation using SimpleMatrix.", + description = "Point implementations.", ref = "src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt" ) feature( id = "ejml-matrix", - description = "The Matrix implementation using SimpleMatrix.", + description = "Matrix implementation.", ref = "src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt" ) feature( id = "ejml-linear-space", - description = "The LinearSpace implementation using SimpleMatrix.", + description = "LinearSpace implementations.", ref = "src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt" ) } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt index 4b6421c9b..71cae4829 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt @@ -5,45 +5,71 @@ package space.kscience.kmath.ejml +import org.ejml.data.DMatrix +import org.ejml.data.DMatrixD1 +import org.ejml.data.DMatrixRMaj +import org.ejml.dense.row.CommonOps_DDRM import org.ejml.dense.row.factory.DecompositionFactory_DDRM -import org.ejml.simple.SimpleMatrix import space.kscience.kmath.linear.* import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.StructureFeature -import space.kscience.kmath.nd.getFeature import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Ring import space.kscience.kmath.structures.DoubleBuffer import kotlin.reflect.KClass import kotlin.reflect.cast /** - * Represents context of basic operations operating with [EjmlMatrix]. + * [LinearSpace] implementation specialized for a certain EJML type. + * + * @param T the type of items in the matrices. + * @param A the element context type. + * @param M the EJML matrix type. + * @author Iaroslav Postovalov + */ +public abstract class EjmlLinearSpace, M : org.ejml.data.Matrix> : LinearSpace { + /** + * Converts this matrix to EJML one. + */ + public abstract fun Matrix.toEjml(): EjmlMatrix + + /** + * Converts this vector to EJML one. + */ + public abstract fun Point.toEjml(): EjmlVector + + public abstract override fun buildMatrix( + rows: Int, + columns: Int, + initializer: A.(i: Int, j: Int) -> T, + ): EjmlMatrix + + public abstract override fun buildVector(size: Int, initializer: A.(Int) -> T): EjmlVector +} + +/** + * [EjmlLinearSpace] implementation based on [CommonOps_DDRM], [DecompositionFactory_DDRM] operations and + * [DMatrixRMaj] matrices. * * @author Iaroslav Postovalov - * @author Alexander Nozik */ -public object EjmlLinearSpace : LinearSpace { +public object EjmlLinearSpaceDDRM : EjmlLinearSpace() { /** * The [DoubleField] reference. */ public override val elementAlgebra: DoubleField get() = DoubleField - /** - * Converts this matrix to EJML one. - */ - @OptIn(UnstableKMathAPI::class) - public fun Matrix.toEjml(): EjmlMatrix = when (val matrix = origin) { - is EjmlMatrix -> matrix + @Suppress("UNCHECKED_CAST") + public override fun Matrix.toEjml(): EjmlDoubleMatrix = when { + this is EjmlDoubleMatrix<*> && origin is DMatrixRMaj -> this as EjmlDoubleMatrix else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } } - /** - * Converts this vector to EJML one. - */ - public fun Point.toEjml(): EjmlVector = when (this) { - is EjmlVector -> this - else -> EjmlVector(SimpleMatrix(size, 1).also { - (0 until it.numRows()).forEach { row -> it[row, 0] = get(row) } + @Suppress("UNCHECKED_CAST") + public override fun Point.toEjml(): EjmlDoubleVector = when { + this is EjmlDoubleVector<*> && origin is DMatrixRMaj -> this as EjmlDoubleVector + else -> EjmlDoubleVector(DMatrixRMaj(size, 1).also { + (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } }) } @@ -51,159 +77,178 @@ public object EjmlLinearSpace : LinearSpace { rows: Int, columns: Int, initializer: DoubleField.(i: Int, j: Int) -> Double, - ): EjmlMatrix = EjmlMatrix(SimpleMatrix(rows, columns).also { + ): EjmlDoubleMatrix = EjmlDoubleMatrix(DMatrixRMaj(rows, columns).also { (0 until rows).forEach { row -> - (0 until columns).forEach { col -> it[row, col] = DoubleField.initializer(row, col) } + (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } } }) - public override fun buildVector(size: Int, initializer: DoubleField.(Int) -> Double): Point = - EjmlVector(SimpleMatrix(size, 1).also { - (0 until it.numRows()).forEach { row -> it[row, 0] = DoubleField.initializer(row) } - }) + public override fun buildVector( + size: Int, + initializer: DoubleField.(Int) -> Double, + ): EjmlDoubleVector = EjmlDoubleVector(DMatrixRMaj(size, 1).also { + (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } + }) - private fun SimpleMatrix.wrapMatrix() = EjmlMatrix(this) - private fun SimpleMatrix.wrapVector() = EjmlVector(this) + private fun T.wrapMatrix() = EjmlDoubleMatrix(this) + private fun T.wrapVector() = EjmlDoubleVector(this) public override fun Matrix.unaryMinus(): Matrix = this * (-1.0) - public override fun Matrix.dot(other: Matrix): EjmlMatrix = - EjmlMatrix(toEjml().origin.mult(other.toEjml().origin)) + public override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.mult(toEjml().origin, other.toEjml().origin, out) + return out.wrapMatrix() + } - public override fun Matrix.dot(vector: Point): EjmlVector = - EjmlVector(toEjml().origin.mult(vector.toEjml().origin)) + public override fun Matrix.dot(vector: Point): EjmlDoubleVector { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.mult(toEjml().origin, vector.toEjml().origin, out) + return out.wrapVector() + } - public override operator fun Matrix.minus(other: Matrix): EjmlMatrix = - (toEjml().origin - other.toEjml().origin).wrapMatrix() + public override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.subtract(toEjml().origin, other.toEjml().origin, out) + return out.wrapMatrix() + } - public override operator fun Matrix.times(value: Double): EjmlMatrix = - toEjml().origin.scale(value).wrapMatrix() + public override operator fun Matrix.times(value: Double): EjmlDoubleMatrix { + val res = this.toEjml().origin.copy() + CommonOps_DDRM.scale(value, res) + return res.wrapMatrix() + } - public override fun Point.unaryMinus(): EjmlVector = - toEjml().origin.negative().wrapVector() + public override fun Point.unaryMinus(): EjmlDoubleVector { + val out = toEjml().origin.copy() + CommonOps_DDRM.changeSign(out) + return out.wrapVector() + } - public override fun Matrix.plus(other: Matrix): EjmlMatrix = - (toEjml().origin + other.toEjml().origin).wrapMatrix() + public override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.add(toEjml().origin, other.toEjml().origin, out) + return out.wrapMatrix() + } - public override fun Point.plus(other: Point): EjmlVector = - (toEjml().origin + other.toEjml().origin).wrapVector() + public override fun Point.plus(other: Point): EjmlDoubleVector { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.add(toEjml().origin, other.toEjml().origin, out) + return out.wrapVector() + } - public override fun Point.minus(other: Point): EjmlVector = - (toEjml().origin - other.toEjml().origin).wrapVector() + public override fun Point.minus(other: Point): EjmlDoubleVector { + val out = DMatrixRMaj(1, 1) + CommonOps_DDRM.subtract(toEjml().origin, other.toEjml().origin, out) + return out.wrapVector() + } - public override fun Double.times(m: Matrix): EjmlMatrix = - m.toEjml().origin.scale(this).wrapMatrix() + public override fun Double.times(m: Matrix): EjmlDoubleMatrix = m * this - public override fun Point.times(value: Double): EjmlVector = - toEjml().origin.scale(value).wrapVector() + public override fun Point.times(value: Double): EjmlDoubleVector { + val res = this.toEjml().origin.copy() + CommonOps_DDRM.scale(value, res) + return res.wrapVector() + } - public override fun Double.times(v: Point): EjmlVector = - v.toEjml().origin.scale(this).wrapVector() + public override fun Double.times(v: Point): EjmlDoubleVector = v * this @UnstableKMathAPI public override fun getFeature(structure: Matrix, type: KClass): F? { - //Return the feature if it is intrinsic to the structure + // Return the feature if it is intrinsic to the structure structure.getFeature(type)?.let { return it } val origin = structure.toEjml().origin return when (type) { InverseMatrixFeature::class -> object : InverseMatrixFeature { - override val inverse: Matrix by lazy { EjmlMatrix(origin.invert()) } + override val inverse: Matrix by lazy { + val res = origin.copy() + CommonOps_DDRM.invert(res) + EjmlDoubleMatrix(res) + } } DeterminantFeature::class -> object : DeterminantFeature { - override val determinant: Double by lazy(origin::determinant) + override val determinant: Double by lazy { CommonOps_DDRM.det(DMatrixRMaj(origin)) } } SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature { private val svd by lazy { - DecompositionFactory_DDRM.svd(origin.numRows(), origin.numCols(), true, true, false) - .apply { decompose(origin.ddrm.copy()) } + DecompositionFactory_DDRM.svd(origin.numRows, origin.numCols, true, true, false) + .apply { decompose(origin.copy()) } } - override val u: Matrix by lazy { EjmlMatrix(SimpleMatrix(svd.getU(null, false))) } - override val s: Matrix by lazy { EjmlMatrix(SimpleMatrix(svd.getW(null))) } - override val v: Matrix by lazy { EjmlMatrix(SimpleMatrix(svd.getV(null, false))) } + override val u: Matrix by lazy { EjmlDoubleMatrix(svd.getU(null, false)) } + override val s: Matrix by lazy { EjmlDoubleMatrix(svd.getW(null)) } + override val v: Matrix by lazy { EjmlDoubleMatrix(svd.getV(null, false)) } override val singularValues: Point by lazy { DoubleBuffer(svd.singularValues) } } QRDecompositionFeature::class -> object : QRDecompositionFeature { private val qr by lazy { - DecompositionFactory_DDRM.qr().apply { decompose(origin.ddrm.copy()) } + DecompositionFactory_DDRM.qr().apply { decompose(origin.copy()) } } override val q: Matrix by lazy { - EjmlMatrix(SimpleMatrix(qr.getQ(null, false))) + OrthogonalFeature + EjmlDoubleMatrix(qr.getQ(null, false)) + OrthogonalFeature } - override val r: Matrix by lazy { EjmlMatrix(SimpleMatrix(qr.getR(null, false))) + UFeature } + override val r: Matrix by lazy { EjmlDoubleMatrix(qr.getR(null, false)) + UFeature } } CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { override val l: Matrix by lazy { val cholesky = - DecompositionFactory_DDRM.chol(structure.rowNum, true).apply { decompose(origin.ddrm.copy()) } + DecompositionFactory_DDRM.chol(structure.rowNum, true).apply { decompose(origin.copy()) } - EjmlMatrix(SimpleMatrix(cholesky.getT(null))) + LFeature + EjmlDoubleMatrix(cholesky.getT(null)) + LFeature } } LupDecompositionFeature::class -> object : LupDecompositionFeature { private val lup by lazy { - DecompositionFactory_DDRM.lu(origin.numRows(), origin.numCols()) - .apply { decompose(origin.ddrm.copy()) } + DecompositionFactory_DDRM.lu(origin.numRows, origin.numCols).apply { decompose(origin.copy()) } } override val l: Matrix by lazy { - EjmlMatrix(SimpleMatrix(lup.getLower(null))) + LFeature + EjmlDoubleMatrix(lup.getLower(null)) + LFeature } override val u: Matrix by lazy { - EjmlMatrix(SimpleMatrix(lup.getUpper(null))) + UFeature + EjmlDoubleMatrix(lup.getUpper(null)) + UFeature } - override val p: Matrix by lazy { EjmlMatrix(SimpleMatrix(lup.getRowPivot(null))) } + override val p: Matrix by lazy { EjmlDoubleMatrix(lup.getRowPivot(null)) } } else -> null }?.let(type::cast) } + + /** + * Solves for *x* in the following equation: *x = [a] -1 · [b]*. + * + * @param a the base matrix. + * @param b n by p matrix. + * @return the solution for 'x' that is n by p. + */ + public fun solve(a: Matrix, b: Matrix): EjmlDoubleMatrix { + val res = DMatrixRMaj(1, 1) + CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) + return EjmlDoubleMatrix(res) + } + + /** + * Solves for *x* in the following equation: *x = [a] -1 · [b]*. + * + * @param a the base matrix. + * @param b n by p vector. + * @return the solution for 'x' that is n by p. + */ + public fun solve(a: Matrix, b: Point): EjmlDoubleVector { + val res = DMatrixRMaj(1, 1) + CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) + return EjmlDoubleVector(res) + } } - -/** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p matrix. - * @return the solution for 'x' that is n by p. - * @author Iaroslav Postovalov - */ -public fun EjmlLinearSpace.solve(a: Matrix, b: Matrix): EjmlMatrix = - EjmlMatrix(a.toEjml().origin.solve(b.toEjml().origin)) - -/** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p vector. - * @return the solution for 'x' that is n by p. - * @author Iaroslav Postovalov - */ -public fun EjmlLinearSpace.solve(a: Matrix, b: Point): EjmlVector = - EjmlVector(a.toEjml().origin.solve(b.toEjml().origin)) - -/** - * Inverts this matrix. - * - * @author Alexander Nozik - */ -@OptIn(UnstableKMathAPI::class) -public fun EjmlMatrix.inverted(): EjmlMatrix = getFeature>()!!.inverse as EjmlMatrix - -/** - * Inverts the given matrix. - * - * @author Alexander Nozik - */ -public fun EjmlLinearSpace.inverse(matrix: Matrix): Matrix = matrix.toEjml().inverted() \ No newline at end of file diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt index 32907d199..92c4d1cf0 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt @@ -5,18 +5,28 @@ package space.kscience.kmath.ejml -import org.ejml.simple.SimpleMatrix -import space.kscience.kmath.linear.Matrix +import org.ejml.data.DMatrix +import org.ejml.data.Matrix +import space.kscience.kmath.nd.Structure2D /** - * The matrix implementation over EJML [SimpleMatrix]. + * [space.kscience.kmath.linear.Matrix] implementation based on EJML [Matrix]. * - * @property origin the underlying [SimpleMatrix]. + * @param T the type of elements contained in the buffer. + * @param M the type of EJML matrix. + * @property origin The underlying EJML matrix. * @author Iaroslav Postovalov */ -public class EjmlMatrix(public val origin: SimpleMatrix) : Matrix { - public override val rowNum: Int get() = origin.numRows() - public override val colNum: Int get() = origin.numCols() +public abstract class EjmlMatrix(public open val origin: M) : Structure2D { + public override val rowNum: Int get() = origin.numRows + public override val colNum: Int get() = origin.numCols +} +/** + * [EjmlMatrix] specialization for [Double]. + * + * @author Iaroslav Postovalov + */ +public class EjmlDoubleMatrix(public override val origin: M) : EjmlMatrix(origin) { public override operator fun get(i: Int, j: Int): Double = origin[i, j] } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt index 2f4b4a8e2..81502d6d0 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt @@ -5,35 +5,41 @@ package space.kscience.kmath.ejml -import org.ejml.simple.SimpleMatrix +import org.ejml.data.DMatrixD1 +import org.ejml.data.Matrix import space.kscience.kmath.linear.Point /** - * Represents point over EJML [SimpleMatrix]. + * [Point] implementation based on EJML [Matrix]. * - * @property origin the underlying [SimpleMatrix]. + * @param T the type of elements contained in the buffer. + * @param M the type of EJML matrix. + * @property origin The underlying matrix. * @author Iaroslav Postovalov */ -public class EjmlVector internal constructor(public val origin: SimpleMatrix) : Point { +public abstract class EjmlVector(public open val origin: M) : Point { public override val size: Int - get() = origin.numRows() + get() = origin.numRows - init { - require(origin.numCols() == 1) { "Only single column matrices are allowed" } - } - - public override operator fun get(index: Int): Double = origin[index] - - public override operator fun iterator(): Iterator = object : Iterator { + public override operator fun iterator(): Iterator = object : Iterator { private var cursor: Int = 0 - override fun next(): Double { + override fun next(): T { cursor += 1 - return origin[cursor - 1] + return this@EjmlVector[cursor - 1] } - override fun hasNext(): Boolean = cursor < origin.numCols() * origin.numRows() + override fun hasNext(): Boolean = cursor < origin.numCols * origin.numRows } public override fun toString(): String = "EjmlVector(origin=$origin)" } + +/** + * [EjmlVector] specialization for [Double]. + * + * @author Iaroslav Postovalov + */ +public class EjmlDoubleVector(public override val origin: M) : EjmlVector(origin) { + public override operator fun get(index: Int): Double = origin[index] +} 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 59f9602d6..485c53c38 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 @@ -5,12 +5,15 @@ package space.kscience.kmath.ejml +import org.ejml.data.DMatrixRMaj +import org.ejml.dense.row.CommonOps_DDRM +import org.ejml.dense.row.RandomMatrices_DDRM import org.ejml.dense.row.factory.DecompositionFactory_DDRM -import org.ejml.simple.SimpleMatrix -import space.kscience.kmath.linear.* +import space.kscience.kmath.linear.DeterminantFeature +import space.kscience.kmath.linear.LupDecompositionFeature +import space.kscience.kmath.linear.getFeature import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.getFeature import kotlin.random.Random import kotlin.random.asJavaRandom import kotlin.test.* @@ -22,65 +25,59 @@ fun assertMatrixEquals(expected: StructureND, actual: StructureND = EjmlLinearSpace.getFeature(w) ?: fail() - assertEquals(m.determinant(), det.determinant) - val lup: LupDecompositionFeature = EjmlLinearSpace.getFeature(w) ?: fail() + val w = EjmlDoubleMatrix(m) + val det: DeterminantFeature = EjmlLinearSpaceDDRM.getFeature(w) ?: fail() + assertEquals(CommonOps_DDRM.det(m), det.determinant) + val lup: LupDecompositionFeature = EjmlLinearSpaceDDRM.getFeature(w) ?: fail() - val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows(), m.numCols()) - .also { it.decompose(m.ddrm.copy()) } + val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows, m.numCols) + .also { it.decompose(m.copy()) } - assertMatrixEquals(EjmlMatrix(SimpleMatrix(ludecompositionF64.getLower(null))), lup.l) - assertMatrixEquals(EjmlMatrix(SimpleMatrix(ludecompositionF64.getUpper(null))), lup.u) - assertMatrixEquals(EjmlMatrix(SimpleMatrix(ludecompositionF64.getRowPivot(null))), lup.p) - } - - private object SomeFeature : MatrixFeature {} - - @OptIn(UnstableKMathAPI::class) - @Test - fun suggestFeature() { - assertNotNull((EjmlMatrix(randomMatrix) + SomeFeature).getFeature()) + assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getLower(null)), lup.l) + assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getUpper(null)), lup.u) + assertMatrixEquals(EjmlDoubleMatrix(ludecompositionF64.getRowPivot(null)), lup.p) } @Test fun get() { val m = randomMatrix - assertEquals(m[0, 0], EjmlMatrix(m)[0, 0]) + assertEquals(m[0, 0], EjmlDoubleMatrix(m)[0, 0]) } @Test fun origin() { val m = randomMatrix - assertSame(m, EjmlMatrix(m).origin) + assertSame(m, EjmlDoubleMatrix(m).origin) } } diff --git a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt index e1bcd269e..9bf76033d 100644 --- a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt +++ b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlVectorTest.kt @@ -5,7 +5,8 @@ package space.kscience.kmath.ejml -import org.ejml.simple.SimpleMatrix +import org.ejml.data.DMatrixRMaj +import org.ejml.dense.row.RandomMatrices_DDRM import kotlin.random.Random import kotlin.random.asJavaRandom import kotlin.test.Test @@ -15,30 +16,34 @@ import kotlin.test.assertSame internal class EjmlVectorTest { private val random = Random(0) - private val randomMatrix: SimpleMatrix - get() = SimpleMatrix.random_DDRM(random.nextInt(2, 100), 1, 0.0, 10.0, random.asJavaRandom()) + private val randomMatrix: DMatrixRMaj + get() { + val d = DMatrixRMaj(random.nextInt(2, 100), 1) + RandomMatrices_DDRM.fillUniform(d, random.asJavaRandom()) + return d + } @Test fun size() { val m = randomMatrix - val w = EjmlVector(m) - assertEquals(m.numRows(), w.size) + val w = EjmlDoubleVector(m) + assertEquals(m.numRows, w.size) } @Test fun get() { val m = randomMatrix - val w = EjmlVector(m) + val w = EjmlDoubleVector(m) assertEquals(m[0, 0], w[0]) } @Test fun iterator() { val m = randomMatrix - val w = EjmlVector(m) + val w = EjmlDoubleVector(m) assertEquals( - m.iterator(true, 0, 0, m.numRows() - 1, 0).asSequence().toList(), + m.iterator(true, 0, 0, m.numRows - 1, 0).asSequence().toList(), w.iterator().asSequence().toList() ) } @@ -46,7 +51,7 @@ internal class EjmlVectorTest { @Test fun origin() { val m = randomMatrix - val w = EjmlVector(m) + val w = EjmlDoubleVector(m) assertSame(m, w.origin) } } diff --git a/kmath-for-real/README.md b/kmath-for-real/README.md index 922e6572b..8b3b8e9e0 100644 --- a/kmath-for-real/README.md +++ b/kmath-for-real/README.md @@ -9,29 +9,27 @@ Specialization of KMath APIs for Double numbers. ## Artifact: -The Maven coordinates of this project are `space.kscience:kmath-for-real:0.3.0-dev-6`. +The Maven coordinates of this project are `space.kscience:kmath-for-real:0.3.0-dev-8`. **Gradle:** ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { - implementation 'space.kscience:kmath-for-real:0.3.0-dev-6' + implementation 'space.kscience:kmath-for-real:0.3.0-dev-8' } ``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { - implementation("space.kscience:kmath-for-real:0.3.0-dev-6") + implementation("space.kscience:kmath-for-real:0.3.0-dev-8") } ``` diff --git a/kmath-for-real/build.gradle.kts b/kmath-for-real/build.gradle.kts index fc454205b..f6d12decd 100644 --- a/kmath-for-real/build.gradle.kts +++ b/kmath-for-real/build.gradle.kts @@ -1,10 +1,3 @@ -/* - * 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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") @@ -22,7 +15,7 @@ readme { All operations are specialized to work with `Double` numbers without declaring algebraic contexts. One can still use generic algebras though. """.trimIndent() - maturity = Maturity.EXPERIMENTAL + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) feature( diff --git a/kmath-functions/README.md b/kmath-functions/README.md index eef677565..4f592c845 100644 --- a/kmath-functions/README.md +++ b/kmath-functions/README.md @@ -11,29 +11,27 @@ Functions and interpolations. ## Artifact: -The Maven coordinates of this project are `space.kscience:kmath-functions:0.3.0-dev-6`. +The Maven coordinates of this project are `space.kscience:kmath-functions:0.3.0-dev-8`. **Gradle:** ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { - implementation 'space.kscience:kmath-functions:0.3.0-dev-6' + implementation 'space.kscience:kmath-functions:0.3.0-dev-8' } ``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { - implementation("space.kscience:kmath-functions:0.3.0-dev-6") + implementation("space.kscience:kmath-functions:0.3.0-dev-8") } ``` diff --git a/kmath-functions/build.gradle.kts b/kmath-functions/build.gradle.kts index ca678bc0e..622b8f8da 100644 --- a/kmath-functions/build.gradle.kts +++ b/kmath-functions/build.gradle.kts @@ -1,8 +1,3 @@ -/* - * 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. - */ - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt index bc23e2f1b..ae82a40be 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegrator.kt @@ -8,6 +8,13 @@ import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.Field import space.kscience.kmath.structures.* +/** + * Set of univariate integration ranges. First components correspond to ranges themselves, second components to number of + * integration nodes per range + */ +public class UnivariateIntegrandRanges(public val ranges: List, Int>>) : IntegrandFeature { + public constructor(vararg pairs: Pair, Int>) : this(pairs.toList()) +} /** * A simple one-pass integrator based on Gauss rule @@ -18,9 +25,29 @@ public class GaussIntegrator( private fun buildRule(integrand: UnivariateIntegrand): Pair, Buffer> { val factory = integrand.getFeature() ?: GaussLegendreRuleFactory - val numPoints = integrand.getFeature()?.maxCalls ?: 100 - val range = integrand.getFeature>()?.range ?: 0.0..1.0 - return factory.build(numPoints, range) + val predefinedRanges = integrand.getFeature() + if (predefinedRanges == null || predefinedRanges.ranges.isEmpty()) { + val numPoints = integrand.getFeature()?.maxCalls ?: 100 + val range = integrand.getFeature()?.range ?: 0.0..1.0 + return factory.build(numPoints, range) + } else { + val ranges = predefinedRanges.ranges + var counter = 0 + val length = ranges.sumOf { it.second } + val pointsArray = DoubleArray(length) + val weightsArray = DoubleArray(length) + + for (range in ranges) { + val rule = factory.build(range.second, range.first) + repeat(rule.first.size) { i -> + pointsArray[counter] = rule.first[i] + weightsArray[counter] = rule.second[i] + counter++ + } + + } + return pointsArray.asBuffer() to weightsArray.asBuffer() + } } override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand = with(algebra) { @@ -49,7 +76,8 @@ public class GaussIntegrator( * Following features are evaluated: * * [GaussIntegratorRuleFactory] - A factory for computing the Gauss integration rule. By default uses [GaussLegendreRuleFactory] * * [IntegrationRange] - the univariate range of integration. By default uses 0..1 interval. - * * [IntegrandMaxCalls] - the maximum number of function calls during integration. For non-iterative rules, always uses the maximum number of points. By default uses 100 points. + * * [IntegrandMaxCalls] - the maximum number of function calls during integration. For non-iterative rules, always uses the maximum number of points. By default uses 10 points. + * * [UnivariateIntegrandRanges] - Set of ranges and number of points per range. Defaults to given [IntegrationRange] and [IntegrandMaxCalls] */ @UnstableKMathAPI public fun Field.integrate( @@ -64,15 +92,25 @@ public fun Field.integrate( @UnstableKMathAPI public fun Field.integrate( range: ClosedRange, - numPoints: Int = 100, + order: Int = 10, + intervals: Int = 10, vararg features: IntegrandFeature, function: (Double) -> T, -): UnivariateIntegrand = GaussIntegrator(this).integrate( - UnivariateIntegrand( - function, - IntegrationRange(range), - GaussLegendreRuleFactory, - IntegrandMaxCalls(numPoints), - *features +): UnivariateIntegrand { + require(range.endInclusive > range.start) { "The range upper bound should be higher than lower bound" } + require(order > 1) { "The order of polynomial must be more than 1" } + require(intervals > 0) { "Number of intervals must be positive" } + val rangeSize = (range.endInclusive - range.start) / intervals + val ranges = UnivariateIntegrandRanges( + (0 until intervals).map { i -> (rangeSize * i)..(rangeSize * (i + 1)) to order } ) -) \ No newline at end of file + return GaussIntegrator(this).integrate( + UnivariateIntegrand( + function, + IntegrationRange(range), + GaussLegendreRuleFactory, + ranges, + *features + ) + ) +} \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt index 0b41a3f8b..bcd5005c4 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt @@ -33,7 +33,7 @@ public fun UnivariateIntegrand( public typealias UnivariateIntegrator = Integrator> @JvmInline -public value class IntegrationRange>(public val range: ClosedRange) : IntegrandFeature +public value class IntegrationRange(public val range: ClosedRange) : IntegrandFeature public val UnivariateIntegrand.value: T? get() = getFeature>()?.value diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/GaussIntegralTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/GaussIntegralTest.kt index 5ec90f42a..195711452 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/GaussIntegralTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/GaussIntegralTest.kt @@ -19,12 +19,12 @@ class GaussIntegralTest { val res = DoubleField.integrate(0.0..2 * PI) { x -> sin(x) } - assertEquals(0.0, res.value!!, 1e-4) + assertEquals(0.0, res.value!!, 1e-2) } @Test fun gaussUniform() { - val res = DoubleField.integrate(0.0..100.0,300) { x -> + val res = DoubleField.integrate(0.0..100.0) { x -> if(x in 30.0..50.0){ 1.0 } else { diff --git a/kmath-geometry/build.gradle.kts b/kmath-geometry/build.gradle.kts index 65db43edf..9b6e593b2 100644 --- a/kmath-geometry/build.gradle.kts +++ b/kmath-geometry/build.gradle.kts @@ -1,10 +1,3 @@ -/* - * 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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") @@ -17,5 +10,5 @@ kotlin.sourceSets.commonMain { } readme { - maturity = Maturity.PROTOTYPE + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE } diff --git a/kmath-histograms/build.gradle.kts b/kmath-histograms/build.gradle.kts index 024e34076..2167726c0 100644 --- a/kmath-histograms/build.gradle.kts +++ b/kmath-histograms/build.gradle.kts @@ -1,10 +1,3 @@ -/* - * 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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") @@ -28,5 +21,5 @@ kotlin.sourceSets { } readme { - maturity = Maturity.PROTOTYPE + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE } diff --git a/kmath-jupyter/build.gradle.kts b/kmath-jupyter/build.gradle.kts new file mode 100644 index 000000000..83a6a771a --- /dev/null +++ b/kmath-jupyter/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + id("ru.mipt.npm.gradle.jvm") + kotlin("jupyter.api") +} + +dependencies { + api(project(":kmath-ast")) + api(project(":kmath-complex")) + api(project(":kmath-for-real")) +} + +kscience{ + useHtml() +} + +readme { + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE +} + +kotlin.sourceSets.all { + languageSettings.useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI") +} diff --git a/kmath-jupyter/src/main/kotlin/space/kscience/kmath/jupyter/KMathJupyter.kt b/kmath-jupyter/src/main/kotlin/space/kscience/kmath/jupyter/KMathJupyter.kt new file mode 100644 index 000000000..78b25af47 --- /dev/null +++ b/kmath-jupyter/src/main/kotlin/space/kscience/kmath/jupyter/KMathJupyter.kt @@ -0,0 +1,129 @@ +package space.kscience.kmath.jupyter + +import kotlinx.html.Unsafe +import kotlinx.html.div +import kotlinx.html.stream.createHTML +import kotlinx.html.unsafe +import org.jetbrains.kotlinx.jupyter.api.DisplayResult +import org.jetbrains.kotlinx.jupyter.api.HTML +import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary +import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration +import space.kscience.kmath.expressions.MST +import space.kscience.kmath.ast.rendering.FeaturedMathRendererWithPostProcess +import space.kscience.kmath.ast.rendering.MathMLSyntaxRenderer +import space.kscience.kmath.ast.rendering.renderWithStringBuilder +import space.kscience.kmath.complex.Complex +import space.kscience.kmath.nd.Structure2D +import space.kscience.kmath.operations.GroupOperations +import space.kscience.kmath.operations.RingOperations +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.asSequence + +/** + * A function for conversion of number to MST for pretty print + */ +public fun Number.toMst(): MST.Numeric = MST.Numeric(this) + +@JupyterLibrary +internal class KMathJupyter : JupyterIntegration() { + private val mathRender = FeaturedMathRendererWithPostProcess.Default + private val syntaxRender = MathMLSyntaxRenderer + + private fun MST.toDisplayResult(): DisplayResult = HTML(createHTML().div { + unsafe { + +syntaxRender.renderWithStringBuilder(mathRender.render(this@toDisplayResult)) + } + }) + + private fun Unsafe.appendCellValue(it: Any?) { + when (it) { + is Number -> { + val s = StringBuilder() + syntaxRender.renderPart(mathRender.render(MST.Numeric(it)), s) + +s.toString() + } + is MST -> { + val s = StringBuilder() + syntaxRender.renderPart(mathRender.render(it), s) + +s.toString() + } + else -> { + +"" + +it.toString() + +"" + } + } + } + + override fun Builder.onLoaded() { + import( + "space.kscience.kmath.ast.*", + "space.kscience.kmath.ast.rendering.*", + "space.kscience.kmath.structures.*", + "space.kscience.kmath.operations.*", + "space.kscience.kmath.expressions.*", + "space.kscience.kmath.nd.*", + "space.kscience.kmath.misc.*", + "space.kscience.kmath.real.*", + ) + + import("space.kscience.kmath.jupyter.toMst") + + render { it.toDisplayResult() } + //render { MST.Numeric(it).toDisplayResult() } + + render> { structure -> + HTML(createHTML().div { + unsafe { + +"" + +"" + +"" + +"" + structure.rows.forEach { row -> + +"" + row.asSequence().forEach { + +"" + appendCellValue(it) + +"" + } + +"" + } + +"" + +"" + +"" + +"" + } + }) + } + + render> { buffer -> + HTML(createHTML().div { + unsafe { + +"" + +"" + +"" + +"" + buffer.asSequence().forEach { + +"" + +"" + appendCellValue(it) + +"" + +"" + } + +"" + +"" + +"" + +"" + } + }) + } + + render { + MST.Binary( + operation = GroupOperations.PLUS_OPERATION, + left = MST.Numeric(it.re), + right = MST.Binary(RingOperations.TIMES_OPERATION, MST.Numeric(it.im), MST.Symbolic("i")), + ).toDisplayResult() + } + } +} diff --git a/kmath-kotlingrad/build.gradle.kts b/kmath-kotlingrad/build.gradle.kts index 576c073c3..ed5b5bcb4 100644 --- a/kmath-kotlingrad/build.gradle.kts +++ b/kmath-kotlingrad/build.gradle.kts @@ -1,19 +1,14 @@ -/* - * 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. - */ - plugins { kotlin("jvm") id("ru.mipt.npm.gradle.common") } dependencies { - implementation("com.github.breandan:kaliningraph:0.1.4") - implementation("com.github.breandan:kotlingrad:0.4.0") + api("com.github.breandan:kaliningraph:0.1.4") + api("com.github.breandan:kotlingrad:0.4.5") api(project(":kmath-ast")) } readme { maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE -} \ No newline at end of file +} diff --git a/kmath-kotlingrad/src/test/kotlin/space/kscience/kmath/kotlingrad/AdaptingTests.kt b/kmath-kotlingrad/src/test/kotlin/space/kscience/kmath/kotlingrad/AdaptingTests.kt index 66018a227..3ed73451d 100644 --- a/kmath-kotlingrad/src/test/kotlin/space/kscience/kmath/kotlingrad/AdaptingTests.kt +++ b/kmath-kotlingrad/src/test/kotlin/space/kscience/kmath/kotlingrad/AdaptingTests.kt @@ -10,6 +10,7 @@ import space.kscience.kmath.asm.compileToExpression import space.kscience.kmath.ast.parseMath import space.kscience.kmath.expressions.MstAlgebra import space.kscience.kmath.expressions.invoke +import space.kscience.kmath.misc.symbol import space.kscience.kmath.operations.DoubleField import kotlin.test.Test import kotlin.test.assertEquals @@ -19,8 +20,8 @@ import kotlin.test.fail internal class AdaptingTests { @Test fun symbol() { - val c1 = MstAlgebra.bindSymbol("x") - assertTrue(c1.toSVar>().name == "x") + val c1 = MstAlgebra.bindSymbol(x.identity) + assertEquals(x.identity, c1.toSVar>().name) val c2 = "kitten".parseMath().toSFun>() if (c2 is SVar) assertTrue(c2.name == "kitten") else fail() } @@ -45,23 +46,27 @@ internal class AdaptingTests { @Test fun simpleFunctionDerivative() { - val x = MstAlgebra.bindSymbol("x").toSVar>() + val xSVar = MstAlgebra.bindSymbol(x.identity).toSVar>() val quadratic = "x^2-4*x-44".parseMath().toSFun>() - val actualDerivative = quadratic.d(x).toMst().compileToExpression(DoubleField) + val actualDerivative = quadratic.d(xSVar).toMst().compileToExpression(DoubleField) val expectedDerivative = "2*x-4".parseMath().compileToExpression(DoubleField) - assertEquals(actualDerivative("x" to 123.0), expectedDerivative("x" to 123.0)) + assertEquals(actualDerivative(x to 123.0), expectedDerivative(x to 123.0)) } @Test fun moreComplexDerivative() { - val x = MstAlgebra.bindSymbol("x").toSVar>() + val xSVar = MstAlgebra.bindSymbol(x.identity).toSVar>() val composition = "-sqrt(sin(x^2)-cos(x)^2-16*x)".parseMath().toSFun>() - val actualDerivative = composition.d(x).toMst().compileToExpression(DoubleField) + val actualDerivative = composition.d(xSVar).toMst().compileToExpression(DoubleField) - val expectedDerivative = - "-(2*x*cos(x^2)+2*sin(x)*cos(x)-16)/(2*sqrt(sin(x^2)-16*x-cos(x)^2))".parseMath().compileToExpression(DoubleField) + val expectedDerivative = "-(2*x*cos(x^2)+2*sin(x)*cos(x)-16)/(2*sqrt(sin(x^2)-16*x-cos(x)^2))" + .parseMath() + .compileToExpression(DoubleField) + assertEquals(actualDerivative(x to 0.1), expectedDerivative(x to 0.1)) + } - assertEquals(actualDerivative("x" to 0.1), expectedDerivative("x" to 0.1)) + private companion object { + private val x by symbol } } diff --git a/kmath-memory/build.gradle.kts b/kmath-memory/build.gradle.kts index 50c317324..288c61a51 100644 --- a/kmath-memory/build.gradle.kts +++ b/kmath-memory/build.gradle.kts @@ -1,8 +1,3 @@ -/* - * 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. - */ - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") @@ -14,4 +9,4 @@ readme { description = """ An API and basic implementation for arranging objects in a continous memory block. """.trimIndent() -} \ No newline at end of file +} diff --git a/kmath-nd4j/README.md b/kmath-nd4j/README.md index 829fe4142..5f33c1db1 100644 --- a/kmath-nd4j/README.md +++ b/kmath-nd4j/README.md @@ -9,30 +9,28 @@ ND4J based implementations of KMath abstractions. ## Artifact: -The Maven coordinates of this project are `space.kscience:kmath-nd4j:0.3.0-dev-6`. +The Maven coordinates of this project are `space.kscience:kmath-nd4j:0.3.0-dev-8`. **Gradle:** ```gradle repositories { maven { url 'https://repo.kotlin.link' } - maven { url 'https://dl.bintray.com/hotkeytlt/maven' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap + mavenCentral() } dependencies { - implementation 'space.kscience:kmath-nd4j:0.3.0-dev-6' + implementation 'space.kscience:kmath-nd4j:0.3.0-dev-8' } ``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap - maven("https://dl.bintray.com/hotkeytlt/maven") // required for a + mavenCentral() } dependencies { - implementation("space.kscience:kmath-nd4j:0.3.0-dev-6") + implementation("space.kscience:kmath-nd4j:0.3.0-dev-8") } ``` diff --git a/kmath-nd4j/build.gradle.kts b/kmath-nd4j/build.gradle.kts index 17232b44f..bc61060db 100644 --- a/kmath-nd4j/build.gradle.kts +++ b/kmath-nd4j/build.gradle.kts @@ -1,10 +1,3 @@ -/* - * 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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("jvm") id("ru.mipt.npm.gradle.common") @@ -13,14 +6,14 @@ plugins { dependencies { api(project(":kmath-core")) api("org.nd4j:nd4j-api:1.0.0-beta7") - testImplementation("org.deeplearning4j:deeplearning4j-core:1.0.0-beta7") + testImplementation("org.nd4j:nd4j-native:1.0.0-beta7") testImplementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") testImplementation("org.slf4j:slf4j-simple:1.7.30") } readme { description = "ND4J NDStructure implementation and according NDAlgebra classes" - maturity = Maturity.EXPERIMENTAL + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) feature( diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt index f2c69adee..0205f1c87 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt @@ -6,7 +6,10 @@ package space.kscience.kmath.nd4j import org.nd4j.linalg.api.ndarray.INDArray +import org.nd4j.linalg.api.ops.impl.scalar.Pow +import org.nd4j.linalg.api.ops.impl.transforms.strict.* import org.nd4j.linalg.factory.Nd4j +import org.nd4j.linalg.ops.transforms.Transforms import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.* import space.kscience.kmath.operations.* @@ -207,7 +210,8 @@ public interface Nd4jArrayField> : FieldND, Nd4jArrayRing< /** * Represents [FieldND] over [Nd4jArrayDoubleStructure]. */ -public class DoubleNd4jArrayField(public override val shape: IntArray) : Nd4jArrayField { +public class DoubleNd4jArrayField(public override val shape: IntArray) : Nd4jArrayField, + ExtendedField> { public override val elementContext: DoubleField get() = DoubleField public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(this).asDoubleStructure() @@ -239,14 +243,31 @@ public class DoubleNd4jArrayField(public override val shape: IntArray) : Nd4jArr public override operator fun Double.minus(arg: StructureND): Nd4jArrayStructure { return arg.ndArray.rsub(this).wrap() } + + override fun sin(arg: StructureND): StructureND = Transforms.sin(arg.ndArray).wrap() + + override fun cos(arg: StructureND): StructureND = Transforms.cos(arg.ndArray).wrap() + + override fun asin(arg: StructureND): StructureND = Transforms.asin(arg.ndArray).wrap() + + override fun acos(arg: StructureND): StructureND = Transforms.acos(arg.ndArray).wrap() + + override fun atan(arg: StructureND): StructureND = Transforms.atan(arg.ndArray).wrap() + + override fun power(arg: StructureND, pow: Number): StructureND = + Transforms.pow(arg.ndArray,pow).wrap() + + override fun exp(arg: StructureND): StructureND = Transforms.exp(arg.ndArray).wrap() + + override fun ln(arg: StructureND): StructureND = Transforms.log(arg.ndArray).wrap() } /** * Represents [FieldND] over [Nd4jArrayStructure] of [Float]. */ -public class FloatNd4jArrayField(public override val shape: IntArray) : Nd4jArrayField { - public override val elementContext: FloatField - get() = FloatField +public class FloatNd4jArrayField(public override val shape: IntArray) : Nd4jArrayField, + ExtendedField> { + public override val elementContext: FloatField get() = FloatField public override fun INDArray.wrap(): Nd4jArrayStructure = checkShape(this).asFloatStructure() @@ -270,6 +291,23 @@ public class FloatNd4jArrayField(public override val shape: IntArray) : Nd4jArra public override operator fun Float.minus(arg: StructureND): Nd4jArrayStructure = arg.ndArray.rsub(this).wrap() + + override fun sin(arg: StructureND): StructureND = Sin(arg.ndArray).z().wrap() + + override fun cos(arg: StructureND): StructureND = Cos(arg.ndArray).z().wrap() + + override fun asin(arg: StructureND): StructureND = ASin(arg.ndArray).z().wrap() + + override fun acos(arg: StructureND): StructureND = ACos(arg.ndArray).z().wrap() + + override fun atan(arg: StructureND): StructureND = ATan(arg.ndArray).z().wrap() + + override fun power(arg: StructureND, pow: Number): StructureND = + Pow(arg.ndArray, pow.toDouble()).z().wrap() + + override fun exp(arg: StructureND): StructureND = Exp(arg.ndArray).z().wrap() + + override fun ln(arg: StructureND): StructureND = Log(arg.ndArray).z().wrap() } /** diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayStructure.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayStructure.kt index b9aa251d2..79db86fcc 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayStructure.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayStructure.kt @@ -20,8 +20,7 @@ public sealed class Nd4jArrayStructure : MutableStructureND { */ public abstract val ndArray: INDArray - public override val shape: IntArray - get() = ndArray.shape().toIntArray() + public override val shape: IntArray get() = ndArray.shape().toIntArray() internal abstract fun elementsIterator(): Iterator> internal fun indicesIterator(): Iterator = ndArray.indicesIterator() diff --git a/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt b/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt index c3874b249..b6c524cc6 100644 --- a/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt +++ b/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt @@ -6,8 +6,12 @@ package space.kscience.kmath.nd4j import org.nd4j.linalg.factory.Nd4j +import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.operations.invoke +import kotlin.math.PI import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue import kotlin.test.fail internal class Nd4jArrayAlgebraTest { @@ -43,4 +47,14 @@ internal class Nd4jArrayAlgebraTest { expected[intArrayOf(1, 1)] = 26 assertEquals(expected, res) } + + @Test + fun testSin() = DoubleNd4jArrayField(intArrayOf(2, 2)).invoke { + val initial = produce { (i, j) -> if (i == j) PI/2 else 0.0 } + val transformed = sin(initial) + val expected = produce { (i, j) -> if (i == j) 1.0 else 0.0 } + + println(transformed) + assertTrue { StructureND.contentEquals(transformed, expected) } + } } diff --git a/kmath-stat/build.gradle.kts b/kmath-stat/build.gradle.kts index dff504ef0..e8f629f7a 100644 --- a/kmath-stat/build.gradle.kts +++ b/kmath-stat/build.gradle.kts @@ -1,10 +1,3 @@ -/* - * 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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("multiplatform") id("ru.mipt.npm.gradle.common") @@ -30,5 +23,5 @@ kotlin.sourceSets { } readme { - maturity = Maturity.EXPERIMENTAL + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL } \ No newline at end of file diff --git a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/CommonsDistributionsTest.kt b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/CommonsDistributionsTest.kt index 066fd69e6..19c01e099 100644 --- a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/CommonsDistributionsTest.kt +++ b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/CommonsDistributionsTest.kt @@ -5,15 +5,14 @@ package space.kscience.kmath.stat -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import space.kscience.kmath.samplers.GaussianSampler internal class CommonsDistributionsTest { @Test - fun testNormalDistributionSuspend() = GlobalScope.launch { + fun testNormalDistributionSuspend() = runBlocking { val distribution = GaussianSampler(7.0, 2.0) val generator = RandomGenerator.default(1) val sample = distribution.sample(generator).nextBuffer(1000) diff --git a/kmath-tensors/README.md b/kmath-tensors/README.md new file mode 100644 index 000000000..7ece44217 --- /dev/null +++ b/kmath-tensors/README.md @@ -0,0 +1,35 @@ +# Module kmath-tensors + +Common linear algebra operations on tensors. + + - [tensor algebra](src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt) : Basic linear algebra operations on tensors (plus, dot, etc.) + - [tensor algebra with broadcasting](src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/BroadcastDoubleTensorAlgebra.kt) : Basic linear algebra operations implemented with broadcasting. + - [linear algebra operations](src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt) : Advanced linear algebra operations like LU decomposition, SVD, etc. + + +## Artifact: + +The Maven coordinates of this project are `space.kscience:kmath-tensors:0.3.0-dev-8`. + +**Gradle:** +```gradle +repositories { + maven { url 'https://repo.kotlin.link' } + mavenCentral() +} + +dependencies { + implementation 'space.kscience:kmath-tensors:0.3.0-dev-8' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + mavenCentral() +} + +dependencies { + implementation("space.kscience:kmath-tensors:0.3.0-dev-8") +} +``` diff --git a/kmath-tensors/build.gradle.kts b/kmath-tensors/build.gradle.kts new file mode 100644 index 000000000..b7f24dc6a --- /dev/null +++ b/kmath-tensors/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + id("ru.mipt.npm.gradle.mpp") +} + +kotlin.sourceSets { + all { + languageSettings.useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI") + } + commonMain { + dependencies { + api(project(":kmath-core")) + api(project(":kmath-stat")) + } + } +} + +tasks.dokkaHtml { + dependsOn(tasks.build) +} + +readme { + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE + propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) + + feature( + id = "tensor algebra", + description = "Basic linear algebra operations on tensors (plus, dot, etc.)", + ref = "src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt" + ) + + feature( + id = "tensor algebra with broadcasting", + description = "Basic linear algebra operations implemented with broadcasting.", + ref = "src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/BroadcastDoubleTensorAlgebra.kt" + ) + + feature( + id = "linear algebra operations", + description = "Advanced linear algebra operations like LU decomposition, SVD, etc.", + ref = "src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt" + ) + +} \ No newline at end of file diff --git a/kmath-tensors/docs/README-TEMPLATE.md b/kmath-tensors/docs/README-TEMPLATE.md new file mode 100644 index 000000000..5fd968afd --- /dev/null +++ b/kmath-tensors/docs/README-TEMPLATE.md @@ -0,0 +1,7 @@ +# Module kmath-tensors + +Common linear algebra operations on tensors. + +${features} + +${artifact} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/AnalyticTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/AnalyticTensorAlgebra.kt new file mode 100644 index 000000000..1db986e77 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/AnalyticTensorAlgebra.kt @@ -0,0 +1,131 @@ +/* + * 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.tensors.api + + +/** + * Analytic operations on [Tensor]. + * + * @param T the type of items closed under analytic functions in the tensors. + */ +public interface AnalyticTensorAlgebra : TensorPartialDivisionAlgebra { + + /** + * @return the mean of all elements in the input tensor. + */ + public fun Tensor.mean(): T + + /** + * Returns the mean of each row of the input tensor in the given dimension [dim]. + * + * If [keepDim] is true, the output tensor is of the same size as + * input except in the dimension [dim] where it is of size 1. + * Otherwise, [dim] is squeezed, resulting in the output tensor having 1 fewer dimension. + * + * @param dim the dimension to reduce. + * @param keepDim whether the output tensor has [dim] retained or not. + * @return the mean of each row of the input tensor in the given dimension [dim]. + */ + public fun Tensor.mean(dim: Int, keepDim: Boolean): Tensor + + /** + * @return the standard deviation of all elements in the input tensor. + */ + public fun Tensor.std(): T + + /** + * Returns the standard deviation of each row of the input tensor in the given dimension [dim]. + * + * If [keepDim] is true, the output tensor is of the same size as + * input except in the dimension [dim] where it is of size 1. + * Otherwise, [dim] is squeezed, resulting in the output tensor having 1 fewer dimension. + * + * @param dim the dimension to reduce. + * @param keepDim whether the output tensor has [dim] retained or not. + * @return the standard deviation of each row of the input tensor in the given dimension [dim]. + */ + public fun Tensor.std(dim: Int, keepDim: Boolean): Tensor + + /** + * @return the variance of all elements in the input tensor. + */ + public fun Tensor.variance(): T + + /** + * Returns the variance of each row of the input tensor in the given dimension [dim]. + * + * If [keepDim] is true, the output tensor is of the same size as + * input except in the dimension [dim] where it is of size 1. + * Otherwise, [dim] is squeezed, resulting in the output tensor having 1 fewer dimension. + * + * @param dim the dimension to reduce. + * @param keepDim whether the output tensor has [dim] retained or not. + * @return the variance of each row of the input tensor in the given dimension [dim]. + */ + public fun Tensor.variance(dim: Int, keepDim: Boolean): Tensor + + /** + * Returns the covariance matrix M of given vectors. + * + * M[i, j] contains covariance of i-th and j-th given vectors + * + * @param tensors the [List] of 1-dimensional tensors with same shape + * @return the covariance matrix + */ + public fun cov(tensors: List>): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.exp.html + public fun Tensor.exp(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.log.html + public fun Tensor.ln(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.sqrt.html + public fun Tensor.sqrt(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.acos.html#torch.cos + public fun Tensor.cos(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.acos.html#torch.acos + public fun Tensor.acos(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.acosh.html#torch.cosh + public fun Tensor.cosh(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.acosh.html#torch.acosh + public fun Tensor.acosh(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.asin.html#torch.sin + public fun Tensor.sin(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.asin.html#torch.asin + public fun Tensor.asin(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.asin.html#torch.sinh + public fun Tensor.sinh(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.asin.html#torch.asinh + public fun Tensor.asinh(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.atan.html#torch.tan + public fun Tensor.tan(): Tensor + + //https://pytorch.org/docs/stable/generated/torch.atan.html#torch.atan + public fun Tensor.atan(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.atanh.html#torch.tanh + public fun Tensor.tanh(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.atanh.html#torch.atanh + public fun Tensor.atanh(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.ceil.html#torch.ceil + public fun Tensor.ceil(): Tensor + + //For information: https://pytorch.org/docs/stable/generated/torch.floor.html#torch.floor + public fun Tensor.floor(): Tensor + +} \ No newline at end of file diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt new file mode 100644 index 000000000..6bdecfa85 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt @@ -0,0 +1,97 @@ +/* + * 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.tensors.api + +/** + * Common linear algebra operations. Operates on [Tensor]. + * + * @param T the type of items closed under division in the tensors. + */ +public interface LinearOpsTensorAlgebra : TensorPartialDivisionAlgebra { + + /** + * Computes the determinant of a square matrix input, or of each square matrix in a batched input. + * For more information: https://pytorch.org/docs/stable/linalg.html#torch.linalg.det + * + * @return the determinant. + */ + public fun Tensor.det(): Tensor + + /** + * Computes the multiplicative inverse matrix of a square matrix input, or of each square matrix in a batched input. + * Given a square matrix `A`, return the matrix `AInv` satisfying + * `A dot AInv = AInv dot A = eye(a.shape[0])`. + * For more information: https://pytorch.org/docs/stable/linalg.html#torch.linalg.inv + * + * @return the multiplicative inverse of a matrix. + */ + public fun Tensor.inv(): Tensor + + /** + * Cholesky decomposition. + * + * Computes the Cholesky decomposition of a Hermitian (or symmetric for real-valued matrices) + * positive-definite matrix or the Cholesky decompositions for a batch of such matrices. + * Each decomposition has the form: + * Given a tensor `input`, return the tensor `L` satisfying `input = L dot L.H`, + * where L is a lower-triangular matrix and L.H is the conjugate transpose of L, + * which is just a transpose for the case of real-valued input matrices. + * For more information: https://pytorch.org/docs/stable/linalg.html#torch.linalg.cholesky + * + * @return the batch of L matrices. + */ + public fun Tensor.cholesky(): Tensor + + /** + * QR decomposition. + * + * Computes the QR decomposition of a matrix or a batch of matrices, and returns a pair `(Q, R)` of tensors. + * Given a tensor `input`, return tensors (Q, R) satisfying ``input = Q dot R``, + * with `Q` being an orthogonal matrix or batch of orthogonal matrices + * and `R` being an upper triangular matrix or batch of upper triangular matrices. + * For more information: https://pytorch.org/docs/stable/linalg.html#torch.linalg.qr + * + * @return pair of Q and R tensors. + */ + public fun Tensor.qr(): Pair, Tensor> + + /** + * LUP decomposition + * + * Computes the LUP decomposition of a matrix or a batch of matrices. + * Given a tensor `input`, return tensors (P, L, U) satisfying `P dot input = L dot U`, + * with `P` being a permutation matrix or batch of matrices, + * `L` being a lower triangular matrix or batch of matrices, + * `U` being an upper triangular matrix or batch of matrices. + * + * * @return triple of P, L and U tensors + */ + public fun Tensor.lu(): Triple, Tensor, Tensor> + + /** + * Singular Value Decomposition. + * + * Computes the singular value decomposition of either a matrix or batch of matrices `input`. + * The singular value decomposition is represented as a triple `(U, S, V)`, + * such that `input = U dot diagonalEmbedding(S) dot V.H`, + * where V.H is the conjugate transpose of V. + * If input is a batch of tensors, then U, S, and Vh are also batched with the same batch dimensions as input. + * For more information: https://pytorch.org/docs/stable/linalg.html#torch.linalg.svd + * + * @return triple `(U, S, V)`. + */ + public fun Tensor.svd(): Triple, Tensor, Tensor> + + /** + * Returns eigenvalues and eigenvectors of a real symmetric matrix input or a batch of real symmetric matrices, + * represented by a pair (eigenvalues, eigenvectors). + * For more information: https://pytorch.org/docs/stable/generated/torch.symeig.html + * + * @return a pair (eigenvalues, eigenvectors) + */ + public fun Tensor.symEig(): Pair, Tensor> + +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/Tensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/Tensor.kt new file mode 100644 index 000000000..179787684 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/Tensor.kt @@ -0,0 +1,5 @@ +package space.kscience.kmath.tensors.api + +import space.kscience.kmath.nd.MutableStructureND + +public typealias Tensor = MutableStructureND diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt new file mode 100644 index 000000000..2eb18ada6 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt @@ -0,0 +1,327 @@ +/* + * 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.tensors.api + +import space.kscience.kmath.operations.Algebra + +/** + * Algebra over a ring on [Tensor]. + * For more information: https://proofwiki.org/wiki/Definition:Algebra_over_Ring + * + * @param T the type of items in the tensors. + */ +public interface TensorAlgebra: Algebra> { + + /** + * Returns a single tensor value of unit dimension if tensor shape equals to [1]. + * + * @return a nullable value of a potentially scalar tensor. + */ + public fun Tensor.valueOrNull(): T? + + /** + * Returns a single tensor value of unit dimension. The tensor shape must be equal to [1]. + * + * @return the value of a scalar tensor. + */ + public fun Tensor.value(): T + + /** + * Each element of the tensor [other] is added to this value. + * The resulting tensor is returned. + * + * @param other tensor to be added. + * @return the sum of this value and tensor [other]. + */ + public operator fun T.plus(other: Tensor): Tensor + + /** + * Adds the scalar [value] to each element of this tensor and returns a new resulting tensor. + * + * @param value the number to be added to each element of this tensor. + * @return the sum of this tensor and [value]. + */ + public operator fun Tensor.plus(value: T): Tensor + + /** + * Each element of the tensor [other] is added to each element of this tensor. + * The resulting tensor is returned. + * + * @param other tensor to be added. + * @return the sum of this tensor and [other]. + */ + public operator fun Tensor.plus(other: Tensor): Tensor + + /** + * Adds the scalar [value] to each element of this tensor. + * + * @param value the number to be added to each element of this tensor. + */ + public operator fun Tensor.plusAssign(value: T): Unit + + /** + * Each element of the tensor [other] is added to each element of this tensor. + * + * @param other tensor to be added. + */ + public operator fun Tensor.plusAssign(other: Tensor): Unit + + + /** + * Each element of the tensor [other] is subtracted from this value. + * The resulting tensor is returned. + * + * @param other tensor to be subtracted. + * @return the difference between this value and tensor [other]. + */ + public operator fun T.minus(other: Tensor): Tensor + + /** + * Subtracts the scalar [value] from each element of this tensor and returns a new resulting tensor. + * + * @param value the number to be subtracted from each element of this tensor. + * @return the difference between this tensor and [value]. + */ + public operator fun Tensor.minus(value: T): Tensor + + /** + * Each element of the tensor [other] is subtracted from each element of this tensor. + * The resulting tensor is returned. + * + * @param other tensor to be subtracted. + * @return the difference between this tensor and [other]. + */ + public operator fun Tensor.minus(other: Tensor): Tensor + + /** + * Subtracts the scalar [value] from each element of this tensor. + * + * @param value the number to be subtracted from each element of this tensor. + */ + public operator fun Tensor.minusAssign(value: T): Unit + + /** + * Each element of the tensor [other] is subtracted from each element of this tensor. + * + * @param other tensor to be subtracted. + */ + public operator fun Tensor.minusAssign(other: Tensor): Unit + + + /** + * Each element of the tensor [other] is multiplied by this value. + * The resulting tensor is returned. + * + * @param other tensor to be multiplied. + * @return the product of this value and tensor [other]. + */ + public operator fun T.times(other: Tensor): Tensor + + /** + * Multiplies the scalar [value] by each element of this tensor and returns a new resulting tensor. + * + * @param value the number to be multiplied by each element of this tensor. + * @return the product of this tensor and [value]. + */ + public operator fun Tensor.times(value: T): Tensor + + /** + * Each element of the tensor [other] is multiplied by each element of this tensor. + * The resulting tensor is returned. + * + * @param other tensor to be multiplied. + * @return the product of this tensor and [other]. + */ + public operator fun Tensor.times(other: Tensor): Tensor + + /** + * Multiplies the scalar [value] by each element of this tensor. + * + * @param value the number to be multiplied by each element of this tensor. + */ + public operator fun Tensor.timesAssign(value: T): Unit + + /** + * Each element of the tensor [other] is multiplied by each element of this tensor. + * + * @param other tensor to be multiplied. + */ + public operator fun Tensor.timesAssign(other: Tensor): Unit + + /** + * Numerical negative, element-wise. + * + * @return tensor negation of the original tensor. + */ + public operator fun Tensor.unaryMinus(): Tensor + + /** + * Returns the tensor at index i + * For more information: https://pytorch.org/cppdocs/notes/tensor_indexing.html + * + * @param i index of the extractable tensor + * @return subtensor of the original tensor with index [i] + */ + public operator fun Tensor.get(i: Int): Tensor + + /** + * Returns a tensor that is a transposed version of this tensor. The given dimensions [i] and [j] are swapped. + * For more information: https://pytorch.org/docs/stable/generated/torch.transpose.html + * + * @param i the first dimension to be transposed + * @param j the second dimension to be transposed + * @return transposed tensor + */ + public fun Tensor.transpose(i: Int = -2, j: Int = -1): Tensor + + /** + * Returns a new tensor with the same data as the self tensor but of a different shape. + * The returned tensor shares the same data and must have the same number of elements, but may have a different size + * For more information: https://pytorch.org/docs/stable/tensor_view.html + * + * @param shape the desired size + * @return tensor with new shape + */ + public fun Tensor.view(shape: IntArray): Tensor + + /** + * View this tensor as the same size as [other]. + * ``this.viewAs(other) is equivalent to this.view(other.shape)``. + * For more information: https://pytorch.org/cppdocs/notes/tensor_indexing.html + * + * @param other the result tensor has the same size as other. + * @return the result tensor with the same size as other. + */ + public fun Tensor.viewAs(other: Tensor): Tensor + + /** + * Matrix product of two tensors. + * + * The behavior depends on the dimensionality of the tensors as follows: + * 1. If both tensors are 1-dimensional, the dot product (scalar) is returned. + * + * 2. If both arguments are 2-dimensional, the matrix-matrix product is returned. + * + * 3. If the first argument is 1-dimensional and the second argument is 2-dimensional, + * a 1 is prepended to its dimension for the purpose of the matrix multiply. + * After the matrix multiply, the prepended dimension is removed. + * + * 4. If the first argument is 2-dimensional and the second argument is 1-dimensional, + * the matrix-vector product is returned. + * + * 5. If both arguments are at least 1-dimensional and at least one argument is N-dimensional (where N > 2), + * then a batched matrix multiply is returned. If the first argument is 1-dimensional, + * a 1 is prepended to its dimension for the purpose of the batched matrix multiply and removed after. + * If the second argument is 1-dimensional, a 1 is appended to its dimension for the purpose of the batched matrix + * multiple and removed after. + * The non-matrix (i.e. batch) dimensions are broadcasted (and thus must be broadcastable). + * For example, if `input` is a (j × 1 × n × n) tensor and `other` is a + * (k × n × n) tensor, out will be a (j × k × n × n) tensor. + * + * For more information: https://pytorch.org/docs/stable/generated/torch.matmul.html + * + * @param other tensor to be multiplied + * @return mathematical product of two tensors + */ + public infix fun Tensor.dot(other: Tensor): Tensor + + /** + * Creates a tensor whose diagonals of certain 2D planes (specified by [dim1] and [dim2]) + * are filled by [diagonalEntries]. + * To facilitate creating batched diagonal matrices, + * the 2D planes formed by the last two dimensions of the returned tensor are chosen by default. + * + * The argument [offset] controls which diagonal to consider: + * 1. If [offset] = 0, it is the main diagonal. + * 1. If [offset] > 0, it is above the main diagonal. + * 1. If [offset] < 0, it is below the main diagonal. + * + * The size of the new matrix will be calculated + * to make the specified diagonal of the size of the last input dimension. + * For more information: https://pytorch.org/docs/stable/generated/torch.diag_embed.html + * + * @param diagonalEntries the input tensor. Must be at least 1-dimensional. + * @param offset which diagonal to consider. Default: 0 (main diagonal). + * @param dim1 first dimension with respect to which to take diagonal. Default: -2. + * @param dim2 second dimension with respect to which to take diagonal. Default: -1. + * + * @return tensor whose diagonals of certain 2D planes (specified by [dim1] and [dim2]) + * are filled by [diagonalEntries] + */ + public fun diagonalEmbedding( + diagonalEntries: Tensor, + offset: Int = 0, + dim1: Int = -2, + dim2: Int = -1 + ): Tensor + + /** + * @return the sum of all elements in the input tensor. + */ + public fun Tensor.sum(): T + + /** + * Returns the sum of each row of the input tensor in the given dimension [dim]. + * + * If [keepDim] is true, the output tensor is of the same size as + * input except in the dimension [dim] where it is of size 1. + * Otherwise, [dim] is squeezed, resulting in the output tensor having 1 fewer dimension. + * + * @param dim the dimension to reduce. + * @param keepDim whether the output tensor has [dim] retained or not. + * @return the sum of each row of the input tensor in the given dimension [dim]. + */ + public fun Tensor.sum(dim: Int, keepDim: Boolean): Tensor + + /** + * @return the minimum value of all elements in the input tensor. + */ + public fun Tensor.min(): T + + /** + * Returns the minimum value of each row of the input tensor in the given dimension [dim]. + * + * If [keepDim] is true, the output tensor is of the same size as + * input except in the dimension [dim] where it is of size 1. + * Otherwise, [dim] is squeezed, resulting in the output tensor having 1 fewer dimension. + * + * @param dim the dimension to reduce. + * @param keepDim whether the output tensor has [dim] retained or not. + * @return the minimum value of each row of the input tensor in the given dimension [dim]. + */ + public fun Tensor.min(dim: Int, keepDim: Boolean): Tensor + + /** + * Returns the maximum value of all elements in the input tensor. + */ + public fun Tensor.max(): T + + /** + * Returns the maximum value of each row of the input tensor in the given dimension [dim]. + * + * If [keepDim] is true, the output tensor is of the same size as + * input except in the dimension [dim] where it is of size 1. + * Otherwise, [dim] is squeezed, resulting in the output tensor having 1 fewer dimension. + * + * @param dim the dimension to reduce. + * @param keepDim whether the output tensor has [dim] retained or not. + * @return the maximum value of each row of the input tensor in the given dimension [dim]. + */ + public fun Tensor.max(dim: Int, keepDim: Boolean): Tensor + + /** + * Returns the index of maximum value of each row of the input tensor in the given dimension [dim]. + * + * If [keepDim] is true, the output tensor is of the same size as + * input except in the dimension [dim] where it is of size 1. + * Otherwise, [dim] is squeezed, resulting in the output tensor having 1 fewer dimension. + * + * @param dim the dimension to reduce. + * @param keepDim whether the output tensor has [dim] retained or not. + * @return the the index of maximum value of each row of the input tensor in the given dimension [dim]. + */ + public fun Tensor.argMax(dim: Int, keepDim: Boolean): Tensor +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorPartialDivisionAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorPartialDivisionAlgebra.kt new file mode 100644 index 000000000..02bf5415d --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorPartialDivisionAlgebra.kt @@ -0,0 +1,55 @@ +/* + * 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.tensors.api + +/** + * Algebra over a field with partial division on [Tensor]. + * For more information: https://proofwiki.org/wiki/Definition:Division_Algebra + * + * @param T the type of items closed under division in the tensors. + */ +public interface TensorPartialDivisionAlgebra : TensorAlgebra { + + /** + * Each element of the tensor [other] is divided by this value. + * The resulting tensor is returned. + * + * @param other tensor to divide by. + * @return the division of this value by the tensor [other]. + */ + public operator fun T.div(other: Tensor): Tensor + + /** + * Divide by the scalar [value] each element of this tensor returns a new resulting tensor. + * + * @param value the number to divide by each element of this tensor. + * @return the division of this tensor by the [value]. + */ + public operator fun Tensor.div(value: T): Tensor + + /** + * Each element of the tensor [other] is divided by each element of this tensor. + * The resulting tensor is returned. + * + * @param other tensor to be divided by. + * @return the division of this tensor by [other]. + */ + public operator fun Tensor.div(other: Tensor): Tensor + + /** + * Divides by the scalar [value] each element of this tensor. + * + * @param value the number to divide by each element of this tensor. + */ + public operator fun Tensor.divAssign(value: T) + + /** + * Each element of this tensor is divided by each element of the [other] tensor. + * + * @param other tensor to be divide by. + */ + public operator fun Tensor.divAssign(other: Tensor) +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt new file mode 100644 index 000000000..b8530f637 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt @@ -0,0 +1,93 @@ +/* + * 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.tensors.core + +import space.kscience.kmath.tensors.api.Tensor +import space.kscience.kmath.tensors.core.internal.array +import space.kscience.kmath.tensors.core.internal.broadcastTensors +import space.kscience.kmath.tensors.core.internal.broadcastTo +import space.kscience.kmath.tensors.core.internal.tensor + +/** + * Basic linear algebra operations implemented with broadcasting. + * For more information: https://pytorch.org/docs/stable/notes/broadcasting.html + */ +public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { + + override fun Tensor.plus(other: Tensor): DoubleTensor { + val broadcast = broadcastTensors(tensor, other.tensor) + val newThis = broadcast[0] + val newOther = broadcast[1] + val resBuffer = DoubleArray(newThis.linearStructure.linearSize) { i -> + newThis.mutableBuffer.array()[i] + newOther.mutableBuffer.array()[i] + } + return DoubleTensor(newThis.shape, resBuffer) + } + + override fun Tensor.plusAssign(other: Tensor) { + val newOther = broadcastTo(other.tensor, tensor.shape) + for (i in 0 until tensor.linearStructure.linearSize) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] += + newOther.mutableBuffer.array()[tensor.bufferStart + i] + } + } + + override fun Tensor.minus(other: Tensor): DoubleTensor { + val broadcast = broadcastTensors(tensor, other.tensor) + val newThis = broadcast[0] + val newOther = broadcast[1] + val resBuffer = DoubleArray(newThis.linearStructure.linearSize) { i -> + newThis.mutableBuffer.array()[i] - newOther.mutableBuffer.array()[i] + } + return DoubleTensor(newThis.shape, resBuffer) + } + + override fun Tensor.minusAssign(other: Tensor) { + val newOther = broadcastTo(other.tensor, tensor.shape) + for (i in 0 until tensor.linearStructure.linearSize) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] -= + newOther.mutableBuffer.array()[tensor.bufferStart + i] + } + } + + override fun Tensor.times(other: Tensor): DoubleTensor { + val broadcast = broadcastTensors(tensor, other.tensor) + val newThis = broadcast[0] + val newOther = broadcast[1] + val resBuffer = DoubleArray(newThis.linearStructure.linearSize) { i -> + newThis.mutableBuffer.array()[newThis.bufferStart + i] * + newOther.mutableBuffer.array()[newOther.bufferStart + i] + } + return DoubleTensor(newThis.shape, resBuffer) + } + + override fun Tensor.timesAssign(other: Tensor) { + val newOther = broadcastTo(other.tensor, tensor.shape) + for (i in 0 until tensor.linearStructure.linearSize) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] *= + newOther.mutableBuffer.array()[tensor.bufferStart + i] + } + } + + override fun Tensor.div(other: Tensor): DoubleTensor { + val broadcast = broadcastTensors(tensor, other.tensor) + val newThis = broadcast[0] + val newOther = broadcast[1] + val resBuffer = DoubleArray(newThis.linearStructure.linearSize) { i -> + newThis.mutableBuffer.array()[newOther.bufferStart + i] / + newOther.mutableBuffer.array()[newOther.bufferStart + i] + } + return DoubleTensor(newThis.shape, resBuffer) + } + + override fun Tensor.divAssign(other: Tensor) { + val newOther = broadcastTo(other.tensor, tensor.shape) + for (i in 0 until tensor.linearStructure.linearSize) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] /= + newOther.mutableBuffer.array()[tensor.bufferStart + i] + } + } +} \ No newline at end of file diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt new file mode 100644 index 000000000..858532abc --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt @@ -0,0 +1,38 @@ +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.nd.Strides +import space.kscience.kmath.structures.* +import space.kscience.kmath.tensors.api.Tensor +import space.kscience.kmath.tensors.core.internal.TensorLinearStructure + +/** + * Represents [Tensor] over a [MutableBuffer] intended to be used through [DoubleTensor] and [IntTensor] + */ +public open class BufferedTensor internal constructor( + override val shape: IntArray, + internal val mutableBuffer: MutableBuffer, + internal val bufferStart: Int +) : Tensor { + + /** + * Buffer strides based on [TensorLinearStructure] implementation + */ + public val linearStructure: Strides + get() = TensorLinearStructure(shape) + + /** + * Number of elements in tensor + */ + public val numElements: Int + get() = linearStructure.linearSize + + override fun get(index: IntArray): T = mutableBuffer[bufferStart + linearStructure.offset(index)] + + override fun set(index: IntArray, value: T) { + mutableBuffer[bufferStart + linearStructure.offset(index)] = value + } + + override fun elements(): Sequence> = linearStructure.indices().map { + it to this[it] + } +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt new file mode 100644 index 000000000..41df50cba --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.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.tensors.core + +import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.tensors.core.internal.toPrettyString + +/** + * Default [BufferedTensor] implementation for [Double] values + */ +public class DoubleTensor internal constructor( + shape: IntArray, + buffer: DoubleArray, + offset: Int = 0 +) : BufferedTensor(shape, DoubleBuffer(buffer), offset) { + override fun toString(): String = toPrettyString() +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt new file mode 100644 index 000000000..c1694644f --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -0,0 +1,937 @@ +/* + * 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.tensors.core + +import space.kscience.kmath.nd.as1D +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra +import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra +import space.kscience.kmath.tensors.api.TensorPartialDivisionAlgebra +import space.kscience.kmath.tensors.api.Tensor +import space.kscience.kmath.tensors.core.internal.dotHelper +import space.kscience.kmath.tensors.core.internal.getRandomNormals +import space.kscience.kmath.tensors.core.internal.* +import space.kscience.kmath.tensors.core.internal.broadcastOuterTensors +import space.kscience.kmath.tensors.core.internal.checkBufferShapeConsistency +import space.kscience.kmath.tensors.core.internal.checkEmptyDoubleBuffer +import space.kscience.kmath.tensors.core.internal.checkEmptyShape +import space.kscience.kmath.tensors.core.internal.checkShapesCompatible +import space.kscience.kmath.tensors.core.internal.checkSquareMatrix +import space.kscience.kmath.tensors.core.internal.checkTranspose +import space.kscience.kmath.tensors.core.internal.checkView +import space.kscience.kmath.tensors.core.internal.minusIndexFrom +import kotlin.math.* + +/** + * Implementation of basic operations over double tensors and basic algebra operations on them. + */ +public open class DoubleTensorAlgebra : + TensorPartialDivisionAlgebra, + AnalyticTensorAlgebra, + LinearOpsTensorAlgebra { + + public companion object : DoubleTensorAlgebra() + + override fun Tensor.valueOrNull(): Double? = if (tensor.shape contentEquals intArrayOf(1)) + tensor.mutableBuffer.array()[tensor.bufferStart] else null + + override fun Tensor.value(): Double = + valueOrNull() ?: throw IllegalArgumentException("Inconsistent value for tensor of with $shape shape") + + /** + * Constructs a tensor with the specified shape and data. + * + * @param shape the desired shape for the tensor. + * @param buffer one-dimensional data array. + * @return tensor with the [shape] shape and [buffer] data. + */ + public fun fromArray(shape: IntArray, buffer: DoubleArray): DoubleTensor { + checkEmptyShape(shape) + checkEmptyDoubleBuffer(buffer) + checkBufferShapeConsistency(shape, buffer) + return DoubleTensor(shape, buffer, 0) + } + + /** + * Constructs a tensor with the specified shape and initializer. + * + * @param shape the desired shape for the tensor. + * @param initializer mapping tensor indices to values. + * @return tensor with the [shape] shape and data generated by the [initializer]. + */ + public fun produce(shape: IntArray, initializer: (IntArray) -> Double): DoubleTensor = + fromArray( + shape, + TensorLinearStructure(shape).indices().map(initializer).toMutableList().toDoubleArray() + ) + + override operator fun Tensor.get(i: Int): DoubleTensor { + val lastShape = tensor.shape.drop(1).toIntArray() + val newShape = if (lastShape.isNotEmpty()) lastShape else intArrayOf(1) + val newStart = newShape.reduce(Int::times) * i + tensor.bufferStart + return DoubleTensor(newShape, tensor.mutableBuffer.array(), newStart) + } + + /** + * Creates a tensor of a given shape and fills all elements with a given value. + * + * @param value the value to fill the output tensor with. + * @param shape array of integers defining the shape of the output tensor. + * @return tensor with the [shape] shape and filled with [value]. + */ + public fun full(value: Double, shape: IntArray): DoubleTensor { + checkEmptyShape(shape) + val buffer = DoubleArray(shape.reduce(Int::times)) { value } + return DoubleTensor(shape, buffer) + } + + /** + * Returns a tensor with the same shape as `input` filled with [value]. + * + * @param value the value to fill the output tensor with. + * @return tensor with the `input` tensor shape and filled with [value]. + */ + public fun Tensor.fullLike(value: Double): DoubleTensor { + val shape = tensor.shape + val buffer = DoubleArray(tensor.numElements) { value } + return DoubleTensor(shape, buffer) + } + + /** + * Returns a tensor filled with the scalar value 0.0, with the shape defined by the variable argument [shape]. + * + * @param shape array of integers defining the shape of the output tensor. + * @return tensor filled with the scalar value 0.0, with the [shape] shape. + */ + public fun zeros(shape: IntArray): DoubleTensor = full(0.0, shape) + + /** + * Returns a tensor filled with the scalar value 0.0, with the same shape as a given array. + * + * @return tensor filled with the scalar value 0.0, with the same shape as `input` tensor. + */ + public fun Tensor.zeroesLike(): DoubleTensor = tensor.fullLike(0.0) + + /** + * Returns a tensor filled with the scalar value 1.0, with the shape defined by the variable argument [shape]. + * + * @param shape array of integers defining the shape of the output tensor. + * @return tensor filled with the scalar value 1.0, with the [shape] shape. + */ + public fun ones(shape: IntArray): DoubleTensor = full(1.0, shape) + + /** + * Returns a tensor filled with the scalar value 1.0, with the same shape as a given array. + * + * @return tensor filled with the scalar value 1.0, with the same shape as `input` tensor. + */ + public fun Tensor.onesLike(): DoubleTensor = tensor.fullLike(1.0) + + /** + * Returns a 2-D tensor with shape ([n], [n]), with ones on the diagonal and zeros elsewhere. + * + * @param n the number of rows and columns + * @return a 2-D tensor with ones on the diagonal and zeros elsewhere. + */ + public fun eye(n: Int): DoubleTensor { + val shape = intArrayOf(n, n) + val buffer = DoubleArray(n * n) { 0.0 } + val res = DoubleTensor(shape, buffer) + for (i in 0 until n) { + res[intArrayOf(i, i)] = 1.0 + } + return res + } + + /** + * Return a copy of the tensor. + * + * @return a copy of the `input` tensor with a copied buffer. + */ + public fun Tensor.copy(): DoubleTensor { + return DoubleTensor(tensor.shape, tensor.mutableBuffer.array().copyOf(), tensor.bufferStart) + } + + override fun Double.plus(other: Tensor): DoubleTensor { + val resBuffer = DoubleArray(other.tensor.numElements) { i -> + other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] + this + } + return DoubleTensor(other.shape, resBuffer) + } + + override fun Tensor.plus(value: Double): DoubleTensor = value + tensor + + override fun Tensor.plus(other: Tensor): DoubleTensor { + checkShapesCompatible(tensor, other.tensor) + val resBuffer = DoubleArray(tensor.numElements) { i -> + tensor.mutableBuffer.array()[i] + other.tensor.mutableBuffer.array()[i] + } + return DoubleTensor(tensor.shape, resBuffer) + } + + override fun Tensor.plusAssign(value: Double) { + for (i in 0 until tensor.numElements) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] += value + } + } + + override fun Tensor.plusAssign(other: Tensor) { + checkShapesCompatible(tensor, other.tensor) + for (i in 0 until tensor.numElements) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] += + other.tensor.mutableBuffer.array()[tensor.bufferStart + i] + } + } + + override fun Double.minus(other: Tensor): DoubleTensor { + val resBuffer = DoubleArray(other.tensor.numElements) { i -> + this - other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] + } + return DoubleTensor(other.shape, resBuffer) + } + + override fun Tensor.minus(value: Double): DoubleTensor { + val resBuffer = DoubleArray(tensor.numElements) { i -> + tensor.mutableBuffer.array()[tensor.bufferStart + i] - value + } + return DoubleTensor(tensor.shape, resBuffer) + } + + override fun Tensor.minus(other: Tensor): DoubleTensor { + checkShapesCompatible(tensor, other) + val resBuffer = DoubleArray(tensor.numElements) { i -> + tensor.mutableBuffer.array()[i] - other.tensor.mutableBuffer.array()[i] + } + return DoubleTensor(tensor.shape, resBuffer) + } + + override fun Tensor.minusAssign(value: Double) { + for (i in 0 until tensor.numElements) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] -= value + } + } + + override fun Tensor.minusAssign(other: Tensor) { + checkShapesCompatible(tensor, other) + for (i in 0 until tensor.numElements) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] -= + other.tensor.mutableBuffer.array()[tensor.bufferStart + i] + } + } + + override fun Double.times(other: Tensor): DoubleTensor { + val resBuffer = DoubleArray(other.tensor.numElements) { i -> + other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] * this + } + return DoubleTensor(other.shape, resBuffer) + } + + override fun Tensor.times(value: Double): DoubleTensor = value * tensor + + override fun Tensor.times(other: Tensor): DoubleTensor { + checkShapesCompatible(tensor, other) + val resBuffer = DoubleArray(tensor.numElements) { i -> + tensor.mutableBuffer.array()[tensor.bufferStart + i] * + other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] + } + return DoubleTensor(tensor.shape, resBuffer) + } + + override fun Tensor.timesAssign(value: Double) { + for (i in 0 until tensor.numElements) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] *= value + } + } + + override fun Tensor.timesAssign(other: Tensor) { + checkShapesCompatible(tensor, other) + for (i in 0 until tensor.numElements) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] *= + other.tensor.mutableBuffer.array()[tensor.bufferStart + i] + } + } + + override fun Double.div(other: Tensor): DoubleTensor { + val resBuffer = DoubleArray(other.tensor.numElements) { i -> + this / other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] + } + return DoubleTensor(other.shape, resBuffer) + } + + override fun Tensor.div(value: Double): DoubleTensor { + val resBuffer = DoubleArray(tensor.numElements) { i -> + tensor.mutableBuffer.array()[tensor.bufferStart + i] / value + } + return DoubleTensor(shape, resBuffer) + } + + override fun Tensor.div(other: Tensor): DoubleTensor { + checkShapesCompatible(tensor, other) + val resBuffer = DoubleArray(tensor.numElements) { i -> + tensor.mutableBuffer.array()[other.tensor.bufferStart + i] / + other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i] + } + return DoubleTensor(tensor.shape, resBuffer) + } + + override fun Tensor.divAssign(value: Double) { + for (i in 0 until tensor.numElements) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] /= value + } + } + + override fun Tensor.divAssign(other: Tensor) { + checkShapesCompatible(tensor, other) + for (i in 0 until tensor.numElements) { + tensor.mutableBuffer.array()[tensor.bufferStart + i] /= + other.tensor.mutableBuffer.array()[tensor.bufferStart + i] + } + } + + override fun Tensor.unaryMinus(): DoubleTensor { + val resBuffer = DoubleArray(tensor.numElements) { i -> + tensor.mutableBuffer.array()[tensor.bufferStart + i].unaryMinus() + } + return DoubleTensor(tensor.shape, resBuffer) + } + + override fun Tensor.transpose(i: Int, j: Int): DoubleTensor { + val ii = tensor.minusIndex(i) + val jj = tensor.minusIndex(j) + checkTranspose(tensor.dimension, ii, jj) + val n = tensor.numElements + val resBuffer = DoubleArray(n) + + val resShape = tensor.shape.copyOf() + resShape[ii] = resShape[jj].also { resShape[jj] = resShape[ii] } + + val resTensor = DoubleTensor(resShape, resBuffer) + + for (offset in 0 until n) { + val oldMultiIndex = tensor.linearStructure.index(offset) + val newMultiIndex = oldMultiIndex.copyOf() + newMultiIndex[ii] = newMultiIndex[jj].also { newMultiIndex[jj] = newMultiIndex[ii] } + + val linearIndex = resTensor.linearStructure.offset(newMultiIndex) + resTensor.mutableBuffer.array()[linearIndex] = + tensor.mutableBuffer.array()[tensor.bufferStart + offset] + } + return resTensor + } + + + override fun Tensor.view(shape: IntArray): DoubleTensor { + checkView(tensor, shape) + return DoubleTensor(shape, tensor.mutableBuffer.array(), tensor.bufferStart) + } + + override fun Tensor.viewAs(other: Tensor): DoubleTensor = + tensor.view(other.shape) + + override infix fun Tensor.dot(other: Tensor): DoubleTensor { + if (tensor.shape.size == 1 && other.shape.size == 1) { + return DoubleTensor(intArrayOf(1), doubleArrayOf(tensor.times(other).tensor.mutableBuffer.array().sum())) + } + + var newThis = tensor.copy() + var newOther = other.copy() + + var penultimateDim = false + var lastDim = false + if (tensor.shape.size == 1) { + penultimateDim = true + newThis = tensor.view(intArrayOf(1) + tensor.shape) + } + if (other.shape.size == 1) { + lastDim = true + newOther = other.tensor.view(other.shape + intArrayOf(1)) + } + + val broadcastTensors = broadcastOuterTensors(newThis.tensor, newOther.tensor) + newThis = broadcastTensors[0] + newOther = broadcastTensors[1] + + val l = newThis.shape[newThis.shape.size - 2] + val m1 = newThis.shape[newThis.shape.size - 1] + val m2 = newOther.shape[newOther.shape.size - 2] + val n = newOther.shape[newOther.shape.size - 1] + check(m1 == m2) { + "Tensors dot operation dimension mismatch: ($l, $m1) x ($m2, $n)" + } + + val resShape = newThis.shape.sliceArray(0..(newThis.shape.size - 2)) + intArrayOf(newOther.shape.last()) + val resSize = resShape.reduce { acc, i -> acc * i } + val resTensor = DoubleTensor(resShape, DoubleArray(resSize)) + + for ((res, ab) in resTensor.matrixSequence().zip(newThis.matrixSequence().zip(newOther.matrixSequence()))) { + val (a, b) = ab + dotHelper(a.as2D(), b.as2D(), res.as2D(), l, m1, n) + } + + if (penultimateDim) { + return resTensor.view( + resTensor.shape.dropLast(2).toIntArray() + + intArrayOf(resTensor.shape.last()) + ) + } + if (lastDim) { + return resTensor.view(resTensor.shape.dropLast(1).toIntArray()) + } + return resTensor + } + + override fun diagonalEmbedding(diagonalEntries: Tensor, offset: Int, dim1: Int, dim2: Int): + DoubleTensor { + val n = diagonalEntries.shape.size + val d1 = minusIndexFrom(n + 1, dim1) + val d2 = minusIndexFrom(n + 1, dim2) + + check(d1 != d2) { + "Diagonal dimensions cannot be identical $d1, $d2" + } + check(d1 <= n && d2 <= n) { + "Dimension out of range" + } + + var lessDim = d1 + var greaterDim = d2 + var realOffset = offset + if (lessDim > greaterDim) { + realOffset *= -1 + lessDim = greaterDim.also { greaterDim = lessDim } + } + + val resShape = diagonalEntries.shape.slice(0 until lessDim).toIntArray() + + intArrayOf(diagonalEntries.shape[n - 1] + abs(realOffset)) + + diagonalEntries.shape.slice(lessDim until greaterDim - 1).toIntArray() + + intArrayOf(diagonalEntries.shape[n - 1] + abs(realOffset)) + + diagonalEntries.shape.slice(greaterDim - 1 until n - 1).toIntArray() + val resTensor = zeros(resShape) + + for (i in 0 until diagonalEntries.tensor.numElements) { + val multiIndex = diagonalEntries.tensor.linearStructure.index(i) + + var offset1 = 0 + var offset2 = abs(realOffset) + if (realOffset < 0) { + offset1 = offset2.also { offset2 = offset1 } + } + val diagonalMultiIndex = multiIndex.slice(0 until lessDim).toIntArray() + + intArrayOf(multiIndex[n - 1] + offset1) + + multiIndex.slice(lessDim until greaterDim - 1).toIntArray() + + intArrayOf(multiIndex[n - 1] + offset2) + + multiIndex.slice(greaterDim - 1 until n - 1).toIntArray() + + resTensor[diagonalMultiIndex] = diagonalEntries[multiIndex] + } + + return resTensor.tensor + } + + /** + * Applies the [transform] function to each element of the tensor and returns the resulting modified tensor. + * + * @param transform the function to be applied to each element of the tensor. + * @return the resulting tensor after applying the function. + */ + public fun Tensor.map(transform: (Double) -> Double): DoubleTensor { + return DoubleTensor( + tensor.shape, + tensor.mutableBuffer.array().map { transform(it) }.toDoubleArray(), + tensor.bufferStart + ) + } + + /** + * Compares element-wise two tensors with a specified precision. + * + * @param other the tensor to compare with `input` tensor. + * @param epsilon permissible error when comparing two Double values. + * @return true if two tensors have the same shape and elements, false otherwise. + */ + public fun Tensor.eq(other: Tensor, epsilon: Double): Boolean = + tensor.eq(other) { x, y -> abs(x - y) < epsilon } + + /** + * Compares element-wise two tensors. + * Comparison of two Double values occurs with 1e-5 precision. + * + * @param other the tensor to compare with `input` tensor. + * @return true if two tensors have the same shape and elements, false otherwise. + */ + public infix fun Tensor.eq(other: Tensor): Boolean = tensor.eq(other, 1e-5) + + private fun Tensor.eq( + other: Tensor, + eqFunction: (Double, Double) -> Boolean + ): Boolean { + checkShapesCompatible(tensor, other) + val n = tensor.numElements + if (n != other.tensor.numElements) { + return false + } + for (i in 0 until n) { + if (!eqFunction( + tensor.mutableBuffer[tensor.bufferStart + i], + other.tensor.mutableBuffer[other.tensor.bufferStart + i] + ) + ) { + return false + } + } + return true + } + + /** + * Returns a tensor of random numbers drawn from normal distributions with 0.0 mean and 1.0 standard deviation. + * + * @param shape the desired shape for the output tensor. + * @param seed the random seed of the pseudo-random number generator. + * @return tensor of a given shape filled with numbers from the normal distribution + * with 0.0 mean and 1.0 standard deviation. + */ + public fun randomNormal(shape: IntArray, seed: Long = 0): DoubleTensor = + DoubleTensor(shape, getRandomNormals(shape.reduce(Int::times), seed)) + + /** + * Returns a tensor with the same shape as `input` of random numbers drawn from normal distributions + * with 0.0 mean and 1.0 standard deviation. + * + * @param seed the random seed of the pseudo-random number generator. + * @return tensor with the same shape as `input` filled with numbers from the normal distribution + * with 0.0 mean and 1.0 standard deviation. + */ + public fun Tensor.randomNormalLike(seed: Long = 0): DoubleTensor = + DoubleTensor(tensor.shape, getRandomNormals(tensor.shape.reduce(Int::times), seed)) + + /** + * Concatenates a sequence of tensors with equal shapes along the first dimension. + * + * @param tensors the [List] of tensors with same shapes to concatenate + * @return tensor with concatenation result + */ + public fun stack(tensors: List>): DoubleTensor { + check(tensors.isNotEmpty()) { "List must have at least 1 element" } + val shape = tensors[0].shape + check(tensors.all { it.shape contentEquals shape }) { "Tensors must have same shapes" } + val resShape = intArrayOf(tensors.size) + shape + val resBuffer = tensors.flatMap { + it.tensor.mutableBuffer.array().drop(it.tensor.bufferStart).take(it.tensor.numElements) + }.toDoubleArray() + return DoubleTensor(resShape, resBuffer, 0) + } + + /** + * Builds tensor from rows of input tensor + * + * @param indices the [IntArray] of 1-dimensional indices + * @return tensor with rows corresponding to rows by [indices] + */ + public fun Tensor.rowsByIndices(indices: IntArray): DoubleTensor { + return stack(indices.map { this[it] }) + } + + internal fun Tensor.fold(foldFunction: (DoubleArray) -> Double): Double = + foldFunction(tensor.toDoubleArray()) + + internal fun Tensor.foldDim( + foldFunction: (DoubleArray) -> Double, + dim: Int, + keepDim: Boolean + ): DoubleTensor { + check(dim < dimension) { "Dimension $dim out of range $dimension" } + val resShape = if (keepDim) { + shape.take(dim).toIntArray() + intArrayOf(1) + shape.takeLast(dimension - dim - 1).toIntArray() + } else { + shape.take(dim).toIntArray() + shape.takeLast(dimension - dim - 1).toIntArray() + } + val resNumElements = resShape.reduce(Int::times) + val resTensor = DoubleTensor(resShape, DoubleArray(resNumElements) { 0.0 }, 0) + for (index in resTensor.linearStructure.indices()) { + val prefix = index.take(dim).toIntArray() + val suffix = index.takeLast(dimension - dim - 1).toIntArray() + resTensor[index] = foldFunction(DoubleArray(shape[dim]) { i -> + tensor[prefix + intArrayOf(i) + suffix] + }) + } + + return resTensor + } + + override fun Tensor.sum(): Double = tensor.fold { it.sum() } + + override fun Tensor.sum(dim: Int, keepDim: Boolean): DoubleTensor = + foldDim({ x -> x.sum() }, dim, keepDim) + + override fun Tensor.min(): Double = this.fold { it.minOrNull()!! } + + override fun Tensor.min(dim: Int, keepDim: Boolean): DoubleTensor = + foldDim({ x -> x.minOrNull()!! }, dim, keepDim) + + override fun Tensor.max(): Double = this.fold { it.maxOrNull()!! } + + override fun Tensor.max(dim: Int, keepDim: Boolean): DoubleTensor = + foldDim({ x -> x.maxOrNull()!! }, dim, keepDim) + + override fun Tensor.argMax(dim: Int, keepDim: Boolean): DoubleTensor = + foldDim({ x -> + x.withIndex().maxByOrNull { it.value }?.index!!.toDouble() + }, dim, keepDim) + + + override fun Tensor.mean(): Double = this.fold { it.sum() / tensor.numElements } + + override fun Tensor.mean(dim: Int, keepDim: Boolean): DoubleTensor = + foldDim( + { arr -> + check(dim < dimension) { "Dimension $dim out of range $dimension" } + arr.sum() / shape[dim] + }, + dim, + keepDim + ) + + override fun Tensor.std(): Double = this.fold { arr -> + val mean = arr.sum() / tensor.numElements + sqrt(arr.sumOf { (it - mean) * (it - mean) } / (tensor.numElements - 1)) + } + + override fun Tensor.std(dim: Int, keepDim: Boolean): DoubleTensor = foldDim( + { arr -> + check(dim < dimension) { "Dimension $dim out of range $dimension" } + val mean = arr.sum() / shape[dim] + sqrt(arr.sumOf { (it - mean) * (it - mean) } / (shape[dim] - 1)) + }, + dim, + keepDim + ) + + override fun Tensor.variance(): Double = this.fold { arr -> + val mean = arr.sum() / tensor.numElements + arr.sumOf { (it - mean) * (it - mean) } / (tensor.numElements - 1) + } + + override fun Tensor.variance(dim: Int, keepDim: Boolean): DoubleTensor = foldDim( + { arr -> + check(dim < dimension) { "Dimension $dim out of range $dimension" } + val mean = arr.sum() / shape[dim] + arr.sumOf { (it - mean) * (it - mean) } / (shape[dim] - 1) + }, + dim, + keepDim + ) + + private fun cov(x: DoubleTensor, y: DoubleTensor): Double { + val n = x.shape[0] + return ((x - x.mean()) * (y - y.mean())).mean() * n / (n - 1) + } + + override fun cov(tensors: List>): DoubleTensor { + check(tensors.isNotEmpty()) { "List must have at least 1 element" } + val n = tensors.size + val m = tensors[0].shape[0] + check(tensors.all { it.shape contentEquals intArrayOf(m) }) { "Tensors must have same shapes" } + val resTensor = DoubleTensor( + intArrayOf(n, n), + DoubleArray(n * n) { 0.0 } + ) + for (i in 0 until n) { + for (j in 0 until n) { + resTensor[intArrayOf(i, j)] = cov(tensors[i].tensor, tensors[j].tensor) + } + } + return resTensor + } + + override fun Tensor.exp(): DoubleTensor = tensor.map(::exp) + + override fun Tensor.ln(): DoubleTensor = tensor.map(::ln) + + override fun Tensor.sqrt(): DoubleTensor = tensor.map(::sqrt) + + override fun Tensor.cos(): DoubleTensor = tensor.map(::cos) + + override fun Tensor.acos(): DoubleTensor = tensor.map(::acos) + + override fun Tensor.cosh(): DoubleTensor = tensor.map(::cosh) + + override fun Tensor.acosh(): DoubleTensor = tensor.map(::acosh) + + override fun Tensor.sin(): DoubleTensor = tensor.map(::sin) + + override fun Tensor.asin(): DoubleTensor = tensor.map(::asin) + + override fun Tensor.sinh(): DoubleTensor = tensor.map(::sinh) + + override fun Tensor.asinh(): DoubleTensor = tensor.map(::asinh) + + override fun Tensor.tan(): DoubleTensor = tensor.map(::tan) + + override fun Tensor.atan(): DoubleTensor = tensor.map(::atan) + + override fun Tensor.tanh(): DoubleTensor = tensor.map(::tanh) + + override fun Tensor.atanh(): DoubleTensor = tensor.map(::atanh) + + override fun Tensor.ceil(): DoubleTensor = tensor.map(::ceil) + + override fun Tensor.floor(): DoubleTensor = tensor.map(::floor) + + override fun Tensor.inv(): DoubleTensor = invLU(1e-9) + + override fun Tensor.det(): DoubleTensor = detLU(1e-9) + + /** + * Computes the LU factorization of a matrix or batches of matrices `input`. + * Returns a tuple containing the LU factorization and pivots of `input`. + * + * @param epsilon permissible error when comparing the determinant of a matrix with zero + * @return pair of `factorization` and `pivots`. + * The `factorization` has the shape ``(*, m, n)``, where``(*, m, n)`` is the shape of the `input` tensor. + * The `pivots` has the shape ``(∗, min(m, n))``. `pivots` stores all the intermediate transpositions of rows. + */ + public fun Tensor.luFactor(epsilon: Double): Pair = + computeLU(tensor, epsilon) + ?: throw IllegalArgumentException("Tensor contains matrices which are singular at precision $epsilon") + + /** + * Computes the LU factorization of a matrix or batches of matrices `input`. + * Returns a tuple containing the LU factorization and pivots of `input`. + * Uses an error of ``1e-9`` when calculating whether a matrix is degenerate. + * + * @return pair of `factorization` and `pivots`. + * The `factorization` has the shape ``(*, m, n)``, where``(*, m, n)`` is the shape of the `input` tensor. + * The `pivots` has the shape ``(∗, min(m, n))``. `pivots` stores all the intermediate transpositions of rows. + */ + public fun Tensor.luFactor(): Pair = luFactor(1e-9) + + /** + * Unpacks the data and pivots from a LU factorization of a tensor. + * Given a tensor [luTensor], return tensors (P, L, U) satisfying ``P * luTensor = L * U``, + * with `P` being a permutation matrix or batch of matrices, + * `L` being a lower triangular matrix or batch of matrices, + * `U` being an upper triangular matrix or batch of matrices. + * + * @param luTensor the packed LU factorization data + * @param pivotsTensor the packed LU factorization pivots + * @return triple of P, L and U tensors + */ + public fun luPivot( + luTensor: Tensor, + pivotsTensor: Tensor + ): Triple { + checkSquareMatrix(luTensor.shape) + check( + luTensor.shape.dropLast(2).toIntArray() contentEquals pivotsTensor.shape.dropLast(1).toIntArray() || + luTensor.shape.last() == pivotsTensor.shape.last() - 1 + ) { "Inappropriate shapes of input tensors" } + + val n = luTensor.shape.last() + val pTensor = luTensor.zeroesLike() + pTensor + .matrixSequence() + .zip(pivotsTensor.tensor.vectorSequence()) + .forEach { (p, pivot) -> pivInit(p.as2D(), pivot.as1D(), n) } + + val lTensor = luTensor.zeroesLike() + val uTensor = luTensor.zeroesLike() + + lTensor.matrixSequence() + .zip(uTensor.matrixSequence()) + .zip(luTensor.tensor.matrixSequence()) + .forEach { (pairLU, lu) -> + val (l, u) = pairLU + luPivotHelper(l.as2D(), u.as2D(), lu.as2D(), n) + } + + return Triple(pTensor, lTensor, uTensor) + } + + /** + * QR decomposition. + * + * Computes the QR decomposition of a matrix or a batch of matrices, and returns a pair `(Q, R)` of tensors. + * Given a tensor `input`, return tensors (Q, R) satisfying ``input = Q * R``, + * with `Q` being an orthogonal matrix or batch of orthogonal matrices + * and `R` being an upper triangular matrix or batch of upper triangular matrices. + * + * @param epsilon permissible error when comparing tensors for equality. + * Used when checking the positive definiteness of the input matrix or matrices. + * @return pair of Q and R tensors. + */ + public fun Tensor.cholesky(epsilon: Double): DoubleTensor { + checkSquareMatrix(shape) + checkPositiveDefinite(tensor, epsilon) + + val n = shape.last() + val lTensor = zeroesLike() + + for ((a, l) in tensor.matrixSequence().zip(lTensor.matrixSequence())) + for (i in 0 until n) choleskyHelper(a.as2D(), l.as2D(), n) + + return lTensor + } + + override fun Tensor.cholesky(): DoubleTensor = cholesky(1e-6) + + override fun Tensor.qr(): Pair { + checkSquareMatrix(shape) + val qTensor = zeroesLike() + val rTensor = zeroesLike() + tensor.matrixSequence() + .zip( + (qTensor.matrixSequence() + .zip(rTensor.matrixSequence())) + ).forEach { (matrix, qr) -> + val (q, r) = qr + qrHelper(matrix.asTensor(), q.asTensor(), r.as2D()) + } + + return qTensor to rTensor + } + + override fun Tensor.svd(): Triple = + svd(epsilon = 1e-10) + + /** + * Singular Value Decomposition. + * + * Computes the singular value decomposition of either a matrix or batch of matrices `input`. + * The singular value decomposition is represented as a triple `(U, S, V)`, + * such that ``input = U.dot(diagonalEmbedding(S).dot(V.T))``. + * If input is a batch of tensors, then U, S, and Vh are also batched with the same batch dimensions as input. + * + * @param epsilon permissible error when calculating the dot product of vectors, + * i.e. the precision with which the cosine approaches 1 in an iterative algorithm. + * @return triple `(U, S, V)`. + */ + public fun Tensor.svd(epsilon: Double): Triple { + val size = tensor.dimension + val commonShape = tensor.shape.sliceArray(0 until size - 2) + val (n, m) = tensor.shape.sliceArray(size - 2 until size) + val uTensor = zeros(commonShape + intArrayOf(min(n, m), n)) + val sTensor = zeros(commonShape + intArrayOf(min(n, m))) + val vTensor = zeros(commonShape + intArrayOf(min(n, m), m)) + + tensor.matrixSequence() + .zip( + uTensor.matrixSequence() + .zip( + sTensor.vectorSequence() + .zip(vTensor.matrixSequence()) + ) + ).forEach { (matrix, USV) -> + val matrixSize = matrix.shape.reduce { acc, i -> acc * i } + val curMatrix = DoubleTensor( + matrix.shape, + matrix.mutableBuffer.array().slice(matrix.bufferStart until matrix.bufferStart + matrixSize) + .toDoubleArray() + ) + svdHelper(curMatrix, USV, m, n, epsilon) + } + + return Triple(uTensor.transpose(), sTensor, vTensor.transpose()) + } + + override fun Tensor.symEig(): Pair = + symEig(epsilon = 1e-15) + + /** + * Returns eigenvalues and eigenvectors of a real symmetric matrix input or a batch of real symmetric matrices, + * represented by a pair (eigenvalues, eigenvectors). + * + * @param epsilon permissible error when comparing tensors for equality + * and when the cosine approaches 1 in the SVD algorithm. + * @return a pair (eigenvalues, eigenvectors) + */ + public fun Tensor.symEig(epsilon: Double): Pair { + checkSymmetric(tensor, epsilon) + val (u, s, v) = tensor.svd(epsilon) + val shp = s.shape + intArrayOf(1) + val utv = u.transpose() dot v + val n = s.shape.last() + for (matrix in utv.matrixSequence()) + cleanSymHelper(matrix.as2D(), n) + + val eig = (utv dot s.view(shp)).view(s.shape) + return eig to v + } + + /** + * Computes the determinant of a square matrix input, or of each square matrix in a batched input + * using LU factorization algorithm. + * + * @param epsilon error in the LU algorithm - permissible error when comparing the determinant of a matrix with zero + * @return the determinant. + */ + public fun Tensor.detLU(epsilon: Double = 1e-9): DoubleTensor { + + checkSquareMatrix(tensor.shape) + val luTensor = tensor.copy() + val pivotsTensor = tensor.setUpPivots() + + val n = shape.size + + val detTensorShape = IntArray(n - 1) { i -> shape[i] } + detTensorShape[n - 2] = 1 + val resBuffer = DoubleArray(detTensorShape.reduce(Int::times)) { 0.0 } + + val detTensor = DoubleTensor( + detTensorShape, + resBuffer + ) + + luTensor.matrixSequence().zip(pivotsTensor.vectorSequence()).forEachIndexed { index, (lu, pivots) -> + resBuffer[index] = if (luHelper(lu.as2D(), pivots.as1D(), epsilon)) + 0.0 else luMatrixDet(lu.as2D(), pivots.as1D()) + } + + return detTensor + } + + /** + * Computes the multiplicative inverse matrix of a square matrix input, or of each square matrix in a batched input + * using LU factorization algorithm. + * Given a square matrix `a`, return the matrix `aInv` satisfying + * ``a.dot(aInv) = aInv.dot(a) = eye(a.shape[0])``. + * + * @param epsilon error in the LU algorithm - permissible error when comparing the determinant of a matrix with zero + * @return the multiplicative inverse of a matrix. + */ + public fun Tensor.invLU(epsilon: Double = 1e-9): DoubleTensor { + val (luTensor, pivotsTensor) = luFactor(epsilon) + val invTensor = luTensor.zeroesLike() + + val seq = luTensor.matrixSequence().zip(pivotsTensor.vectorSequence()).zip(invTensor.matrixSequence()) + for ((luP, invMatrix) in seq) { + val (lu, pivots) = luP + luMatrixInv(lu.as2D(), pivots.as1D(), invMatrix.as2D()) + } + + return invTensor + } + + /** + * LUP decomposition + * + * Computes the LUP decomposition of a matrix or a batch of matrices. + * Given a tensor `input`, return tensors (P, L, U) satisfying ``P * input = L * U``, + * with `P` being a permutation matrix or batch of matrices, + * `L` being a lower triangular matrix or batch of matrices, + * `U` being an upper triangular matrix or batch of matrices. + * + * @param epsilon permissible error when comparing the determinant of a matrix with zero + * @return triple of P, L and U tensors + */ + public fun Tensor.lu(epsilon: Double = 1e-9): Triple { + val (lu, pivots) = tensor.luFactor(epsilon) + return luPivot(lu, pivots) + } + + override fun Tensor.lu(): Triple = lu(1e-9) +} + + diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensor.kt new file mode 100644 index 000000000..ae1e6c8c8 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensor.kt @@ -0,0 +1,17 @@ +/* + * 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.tensors.core + +import space.kscience.kmath.structures.IntBuffer + +/** + * Default [BufferedTensor] implementation for [Int] values + */ +public class IntTensor internal constructor( + shape: IntArray, + buffer: IntArray, + offset: Int = 0 +) : BufferedTensor(shape, IntBuffer(buffer), offset) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/TensorLinearStructure.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/TensorLinearStructure.kt new file mode 100644 index 000000000..4bdd987db --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/TensorLinearStructure.kt @@ -0,0 +1,57 @@ +/* + * 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.tensors.core.internal + +import space.kscience.kmath.nd.Strides +import kotlin.math.max + + +internal fun stridesFromShape(shape: IntArray): IntArray { + val nDim = shape.size + val res = IntArray(nDim) + if (nDim == 0) + return res + + var current = nDim - 1 + res[current] = 1 + + while (current > 0) { + res[current - 1] = max(1, shape[current]) * res[current] + current-- + } + return res +} + +internal fun indexFromOffset(offset: Int, strides: IntArray, nDim: Int): IntArray { + val res = IntArray(nDim) + var current = offset + var strideIndex = 0 + + while (strideIndex < nDim) { + res[strideIndex] = (current / strides[strideIndex]) + current %= strides[strideIndex] + strideIndex++ + } + return res +} + +/** + * This [Strides] implementation follows the last dimension first convention + * For more information: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.strides.html + * + * @param shape the shape of the tensor. + */ +internal class TensorLinearStructure(override val shape: IntArray) : Strides { + override val strides: IntArray + get() = stridesFromShape(shape) + + override fun index(offset: Int): IntArray = + indexFromOffset(offset, strides, shape.size) + + override val linearSize: Int + get() = shape.reduce(Int::times) + +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt new file mode 100644 index 000000000..6324dc242 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/broadcastUtils.kt @@ -0,0 +1,146 @@ +/* + * 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.tensors.core.internal + +import space.kscience.kmath.tensors.core.DoubleTensor +import kotlin.math.max + +internal fun multiIndexBroadCasting(tensor: DoubleTensor, resTensor: DoubleTensor, linearSize: Int) { + for (linearIndex in 0 until linearSize) { + val totalMultiIndex = resTensor.linearStructure.index(linearIndex) + val curMultiIndex = tensor.shape.copyOf() + + val offset = totalMultiIndex.size - curMultiIndex.size + + for (i in curMultiIndex.indices) { + if (curMultiIndex[i] != 1) { + curMultiIndex[i] = totalMultiIndex[i + offset] + } else { + curMultiIndex[i] = 0 + } + } + + val curLinearIndex = tensor.linearStructure.offset(curMultiIndex) + resTensor.mutableBuffer.array()[linearIndex] = + tensor.mutableBuffer.array()[tensor.bufferStart + curLinearIndex] + } +} + +internal fun broadcastShapes(vararg shapes: IntArray): IntArray { + var totalDim = 0 + for (shape in shapes) { + totalDim = max(totalDim, shape.size) + } + + val totalShape = IntArray(totalDim) { 0 } + for (shape in shapes) { + for (i in shape.indices) { + val curDim = shape[i] + val offset = totalDim - shape.size + totalShape[i + offset] = max(totalShape[i + offset], curDim) + } + } + + for (shape in shapes) { + for (i in shape.indices) { + val curDim = shape[i] + val offset = totalDim - shape.size + check(curDim == 1 || totalShape[i + offset] == curDim) { + "Shapes are not compatible and cannot be broadcast" + } + } + } + + return totalShape +} + +internal fun broadcastTo(tensor: DoubleTensor, newShape: IntArray): DoubleTensor { + require(tensor.shape.size <= newShape.size) { + "Tensor is not compatible with the new shape" + } + + val n = newShape.reduce { acc, i -> acc * i } + val resTensor = DoubleTensor(newShape, DoubleArray(n)) + + for (i in tensor.shape.indices) { + val curDim = tensor.shape[i] + val offset = newShape.size - tensor.shape.size + check(curDim == 1 || newShape[i + offset] == curDim) { + "Tensor is not compatible with the new shape and cannot be broadcast" + } + } + + multiIndexBroadCasting(tensor, resTensor, n) + return resTensor +} + +internal fun broadcastTensors(vararg tensors: DoubleTensor): List { + val totalShape = broadcastShapes(*(tensors.map { it.shape }).toTypedArray()) + val n = totalShape.reduce { acc, i -> acc * i } + + return tensors.map { tensor -> + val resTensor = DoubleTensor(totalShape, DoubleArray(n)) + multiIndexBroadCasting(tensor, resTensor, n) + resTensor + } +} + +internal fun broadcastOuterTensors(vararg tensors: DoubleTensor): List { + val onlyTwoDims = tensors.asSequence().onEach { + require(it.shape.size >= 2) { + "Tensors must have at least 2 dimensions" + } + }.any { it.shape.size != 2 } + + if (!onlyTwoDims) { + return tensors.asList() + } + + val totalShape = broadcastShapes(*(tensors.map { it.shape.sliceArray(0..it.shape.size - 3) }).toTypedArray()) + val n = totalShape.reduce { acc, i -> acc * i } + + return buildList { + for (tensor in tensors) { + val matrixShape = tensor.shape.sliceArray(tensor.shape.size - 2 until tensor.shape.size).copyOf() + val matrixSize = matrixShape[0] * matrixShape[1] + val matrix = DoubleTensor(matrixShape, DoubleArray(matrixSize)) + + val outerTensor = DoubleTensor(totalShape, DoubleArray(n)) + val resTensor = DoubleTensor(totalShape + matrixShape, DoubleArray(n * matrixSize)) + + for (linearIndex in 0 until n) { + val totalMultiIndex = outerTensor.linearStructure.index(linearIndex) + var curMultiIndex = tensor.shape.sliceArray(0..tensor.shape.size - 3).copyOf() + curMultiIndex = IntArray(totalMultiIndex.size - curMultiIndex.size) { 1 } + curMultiIndex + + val newTensor = DoubleTensor(curMultiIndex + matrixShape, tensor.mutableBuffer.array()) + + for (i in curMultiIndex.indices) { + if (curMultiIndex[i] != 1) { + curMultiIndex[i] = totalMultiIndex[i] + } else { + curMultiIndex[i] = 0 + } + } + + for (i in 0 until matrixSize) { + val curLinearIndex = newTensor.linearStructure.offset( + curMultiIndex + + matrix.linearStructure.index(i) + ) + val newLinearIndex = resTensor.linearStructure.offset( + totalMultiIndex + + matrix.linearStructure.index(i) + ) + + resTensor.mutableBuffer.array()[resTensor.bufferStart + newLinearIndex] = + newTensor.mutableBuffer.array()[newTensor.bufferStart + curLinearIndex] + } + } + add(resTensor) + } + } +} \ No newline at end of file diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/checks.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/checks.kt new file mode 100644 index 000000000..f1c158770 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/checks.kt @@ -0,0 +1,64 @@ +/* + * 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.tensors.core.internal + +import space.kscience.kmath.tensors.api.Tensor +import space.kscience.kmath.tensors.core.DoubleTensor +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra + + +internal fun checkEmptyShape(shape: IntArray) = + check(shape.isNotEmpty()) { + "Illegal empty shape provided" + } + +internal fun checkEmptyDoubleBuffer(buffer: DoubleArray) = + check(buffer.isNotEmpty()) { + "Illegal empty buffer provided" + } + +internal fun checkBufferShapeConsistency(shape: IntArray, buffer: DoubleArray) = + check(buffer.size == shape.reduce(Int::times)) { + "Inconsistent shape ${shape.toList()} for buffer of size ${buffer.size} provided" + } + +internal fun checkShapesCompatible(a: Tensor, b: Tensor) = + check(a.shape contentEquals b.shape) { + "Incompatible shapes ${a.shape.toList()} and ${b.shape.toList()} " + } + +internal fun checkTranspose(dim: Int, i: Int, j: Int) = + check((i < dim) and (j < dim)) { + "Cannot transpose $i to $j for a tensor of dim $dim" + } + +internal fun checkView(a: Tensor, shape: IntArray) = + check(a.shape.reduce(Int::times) == shape.reduce(Int::times)) + +internal fun checkSquareMatrix(shape: IntArray) { + val n = shape.size + check(n >= 2) { + "Expected tensor with 2 or more dimensions, got size $n instead" + } + check(shape[n - 1] == shape[n - 2]) { + "Tensor must be batches of square matrices, but they are ${shape[n - 1]} by ${shape[n - 1]} matrices" + } +} + +internal fun DoubleTensorAlgebra.checkSymmetric( + tensor: Tensor, epsilon: Double = 1e-6 +) = + check(tensor.eq(tensor.transpose(), epsilon)) { + "Tensor is not symmetric about the last 2 dimensions at precision $epsilon" + } + +internal fun DoubleTensorAlgebra.checkPositiveDefinite(tensor: DoubleTensor, epsilon: Double = 1e-6) { + checkSymmetric(tensor, epsilon) + for (mat in tensor.matrixSequence()) + check(mat.asTensor().detLU().value() > 0.0) { + "Tensor contains matrices which are not positive definite ${mat.asTensor().detLU().value()}" + } +} \ No newline at end of file diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt new file mode 100644 index 000000000..7d3617547 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt @@ -0,0 +1,342 @@ +/* + * 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.tensors.core.internal + +import space.kscience.kmath.nd.MutableStructure1D +import space.kscience.kmath.nd.MutableStructure2D +import space.kscience.kmath.nd.as1D +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.* +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.valueOrNull +import kotlin.math.abs +import kotlin.math.min +import kotlin.math.sign +import kotlin.math.sqrt + + +internal fun BufferedTensor.vectorSequence(): Sequence> = sequence { + val n = shape.size + val vectorOffset = shape[n - 1] + val vectorShape = intArrayOf(shape.last()) + for (offset in 0 until numElements step vectorOffset) { + val vector = BufferedTensor(vectorShape, mutableBuffer, bufferStart + offset) + yield(vector) + } +} + +internal fun BufferedTensor.matrixSequence(): Sequence> = sequence { + val n = shape.size + check(n >= 2) { "Expected tensor with 2 or more dimensions, got size $n" } + val matrixOffset = shape[n - 1] * shape[n - 2] + val matrixShape = intArrayOf(shape[n - 2], shape[n - 1]) + for (offset in 0 until numElements step matrixOffset) { + val matrix = BufferedTensor(matrixShape, mutableBuffer, bufferStart + offset) + yield(matrix) + } +} + +internal fun dotHelper( + a: MutableStructure2D, + b: MutableStructure2D, + res: MutableStructure2D, + l: Int, m: Int, n: Int +) { + for (i in 0 until l) { + for (j in 0 until n) { + var curr = 0.0 + for (k in 0 until m) { + curr += a[i, k] * b[k, j] + } + res[i, j] = curr + } + } +} + +internal fun luHelper( + lu: MutableStructure2D, + pivots: MutableStructure1D, + epsilon: Double +): Boolean { + + val m = lu.rowNum + + for (row in 0..m) pivots[row] = row + + for (i in 0 until m) { + var maxVal = 0.0 + var maxInd = i + + for (k in i until m) { + val absA = abs(lu[k, i]) + if (absA > maxVal) { + maxVal = absA + maxInd = k + } + } + + if (abs(maxVal) < epsilon) + return true // matrix is singular + + if (maxInd != i) { + + val j = pivots[i] + pivots[i] = pivots[maxInd] + pivots[maxInd] = j + + for (k in 0 until m) { + val tmp = lu[i, k] + lu[i, k] = lu[maxInd, k] + lu[maxInd, k] = tmp + } + + pivots[m] += 1 + + } + + for (j in i + 1 until m) { + lu[j, i] /= lu[i, i] + for (k in i + 1 until m) { + lu[j, k] -= lu[j, i] * lu[i, k] + } + } + } + return false +} + +internal fun BufferedTensor.setUpPivots(): IntTensor { + val n = this.shape.size + val m = this.shape.last() + val pivotsShape = IntArray(n - 1) { i -> this.shape[i] } + pivotsShape[n - 2] = m + 1 + + return IntTensor( + pivotsShape, + IntArray(pivotsShape.reduce(Int::times)) { 0 } + ) +} + +internal fun DoubleTensorAlgebra.computeLU( + tensor: DoubleTensor, + epsilon: Double +): Pair? { + + checkSquareMatrix(tensor.shape) + val luTensor = tensor.copy() + val pivotsTensor = tensor.setUpPivots() + + for ((lu, pivots) in luTensor.matrixSequence().zip(pivotsTensor.vectorSequence())) + if (luHelper(lu.as2D(), pivots.as1D(), epsilon)) + return null + + return Pair(luTensor, pivotsTensor) +} + +internal fun pivInit( + p: MutableStructure2D, + pivot: MutableStructure1D, + n: Int +) { + for (i in 0 until n) { + p[i, pivot[i]] = 1.0 + } +} + +internal fun luPivotHelper( + l: MutableStructure2D, + u: MutableStructure2D, + lu: MutableStructure2D, + n: Int +) { + for (i in 0 until n) { + for (j in 0 until n) { + if (i == j) { + l[i, j] = 1.0 + } + if (j < i) { + l[i, j] = lu[i, j] + } + if (j >= i) { + u[i, j] = lu[i, j] + } + } + } +} + +internal fun choleskyHelper( + a: MutableStructure2D, + l: MutableStructure2D, + n: Int +) { + for (i in 0 until n) { + for (j in 0 until i) { + var h = a[i, j] + for (k in 0 until j) { + h -= l[i, k] * l[j, k] + } + l[i, j] = h / l[j, j] + } + var h = a[i, i] + for (j in 0 until i) { + h -= l[i, j] * l[i, j] + } + l[i, i] = sqrt(h) + } +} + +internal fun luMatrixDet(lu: MutableStructure2D, pivots: MutableStructure1D): Double { + if (lu[0, 0] == 0.0) { + return 0.0 + } + val m = lu.shape[0] + val sign = if ((pivots[m] - m) % 2 == 0) 1.0 else -1.0 + return (0 until m).asSequence().map { lu[it, it] }.fold(sign) { left, right -> left * right } +} + +internal fun luMatrixInv( + lu: MutableStructure2D, + pivots: MutableStructure1D, + invMatrix: MutableStructure2D +) { + val m = lu.shape[0] + + for (j in 0 until m) { + for (i in 0 until m) { + if (pivots[i] == j) { + invMatrix[i, j] = 1.0 + } + + for (k in 0 until i) { + invMatrix[i, j] -= lu[i, k] * invMatrix[k, j] + } + } + + for (i in m - 1 downTo 0) { + for (k in i + 1 until m) { + invMatrix[i, j] -= lu[i, k] * invMatrix[k, j] + } + invMatrix[i, j] /= lu[i, i] + } + } +} + +internal fun DoubleTensorAlgebra.qrHelper( + matrix: DoubleTensor, + q: DoubleTensor, + r: MutableStructure2D +) { + checkSquareMatrix(matrix.shape) + val n = matrix.shape[0] + val qM = q.as2D() + val matrixT = matrix.transpose(0, 1) + val qT = q.transpose(0, 1) + + for (j in 0 until n) { + val v = matrixT[j] + val vv = v.as1D() + if (j > 0) { + for (i in 0 until j) { + r[i, j] = (qT[i] dot matrixT[j]).value() + for (k in 0 until n) { + val qTi = qT[i].as1D() + vv[k] = vv[k] - r[i, j] * qTi[k] + } + } + } + r[j, j] = DoubleTensorAlgebra { (v dot v).sqrt().value() } + for (i in 0 until n) { + qM[i, j] = vv[i] / r[j, j] + } + } +} + +internal fun DoubleTensorAlgebra.svd1d(a: DoubleTensor, epsilon: Double = 1e-10): DoubleTensor { + val (n, m) = a.shape + var v: DoubleTensor + val b: DoubleTensor + if (n > m) { + b = a.transpose(0, 1).dot(a) + v = DoubleTensor(intArrayOf(m), getRandomUnitVector(m, 0)) + } else { + b = a.dot(a.transpose(0, 1)) + v = DoubleTensor(intArrayOf(n), getRandomUnitVector(n, 0)) + } + + var lastV: DoubleTensor + while (true) { + lastV = v + v = b.dot(lastV) + val norm = DoubleTensorAlgebra { (v dot v).sqrt().value() } + v = v.times(1.0 / norm) + if (abs(v.dot(lastV).value()) > 1 - epsilon) { + return v + } + } +} + +internal fun DoubleTensorAlgebra.svdHelper( + matrix: DoubleTensor, + USV: Pair, Pair, BufferedTensor>>, + m: Int, n: Int, epsilon: Double +) { + val res = ArrayList>(0) + val (matrixU, SV) = USV + val (matrixS, matrixV) = SV + + for (k in 0 until min(n, m)) { + var a = matrix.copy() + for ((singularValue, u, v) in res.slice(0 until k)) { + val outerProduct = DoubleArray(u.shape[0] * v.shape[0]) + for (i in 0 until u.shape[0]) { + for (j in 0 until v.shape[0]) { + outerProduct[i * v.shape[0] + j] = u[i].value() * v[j].value() + } + } + a = a - singularValue.times(DoubleTensor(intArrayOf(u.shape[0], v.shape[0]), outerProduct)) + } + var v: DoubleTensor + var u: DoubleTensor + var norm: Double + if (n > m) { + v = svd1d(a, epsilon) + u = matrix.dot(v) + norm = DoubleTensorAlgebra { (u dot u).sqrt().value() } + u = u.times(1.0 / norm) + } else { + u = svd1d(a, epsilon) + v = matrix.transpose(0, 1).dot(u) + norm = DoubleTensorAlgebra { (v dot v).sqrt().value() } + v = v.times(1.0 / norm) + } + + res.add(Triple(norm, u, v)) + } + + val s = res.map { it.first }.toDoubleArray() + val uBuffer = res.map { it.second }.flatMap { it.mutableBuffer.array().toList() }.toDoubleArray() + val vBuffer = res.map { it.third }.flatMap { it.mutableBuffer.array().toList() }.toDoubleArray() + for (i in uBuffer.indices) { + matrixU.mutableBuffer.array()[matrixU.bufferStart + i] = uBuffer[i] + } + for (i in s.indices) { + matrixS.mutableBuffer.array()[matrixS.bufferStart + i] = s[i] + } + for (i in vBuffer.indices) { + matrixV.mutableBuffer.array()[matrixV.bufferStart + i] = vBuffer[i] + } +} + +internal fun cleanSymHelper(matrix: MutableStructure2D, n: Int) { + for (i in 0 until n) + for (j in 0 until n) { + if (i == j) { + matrix[i, j] = sign(matrix[i, j]) + } else { + matrix[i, j] = 0.0 + } + } +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt new file mode 100644 index 000000000..d965b6bcd --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/tensorCastsUtils.kt @@ -0,0 +1,44 @@ +/* + * 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.tensors.core.internal + +import space.kscience.kmath.nd.MutableBufferND +import space.kscience.kmath.structures.asMutableBuffer +import space.kscience.kmath.tensors.api.Tensor +import space.kscience.kmath.tensors.core.BufferedTensor +import space.kscience.kmath.tensors.core.DoubleTensor +import space.kscience.kmath.tensors.core.IntTensor + +internal fun BufferedTensor.asTensor(): IntTensor = + IntTensor(this.shape, this.mutableBuffer.array(), this.bufferStart) + +internal fun BufferedTensor.asTensor(): DoubleTensor = + DoubleTensor(this.shape, this.mutableBuffer.array(), this.bufferStart) + +internal fun Tensor.copyToBufferedTensor(): BufferedTensor = + BufferedTensor( + this.shape, + TensorLinearStructure(this.shape).indices().map(this::get).toMutableList().asMutableBuffer(), 0 + ) + +internal fun Tensor.toBufferedTensor(): BufferedTensor = when (this) { + is BufferedTensor -> this + is MutableBufferND -> if (this.strides.strides contentEquals TensorLinearStructure(this.shape).strides) + BufferedTensor(this.shape, this.mutableBuffer, 0) else this.copyToBufferedTensor() + else -> this.copyToBufferedTensor() +} + +internal val Tensor.tensor: DoubleTensor + get() = when (this) { + is DoubleTensor -> this + else -> this.toBufferedTensor().asTensor() + } + +internal val Tensor.tensor: IntTensor + get() = when (this) { + is IntTensor -> this + else -> this.toBufferedTensor().asTensor() + } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt new file mode 100644 index 000000000..0ffaf39e7 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt @@ -0,0 +1,124 @@ +/* + * 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.tensors.core.internal + +import space.kscience.kmath.nd.as1D +import space.kscience.kmath.samplers.GaussianSampler +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.structures.* +import space.kscience.kmath.tensors.core.BufferedTensor +import space.kscience.kmath.tensors.core.DoubleTensor +import kotlin.math.* + +/** + * Returns a reference to [IntArray] containing all of the elements of this [Buffer] or copy the data. + */ +internal fun Buffer.array(): IntArray = when (this) { + is IntBuffer -> array + else -> this.toIntArray() +} + +/** + * Returns a reference to [DoubleArray] containing all of the elements of this [Buffer] or copy the data. + */ +internal fun Buffer.array(): DoubleArray = when (this) { + is DoubleBuffer -> array + else -> this.toDoubleArray() +} + +internal fun getRandomNormals(n: Int, seed: Long): DoubleArray { + val distribution = GaussianSampler(0.0, 1.0) + val generator = RandomGenerator.default(seed) + return distribution.sample(generator).nextBufferBlocking(n).toDoubleArray() +} + +internal fun getRandomUnitVector(n: Int, seed: Long): DoubleArray { + val unnorm = getRandomNormals(n, seed) + val norm = sqrt(unnorm.sumOf { it * it }) + return unnorm.map { it / norm }.toDoubleArray() +} + +internal fun minusIndexFrom(n: Int, i: Int): Int = if (i >= 0) i else { + val ii = n + i + check(ii >= 0) { + "Out of bound index $i for tensor of dim $n" + } + ii +} + +internal fun BufferedTensor.minusIndex(i: Int): Int = minusIndexFrom(this.dimension, i) + +internal fun format(value: Double, digits: Int = 4): String = buildString { + val res = buildString { + val ten = 10.0 + val approxOrder = if (value == 0.0) 0 else ceil(log10(abs(value))).toInt() + val order = if ( + ((value % ten) == 0.0) || + (value == 1.0) || + ((1 / value) % ten == 0.0) + ) approxOrder else approxOrder - 1 + val lead = value / ten.pow(order) + if (value >= 0.0) append(' ') + append(round(lead * ten.pow(digits)) / ten.pow(digits)) + when { + order == 0 -> Unit + order > 0 -> { + append("e+") + append(order) + } + else -> { + append('e') + append(order) + } + } + } + val fLength = digits + 6 + append(res) + repeat(fLength - res.length) { append(' ') } +} + +internal fun DoubleTensor.toPrettyString(): String = buildString { + var offset = 0 + val shape = this@toPrettyString.shape + val linearStructure = this@toPrettyString.linearStructure + val vectorSize = shape.last() + append("DoubleTensor(\n") + var charOffset = 3 + for (vector in vectorSequence()) { + repeat(charOffset) { append(' ') } + val index = linearStructure.index(offset) + for (ind in index.reversed()) { + if (ind != 0) { + break + } + append('[') + charOffset += 1 + } + + val values = vector.as1D().toMutableList().map(::format) + + values.joinTo(this, separator = ", ") + + append(']') + charOffset -= 1 + + index.reversed().zip(shape.reversed()).drop(1).forEach { (ind, maxInd) -> + if (ind != maxInd - 1) { + return@forEach + } + append(']') + charOffset -= 1 + } + + offset += vectorSize + if (this@toPrettyString.numElements == offset) { + break + } + + append(",\n") + } + append("\n)") +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorCasts.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorCasts.kt new file mode 100644 index 000000000..142cb2156 --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorCasts.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.tensors.core + +import space.kscience.kmath.tensors.api.Tensor +import space.kscience.kmath.tensors.core.internal.tensor + +/** + * Casts [Tensor] of [Double] to [DoubleTensor] + */ +public fun Tensor.toDoubleTensor(): DoubleTensor = this.tensor + +/** + * Casts [Tensor] of [Int] to [IntTensor] + */ +public fun Tensor.toIntTensor(): IntTensor = this.tensor + +/** + * Returns [DoubleArray] of tensor elements + */ +public fun DoubleTensor.toDoubleArray(): DoubleArray { + return DoubleArray(numElements) { i -> + mutableBuffer[bufferStart + i] + } +} + +/** + * Returns [IntArray] of tensor elements + */ +public fun IntTensor.toIntArray(): IntArray { + return IntArray(numElements) { i -> + mutableBuffer[bufferStart + i] + } +} diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestBroadcasting.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestBroadcasting.kt new file mode 100644 index 000000000..35e605fd9 --- /dev/null +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestBroadcasting.kt @@ -0,0 +1,105 @@ +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.internal.* +import kotlin.test.Test +import kotlin.test.assertTrue + +internal class TestBroadcasting { + + @Test + fun testBroadcastShapes() = DoubleTensorAlgebra { + assertTrue( + broadcastShapes( + intArrayOf(2, 3), intArrayOf(1, 3), intArrayOf(1, 1, 1) + ) contentEquals intArrayOf(1, 2, 3) + ) + + assertTrue( + broadcastShapes( + intArrayOf(6, 7), intArrayOf(5, 6, 1), intArrayOf(7), intArrayOf(5, 1, 7) + ) contentEquals intArrayOf(5, 6, 7) + ) + } + + @Test + fun testBroadcastTo() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor2 = fromArray(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) + + val res = broadcastTo(tensor2, tensor1.shape) + assertTrue(res.shape contentEquals intArrayOf(2, 3)) + assertTrue(res.mutableBuffer.array() contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0)) + } + + @Test + fun testBroadcastTensors() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor2 = fromArray(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) + val tensor3 = fromArray(intArrayOf(1, 1, 1), doubleArrayOf(500.0)) + + val res = broadcastTensors(tensor1, tensor2, tensor3) + + assertTrue(res[0].shape contentEquals intArrayOf(1, 2, 3)) + assertTrue(res[1].shape contentEquals intArrayOf(1, 2, 3)) + assertTrue(res[2].shape contentEquals intArrayOf(1, 2, 3)) + + assertTrue(res[0].mutableBuffer.array() contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + assertTrue(res[1].mutableBuffer.array() contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0)) + assertTrue(res[2].mutableBuffer.array() contentEquals doubleArrayOf(500.0, 500.0, 500.0, 500.0, 500.0, 500.0)) + } + + @Test + fun testBroadcastOuterTensors() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor2 = fromArray(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) + val tensor3 = fromArray(intArrayOf(1, 1, 1), doubleArrayOf(500.0)) + + val res = broadcastOuterTensors(tensor1, tensor2, tensor3) + + assertTrue(res[0].shape contentEquals intArrayOf(1, 2, 3)) + assertTrue(res[1].shape contentEquals intArrayOf(1, 1, 3)) + assertTrue(res[2].shape contentEquals intArrayOf(1, 1, 1)) + + assertTrue(res[0].mutableBuffer.array() contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + assertTrue(res[1].mutableBuffer.array() contentEquals doubleArrayOf(10.0, 20.0, 30.0)) + assertTrue(res[2].mutableBuffer.array() contentEquals doubleArrayOf(500.0)) + } + + @Test + fun testBroadcastOuterTensorsShapes() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(2, 1, 3, 2, 3), DoubleArray(2 * 1 * 3 * 2 * 3) {0.0}) + val tensor2 = fromArray(intArrayOf(4, 2, 5, 1, 3, 3), DoubleArray(4 * 2 * 5 * 1 * 3 * 3) {0.0}) + val tensor3 = fromArray(intArrayOf(1, 1), doubleArrayOf(500.0)) + + val res = broadcastOuterTensors(tensor1, tensor2, tensor3) + + assertTrue(res[0].shape contentEquals intArrayOf(4, 2, 5, 3, 2, 3)) + assertTrue(res[1].shape contentEquals intArrayOf(4, 2, 5, 3, 3, 3)) + assertTrue(res[2].shape contentEquals intArrayOf(4, 2, 5, 3, 1, 1)) + } + + @Test + fun testMinusTensor() = BroadcastDoubleTensorAlgebra.invoke { + val tensor1 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor2 = fromArray(intArrayOf(1, 3), doubleArrayOf(10.0, 20.0, 30.0)) + val tensor3 = fromArray(intArrayOf(1, 1, 1), doubleArrayOf(500.0)) + + val tensor21 = tensor2 - tensor1 + val tensor31 = tensor3 - tensor1 + val tensor32 = tensor3 - tensor2 + + assertTrue(tensor21.shape contentEquals intArrayOf(2, 3)) + assertTrue(tensor21.mutableBuffer.array() contentEquals doubleArrayOf(9.0, 18.0, 27.0, 6.0, 15.0, 24.0)) + + assertTrue(tensor31.shape contentEquals intArrayOf(1, 2, 3)) + assertTrue( + tensor31.mutableBuffer.array() + contentEquals doubleArrayOf(499.0, 498.0, 497.0, 496.0, 495.0, 494.0) + ) + + assertTrue(tensor32.shape contentEquals intArrayOf(1, 1, 3)) + assertTrue(tensor32.mutableBuffer.array() contentEquals doubleArrayOf(490.0, 480.0, 470.0)) + } + +} diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleAnalyticTensorAlgebra.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleAnalyticTensorAlgebra.kt new file mode 100644 index 000000000..3b4c615b4 --- /dev/null +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleAnalyticTensorAlgebra.kt @@ -0,0 +1,158 @@ +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.operations.invoke +import kotlin.math.* +import kotlin.test.Test +import kotlin.test.assertTrue + +internal class TestDoubleAnalyticTensorAlgebra { + + val shape = intArrayOf(2, 1, 3, 2) + val buffer = doubleArrayOf( + 27.1, 20.0, 19.84, + 23.123, 3.0, 2.0, + + 3.23, 133.7, 25.3, + 100.3, 11.0, 12.012 + ) + val tensor = DoubleTensor(shape, buffer) + + fun DoubleArray.fmap(transform: (Double) -> Double): DoubleArray { + return this.map(transform).toDoubleArray() + } + + fun expectedTensor(transform: (Double) -> Double): DoubleTensor { + return DoubleTensor(shape, buffer.fmap(transform)) + } + + @Test + fun testExp() = DoubleTensorAlgebra { + assertTrue { tensor.exp() eq expectedTensor(::exp) } + } + + @Test + fun testLog() = DoubleTensorAlgebra { + assertTrue { tensor.ln() eq expectedTensor(::ln) } + } + + @Test + fun testSqrt() = DoubleTensorAlgebra { + assertTrue { tensor.sqrt() eq expectedTensor(::sqrt) } + } + + @Test + fun testCos() = DoubleTensorAlgebra { + assertTrue { tensor.cos() eq expectedTensor(::cos) } + } + + + @Test + fun testCosh() = DoubleTensorAlgebra { + assertTrue { tensor.cosh() eq expectedTensor(::cosh) } + } + + @Test + fun testAcosh() = DoubleTensorAlgebra { + assertTrue { tensor.acosh() eq expectedTensor(::acosh) } + } + + @Test + fun testSin() = DoubleTensorAlgebra { + assertTrue { tensor.sin() eq expectedTensor(::sin) } + } + + @Test + fun testSinh() = DoubleTensorAlgebra { + assertTrue { tensor.sinh() eq expectedTensor(::sinh) } + } + + @Test + fun testAsinh() = DoubleTensorAlgebra { + assertTrue { tensor.asinh() eq expectedTensor(::asinh) } + } + + @Test + fun testTan() = DoubleTensorAlgebra { + assertTrue { tensor.tan() eq expectedTensor(::tan) } + } + + @Test + fun testAtan() = DoubleTensorAlgebra { + assertTrue { tensor.atan() eq expectedTensor(::atan) } + } + + @Test + fun testTanh() = DoubleTensorAlgebra { + assertTrue { tensor.tanh() eq expectedTensor(::tanh) } + } + + @Test + fun testCeil() = DoubleTensorAlgebra { + assertTrue { tensor.ceil() eq expectedTensor(::ceil) } + } + + @Test + fun testFloor() = DoubleTensorAlgebra { + assertTrue { tensor.floor() eq expectedTensor(::floor) } + } + + val shape2 = intArrayOf(2, 2) + val buffer2 = doubleArrayOf( + 1.0, 2.0, + -3.0, 4.0 + ) + val tensor2 = DoubleTensor(shape2, buffer2) + + @Test + fun testMin() = DoubleTensorAlgebra { + assertTrue { tensor2.min() == -3.0 } + assertTrue { tensor2.min(0, true) eq fromArray( + intArrayOf(1, 2), + doubleArrayOf(-3.0, 2.0) + )} + assertTrue { tensor2.min(1, false) eq fromArray( + intArrayOf(2), + doubleArrayOf(1.0, -3.0) + )} + } + + @Test + fun testMax() = DoubleTensorAlgebra { + assertTrue { tensor2.max() == 4.0 } + assertTrue { tensor2.max(0, true) eq fromArray( + intArrayOf(1, 2), + doubleArrayOf(1.0, 4.0) + )} + assertTrue { tensor2.max(1, false) eq fromArray( + intArrayOf(2), + doubleArrayOf(2.0, 4.0) + )} + } + + @Test + fun testSum() = DoubleTensorAlgebra { + assertTrue { tensor2.sum() == 4.0 } + assertTrue { tensor2.sum(0, true) eq fromArray( + intArrayOf(1, 2), + doubleArrayOf(-2.0, 6.0) + )} + assertTrue { tensor2.sum(1, false) eq fromArray( + intArrayOf(2), + doubleArrayOf(3.0, 1.0) + )} + } + + @Test + fun testMean() = DoubleTensorAlgebra { + assertTrue { tensor2.mean() == 1.0 } + assertTrue { tensor2.mean(0, true) eq fromArray( + intArrayOf(1, 2), + doubleArrayOf(-1.0, 3.0) + )} + assertTrue { tensor2.mean(1, false) eq fromArray( + intArrayOf(2), + doubleArrayOf(1.5, 0.5) + )} + } + +} diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt new file mode 100644 index 000000000..347bb683f --- /dev/null +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt @@ -0,0 +1,196 @@ +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.internal.array +import space.kscience.kmath.tensors.core.internal.svd1d +import kotlin.math.abs +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +internal class TestDoubleLinearOpsTensorAlgebra { + + @Test + fun testDetLU() = DoubleTensorAlgebra { + val tensor = fromArray( + intArrayOf(2, 2, 2), + doubleArrayOf( + 1.0, 3.0, + 1.0, 2.0, + 1.5, 1.0, + 10.0, 2.0 + ) + ) + + val expectedTensor = fromArray( + intArrayOf(2, 1), + doubleArrayOf( + -1.0, + -7.0 + ) + ) + val detTensor = tensor.detLU() + + assertTrue(detTensor.eq(expectedTensor)) + + } + + @Test + fun testDet() = DoubleTensorAlgebra { + val expectedValue = 0.019827417 + val m = fromArray( + intArrayOf(3, 3), doubleArrayOf( + 2.1843, 1.4391, -0.4845, + 1.4391, 1.7772, 0.4055, + -0.4845, 0.4055, 0.7519 + ) + ) + + assertTrue { abs(m.det().value() - expectedValue) < 1e-5 } + } + + @Test + fun testDetSingle() = DoubleTensorAlgebra { + val expectedValue = 48.151623 + val m = fromArray( + intArrayOf(1, 1), doubleArrayOf( + expectedValue + ) + ) + + assertTrue { abs(m.det().value() - expectedValue) < 1e-5 } + } + + @Test + fun testInvLU() = DoubleTensorAlgebra { + val tensor = fromArray( + intArrayOf(2, 2, 2), + doubleArrayOf( + 1.0, 0.0, + 0.0, 2.0, + 1.0, 1.0, + 1.0, 0.0 + ) + ) + + val expectedTensor = fromArray( + intArrayOf(2, 2, 2), doubleArrayOf( + 1.0, 0.0, + 0.0, 0.5, + 0.0, 1.0, + 1.0, -1.0 + ) + ) + + val invTensor = tensor.invLU() + assertTrue(invTensor.eq(expectedTensor)) + } + + @Test + fun testScalarProduct() = DoubleTensorAlgebra { + val a = fromArray(intArrayOf(3), doubleArrayOf(1.8, 2.5, 6.8)) + val b = fromArray(intArrayOf(3), doubleArrayOf(5.5, 2.6, 6.4)) + assertEquals(a.dot(b).value(), 59.92) + } + + @Test + fun testQR() = DoubleTensorAlgebra { + val shape = intArrayOf(2, 2, 2) + val buffer = doubleArrayOf( + 1.0, 3.0, + 1.0, 2.0, + 1.5, 1.0, + 10.0, 2.0 + ) + + val tensor = fromArray(shape, buffer) + + val (q, r) = tensor.qr() + + assertTrue { q.shape contentEquals shape } + assertTrue { r.shape contentEquals shape } + + assertTrue((q dot r).eq(tensor)) + + } + + @Test + fun testLU() = DoubleTensorAlgebra { + val shape = intArrayOf(2, 2, 2) + val buffer = doubleArrayOf( + 1.0, 3.0, + 1.0, 2.0, + 1.5, 1.0, + 10.0, 2.0 + ) + val tensor = fromArray(shape, buffer) + + val (p, l, u) = tensor.lu() + + assertTrue { p.shape contentEquals shape } + assertTrue { l.shape contentEquals shape } + assertTrue { u.shape contentEquals shape } + + assertTrue((p dot tensor).eq(l dot u)) + } + + @Test + fun testCholesky() = DoubleTensorAlgebra { + val tensor = randomNormal(intArrayOf(2, 5, 5), 0) + val sigma = (tensor dot tensor.transpose()) + diagonalEmbedding( + fromArray(intArrayOf(2, 5), DoubleArray(10) { 0.1 }) + ) + val low = sigma.cholesky() + val sigmChol = low dot low.transpose() + assertTrue(sigma.eq(sigmChol)) + } + + @Test + fun testSVD1D() = DoubleTensorAlgebra { + val tensor2 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + + val res = svd1d(tensor2) + + assertTrue(res.shape contentEquals intArrayOf(2)) + assertTrue { abs(abs(res.mutableBuffer.array()[res.bufferStart]) - 0.386) < 0.01 } + assertTrue { abs(abs(res.mutableBuffer.array()[res.bufferStart + 1]) - 0.922) < 0.01 } + } + + @Test + fun testSVD() = DoubleTensorAlgebra{ + testSVDFor(fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))) + testSVDFor(fromArray(intArrayOf(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0))) + } + + @Test + fun testBatchedSVD() = DoubleTensorAlgebra { + val tensor = randomNormal(intArrayOf(2, 5, 3), 0) + val (tensorU, tensorS, tensorV) = tensor.svd() + val tensorSVD = tensorU dot (diagonalEmbedding(tensorS) dot tensorV.transpose()) + assertTrue(tensor.eq(tensorSVD)) + } + + @Test + fun testBatchedSymEig() = DoubleTensorAlgebra { + val tensor = randomNormal(shape = intArrayOf(2, 3, 3), 0) + val tensorSigma = tensor + tensor.transpose() + val (tensorS, tensorV) = tensorSigma.symEig() + val tensorSigmaCalc = tensorV dot (diagonalEmbedding(tensorS) dot tensorV.transpose()) + assertTrue(tensorSigma.eq(tensorSigmaCalc)) + } + + +} + + +private fun DoubleTensorAlgebra.testSVDFor(tensor: DoubleTensor, epsilon: Double = 1e-10): Unit { + val svd = tensor.svd() + + val tensorSVD = svd.first + .dot( + diagonalEmbedding(svd.second) + .dot(svd.third.transpose()) + ) + + assertTrue(tensor.eq(tensorSVD, epsilon)) +} diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt new file mode 100644 index 000000000..a6c6a7618 --- /dev/null +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt @@ -0,0 +1,89 @@ +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.nd.DefaultStrides +import space.kscience.kmath.nd.MutableBufferND +import space.kscience.kmath.nd.as1D +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.toDoubleArray +import space.kscience.kmath.tensors.core.internal.array +import space.kscience.kmath.tensors.core.internal.asTensor +import space.kscience.kmath.tensors.core.internal.matrixSequence +import space.kscience.kmath.tensors.core.internal.toBufferedTensor +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +internal class TestDoubleTensor { + + @Test + fun testValue() = DoubleTensorAlgebra { + val value = 12.5 + val tensor = fromArray(intArrayOf(1), doubleArrayOf(value)) + assertEquals(tensor.value(), value) + } + + @Test + fun testStrides() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(2, 2), doubleArrayOf(3.5, 5.8, 58.4, 2.4)) + assertEquals(tensor[intArrayOf(0, 1)], 5.8) + assertTrue( + tensor.elements().map { it.second }.toList().toDoubleArray() contentEquals tensor.mutableBuffer.toDoubleArray() + ) + } + + @Test + fun testGet() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(1, 2, 2), doubleArrayOf(3.5, 5.8, 58.4, 2.4)) + val matrix = tensor[0].as2D() + assertEquals(matrix[0, 1], 5.8) + + val vector = tensor[0][1].as1D() + assertEquals(vector[0], 58.4) + + matrix[0, 1] = 77.89 + assertEquals(tensor[intArrayOf(0, 0, 1)], 77.89) + + vector[0] = 109.56 + assertEquals(tensor[intArrayOf(0, 1, 0)], 109.56) + + tensor.matrixSequence().forEach { + val a = it.asTensor() + val secondRow = a[1].as1D() + val secondColumn = a.transpose(0, 1)[1].as1D() + assertEquals(secondColumn[0], 77.89) + assertEquals(secondRow[1], secondColumn[1]) + } + } + + @Test + fun testNoBufferProtocol() { + + // create buffer + val doubleArray = DoubleBuffer(doubleArrayOf(1.0, 2.0, 3.0)) + + // create ND buffers, no data is copied + val ndArray = MutableBufferND(DefaultStrides(intArrayOf(3)), doubleArray) + + // map to tensors + val bufferedTensorArray = ndArray.toBufferedTensor() // strides are flipped so data copied + val tensorArray = bufferedTensorArray.asTensor() // data not contiguous so copied again + + val tensorArrayPublic = ndArray.toDoubleTensor() // public API, data copied twice + val sharedTensorArray = tensorArrayPublic.toDoubleTensor() // no data copied by matching type + + assertTrue(tensorArray.mutableBuffer.array() contentEquals sharedTensorArray.mutableBuffer.array()) + + tensorArray[intArrayOf(0)] = 55.9 + assertEquals(tensorArrayPublic[intArrayOf(0)], 1.0) + + tensorArrayPublic[intArrayOf(0)] = 55.9 + assertEquals(sharedTensorArray[intArrayOf(0)], 55.9) + assertEquals(bufferedTensorArray[intArrayOf(0)], 1.0) + + bufferedTensorArray[intArrayOf(0)] = 55.9 + assertEquals(ndArray[intArrayOf(0)], 1.0) + + } +} diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt new file mode 100644 index 000000000..e7e898008 --- /dev/null +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt @@ -0,0 +1,167 @@ +package space.kscience.kmath.tensors.core + + +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.internal.array +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +internal class TestDoubleTensorAlgebra { + + @Test + fun testDoublePlus() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(2), doubleArrayOf(1.0, 2.0)) + val res = 10.0 + tensor + assertTrue(res.mutableBuffer.array() contentEquals doubleArrayOf(11.0, 12.0)) + } + + @Test + fun TestDoubleDiv() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(2), doubleArrayOf(2.0, 4.0)) + val res = 2.0/tensor + assertTrue(res.mutableBuffer.array() contentEquals doubleArrayOf(1.0, 0.5)) + } + + @Test + fun testDivDouble() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(2), doubleArrayOf(10.0, 5.0)) + val res = tensor / 2.5 + assertTrue(res.mutableBuffer.array() contentEquals doubleArrayOf(4.0, 2.0)) + } + + @Test + fun testTranspose1x1() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(1), doubleArrayOf(0.0)) + val res = tensor.transpose(0, 0) + + assertTrue(res.mutableBuffer.array() contentEquals doubleArrayOf(0.0)) + assertTrue(res.shape contentEquals intArrayOf(1)) + } + + @Test + fun testTranspose3x2() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val res = tensor.transpose(1, 0) + + assertTrue(res.mutableBuffer.array() contentEquals doubleArrayOf(1.0, 3.0, 5.0, 2.0, 4.0, 6.0)) + assertTrue(res.shape contentEquals intArrayOf(2, 3)) + } + + @Test + fun testTranspose1x2x3() = DoubleTensorAlgebra { + val tensor = fromArray(intArrayOf(1, 2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val res01 = tensor.transpose(0, 1) + val res02 = tensor.transpose(-3, 2) + val res12 = tensor.transpose() + + assertTrue(res01.shape contentEquals intArrayOf(2, 1, 3)) + assertTrue(res02.shape contentEquals intArrayOf(3, 2, 1)) + assertTrue(res12.shape contentEquals intArrayOf(1, 3, 2)) + + assertTrue(res01.mutableBuffer.array() contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + assertTrue(res02.mutableBuffer.array() contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0)) + assertTrue(res12.mutableBuffer.array() contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0)) + } + + @Test + fun testLinearStructure() = DoubleTensorAlgebra { + val shape = intArrayOf(3) + val tensorA = full(value = -4.5, shape = shape) + val tensorB = full(value = 10.9, shape = shape) + val tensorC = full(value = 789.3, shape = shape) + val tensorD = full(value = -72.9, shape = shape) + val tensorE = full(value = 553.1, shape = shape) + val result = 15.8 * tensorA - 1.5 * tensorB * (-tensorD) + 0.02 * tensorC / tensorE - 39.4 + + val expected = fromArray( + shape, + (1..3).map { + 15.8 * (-4.5) - 1.5 * 10.9 * 72.9 + 0.02 * 789.3 / 553.1 - 39.4 + }.toDoubleArray() + ) + + val assignResult = zeros(shape) + tensorA *= 15.8 + tensorB *= 1.5 + tensorB *= -tensorD + tensorC *= 0.02 + tensorC /= tensorE + assignResult += tensorA + assignResult -= tensorB + assignResult += tensorC + assignResult += -39.4 + + assertTrue(expected.mutableBuffer.array() contentEquals result.mutableBuffer.array()) + assertTrue(expected.mutableBuffer.array() contentEquals assignResult.mutableBuffer.array()) + } + + @Test + fun testDot() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor11 = fromArray(intArrayOf(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor2 = fromArray(intArrayOf(3), doubleArrayOf(10.0, 20.0, 30.0)) + val tensor3 = fromArray(intArrayOf(1, 1, 3), doubleArrayOf(-1.0, -2.0, -3.0)) + + val res12 = tensor1.dot(tensor2) + assertTrue(res12.mutableBuffer.array() contentEquals doubleArrayOf(140.0, 320.0)) + assertTrue(res12.shape contentEquals intArrayOf(2)) + + val res32 = tensor3.dot(tensor2) + assertTrue(res32.mutableBuffer.array() contentEquals doubleArrayOf(-140.0)) + assertTrue(res32.shape contentEquals intArrayOf(1, 1)) + + val res22 = tensor2.dot(tensor2) + assertTrue(res22.mutableBuffer.array() contentEquals doubleArrayOf(1400.0)) + assertTrue(res22.shape contentEquals intArrayOf(1)) + + val res11 = tensor1.dot(tensor11) + assertTrue(res11.mutableBuffer.array() contentEquals doubleArrayOf(22.0, 28.0, 49.0, 64.0)) + assertTrue(res11.shape contentEquals intArrayOf(2, 2)) + } + + @Test + fun testDiagonalEmbedding() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(3), doubleArrayOf(10.0, 20.0, 30.0)) + val tensor2 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor3 = zeros(intArrayOf(2, 3, 4, 5)) + + assertTrue(diagonalEmbedding(tensor3, 0, 3, 4).shape contentEquals + intArrayOf(2, 3, 4, 5, 5)) + assertTrue(diagonalEmbedding(tensor3, 1, 3, 4).shape contentEquals + intArrayOf(2, 3, 4, 6, 6)) + assertTrue(diagonalEmbedding(tensor3, 2, 0, 3).shape contentEquals + intArrayOf(7, 2, 3, 7, 4)) + + val diagonal1 = diagonalEmbedding(tensor1, 0, 1, 0) + assertTrue(diagonal1.shape contentEquals intArrayOf(3, 3)) + assertTrue(diagonal1.mutableBuffer.array() contentEquals + doubleArrayOf(10.0, 0.0, 0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 30.0)) + + val diagonal1Offset = diagonalEmbedding(tensor1, 1, 1, 0) + assertTrue(diagonal1Offset.shape contentEquals intArrayOf(4, 4)) + assertTrue(diagonal1Offset.mutableBuffer.array() contentEquals + doubleArrayOf(0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 30.0, 0.0)) + + val diagonal2 = diagonalEmbedding(tensor2, 1, 0, 2) + assertTrue(diagonal2.shape contentEquals intArrayOf(4, 2, 4)) + assertTrue(diagonal2.mutableBuffer.array() contentEquals + doubleArrayOf( + 0.0, 1.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, + 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 5.0, 0.0, + 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 6.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)) + } + + @Test + fun testEq() = DoubleTensorAlgebra { + val tensor1 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor2 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) + val tensor3 = fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 5.0)) + + assertTrue(tensor1 eq tensor1) + assertTrue(tensor1 eq tensor2) + assertFalse(tensor1.eq(tensor3)) + + } +} diff --git a/kmath-viktor/build.gradle.kts b/kmath-viktor/build.gradle.kts index 0d853dea7..2e932b441 100644 --- a/kmath-viktor/build.gradle.kts +++ b/kmath-viktor/build.gradle.kts @@ -1,10 +1,3 @@ -/* - * 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. - */ - -import ru.mipt.npm.gradle.Maturity - plugins { kotlin("jvm") id("ru.mipt.npm.gradle.common") @@ -14,9 +7,9 @@ description = "Binding for https://github.com/JetBrains-Research/viktor" dependencies { api(project(":kmath-core")) - api("org.jetbrains.bio:viktor:1.0.1") + api("org.jetbrains.bio:viktor:1.1.0") } readme { - maturity = Maturity.DEVELOPMENT -} \ No newline at end of file + maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT +} diff --git a/license/COPYRIGHT.txt b/license/COPYRIGHT.txt deleted file mode 100644 index 7bf2faffd..000000000 --- a/license/COPYRIGHT.txt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2018-2021 KMath contributors. - * - * 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. - */ \ No newline at end of file diff --git a/license/LICENSE.txt b/license/LICENSE.txt deleted file mode 100644 index 84b106a07..000000000 --- a/license/LICENSE.txt +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2021 KMath contributors. - - 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. diff --git a/license/README.md b/license/README.md deleted file mode 100644 index cfef3de79..000000000 --- a/license/README.md +++ /dev/null @@ -1,63 +0,0 @@ -The Apache 2 license (given in full in LICENSE.txt) applies to all code in this repository, which is copyright by the -contributors of KMath. The following sections of the repository contain third-party code, to which different licenses -may apply: - -## KMath Libraries - -The following modules contain third-party code and are incorporated into the KMath Libraries: - -- Path: kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt - - License: Apache 2 ([cm](third_party/cm_license.txt)) - - Origin: Derived from Apache Commons Math, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt - - License: Apache 2 ([cm](third_party/cm_license.txt)) - - Origin: Derived from Apache Commons Math, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LoessInterpolator.kt - - License: Apache 2 ([cm](third_party/cm_license.txt)) - - Origin: Derived from Apache Commons Math, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt - - License: Apache 2 ([cm](third_party/cm_license.txt)) - - Origin: Derived from Apache Commons Math, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt - - License: Apache 2 ([numky](third_party/numky_license.txt)) - - Origin: Initial implementation was taken from Numky -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AliasMethodDiscreteSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/BoxMullerSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/GaussianSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/KempSmallMeanPoissonSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MarsagliaNormalizedGaussianSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/NormalizedGaussianSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/PoissonSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/ZigguratNormalizedGaussianSampler.kt - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation -- Path: - - License: Apache 2 ([cm](third_party/crng_license.txt)) - - Origin: Derived from Apache Commons RNG, (c) 2001-2020 The Apache Software Foundation - \ No newline at end of file diff --git a/license/third_party/cm_license.txt b/license/third_party/cm_license.txt deleted file mode 100644 index 6172c3fb2..000000000 --- a/license/third_party/cm_license.txt +++ /dev/null @@ -1,457 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - - -Apache Commons Math includes the following code provided to the ASF under the -Apache License 2.0: - - - The inverse error function implementation in the Erf class is based on CUDA - code developed by Mike Giles, Oxford-Man Institute of Quantitative Finance, - and published in GPU Computing Gems, volume 2, 2010 (grant received on - March 23th 2013) - - The LinearConstraint, LinearObjectiveFunction, LinearOptimizer, - RelationShip, SimplexSolver and SimplexTableau classes in package - org.apache.commons.math3.optimization.linear include software developed by - Benjamin McCann (http://www.benmccann.com) and distributed with - the following copyright: Copyright 2009 Google Inc. (grant received on - March 16th 2009) - - The class "org.apache.commons.math3.exception.util.LocalizedFormatsTest" which - is an adapted version of "OrekitMessagesTest" test class for the Orekit library - - The "org.apache.commons.math3.analysis.interpolation.HermiteInterpolator" - has been imported from the Orekit space flight dynamics library. - -=============================================================================== - - - -APACHE COMMONS MATH DERIVATIVE WORKS: - -The Apache commons-math library includes a number of subcomponents -whose implementation is derived from original sources written -in C or Fortran. License terms of the original sources -are reproduced below. - -=============================================================================== -For the lmder, lmpar and qrsolv Fortran routine from minpack and translated in -the LevenbergMarquardtOptimizer class in package -org.apache.commons.math3.optimization.general -Original source copyright and license statement: - -Minpack Copyright Notice (1999) University of Chicago. All rights reserved - -Redistribution and use in source and binary forms, with or -without modification, are permitted provided that the -following conditions are met: - -1. Redistributions of source code must retain the above -copyright notice, this list of conditions and the following -disclaimer. - -2. Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following -disclaimer in the documentation and/or other materials -provided with the distribution. - -3. The end-user documentation included with the -redistribution, if any, must include the following -acknowledgment: - - "This product includes software developed by the - University of Chicago, as Operator of Argonne National - Laboratory. - -Alternately, this acknowledgment may appear in the software -itself, if and wherever such third-party acknowledgments -normally appear. - -4. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" -WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE -UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND -THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE -OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY -OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR -USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF -THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) -DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION -UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL -BE CORRECTED. - -5. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT -HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF -ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, -INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF -ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF -PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER -SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT -(INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, -EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE -POSSIBILITY OF SUCH LOSS OR DAMAGES. -=============================================================================== - -Copyright and license statement for the odex Fortran routine developed by -E. Hairer and G. Wanner and translated in GraggBulirschStoerIntegrator class -in package org.apache.commons.math3.ode.nonstiff: - - -Copyright (c) 2004, Ernst Hairer - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=============================================================================== - -Copyright and license statement for the original Mersenne twister C -routines translated in MersenneTwister class in package -org.apache.commons.math3.random: - - Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -=============================================================================== - -The initial code for shuffling an array (originally in class -"org.apache.commons.math3.random.RandomDataGenerator", now replaced by -a method in class "org.apache.commons.math3.util.MathArrays") was -inspired from the algorithm description provided in -"Algorithms", by Ian Craw and John Pulham (University of Aberdeen 1999). -The textbook (containing a proof that the shuffle is uniformly random) is -available here: - http://citeseerx.ist.psu.edu/viewdoc/download;?doi=10.1.1.173.1898&rep=rep1&type=pdf - -=============================================================================== -License statement for the direction numbers in the resource files for Sobol sequences. - ------------------------------------------------------------------------------ -Licence pertaining to sobol.cc and the accompanying sets of direction numbers - ------------------------------------------------------------------------------ -Copyright (c) 2008, Frances Y. Kuo and Stephen Joe -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the names of the copyright holders nor the names of the - University of New South Wales and the University of Waikato - and its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=============================================================================== - -The initial commit of package "org.apache.commons.math3.ml.neuralnet" is -an adapted version of code developed in the context of the Data Processing -and Analysis Consortium (DPAC) of the "Gaia" project of the European Space -Agency (ESA). -=============================================================================== - -The initial commit of the class "org.apache.commons.math3.special.BesselJ" is -an adapted version of code translated from the netlib Fortran program, rjbesl -http://www.netlib.org/specfun/rjbesl by R.J. Cody at Argonne National -Laboratory (USA). There is no license or copyright statement included with the -original Fortran sources. -=============================================================================== - - -The BracketFinder (package org.apache.commons.math3.optimization.univariate) -and PowellOptimizer (package org.apache.commons.math3.optimization.general) -classes are based on the Python code in module "optimize.py" (version 0.5) -developed by Travis E. Oliphant for the SciPy library (http://www.scipy.org/) -Copyright © 2003-2009 SciPy Developers. - -SciPy license -Copyright © 2001, 2002 Enthought, Inc. -All rights reserved. - -Copyright © 2003-2013 SciPy Developers. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of Enthought nor the names of the SciPy Developers may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=============================================================================== diff --git a/license/third_party/crng_license.txt b/license/third_party/crng_license.txt deleted file mode 100644 index dec0e2a5c..000000000 --- a/license/third_party/crng_license.txt +++ /dev/null @@ -1,275 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - -================================================================================ - -Class "org.apache.commons.rng.core.source64.MersenneTwister64" contains -Java code partly ported from the reference implementation in C. -That source file contained the following notice: - - Copyright (C) 2004, Makoto Matsumoto and Takuji Nishimura, - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -================================================================================ - -Class "org.apache.commons.rng.core.source32.MersenneTwister" contains -Java code partly ported from the reference implementation in C. -That source file contained the following notice: - - Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -================================================================================ \ No newline at end of file diff --git a/license/third_party/numky_license.txt b/license/third_party/numky_license.txt deleted file mode 100644 index f49a4e16e..000000000 --- a/license/third_party/numky_license.txt +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index babad3672..27e74c902 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,27 +1,23 @@ -/* - * 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. - */ - pluginManagement { repositories { - mavenLocal() mavenCentral() gradlePluginPortal() maven("https://repo.kotlin.link") } - val toolsVersion = "0.9.5-dev-2" - val kotlinVersion = "1.5.0-RC" + val toolsVersion = "0.9.6" + val kotlinVersion = "1.5.0" plugins { + id("ru.mipt.npm.gradle.project") version toolsVersion + id("ru.mipt.npm.gradle.mpp") version toolsVersion + id("ru.mipt.npm.gradle.jvm") version toolsVersion kotlin("multiplatform") version kotlinVersion kotlin("jvm") version kotlinVersion kotlin("plugin.allopen") version kotlinVersion id("org.jetbrains.kotlinx.benchmark") version "0.3.0" - id("ru.mipt.npm.gradle.project") version toolsVersion - id("ru.mipt.npm.gradle.mpp") version toolsVersion - id("ru.mipt.npm.gradle.jvm") version toolsVersion + kotlin("jupyter.api") version "0.9.1-61" + } } @@ -44,5 +40,8 @@ include( ":kmath-ast", ":kmath-ejml", ":kmath-kotlingrad", - ":examples" + ":kmath-tensors", + ":kmath-jupyter", + ":examples", + ":benchmarks" )