MST compilation to WebAssembly with Binaryen, reorganize internal JS bindings, tests refactor #284

Merged
CommanderTvis merged 1 commits from commandertvis/binaryen into dev 2021-04-21 15:31:31 +03:00
66 changed files with 3451 additions and 692 deletions

View File

@ -76,6 +76,12 @@ KMath is a modular library. Different modules provide different features with di
<hr/> <hr/>
* ### [benchmarks](benchmarks)
>
>
> **Maturity**: EXPERIMENTAL
<hr/>
* ### [examples](examples) * ### [examples](examples)
> >
> >
@ -88,12 +94,10 @@ KMath is a modular library. Different modules provide different features with di
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
> >
> **Features:** > **Features:**
> - [expression-language](kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser > - [expression-language](kmath-ast/src/commonMain/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
> - [mst-jvm-codegen](kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler > - [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 > - [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
<hr/> <hr/>
@ -266,8 +270,8 @@ repositories {
} }
dependencies { dependencies {
api("space.kscience:kmath-core:0.3.0-dev-6") api("space.kscience:kmath-core:0.3.0-dev-7")
// api("space.kscience:kmath-core-jvm:0.3.0-dev-6") for jvm-specific version // api("space.kscience:kmath-core-jvm:0.3.0-dev-7") for jvm-specific version
} }
``` ```

View File

@ -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. * 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 { plugins {
kotlin("multiplatform") kotlin("multiplatform")
kotlin("plugin.allopen") kotlin("plugin.allopen")
@ -14,8 +12,6 @@ plugins {
allOpen.annotation("org.openjdk.jmh.annotations.State") allOpen.annotation("org.openjdk.jmh.annotations.State")
sourceSets.register("benchmarks") sourceSets.register("benchmarks")
repositories { repositories {
mavenCentral() mavenCentral()
jcenter() jcenter()
@ -77,43 +73,35 @@ benchmark {
register("jvm") register("jvm")
} }
fun kotlinx.benchmark.gradle.BenchmarkConfiguration.commonConfiguration() {
warmups = 1
iterations = 5
iterationTime = 1000
iterationTimeUnit = "ms"
}
configurations.register("buffer") { configurations.register("buffer") {
warmups = 1 // number of warmup iterations commonConfiguration()
iterations = 3 // number of iterations
iterationTime = 500 // time in seconds per iteration
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
include("BufferBenchmark") include("BufferBenchmark")
} }
configurations.register("dot") { configurations.register("dot") {
warmups = 1 // number of warmup iterations commonConfiguration()
iterations = 3 // number of iterations
iterationTime = 500 // time in seconds per iteration
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
include("DotBenchmark") include("DotBenchmark")
} }
configurations.register("expressions") { configurations.register("expressions") {
warmups = 1 // number of warmup iterations commonConfiguration()
iterations = 3 // number of iterations
iterationTime = 500 // time in seconds per iteration
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
include("ExpressionsInterpretersBenchmark") include("ExpressionsInterpretersBenchmark")
} }
configurations.register("matrixInverse") { configurations.register("matrixInverse") {
warmups = 1 // number of warmup iterations commonConfiguration()
iterations = 3 // number of iterations
iterationTime = 500 // time in seconds per iteration
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
include("MatrixInverseBenchmark") include("MatrixInverseBenchmark")
} }
configurations.register("bigInt") { configurations.register("bigInt") {
warmups = 1 // number of warmup iterations commonConfiguration()
iterations = 3 // number of iterations
iterationTime = 500 // time in seconds per iteration
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
include("BigIntBenchmark") include("BigIntBenchmark")
} }
} }
@ -121,7 +109,7 @@ benchmark {
// Fix kotlinx-benchmarks bug // Fix kotlinx-benchmarks bug
afterEvaluate { afterEvaluate {
val jvmBenchmarkJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) { val jvmBenchmarkJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) {
duplicatesStrategy = org.gradle.api.file.DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
} }
} }
@ -143,5 +131,5 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
readme { readme {
maturity = Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
} }

View File

@ -11,6 +11,7 @@ import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import space.kscience.kmath.asm.compileToExpression import space.kscience.kmath.asm.compileToExpression
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.misc.Symbol
import space.kscience.kmath.misc.symbol import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.operations.bindSymbol
@ -20,50 +21,22 @@ import kotlin.random.Random
@State(Scope.Benchmark) @State(Scope.Benchmark)
internal class ExpressionsInterpretersBenchmark { internal class ExpressionsInterpretersBenchmark {
@Benchmark @Benchmark
fun functionalExpression(blackhole: Blackhole) { fun functionalExpression(blackhole: Blackhole) = invokeAndSum(functional, blackhole)
val expr = algebra.expressionInField {
val x = bindSymbol(x)
x * const(2.0) + const(2.0) / x - const(16.0)
}
invokeAndSum(expr, blackhole)
}
@Benchmark @Benchmark
fun mstExpression(blackhole: Blackhole) { fun mstExpression(blackhole: Blackhole) = invokeAndSum(mst, blackhole)
val expr = MstField {
val x = bindSymbol(x)
x * 2.0 + number(2.0) / x - 16.0
}.toExpression(algebra)
invokeAndSum(expr, blackhole)
}
@Benchmark @Benchmark
fun asmExpression(blackhole: Blackhole) { fun asmExpression(blackhole: Blackhole) = invokeAndSum(asm, blackhole)
val expr = MstField {
val x = bindSymbol(x)
x * 2.0 + number(2.0) / x - 16.0
}.compileToExpression(algebra)
invokeAndSum(expr, blackhole)
}
@Benchmark @Benchmark
fun rawExpression(blackhole: Blackhole) { fun rawExpression(blackhole: Blackhole) = invokeAndSum(raw, blackhole)
val expr = Expression<Double> { args ->
val x = args.getValue(x)
x * 2.0 + 2.0 / x - 16.0
}
invokeAndSum(expr, blackhole)
}
private fun invokeAndSum(expr: Expression<Double>, blackhole: Blackhole) { private fun invokeAndSum(expr: Expression<Double>, blackhole: Blackhole) {
val random = Random(0) val random = Random(0)
var sum = 0.0 var sum = 0.0
repeat(1000000) { repeat(times) {
sum += expr(x to random.nextDouble()) sum += expr(x to random.nextDouble())
} }
@ -71,7 +44,23 @@ internal class ExpressionsInterpretersBenchmark {
} }
private companion object { private companion object {
private val algebra = DoubleField private val x: Symbol by symbol
private val x by symbol private val algebra: DoubleField = DoubleField
private const val times = 1_000_000
private val functional: Expression<Double> = 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<Double> = node.toExpression(DoubleField)
private val asm: Expression<Double> = node.compileToExpression(DoubleField)
private val raw: Expression<Double> = Expression { args ->
args.getValue(x) * 2.0 + 2.0 / args.getValue(x) - 16.0 / kotlin.math.sin(args.getValue(x))
}
} }
} }

View File

@ -3,9 +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. * 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 { plugins {
id("ru.mipt.npm.gradle.project") id("ru.mipt.npm.gradle.project")
} }
@ -17,8 +14,7 @@ allprojects {
maven("https://dl.bintray.com/egor-bogomolov/astminer/") maven("https://dl.bintray.com/egor-bogomolov/astminer/")
maven("https://dl.bintray.com/hotkeytlt/maven") maven("https://dl.bintray.com/hotkeytlt/maven")
maven("https://jitpack.io") maven("https://jitpack.io")
maven{ maven("http://logicrunch.research.it.uu.se/maven/") {
setUrl("http://logicrunch.research.it.uu.se/maven/")
isAllowInsecureProtocol = true isAllowInsecureProtocol = true
} }
mavenCentral() mavenCentral()
@ -32,7 +28,7 @@ subprojects {
if (name.startsWith("kmath")) apply<MavenPublishPlugin>() if (name.startsWith("kmath")) apply<MavenPublishPlugin>()
afterEvaluate { afterEvaluate {
tasks.withType<DokkaTask> { tasks.withType<org.jetbrains.dokka.gradle.DokkaTask> {
dokkaSourceSets.all { dokkaSourceSets.all {
val readmeFile = File(this@subprojects.projectDir, "./README.md") val readmeFile = File(this@subprojects.projectDir, "./README.md")
if (readmeFile.exists()) if (readmeFile.exists())
@ -42,7 +38,7 @@ subprojects {
"http://ejml.org/javadoc/", "http://ejml.org/javadoc/",
"https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/", "https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/",
"https://deeplearning4j.org/api/latest/" "https://deeplearning4j.org/api/latest/"
).map { URL("${it}package-list") to URL(it) }.forEach { (a, b) -> ).map { java.net.URL("${it}package-list") to java.net.URL(it) }.forEach { (a, b) ->
externalDocumentationLink { externalDocumentationLink {
packageListUrl.set(a) packageListUrl.set(a)
url.set(b) url.set(b)

View File

@ -3,9 +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. * 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 { plugins {
kotlin("jvm") kotlin("jvm")
} }
@ -64,7 +61,7 @@ kotlin.sourceSets.all {
} }
} }
tasks.withType<KotlinCompile> { tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions{ kotlinOptions{
jvmTarget = "11" jvmTarget = "11"
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all"
@ -72,5 +69,5 @@ tasks.withType<KotlinCompile> {
} }
readme { readme {
maturity = Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
} }

View File

@ -25,5 +25,5 @@ fun main() {
val expectedDerivative = "2*x-4".parseMath().compileToExpression(DoubleField) 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))
} }

View File

@ -2,17 +2,15 @@
Abstract syntax tree expression representation and related optimizations. Abstract syntax tree expression representation and related optimizations.
- [expression-language](src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser - [expression-language](src/commonMain/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
- [mst-jvm-codegen](src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler - [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 - [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: ## 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-7`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -23,7 +21,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-ast:0.3.0-dev-6' implementation 'space.kscience:kmath-ast:0.3.0-dev-7'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -35,7 +33,7 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-ast:0.3.0-dev-6") implementation("space.kscience:kmath-ast:0.3.0-dev-7")
} }
``` ```
@ -49,10 +47,10 @@ a special implementation of `Expression<T>` with implemented `invoke` function.
For example, the following builder: For example, the following builder:
```kotlin ```kotlin
DoubleField.mstInField { symbol("x") + 2 }.compile() 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 ```java
package space.kscience.kmath.asm.generated; package space.kscience.kmath.asm.generated;
@ -77,15 +75,6 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
``` ```
### 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 #### Known issues
- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid - The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid
@ -97,7 +86,7 @@ DoubleField.expression("x+2".parseMath())
A similar feature is also available on JS. A similar feature is also available on JS.
```kotlin ```kotlin
DoubleField.mstInField { symbol("x") + 2 }.compile() MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
``` ```
The code above returns expression implemented with such a JS function: The code above returns expression implemented with such a JS function:
@ -108,13 +97,32 @@ var executable = function (constants, arguments) {
}; };
``` ```
```kotlin
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 #### 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 ## 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: Example usage:
@ -135,7 +143,7 @@ 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) ![](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)
@ -145,5 +153,5 @@ Result MathML (embedding MathML is not allowed by GitHub Markdown):
<mrow><msup><mrow><mi>e</mi></mrow><mrow><msqrt><mi>x</mi></msqrt></mrow></msup><mo>-</mo><mfrac><mrow><mfrac><mrow><msup><mrow><mo>sin</mo></mrow><mrow><mo>-</mo><mn>1</mn></mrow></msup><mspace width="0.167em"></mspace><mfenced open="(" close=")" separators=""><mn>2</mn><mspace width="0.167em"></mspace><mi>x</mi></mfenced></mrow><mrow><mn>2</mn><mo>&times;</mo><msup><mrow><mn>10</mn></mrow><mrow><mn>10</mn></mrow></msup><mo>+</mo><msup><mrow><mi>x</mi></mrow><mrow><mn>3</mn></mrow></msup></mrow></mfrac></mrow><mrow><mo>-</mo><mn>12</mn></mrow></mfrac></mrow> <mrow><msup><mrow><mi>e</mi></mrow><mrow><msqrt><mi>x</mi></msqrt></mrow></msup><mo>-</mo><mfrac><mrow><mfrac><mrow><msup><mrow><mo>sin</mo></mrow><mrow><mo>-</mo><mn>1</mn></mrow></msup><mspace width="0.167em"></mspace><mfenced open="(" close=")" separators=""><mn>2</mn><mspace width="0.167em"></mspace><mi>x</mi></mfenced></mrow><mrow><mn>2</mn><mo>&times;</mo><msup><mrow><mn>10</mn></mrow><mrow><mn>10</mn></mrow></msup><mo>+</mo><msup><mrow><mi>x</mi></mrow><mrow><mn>3</mn></mrow></msup></mrow></mfrac></mrow><mrow><mo>-</mo><mn>12</mn></mrow></mfrac></mrow>
``` ```
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). (see API reference).

View File

@ -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. * 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 { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("ru.mipt.npm.gradle.common") id("ru.mipt.npm.gradle.common")
@ -41,6 +39,9 @@ kotlin.sourceSets {
jsMain { jsMain {
dependencies { dependencies {
implementation(npm("astring", "1.7.4")) implementation(npm("astring", "1.7.4"))
implementation(npm("binaryen", "100.0"))
implementation(npm("js-base64", "3.6.0"))
implementation(npm("webassembly", "0.11.0"))
} }
} }
@ -58,7 +59,7 @@ tasks.dokkaHtml {
} }
readme { readme {
maturity = Maturity.PROTOTYPE maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE
propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
feature( feature(

View File

@ -16,7 +16,7 @@ a special implementation of `Expression<T>` with implemented `invoke` function.
For example, the following builder: For example, the following builder:
```kotlin ```kotlin
DoubleField.mstInField { symbol("x") + 2 }.compile() 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:
@ -44,15 +44,6 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
``` ```
### 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 #### Known issues
- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid - The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid
@ -64,7 +55,7 @@ DoubleField.expression("x+2".parseMath())
A similar feature is also available on JS. A similar feature is also available on JS.
```kotlin ```kotlin
DoubleField.mstInField { symbol("x") + 2 }.compile() MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
``` ```
The code above returns expression implemented with such a JS function: The code above returns expression implemented with such a JS function:
@ -75,13 +66,32 @@ var executable = function (constants, arguments) {
}; };
``` ```
```kotlin
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 #### 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 ## 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: Example usage:
@ -102,7 +112,7 @@ 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) ![](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)
@ -112,5 +122,5 @@ Result MathML (embedding MathML is not allowed by GitHub Markdown):
<mrow><msup><mrow><mi>e</mi></mrow><mrow><msqrt><mi>x</mi></msqrt></mrow></msup><mo>-</mo><mfrac><mrow><mfrac><mrow><msup><mrow><mo>sin</mo></mrow><mrow><mo>-</mo><mn>1</mn></mrow></msup><mspace width="0.167em"></mspace><mfenced open="(" close=")" separators=""><mn>2</mn><mspace width="0.167em"></mspace><mi>x</mi></mfenced></mrow><mrow><mn>2</mn><mo>&times;</mo><msup><mrow><mn>10</mn></mrow><mrow><mn>10</mn></mrow></msup><mo>+</mo><msup><mrow><mi>x</mi></mrow><mrow><mn>3</mn></mrow></msup></mrow></mfrac></mrow><mrow><mo>-</mo><mn>12</mn></mrow></mfrac></mrow> <mrow><msup><mrow><mi>e</mi></mrow><mrow><msqrt><mi>x</mi></msqrt></mrow></msup><mo>-</mo><mfrac><mrow><mfrac><mrow><msup><mrow><mo>sin</mo></mrow><mrow><mo>-</mo><mn>1</mn></mrow></msup><mspace width="0.167em"></mspace><mfenced open="(" close=")" separators=""><mn>2</mn><mspace width="0.167em"></mspace><mi>x</mi></mfenced></mrow><mrow><mn>2</mn><mo>&times;</mo><msup><mrow><mn>10</mn></mrow><mrow><mn>10</mn></mrow></msup><mo>+</mo><msup><mrow><mi>x</mi></mrow><mrow><mn>3</mn></mrow></msup></mrow></mfrac></mrow><mrow><mo>-</mo><mn>12</mn></mrow></mfrac></mrow>
``` ```
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). (see API reference).

View File

@ -1,27 +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.kscisnce.kmath.ast
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.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke
import kotlin.test.Test
class InterpretTest {
@Test
fun interpretation(){
val expr = MstField {
val x = bindSymbol(x)
x * 2.0 + number(2.0) / x - 16.0
}.toExpression(DoubleField)
expr(x to 2.2)
}
}

View File

@ -1,19 +1,12 @@
/* package space.kscisnce.kmath.ast
* 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.ast.parseMath
import space.kscience.kmath.expressions.evaluate import space.kscience.kmath.expressions.evaluate
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Field
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class ParserPrecedenceTest { internal class ParserPrecedenceTest {
private val f: Field<Double> = DoubleField
@Test @Test
fun test1(): Unit = assertEquals(6.0, f.evaluate("2*2+2".parseMath())) fun test1(): Unit = assertEquals(6.0, f.evaluate("2*2+2".parseMath()))
@ -37,4 +30,8 @@ internal class ParserPrecedenceTest {
@Test @Test
fun test8(): Unit = assertEquals(18.0, f.evaluate("2*2^3+2".parseMath())) fun test8(): Unit = assertEquals(18.0, f.evaluate("2*2^3+2".parseMath()))
private companion object {
private val f = DoubleField
}
} }

View File

@ -1,37 +1,24 @@
/* package space.kscisnce.kmath.ast
* 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.ast.parseMath
import space.kscience.kmath.complex.Complex import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.ComplexField import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.expressions.MstField
import space.kscience.kmath.expressions.evaluate import space.kscience.kmath.expressions.evaluate
import space.kscience.kmath.expressions.interpret
import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.Algebra
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class ParserTest { internal class ParserTest {
@Test @Test
fun `evaluate MST`() { fun evaluateParsedMst() {
val mst = "2+2*(2+2)".parseMath() val mst = "2+2*(2+2)".parseMath()
val res = ComplexField.evaluate(mst) val res = ComplexField.evaluate(mst)
assertEquals(Complex(10.0, 0.0), res) assertEquals(Complex(10.0, 0.0), res)
} }
@Test @Test
fun `evaluate MSTExpression`() { fun evaluateMstSymbol() {
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`() {
val mst = "i".parseMath() val mst = "i".parseMath()
val res = ComplexField.evaluate(mst) val res = ComplexField.evaluate(mst)
assertEquals(ComplexField.i, res) assertEquals(ComplexField.i, res)
@ -39,14 +26,14 @@ internal class ParserTest {
@Test @Test
fun `evaluate MST with unary function`() { fun evaluateMstUnary() {
val mst = "sin(0)".parseMath() val mst = "sin(0)".parseMath()
val res = DoubleField.evaluate(mst) val res = DoubleField.evaluate(mst)
assertEquals(0.0, res) assertEquals(0.0, res)
} }
@Test @Test
fun `evaluate MST with binary function`() { fun evaluateMstBinary() {
val magicalAlgebra = object : Algebra<String> { val magicalAlgebra = object : Algebra<String> {
override fun bindSymbolOrNull(value: String): String = value override fun bindSymbolOrNull(value: String): String = value

View File

@ -6,11 +6,11 @@
package space.kscience.kmath.estree package space.kscience.kmath.estree
import space.kscience.kmath.estree.internal.ESTreeBuilder 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.Expression
import space.kscience.kmath.expressions.MST import space.kscience.kmath.expressions.MST
import space.kscience.kmath.expressions.MST.* import space.kscience.kmath.expressions.MST.*
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.internal.estree.BaseExpression
import space.kscience.kmath.misc.Symbol import space.kscience.kmath.misc.Symbol
import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.Algebra
import space.kscience.kmath.operations.NumericAlgebra import space.kscience.kmath.operations.NumericAlgebra

View File

@ -5,9 +5,14 @@
package space.kscience.kmath.estree.internal 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.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 import space.kscience.kmath.misc.Symbol
internal class ESTreeBuilder<T>(val bodyCallback: ESTreeBuilder<T>.() -> BaseExpression) { internal class ESTreeBuilder<T>(val bodyCallback: ESTreeBuilder<T>.() -> BaseExpression) {

View File

@ -1,12 +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.internal.stream
import space.kscience.kmath.estree.internal.emitter.Emitter
internal open external class Stream : Emitter {
open fun pipe(dest: Any, options: Any): Any
}

View File

@ -6,9 +6,9 @@
@file:JsModule("astring") @file:JsModule("astring")
@file:JsNonModule @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 { internal external interface Options {
var indent: String? var indent: String?

View File

@ -0,0 +1,3 @@
package space.kscience.kmath.internal.astring
internal typealias Generator = Any

View File

@ -0,0 +1,49 @@
@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

View File

@ -0,0 +1,11 @@
@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

View File

@ -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. * 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 { internal open external class Emitter {
constructor(obj: Any) constructor(obj: Any)

View File

@ -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. * 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 { internal fun Program(sourceType: String, vararg body: dynamic) = object : Program {
override var type = "Program" override var type = "Program"

View File

@ -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. * 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 import kotlin.js.RegExp

View File

@ -0,0 +1,7 @@
package space.kscience.kmath.internal.stream
import space.kscience.kmath.internal.emitter.Emitter
internal open external class Stream : Emitter {
open fun pipe(dest: Any, options: Any): Any
}

View File

@ -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. * 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<TYield> { internal external interface IteratorYieldResult<TYield> {
var done: Boolean? var done: Boolean?

View File

@ -5,7 +5,7 @@
@file:Suppress("UNUSED_TYPEALIAS_PARAMETER", "DEPRECATION") @file:Suppress("UNUSED_TYPEALIAS_PARAMETER", "DEPRECATION")
package space.kscience.kmath.estree.internal.tsstdlib package space.kscience.kmath.internal.tsstdlib
import kotlin.js.RegExp import kotlin.js.RegExp
@ -38,6 +38,8 @@ internal external interface RegExpConstructor {
var lastMatch: String var lastMatch: String
} }
internal typealias Record<K, T> = Any
internal external interface ConcatArray<T> { internal external interface ConcatArray<T> {
var length: Number var length: Number
@ -85,3 +87,10 @@ internal external interface ArrayLike<T> {
} }
internal typealias Extract<T, U> = Any internal typealias Extract<T, U> = Any
internal external interface PromiseLike<T> {
fun then(
onfulfilled: ((value: T) -> Any?)? = definedExternally,
onrejected: ((reason: Any) -> Any?)? = definedExternally
): PromiseLike<dynamic /* TResult1 | TResult2 */>
}

View File

@ -0,0 +1,231 @@
@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<ArrayBuffer>
fun exports(moduleObject: Module): Array<ModuleExportDescriptor>
fun imports(moduleObject: Module): Array<ModuleImportDescriptor>
}
}
@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<Module>
internal external fun compile(bytes: ArrayBuffer): Promise<Module>
internal external fun compileStreaming(source: Response): Promise<Module>
internal external fun compileStreaming(source: Promise<Response>): Promise<Module>
internal external fun instantiate(
bytes: ArrayBufferView,
importObject: Imports = definedExternally,
): Promise<WebAssemblyInstantiatedSource>
internal external fun instantiate(bytes: ArrayBufferView): Promise<WebAssemblyInstantiatedSource>
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<Instance>
internal external fun instantiate(moduleObject: Module): Promise<Instance>
internal external fun instantiateStreaming(
response: Response,
importObject: Imports = definedExternally,
): Promise<WebAssemblyInstantiatedSource>
internal external fun instantiateStreaming(response: Response): Promise<WebAssemblyInstantiatedSource>
internal external fun instantiateStreaming(
response: PromiseLike<Response>,
importObject: Imports = definedExternally,
): Promise<WebAssemblyInstantiatedSource>
internal external fun instantiateStreaming(response: PromiseLike<Response>): Promise<WebAssemblyInstantiatedSource>
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<ArrayBuffer>
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<Module>
internal external interface ResultObject {
var module: Module
var instance: Instance
}
internal external fun instantiate(
bufferSource: Uint8Array,
importObject: Any = definedExternally,
): Promise<ResultObject>
internal external fun instantiate(bufferSource: Uint8Array): Promise<ResultObject>
internal external fun validate(bufferSource: Uint8Array): Boolean

View File

@ -0,0 +1,22 @@
@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<String, dynamic /* Function<*> | Global | Memory | Table */>
internal typealias ModuleImports = Record<String, dynamic /* Function<*> | Global | Memory | Table | Number */>
internal typealias Imports = Record<String, ModuleImports>
internal typealias CompileError1 = Error
internal typealias LinkError1 = Error
internal typealias RuntimeError1 = Error

View File

@ -0,0 +1,155 @@
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<T>(
val binaryenType: Type,
val algebra: Algebra<T>,
val target: MST,
) where T : Number {
val keys: MutableList<String> = mutableListOf()
lateinit var ctx: BinaryenModule
open fun visitSymbolic(mst: 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<T> { args ->
val params = symbols.map(args::getValue).toTypedArray()
spreader(i.exports.asDynamic().executable, params) as T
}
}
}
internal class DoubleWasmBuilder(target: MST) : WasmBuilder<Double>(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<Int>(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)
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,77 @@
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.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
*/
public fun DoubleField.expression(mst: MST): Expression<Double> =
DoubleWasmBuilder(mst).instance
/**
* Compiles an [MST] to WASM in the context of integers.
*
* @author Iaroslav Postovalov
*/
public fun IntRing.expression(mst: MST): Expression<Int> =
IntWasmBuilder(mst).instance
/**
* Create a compiled expression with given [MST] and given [algebra].
*
* @author Iaroslav Postovalov
*/
public fun MST.compileToExpression(algebra: IntRing): Expression<Int> = compileWith(algebra)
/**
* Compile given MST to expression and evaluate it against [arguments].
*
* @author Iaroslav Postovalov
*/
public fun MST.compile(algebra: IntRing, arguments: Map<Symbol, Int>): Int =
compileToExpression(algebra).invoke(arguments)
/**
* Compile given MST to expression and evaluate it against [arguments].
*
* @author Iaroslav Postovalov
*/
public fun MST.compile(algebra: IntRing, vararg arguments: Pair<Symbol, Int>): Int =
compileToExpression(algebra)(*arguments)
/**
* Create a compiled expression with given [MST] and given [algebra].
*
* @author Iaroslav Postovalov
*/
public fun MST.compileToExpression(algebra: DoubleField): Expression<Double> = compileWith(algebra)
/**
* Compile given MST to expression and evaluate it against [arguments].
*
* @author Iaroslav Postovalov
*/
public fun MST.compile(algebra: DoubleField, arguments: Map<Symbol, Double>): Double =
compileToExpression(algebra).invoke(arguments)
/**
* Compile given MST to expression and evaluate it against [arguments].
*
* @author Iaroslav Postovalov
*/
public fun MST.compile(algebra: DoubleField, vararg arguments: Pair<Symbol, Double>): Double =
compileToExpression(algebra).invoke(*arguments)

View File

@ -0,0 +1,67 @@
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<Double> = 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<Double> { args ->
args.getValue(x) * 2.0 + 2.0 / args.getValue(x) - 16.0 / sin(args.getValue(x))
}
}
private fun invokeAndSum(name: String, expr: Expression<Double>) {
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)
}

View File

@ -8,18 +8,17 @@ package space.kscience.kmath.estree
import space.kscience.kmath.complex.ComplexField import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.toComplex import space.kscience.kmath.complex.toComplex
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.misc.Symbol import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.ByteRing import space.kscience.kmath.operations.ByteRing
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class TestESTreeConsistencyWithInterpreter { internal class TestESTreeConsistencyWithInterpreter {
@Test @Test
fun mstSpace() { fun mstSpace() {
val mst = MstGroup { val mst = MstGroup {
binaryOperationFunction("+")( binaryOperationFunction("+")(
unaryOperationFunction("+")( unaryOperationFunction("+")(
@ -30,12 +29,12 @@ internal class TestESTreeConsistencyWithInterpreter {
), ),
number(1) number(1)
) + bindSymbol("x") + zero ) + bindSymbol(x) + zero
} }
assertEquals( assertEquals(
mst.interpret(MstGroup, Symbol.x to MST.Numeric(2)), mst.interpret(MstGroup, x to MST.Numeric(2)),
mst.compile(MstGroup, Symbol.x to MST.Numeric(2)) mst.compile(MstGroup, x to MST.Numeric(2))
) )
} }
@ -44,7 +43,7 @@ internal class TestESTreeConsistencyWithInterpreter {
val mst = MstRing { val mst = MstRing {
binaryOperationFunction("+")( binaryOperationFunction("+")(
unaryOperationFunction("+")( unaryOperationFunction("+")(
(bindSymbol("x") - (2.toByte() + (scale( (bindSymbol(x) - (2.toByte() + (scale(
add(number(1), number(1)), add(number(1), number(1)),
2.0 2.0
) + 1.toByte()))) * 3.0 - 1.toByte() ) + 1.toByte()))) * 3.0 - 1.toByte()
@ -55,24 +54,24 @@ internal class TestESTreeConsistencyWithInterpreter {
} }
assertEquals( assertEquals(
mst.interpret(ByteRing, Symbol.x to 3.toByte()), mst.interpret(ByteRing, x to 3.toByte()),
mst.compile(ByteRing, Symbol.x to 3.toByte()) mst.compile(ByteRing, x to 3.toByte())
) )
} }
@Test @Test
fun realField() { fun doubleField() {
val mst = MstField { val mst = MstField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( +(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 (3.0 - (bindSymbol(x) + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1), + number(1),
number(1) / 2 + number(2.0) * one number(1) / 2 + number(2.0) * one
) + zero ) + zero
} }
assertEquals( assertEquals(
mst.interpret(DoubleField, Symbol.x to 2.0), mst.interpret(DoubleField, x to 2.0),
mst.compile(DoubleField, Symbol.x to 2.0) mst.compile(DoubleField, x to 2.0)
) )
} }
@ -80,15 +79,19 @@ internal class TestESTreeConsistencyWithInterpreter {
fun complexField() { fun complexField() {
val mst = MstField { val mst = MstField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( +(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 (3.0 - (bindSymbol(x) + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1), + number(1),
number(1) / 2 + number(2.0) * one number(1) / 2 + number(2.0) * one
) + zero ) + zero
} }
assertEquals( assertEquals(
mst.interpret(ComplexField, Symbol.x to 2.0.toComplex()), mst.interpret(ComplexField, x to 2.0.toComplex()),
mst.compile(ComplexField, Symbol.x to 2.0.toComplex()) mst.compile(ComplexField, x to 2.0.toComplex()),
) )
} }
private companion object {
private val x by symbol
}
} }

View File

@ -5,43 +5,38 @@
package space.kscience.kmath.estree package space.kscience.kmath.estree
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.expressions.invoke
import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.random.Random
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class TestESTreeOperationsSupport { internal class TestESTreeOperationsSupport {
@Test @Test
fun testUnaryOperationInvocation() { fun testUnaryOperationInvocation() {
val expression = MstExtendedField { -bindSymbol("x") }.compileToExpression(DoubleField) val expression = MstGroup { -bindSymbol(x) }.compileToExpression(DoubleField)
val res = expression("x" to 2.0) val res = expression(x to 2.0)
assertEquals(-2.0, res) assertEquals(-2.0, res)
} }
@Test @Test
fun testBinaryOperationInvocation() { fun testBinaryOperationInvocation() {
val expression = MstExtendedField { -bindSymbol("x") + number(1.0) }.compileToExpression(DoubleField) val expression = MstGroup { -bindSymbol(x) + number(1.0) }.compileToExpression(DoubleField)
val res = expression("x" to 2.0) val res = expression(x to 2.0)
assertEquals(-1.0, res) assertEquals(-1.0, res)
} }
@Test @Test
fun testConstProductInvocation() { fun testConstProductInvocation() {
val res = MstExtendedField { bindSymbol("x") * 2 }.compileToExpression(DoubleField)("x" to 2.0) val res = MstField { bindSymbol(x) * 2 }.compileToExpression(DoubleField)(x to 2.0)
assertEquals(4.0, res) assertEquals(4.0, res)
} }
@Test private companion object {
fun testMultipleCalls() { private val x by symbol
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)
} }
} }

View File

@ -7,7 +7,9 @@ package space.kscience.kmath.estree
import space.kscience.kmath.expressions.MstExtendedField import space.kscience.kmath.expressions.MstExtendedField
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -15,55 +17,60 @@ import kotlin.test.assertEquals
internal class TestESTreeSpecialization { internal class TestESTreeSpecialization {
@Test @Test
fun testUnaryPlus() { fun testUnaryPlus() {
val expr = MstExtendedField { unaryOperationFunction("+")(bindSymbol("x")) }.compileToExpression(DoubleField) val expr = MstExtendedField { unaryOperationFunction("+")(bindSymbol(x)) }.compileToExpression(DoubleField)
assertEquals(2.0, expr("x" to 2.0)) assertEquals(2.0, expr(x to 2.0))
} }
@Test @Test
fun testUnaryMinus() { fun testUnaryMinus() {
val expr = MstExtendedField { unaryOperationFunction("-")(bindSymbol("x")) }.compileToExpression(DoubleField) val expr = MstExtendedField { unaryOperationFunction("-")(bindSymbol(x)) }.compileToExpression(DoubleField)
assertEquals(-2.0, expr("x" to 2.0)) assertEquals(-2.0, expr(x to 2.0))
} }
@Test @Test
fun testAdd() { fun testAdd() {
val expr = MstExtendedField { val expr = MstExtendedField {
binaryOperationFunction("+")(bindSymbol("x"), binaryOperationFunction("+")(
bindSymbol("x")) bindSymbol(x),
bindSymbol(x),
)
}.compileToExpression(DoubleField) }.compileToExpression(DoubleField)
assertEquals(4.0, expr("x" to 2.0)) assertEquals(4.0, expr(x to 2.0))
} }
@Test @Test
fun testSine() { fun testSine() {
val expr = MstExtendedField { unaryOperationFunction("sin")(bindSymbol("x")) }.compileToExpression(DoubleField) val expr = MstExtendedField { unaryOperationFunction("sin")(bindSymbol(x)) }.compileToExpression(DoubleField)
assertEquals(0.0, expr("x" to 0.0)) assertEquals(0.0, expr(x to 0.0))
} }
@Test @Test
fun testMinus() { fun testSubtract() {
val expr = MstExtendedField { val expr = MstExtendedField {
binaryOperationFunction("-")(bindSymbol("x"), binaryOperationFunction("-")(bindSymbol(x),
bindSymbol("x")) bindSymbol(x))
}.compileToExpression(DoubleField) }.compileToExpression(DoubleField)
assertEquals(0.0, expr("x" to 2.0)) assertEquals(0.0, expr(x to 2.0))
} }
@Test @Test
fun testDivide() { fun testDivide() {
val expr = MstExtendedField { val expr = MstExtendedField {
binaryOperationFunction("/")(bindSymbol("x"), binaryOperationFunction("/")(bindSymbol(x), bindSymbol(x))
bindSymbol("x"))
}.compileToExpression(DoubleField) }.compileToExpression(DoubleField)
assertEquals(1.0, expr("x" to 2.0)) assertEquals(1.0, expr(x to 2.0))
} }
@Test @Test
fun testPower() { fun testPower() {
val expr = MstExtendedField { val expr = MstExtendedField {
binaryOperationFunction("pow")(bindSymbol("x"), number(2)) binaryOperationFunction("pow")(bindSymbol(x), number(2))
}.compileToExpression(DoubleField) }.compileToExpression(DoubleField)
assertEquals(4.0, expr("x" to 2.0)) assertEquals(4.0, expr(x to 2.0))
}
private companion object {
private val x by symbol
} }
} }

View File

@ -7,7 +7,9 @@ package space.kscience.kmath.estree
import space.kscience.kmath.expressions.MstRing import space.kscience.kmath.expressions.MstRing
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.ByteRing import space.kscience.kmath.operations.ByteRing
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -16,13 +18,17 @@ import kotlin.test.assertFailsWith
internal class TestESTreeVariables { internal class TestESTreeVariables {
@Test @Test
fun testVariable() { fun testVariable() {
val expr = MstRing{ bindSymbol("x") }.compileToExpression(ByteRing) val expr = MstRing { bindSymbol(x) }.compileToExpression(ByteRing)
assertEquals(1.toByte(), expr("x" to 1.toByte())) assertEquals(1.toByte(), expr(x to 1.toByte()))
} }
@Test @Test
fun testUndefinedVariableFails() { fun testUndefinedVariableFails() {
val expr = MstRing { bindSymbol("x") }.compileToExpression(ByteRing) val expr = MstRing { bindSymbol(x) }.compileToExpression(ByteRing)
assertFailsWith<NoSuchElementException> { expr() } assertFailsWith<NoSuchElementException> { expr() }
} }
private companion object {
private val x by symbol
}
} }

View File

@ -0,0 +1,60 @@
/*
* 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.MstField
import space.kscience.kmath.expressions.MstRing
import space.kscience.kmath.expressions.interpret
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 TestWasmConsistencyWithInterpreter {
@Test
fun intRing() {
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() {
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)
)
}
private companion object {
private val x by symbol
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.MstField
import space.kscience.kmath.expressions.MstGroup
import space.kscience.kmath.expressions.invoke
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
import kotlin.test.assertEquals
internal class TestWasmOperationsSupport {
@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)
}
private companion object {
private val x by symbol
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.invoke
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
import kotlin.test.assertEquals
internal class TestWasmSpecialization {
@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 testSubtract() {
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))
}
private companion object {
private val x by symbol
}
}

View File

@ -0,0 +1,48 @@
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
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.MstRing
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.misc.symbol
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 TestWasmVariables {
@Test
fun testVariable() {
val expr = MstRing { bindSymbol(x) }.compileToExpression(IntRing)
assertEquals(1, expr(x to 1))
}
@Test
fun testUndefinedVariableFails() {
val expr = MstRing { bindSymbol(x) }.compileToExpression(IntRing)
assertFailsWith<NoSuchElementException> { expr() }
}
private companion object {
private val x by symbol
}
}

View File

@ -8,18 +8,17 @@ package space.kscience.kmath.asm
import space.kscience.kmath.complex.ComplexField import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.toComplex import space.kscience.kmath.complex.toComplex
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.misc.Symbol.Companion.x import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.ByteRing import space.kscience.kmath.operations.ByteRing
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class TestAsmConsistencyWithInterpreter { internal class TestAsmConsistencyWithInterpreter {
@Test @Test
fun mstSpace() { fun mstSpace() {
val mst = MstGroup { val mst = MstGroup {
binaryOperationFunction("+")( binaryOperationFunction("+")(
unaryOperationFunction("+")( unaryOperationFunction("+")(
@ -30,7 +29,7 @@ internal class TestAsmConsistencyWithInterpreter {
), ),
number(1) number(1)
) + bindSymbol("x") + zero ) + bindSymbol(x) + zero
} }
assertEquals( assertEquals(
@ -44,7 +43,7 @@ internal class TestAsmConsistencyWithInterpreter {
val mst = MstRing { val mst = MstRing {
binaryOperationFunction("+")( binaryOperationFunction("+")(
unaryOperationFunction("+")( unaryOperationFunction("+")(
(bindSymbol("x") - (2.toByte() + (scale( (bindSymbol(x) - (2.toByte() + (scale(
add(number(1), number(1)), add(number(1), number(1)),
2.0 2.0
) + 1.toByte()))) * 3.0 - 1.toByte() ) + 1.toByte()))) * 3.0 - 1.toByte()
@ -61,10 +60,10 @@ internal class TestAsmConsistencyWithInterpreter {
} }
@Test @Test
fun realField() { fun doubleField() {
val mst = MstField { val mst = MstField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( +(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 (3.0 - (bindSymbol(x) + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1), + number(1),
number(1) / 2 + number(2.0) * one number(1) / 2 + number(2.0) * one
) + zero ) + zero
@ -80,7 +79,7 @@ internal class TestAsmConsistencyWithInterpreter {
fun complexField() { fun complexField() {
val mst = MstField { val mst = MstField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( +(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 (3.0 - (bindSymbol(x) + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1), + number(1),
number(1) / 2 + number(2.0) * one number(1) / 2 + number(2.0) * one
) + zero ) + zero
@ -91,4 +90,8 @@ internal class TestAsmConsistencyWithInterpreter {
mst.compile(ComplexField, x to 2.0.toComplex()) mst.compile(ComplexField, x to 2.0.toComplex())
) )
} }
private companion object {
private val x by symbol
}
} }

View File

@ -5,45 +5,38 @@
package space.kscience.kmath.asm package space.kscience.kmath.asm
import space.kscience.kmath.expressions.MstExtendedField
import space.kscience.kmath.expressions.MstField import space.kscience.kmath.expressions.MstField
import space.kscience.kmath.expressions.MstGroup import space.kscience.kmath.expressions.MstGroup
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.random.Random
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class TestAsmOperationsSupport { internal class TestAsmOperationsSupport {
@Test @Test
fun testUnaryOperationInvocation() { fun testUnaryOperationInvocation() {
val expression = MstGroup { -bindSymbol("x") }.compileToExpression(DoubleField) val expression = MstGroup { -bindSymbol(x) }.compileToExpression(DoubleField)
val res = expression("x" to 2.0) val res = expression(x to 2.0)
assertEquals(-2.0, res) assertEquals(-2.0, res)
} }
@Test @Test
fun testBinaryOperationInvocation() { fun testBinaryOperationInvocation() {
val expression = MstGroup { -bindSymbol("x") + number(1.0) }.compileToExpression(DoubleField) val expression = MstGroup { -bindSymbol(x) + number(1.0) }.compileToExpression(DoubleField)
val res = expression("x" to 2.0) val res = expression(x to 2.0)
assertEquals(-1.0, res) assertEquals(-1.0, res)
} }
@Test @Test
fun testConstProductInvocation() { fun testConstProductInvocation() {
val res = MstField { bindSymbol("x") * 2 }.compileToExpression(DoubleField)("x" to 2.0) val res = MstField { bindSymbol(x) * 2 }.compileToExpression(DoubleField)(x to 2.0)
assertEquals(4.0, res) assertEquals(4.0, res)
} }
@Test private companion object {
fun testMultipleCalls() { private val x by symbol
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)
} }
} }

View File

@ -7,7 +7,9 @@ package space.kscience.kmath.asm
import space.kscience.kmath.expressions.MstExtendedField import space.kscience.kmath.expressions.MstExtendedField
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -15,55 +17,60 @@ import kotlin.test.assertEquals
internal class TestAsmSpecialization { internal class TestAsmSpecialization {
@Test @Test
fun testUnaryPlus() { fun testUnaryPlus() {
val expr = MstExtendedField { unaryOperationFunction("+")(bindSymbol("x")) }.compileToExpression(DoubleField) val expr = MstExtendedField { unaryOperationFunction("+")(bindSymbol(x)) }.compileToExpression(DoubleField)
assertEquals(2.0, expr("x" to 2.0)) assertEquals(2.0, expr(x to 2.0))
} }
@Test @Test
fun testUnaryMinus() { fun testUnaryMinus() {
val expr = MstExtendedField { unaryOperationFunction("-")(bindSymbol("x")) }.compileToExpression(DoubleField) val expr = MstExtendedField { unaryOperationFunction("-")(bindSymbol(x)) }.compileToExpression(DoubleField)
assertEquals(-2.0, expr("x" to 2.0)) assertEquals(-2.0, expr(x to 2.0))
} }
@Test @Test
fun testAdd() { fun testAdd() {
val expr = MstExtendedField { val expr = MstExtendedField {
binaryOperationFunction("+")(bindSymbol("x"), binaryOperationFunction("+")(
bindSymbol("x")) bindSymbol(x),
bindSymbol(x),
)
}.compileToExpression(DoubleField) }.compileToExpression(DoubleField)
assertEquals(4.0, expr("x" to 2.0)) assertEquals(4.0, expr(x to 2.0))
} }
@Test @Test
fun testSine() { fun testSine() {
val expr = MstExtendedField { unaryOperationFunction("sin")(bindSymbol("x")) }.compileToExpression(DoubleField) val expr = MstExtendedField { unaryOperationFunction("sin")(bindSymbol(x)) }.compileToExpression(DoubleField)
assertEquals(0.0, expr("x" to 0.0)) assertEquals(0.0, expr(x to 0.0))
} }
@Test @Test
fun testMinus() { fun testSubtract() {
val expr = MstExtendedField { val expr = MstExtendedField {
binaryOperationFunction("-")(bindSymbol("x"), binaryOperationFunction("-")(bindSymbol(x),
bindSymbol("x")) bindSymbol(x))
}.compileToExpression(DoubleField) }.compileToExpression(DoubleField)
assertEquals(0.0, expr("x" to 2.0)) assertEquals(0.0, expr(x to 2.0))
} }
@Test @Test
fun testDivide() { fun testDivide() {
val expr = MstExtendedField { val expr = MstExtendedField {
binaryOperationFunction("/")(bindSymbol("x"), binaryOperationFunction("/")(bindSymbol(x), bindSymbol(x))
bindSymbol("x"))
}.compileToExpression(DoubleField) }.compileToExpression(DoubleField)
assertEquals(1.0, expr("x" to 2.0)) assertEquals(1.0, expr(x to 2.0))
} }
@Test @Test
fun testPower() { fun testPower() {
val expr = MstExtendedField { val expr = MstExtendedField {
binaryOperationFunction("pow")(bindSymbol("x"), number(2)) binaryOperationFunction("pow")(bindSymbol(x), number(2))
}.compileToExpression(DoubleField) }.compileToExpression(DoubleField)
assertEquals(4.0, expr("x" to 2.0)) assertEquals(4.0, expr(x to 2.0))
}
private companion object {
private val x by symbol
} }
} }

View File

@ -7,7 +7,9 @@ package space.kscience.kmath.asm
import space.kscience.kmath.expressions.MstRing import space.kscience.kmath.expressions.MstRing
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.ByteRing import space.kscience.kmath.operations.ByteRing
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -16,13 +18,17 @@ import kotlin.test.assertFailsWith
internal class TestAsmVariables { internal class TestAsmVariables {
@Test @Test
fun testVariable() { fun testVariable() {
val expr = MstRing { bindSymbol("x") }.compileToExpression(ByteRing) val expr = MstRing { bindSymbol(x) }.compileToExpression(ByteRing)
assertEquals(1.toByte(), expr("x" to 1.toByte())) assertEquals(1.toByte(), expr(x to 1.toByte()))
} }
@Test @Test
fun testUndefinedVariableFails() { fun testUndefinedVariableFails() {
val expr = MstRing { bindSymbol("x") }.compileToExpression(ByteRing) val expr = MstRing { bindSymbol(x) }.compileToExpression(ByteRing)
assertFailsWith<NoSuchElementException> { expr() } assertFailsWith<NoSuchElementException> { expr() }
} }
private companion object {
private val x by symbol
}
} }

View File

@ -1,100 +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.ast.rendering
import space.kscience.kmath.ast.rendering.TestUtils.testLatex
import space.kscience.kmath.expressions.MST.Numeric
import kotlin.test.Test
internal class TestFeatures {
@Test
fun printSymbolic() = testLatex("x", "x")
@Test
fun printNumeric() {
val num = object : Number() {
override fun toByte(): Byte = throw UnsupportedOperationException()
override fun toChar(): Char = throw UnsupportedOperationException()
override fun toDouble(): Double = throw UnsupportedOperationException()
override fun toFloat(): Float = throw UnsupportedOperationException()
override fun toInt(): Int = throw UnsupportedOperationException()
override fun toLong(): Long = throw UnsupportedOperationException()
override fun toShort(): Short = throw UnsupportedOperationException()
override fun toString(): String = "foo"
}
testLatex(Numeric(num), "foo")
}
@Test
fun prettyPrintFloats() {
testLatex(Numeric(Double.NaN), "NaN")
testLatex(Numeric(Double.POSITIVE_INFINITY), "\\infty")
testLatex(Numeric(Double.NEGATIVE_INFINITY), "-\\infty")
testLatex(Numeric(1.0), "1")
testLatex(Numeric(-1.0), "-1")
testLatex(Numeric(1.42), "1.42")
testLatex(Numeric(-1.42), "-1.42")
testLatex(Numeric(1.1e10), "1.1\\times10^{10}")
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}")
}
@Test
fun prettyPrintIntegers() {
testLatex(Numeric(42), "42")
testLatex(Numeric(-42), "-42")
}
@Test
fun prettyPrintPi() {
testLatex("pi", "\\pi")
}
@Test
fun binaryPlus() = testLatex("2+2", "2+2")
@Test
fun binaryMinus() = testLatex("2-2", "2-2")
@Test
fun fraction() = testLatex("2/2", "\\frac{2}{2}")
@Test
fun binaryOperator() = testLatex("f(x, y)", "\\operatorname{f}\\left(x,y\\right)")
@Test
fun unaryOperator() = testLatex("f(x)", "\\operatorname{f}\\,\\left(x\\right)")
@Test
fun power() = testLatex("x^y", "x^{y}")
@Test
fun squareRoot() = testLatex("sqrt(x)", "\\sqrt{x}")
@Test
fun exponential() = testLatex("exp(x)", "e^{x}")
@Test
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)")
}
// @Test
// fun unaryPlus() {
// testLatex("+1", "+1")
// testLatex("+1", "++1")
// }
}

View File

@ -1,73 +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.ast.rendering
import space.kscience.kmath.ast.rendering.TestUtils.testLatex
import space.kscience.kmath.expressions.MST
import space.kscience.kmath.operations.GroupOperations
import kotlin.test.Test
internal class TestLatex {
@Test
fun number() = testLatex("42", "42")
@Test
fun symbol() = testLatex("x", "x")
@Test
fun operatorName() = testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)")
@Test
fun specialSymbol() {
testLatex(MST.Numeric(Double.POSITIVE_INFINITY), "\\infty")
testLatex("pi", "\\pi")
}
@Test
fun operand() {
testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)")
testLatex("1+1", "1+1")
}
@Test
fun unaryOperator() = testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)")
@Test
fun unaryPlus() = testLatex(MST.Unary(GroupOperations.PLUS_OPERATION, MST.Numeric(1)), "+1")
@Test
fun unaryMinus() = testLatex("-x", "-x")
@Test
fun radical() = testLatex("sqrt(x)", "\\sqrt{x}")
@Test
fun superscript() = testLatex("x^y", "x^{y}")
@Test
fun subscript() = testLatex(SubscriptSyntax("", SymbolSyntax("x"), NumberSyntax("123")), "x_{123}")
@Test
fun binaryOperator() = testLatex("f(x, y)", "\\operatorname{f}\\left(x,y\\right)")
@Test
fun binaryPlus() = testLatex("x+x", "x+x")
@Test
fun binaryMinus() = testLatex("x-x", "x-x")
@Test
fun fraction() = testLatex("x/x", "\\frac{x}{x}")
@Test
fun radicalWithIndex() = testLatex(RadicalWithIndexSyntax("", SymbolSyntax("x"), SymbolSyntax("y")), "\\sqrt[x]{y}")
@Test
fun multiplication() {
testLatex("x*1", "x\\times1")
testLatex("1*x", "1\\,x")
}
}

View File

@ -1,92 +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.ast.rendering
import space.kscience.kmath.ast.rendering.TestUtils.testMathML
import space.kscience.kmath.expressions.MST
import space.kscience.kmath.operations.GroupOperations
import kotlin.test.Test
internal class TestMathML {
@Test
fun number() = testMathML("42", "<mn>42</mn>")
@Test
fun symbol() = testMathML("x", "<mi>x</mi>")
@Test
fun operatorName() = testMathML(
"sin(1)",
"<mo>sin</mo><mspace width=\"0.167em\"></mspace><mfenced open=\"(\" close=\")\" separators=\"\"><mn>1</mn></mfenced>",
)
@Test
fun specialSymbol() {
testMathML(MST.Numeric(Double.POSITIVE_INFINITY), "<mo>&infin;</mo>")
testMathML("pi", "<mo>&pi;</mo>")
}
@Test
fun operand() {
testMathML(
"sin(1)",
"<mo>sin</mo><mspace width=\"0.167em\"></mspace><mfenced open=\"(\" close=\")\" separators=\"\"><mn>1</mn></mfenced>",
)
testMathML("1+1", "<mn>1</mn><mo>+</mo><mn>1</mn>")
}
@Test
fun unaryOperator() = testMathML(
"sin(1)",
"<mo>sin</mo><mspace width=\"0.167em\"></mspace><mfenced open=\"(\" close=\")\" separators=\"\"><mn>1</mn></mfenced>",
)
@Test
fun unaryPlus() =
testMathML(MST.Unary(GroupOperations.PLUS_OPERATION, MST.Numeric(1)), "<mo>+</mo><mn>1</mn>")
@Test
fun unaryMinus() = testMathML("-x", "<mo>-</mo><mi>x</mi>")
@Test
fun radical() = testMathML("sqrt(x)", "<msqrt><mi>x</mi></msqrt>")
@Test
fun superscript() = testMathML("x^y", "<msup><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow></msup>")
@Test
fun subscript() = testMathML(
SubscriptSyntax("", SymbolSyntax("x"), NumberSyntax("123")),
"<msub><mrow><mi>x</mi></mrow><mrow><mn>123</mn></mrow></msub>",
)
@Test
fun binaryOperator() = testMathML(
"f(x, y)",
"<mo>f</mo><mfenced open=\"(\" close=\")\" separators=\"\"><mi>x</mi><mo>,</mo><mi>y</mi></mfenced>",
)
@Test
fun binaryPlus() = testMathML("x+x", "<mi>x</mi><mo>+</mo><mi>x</mi>")
@Test
fun binaryMinus() = testMathML("x-x", "<mi>x</mi><mo>-</mo><mi>x</mi>")
@Test
fun fraction() = testMathML("x/x", "<mfrac><mrow><mi>x</mi></mrow><mrow><mi>x</mi></mrow></mfrac>")
@Test
fun radicalWithIndex() =
testMathML(RadicalWithIndexSyntax("", SymbolSyntax("x"), SymbolSyntax("y")),
"<mroot><mrow><mi>y</mi></mrow><mrow><mi>x</mi></mrow></mroot>")
@Test
fun multiplication() {
testMathML("x*1", "<mi>x</mi><mo>&times;</mo><mn>1</mn>")
testMathML("1*x", "<mn>1</mn><mspace width=\"0.167em\"></mspace><mi>x</mi>")
}
}

View File

@ -1,33 +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.ast.rendering
import space.kscience.kmath.ast.rendering.TestUtils.testLatex
import kotlin.test.Test
internal class TestStages {
@Test
fun betterMultiplication() {
testLatex("a*1", "a\\times1")
testLatex("1*(2/3)", "1\\times\\left(\\frac{2}{3}\\right)")
testLatex("1*1", "1\\times1")
testLatex("2e10", "2\\times10^{10}")
testLatex("2*x", "2\\,x")
testLatex("2*(x+1)", "2\\,\\left(x+1\\right)")
testLatex("x*y", "x\\,y")
}
@Test
fun parentheses() {
testLatex("(x+1)", "x+1")
testLatex("x*x*x", "x\\,x\\,x")
testLatex("(x+x)*x", "\\left(x+x\\right)\\,x")
testLatex("x+x*x", "x+x\\,x")
testLatex("x+x^x*x+x", "x+x^{x}\\,x+x")
testLatex("(x+x)^x+x*x", "\\left(x+x\\right)^{x}+x\\,x")
testLatex("x^(x+x)", "x^{x+x}")
}
}

View File

@ -1,46 +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.ast.rendering
import space.kscience.kmath.ast.parseMath
import space.kscience.kmath.expressions.MST
import kotlin.test.assertEquals
internal object TestUtils {
private fun mathSyntax(mst: MST) = FeaturedMathRendererWithPostProcess.Default.render(mst)
private fun latex(mst: MST) = LatexSyntaxRenderer.renderWithStringBuilder(mathSyntax(mst))
private fun mathML(mst: MST) = MathMLSyntaxRenderer.renderWithStringBuilder(mathSyntax(mst))
internal fun testLatex(mst: MST, expectedLatex: String) = assertEquals(
expected = expectedLatex,
actual = latex(mst),
)
internal fun testLatex(expression: String, expectedLatex: String) = assertEquals(
expected = expectedLatex,
actual = latex(expression.parseMath()),
)
internal fun testLatex(expression: MathSyntax, expectedLatex: String) = assertEquals(
expected = expectedLatex,
actual = LatexSyntaxRenderer.renderWithStringBuilder(expression),
)
internal fun testMathML(mst: MST, expectedMathML: String) = assertEquals(
expected = "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow>$expectedMathML</mrow></math>",
actual = mathML(mst),
)
internal fun testMathML(expression: String, expectedMathML: String) = assertEquals(
expected = "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow>$expectedMathML</mrow></math>",
actual = mathML(expression.parseMath()),
)
internal fun testMathML(expression: MathSyntax, expectedMathML: String) = assertEquals(
expected = "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow>$expectedMathML</mrow></math>",
actual = MathMLSyntaxRenderer.renderWithStringBuilder(expression),
)
}

View File

@ -8,7 +8,7 @@ Complex and hypercomplex number systems in KMath.
## Artifact: ## 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-7`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -19,7 +19,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-complex:0.3.0-dev-6' implementation 'space.kscience:kmath-complex:0.3.0-dev-7'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -31,6 +31,6 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-complex:0.3.0-dev-6") implementation("space.kscience:kmath-complex:0.3.0-dev-7")
} }
``` ```

View File

@ -1,5 +1,3 @@
import ru.mipt.npm.gradle.Maturity
/* /*
* Copyright 2018-2021 KMath contributors. * 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
@ -21,7 +19,7 @@ kotlin.sourceSets {
readme { readme {
description = "Complex numbers and quaternions." description = "Complex numbers and quaternions."
maturity = Maturity.PROTOTYPE maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE
propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
feature( feature(

View File

@ -15,7 +15,7 @@ performance calculations to code generation.
## Artifact: ## 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-7`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -26,7 +26,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-core:0.3.0-dev-6' implementation 'space.kscience:kmath-core:0.3.0-dev-7'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -38,6 +38,6 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-core:0.3.0-dev-6") implementation("space.kscience:kmath-core:0.3.0-dev-7")
} }
``` ```

View File

@ -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. * 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 { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("ru.mipt.npm.gradle.common") id("ru.mipt.npm.gradle.common")
@ -21,7 +19,7 @@ kotlin.sourceSets {
readme { readme {
description = "Core classes, algebra definitions, basic linear algebra" 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")) propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
feature( feature(

View File

@ -0,0 +1,18 @@
package 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.test.Test
internal class InterpretTest {
@Test
fun interpretation() {
val expr = MstField {
val x = bindSymbol(Symbol.x)
x * 2.0 + number(2.0) / x - 16.0
}.toExpression(DoubleField)
expr(Symbol.x to 2.2)
}
}

View File

@ -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. * 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 { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("ru.mipt.npm.gradle.common") id("ru.mipt.npm.gradle.common")
@ -29,5 +27,5 @@ kotlin.sourceSets {
} }
readme { readme {
maturity = Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
} }

View File

@ -9,7 +9,7 @@ EJML based linear algebra implementation.
## Artifact: ## 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-7`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -20,7 +20,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-ejml:0.3.0-dev-6' implementation 'space.kscience:kmath-ejml:0.3.0-dev-7'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -32,6 +32,6 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-ejml:0.3.0-dev-6") implementation("space.kscience:kmath-ejml:0.3.0-dev-7")
} }
``` ```

View File

@ -9,7 +9,7 @@ Specialization of KMath APIs for Double numbers.
## Artifact: ## 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-7`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -20,7 +20,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-for-real:0.3.0-dev-6' implementation 'space.kscience:kmath-for-real:0.3.0-dev-7'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -32,6 +32,6 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-for-real:0.3.0-dev-6") implementation("space.kscience:kmath-for-real:0.3.0-dev-7")
} }
``` ```

View File

@ -11,7 +11,7 @@ Functions and interpolations.
## Artifact: ## 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-7`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -22,7 +22,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-functions:0.3.0-dev-6' implementation 'space.kscience:kmath-functions:0.3.0-dev-7'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -34,6 +34,6 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-functions:0.3.0-dev-6") implementation("space.kscience:kmath-functions:0.3.0-dev-7")
} }
``` ```

View File

@ -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. * 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 { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("ru.mipt.npm.gradle.common") id("ru.mipt.npm.gradle.common")
@ -17,5 +15,5 @@ kotlin.sourceSets.commonMain {
} }
readme { readme {
maturity = Maturity.PROTOTYPE maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE
} }

View File

@ -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. * 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 { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("ru.mipt.npm.gradle.common") id("ru.mipt.npm.gradle.common")
@ -28,5 +26,5 @@ kotlin.sourceSets {
} }
readme { readme {
maturity = Maturity.PROTOTYPE maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE
} }

View File

@ -10,6 +10,7 @@ import space.kscience.kmath.asm.compileToExpression
import space.kscience.kmath.ast.parseMath import space.kscience.kmath.ast.parseMath
import space.kscience.kmath.expressions.MstAlgebra import space.kscience.kmath.expressions.MstAlgebra
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.misc.symbol
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -19,8 +20,8 @@ import kotlin.test.fail
internal class AdaptingTests { internal class AdaptingTests {
@Test @Test
fun symbol() { fun symbol() {
val c1 = MstAlgebra.bindSymbol("x") val c1 = MstAlgebra.bindSymbol(x.identity)
assertTrue(c1.toSVar<KMathNumber<Double, DoubleField>>().name == "x") assertEquals(x.identity, c1.toSVar<KMathNumber<Double, DoubleField>>().name)
val c2 = "kitten".parseMath().toSFun<KMathNumber<Double, DoubleField>>() val c2 = "kitten".parseMath().toSFun<KMathNumber<Double, DoubleField>>()
if (c2 is SVar) assertTrue(c2.name == "kitten") else fail() if (c2 is SVar) assertTrue(c2.name == "kitten") else fail()
} }
@ -45,23 +46,27 @@ internal class AdaptingTests {
@Test @Test
fun simpleFunctionDerivative() { fun simpleFunctionDerivative() {
val x = MstAlgebra.bindSymbol("x").toSVar<KMathNumber<Double, DoubleField>>() val xSVar = MstAlgebra.bindSymbol(x.identity).toSVar<KMathNumber<Double, DoubleField>>()
val quadratic = "x^2-4*x-44".parseMath().toSFun<KMathNumber<Double, DoubleField>>() val quadratic = "x^2-4*x-44".parseMath().toSFun<KMathNumber<Double, DoubleField>>()
val actualDerivative = quadratic.d(x).toMst().compileToExpression(DoubleField) val actualDerivative = quadratic.d(xSVar).toMst().compileToExpression(DoubleField)
val expectedDerivative = "2*x-4".parseMath().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 @Test
fun moreComplexDerivative() { fun moreComplexDerivative() {
val x = MstAlgebra.bindSymbol("x").toSVar<KMathNumber<Double, DoubleField>>() val xSVar = MstAlgebra.bindSymbol(x.identity).toSVar<KMathNumber<Double, DoubleField>>()
val composition = "-sqrt(sin(x^2)-cos(x)^2-16*x)".parseMath().toSFun<KMathNumber<Double, DoubleField>>() val composition = "-sqrt(sin(x^2)-cos(x)^2-16*x)".parseMath().toSFun<KMathNumber<Double, DoubleField>>()
val actualDerivative = composition.d(x).toMst().compileToExpression(DoubleField) val actualDerivative = composition.d(xSVar).toMst().compileToExpression(DoubleField)
val expectedDerivative = val expectedDerivative = "-(2*x*cos(x^2)+2*sin(x)*cos(x)-16)/(2*sqrt(sin(x^2)-16*x-cos(x)^2))"
"-(2*x*cos(x^2)+2*sin(x)*cos(x)-16)/(2*sqrt(sin(x^2)-16*x-cos(x)^2))".parseMath().compileToExpression(DoubleField) .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
} }
} }

View File

@ -9,7 +9,7 @@ ND4J based implementations of KMath abstractions.
## Artifact: ## 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-7`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -20,7 +20,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-nd4j:0.3.0-dev-6' implementation 'space.kscience:kmath-nd4j:0.3.0-dev-7'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -32,7 +32,7 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-nd4j:0.3.0-dev-6") implementation("space.kscience:kmath-nd4j:0.3.0-dev-7")
} }
``` ```

View File

@ -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. * 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 { plugins {
kotlin("jvm") kotlin("jvm")
id("ru.mipt.npm.gradle.common") id("ru.mipt.npm.gradle.common")
@ -20,7 +18,7 @@ dependencies {
readme { readme {
description = "ND4J NDStructure implementation and according NDAlgebra classes" 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")) propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
feature( feature(

View File

@ -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. * 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 { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("ru.mipt.npm.gradle.common") id("ru.mipt.npm.gradle.common")
@ -30,5 +28,5 @@ kotlin.sourceSets {
} }
readme { readme {
maturity = Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
} }

View File

@ -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. * 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 { plugins {
kotlin("jvm") kotlin("jvm")
id("ru.mipt.npm.gradle.common") id("ru.mipt.npm.gradle.common")
@ -18,5 +16,5 @@ dependencies {
} }
readme { readme {
maturity = Maturity.DEVELOPMENT maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT
} }