Feature/advanced optimization #404

Merged
altavir merged 17 commits from feature/advanced-optimization into dev 2021-08-29 11:44:10 +03:00
238 changed files with 2830 additions and 1898 deletions
Showing only changes of commit a020d1545c - Show all commits

View File

@ -1,6 +1,9 @@
name: Gradle build name: Gradle build
on: [ push ] on:
push:
branches: [ dev, master ]
pull_request:
jobs: jobs:
build: build:
@ -8,23 +11,22 @@ jobs:
matrix: matrix:
os: [ macOS-latest, windows-latest ] os: [ macOS-latest, windows-latest ]
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
timeout-minutes: 30 timeout-minutes: 40
steps: steps:
- name: Checkout the repo - name: Checkout the repo
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up JDK 11 - name: Set up JDK 11
uses: DeLaGuardo/setup-graalvm@4.0 uses: DeLaGuardo/setup-graalvm@4.0
with: with:
graalvm: 21.1.0 graalvm: 21.2.0
java: java11 java: java11
arch: amd64 arch: amd64
- name: Add msys to path
if: matrix.os == 'windows-latest'
run: SETX PATH "%PATH%;C:\msys64\mingw64\bin"
- name: Cache gradle - name: Cache gradle
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ~/.gradle/caches path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }} key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: | restore-keys: |
${{ runner.os }}-gradle- ${{ runner.os }}-gradle-
@ -36,4 +38,4 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-gradle- ${{ runner.os }}-gradle-
- name: Build - name: Build
run: ./gradlew build --no-daemon --stacktrace run: ./gradlew build --build-cache --no-daemon --stacktrace

View File

@ -2,23 +2,27 @@ name: Dokka publication
on: on:
push: push:
branches: branches: [ master ]
- master
jobs: jobs:
build: build:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
timeout-minutes: 40
steps: steps:
- name: Checkout the repo - uses: actions/checkout@v2
uses: actions/checkout@v2 - uses: DeLaGuardo/setup-graalvm@4.0
- name: Set up JDK 11
uses: actions/setup-java@v1
with: with:
java-version: 11 graalvm: 21.2.0
- name: Build java: java11
run: ./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace arch: amd64
- name: Deploy to GitHub Pages - uses: actions/cache@v2
uses: JamesIves/github-pages-deploy-action@4.1.0 with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- run: ./gradlew dokkaHtmlMultiModule --build-cache --no-daemon --no-parallel --stacktrace
- uses: JamesIves/github-pages-deploy-action@4.1.0
with: with:
branch: gh-pages branch: gh-pages
folder: build/dokka/htmlMultiModule folder: build/dokka/htmlMultiModule

View File

@ -3,8 +3,7 @@ name: Gradle publish
on: on:
workflow_dispatch: workflow_dispatch:
release: release:
types: types: [ created ]
- created
jobs: jobs:
publish: publish:
@ -20,16 +19,15 @@ jobs:
- name: Set up JDK 11 - name: Set up JDK 11
uses: DeLaGuardo/setup-graalvm@4.0 uses: DeLaGuardo/setup-graalvm@4.0
with: with:
graalvm: 21.1.0 graalvm: 21.2.0
java: java11 java: java11
arch: amd64 arch: amd64
- name: Add msys to path
if: matrix.os == 'windows-latest'
run: SETX PATH "%PATH%;C:\msys64\mingw64\bin"
- name: Cache gradle - name: Cache gradle
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ~/.gradle/caches path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }} key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: | restore-keys: |
${{ runner.os }}-gradle- ${{ runner.os }}-gradle-
@ -42,20 +40,14 @@ jobs:
${{ runner.os }}-gradle- ${{ runner.os }}-gradle-
- name: Publish Windows Artifacts - name: Publish Windows Artifacts
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
shell: cmd
run: > run: >
./gradlew release --no-daemon ./gradlew release --no-daemon --build-cache -Ppublishing.enabled=true
-Ppublishing.enabled=true -Ppublishing.space.user=${{ secrets.SPACE_APP_ID }}
-Ppublishing.github.user=${{ secrets.PUBLISHING_GITHUB_USER }} -Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }}
-Ppublishing.github.token=${{ secrets.PUBLISHING_GITHUB_TOKEN }}
-Ppublishing.space.user=${{ secrets.PUBLISHING_SPACE_USER }}
-Ppublishing.space.token=${{ secrets.PUBLISHING_SPACE_TOKEN }}
- name: Publish Mac Artifacts - name: Publish Mac Artifacts
if: matrix.os == 'macOS-latest' if: matrix.os == 'macOS-latest'
run: > run: >
./gradlew release --no-daemon ./gradlew release --no-daemon --build-cache -Ppublishing.enabled=true -Ppublishing.platform=macosX64
-Ppublishing.enabled=true -Ppublishing.space.user=${{ secrets.SPACE_APP_ID }}
-Ppublishing.platform=macosX64 -Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }}
-Ppublishing.github.user=${{ secrets.PUBLISHING_GITHUB_USER }}
-Ppublishing.github.token=${{ secrets.PUBLISHING_GITHUB_TOKEN }}
-Ppublishing.space.user=${{ secrets.PUBLISHING_SPACE_USER }}
-Ppublishing.space.token=${{ secrets.PUBLISHING_SPACE_TOKEN }}

View File

@ -2,11 +2,11 @@
## [Unreleased] ## [Unreleased]
### Added ### Added
- ScaleOperations interface - `ScaleOperations` interface
- Field extends ScaleOperations - `Field` extends `ScaleOperations`
- Basic integration API - Basic integration API
- Basic MPP distributions and samplers - Basic MPP distributions and samplers
- bindSymbolOrNull - `bindSymbolOrNull`
- Blocking chains and Statistics - Blocking chains and Statistics
- Multiplatform integration - Multiplatform integration
- Integration for any Field element - Integration for any Field element
@ -15,6 +15,8 @@
- `@PerformancePitfall` annotation to mark possibly slow API - `@PerformancePitfall` annotation to mark possibly slow API
- BigInt operation performance improvement and fixes by @zhelenskiy (#328) - BigInt operation performance improvement and fixes by @zhelenskiy (#328)
- Unified architecture for Integration and Optimization using features. - Unified architecture for Integration and Optimization using features.
- `BigInt` operation performance improvement and fixes by @zhelenskiy (#328)
- Integration between `MST` and Symja `IExpr`
### Changed ### Changed
- Exponential operations merged with hyperbolic functions - Exponential operations merged with hyperbolic functions
@ -53,6 +55,7 @@
### Fixed ### Fixed
- Ring inherits RingOperations, not GroupOperations - Ring inherits RingOperations, not GroupOperations
- Univariate histogram filling
### Security ### Security

View File

@ -2,14 +2,14 @@
[![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382) [![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382)
![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg) ![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg)
[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22) [![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22)
[![Space](https://img.shields.io/maven-metadata/v?label=Space&metadataUrl=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fkscience%2Fkmath%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/) [![Space](https://img.shields.io/badge/dynamic/xml?color=orange&label=Space&query=//metadata/versioning/latest&url=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fspace%2Fkscience%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/)
# KMath # KMath
Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based analog to Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based
Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior architecture analog to Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior
designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like
be achieved with [kmath-for-real](/kmath-for-real) extension module. experience could be achieved with [kmath-for-real](/kmath-for-real) extension module.
[Documentation site (**WIP**)](https://mipt-npm.github.io/kmath/) [Documentation site (**WIP**)](https://mipt-npm.github.io/kmath/)
@ -21,26 +21,33 @@ be achieved with [kmath-for-real](/kmath-for-real) extension module.
# Goal # Goal
* Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS and Native). * Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS and Native)
.
* Provide basic multiplatform implementations for those abstractions (without significant performance optimization). * Provide basic multiplatform implementations for those abstractions (without significant performance optimization).
* Provide bindings and wrappers with those abstractions for popular optimized platform libraries. * Provide bindings and wrappers with those abstractions for popular optimized platform libraries.
## Non-goals ## Non-goals
* Be like NumPy. It was the idea at the beginning, but we decided that we can do better in terms of API. * Be like NumPy. It was the idea at the beginning, but we decided that we can do better in API.
* Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them. * Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them.
* Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually. * Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually.
* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like * Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like
for `Double` in the core. For that we will have specialization modules like `kmath-for-real`, which will give better for `Double` in the core. For that we will have specialization modules like `kmath-for-real`, which will give better
experience for those, who want to work with specific types. experience for those, who want to work with specific types.
## Features and stability ## Features and stability
KMath is a modular library. Different modules provide different features with different API stability guarantees. All core modules are released with the same version, but with different API change policy. The features are described in module definitions below. The module stability could have following levels: KMath is a modular library. Different modules provide different features with different API stability guarantees. All
core modules are released with the same version, but with different API change policy. The features are described in
module definitions below. The module stability could have the following levels:
* **PROTOTYPE**. On this level there are no compatibility guarantees. All methods and classes form those modules could break any moment. You can still use it, but be sure to fix the specific version. * **PROTOTYPE**. On this level there are no compatibility guarantees. All methods and classes form those modules could
* **EXPERIMENTAL**. The general API is decided, but some changes could be made. Volatile API is marked with `@UnstableKmathAPI` or other stability warning annotations. break any moment. You can still use it, but be sure to fix the specific version.
* **DEVELOPMENT**. API breaking generally follows semantic versioning ideology. There could be changes in minor versions, but not in patch versions. API is protected with [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator) tool. * **EXPERIMENTAL**. The general API is decided, but some changes could be made. Volatile API is marked
with `@UnstableKmathAPI` or other stability warning annotations.
* **DEVELOPMENT**. API breaking generally follows semantic versioning ideology. There could be changes in minor
versions, but not in patch versions. API is protected
with [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator) tool.
* **STABLE**. The API stabilized. Breaking changes are allowed only in major releases. * **STABLE**. The API stabilized. Breaking changes are allowed only in major releases.
<!--Current feature list is [here](/docs/features.md)--> <!--Current feature list is [here](/docs/features.md)-->
@ -132,7 +139,7 @@ KMath is a modular library. Different modules provide different features with di
objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high
performance calculations to code generation. performance calculations to code generation.
> - [domains](kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains) : Domains > - [domains](kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains) : Domains
> - [autodif](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation > - [autodiff](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation
<hr/> <hr/>
@ -175,7 +182,7 @@ One can still use generic algebras though.
<hr/> <hr/>
* ### [kmath-functions](kmath-functions) * ### [kmath-functions](kmath-functions)
> Functions, integration and interpolation >
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
> >
@ -200,6 +207,16 @@ One can still use generic algebras though.
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
<hr/> <hr/>
* ### [kmath-jafama](kmath-jafama)
>
>
> **Maturity**: PROTOTYPE
>
> **Features:**
> - [jafama-double](kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/) : Double ExtendedField implementations based on Jafama
<hr/>
* ### [kmath-jupyter](kmath-jupyter) * ### [kmath-jupyter](kmath-jupyter)
> >
> >
@ -207,24 +224,24 @@ One can still use generic algebras though.
<hr/> <hr/>
* ### [kmath-kotlingrad](kmath-kotlingrad) * ### [kmath-kotlingrad](kmath-kotlingrad)
> Functions, integration and interpolation >
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
> >
> **Features:** > **Features:**
> - [differentiable-mst-expression](kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt) : MST based DifferentiableExpression. > - [differentiable-mst-expression](kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt) : MST based DifferentiableExpression.
> - [differentiable-mst-expression](kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt) : Conversions between Kotlin∇'s SFun and MST > - [scalars-adapters](kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/scalarsAdapters.kt) : Conversions between Kotlin∇'s SFun and MST
<hr/> <hr/>
* ### [kmath-memory](kmath-memory) * ### [kmath-memory](kmath-memory)
> An API and basic implementation for arranging objects in a continous memory block. > An API and basic implementation for arranging objects in a continuous memory block.
> >
> **Maturity**: DEVELOPMENT > **Maturity**: DEVELOPMENT
<hr/> <hr/>
* ### [kmath-nd4j](kmath-nd4j) * ### [kmath-nd4j](kmath-nd4j)
> ND4J NDStructure implementation and according NDAlgebra classes >
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
> >
@ -241,6 +258,12 @@ One can still use generic algebras though.
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
<hr/> <hr/>
* ### [kmath-symja](kmath-symja)
>
>
> **Maturity**: PROTOTYPE
<hr/>
* ### [kmath-tensors](kmath-tensors) * ### [kmath-tensors](kmath-tensors)
> >
> >
@ -248,7 +271,7 @@ One can still use generic algebras though.
> >
> **Features:** > **Features:**
> - [tensor algebra](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt) : Basic linear algebra operations on tensors (plus, dot, etc.) > - [tensor algebra](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt) : Basic linear algebra operations on tensors (plus, dot, etc.)
> - [tensor algebra with broadcasting](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/BroadcastDoubleTensorAlgebra.kt) : Basic linear algebra operations implemented with broadcasting. > - [tensor algebra with broadcasting](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt) : Basic linear algebra operations implemented with broadcasting.
> - [linear algebra operations](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt) : Advanced linear algebra operations like LU decomposition, SVD, etc. > - [linear algebra operations](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt) : Advanced linear algebra operations like LU decomposition, SVD, etc.
<hr/> <hr/>
@ -270,8 +293,8 @@ feedback are also welcome.
## Performance ## Performance
Calculation performance is one of major goals of KMath in the future, but in some cases it is impossible to achieve Calculation performance is one of major goals of KMath in the future, but in some cases it is impossible to achieve both
both performance and flexibility. performance and flexibility.
We expect to focus on creating convenient universal API first and then work on increasing performance for specific We expect to focus on creating convenient universal API first and then work on increasing performance for specific
cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized
@ -280,12 +303,15 @@ better than SciPy.
## Requirements ## Requirements
KMath currently relies on JDK 11 for compilation and execution of Kotlin-JVM part. We recommend to use GraalVM-CE 11 for execution in order to get better performance. KMath currently relies on JDK 11 for compilation and execution of Kotlin-JVM part. We recommend to use GraalVM-CE 11 for
execution to get better performance.
### Repositories ### Repositories
Release and development artifacts are accessible from mipt-npm [Space](https://www.jetbrains.com/space/) repository `https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven` (see documentation of Release and development artifacts are accessible from mipt-npm [Space](https://www.jetbrains.com/space/)
[Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details). The repository could be reached through [repo.kotlin.link](https://repo.kotlin.link) proxy: repository `https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven` (see documentation of
[Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details). The repository could
be reached through [repo.kotlin.link](https://repo.kotlin.link) proxy:
```kotlin ```kotlin
repositories { repositories {
@ -293,8 +319,8 @@ repositories {
} }
dependencies { dependencies {
api("space.kscience:kmath-core:0.3.0-dev-11") api("space.kscience:kmath-core:0.3.0-dev-14")
// api("space.kscience:kmath-core-jvm:0.3.0-dev-11") for jvm-specific version // api("space.kscience:kmath-core-jvm:0.3.0-dev-14") for jvm-specific version
} }
``` ```
@ -303,6 +329,6 @@ Gradle `6.0+` is required for multiplatform artifacts.
## Contributing ## Contributing
The project requires a lot of additional work. The most important thing we need is a feedback about what features are The project requires a lot of additional work. The most important thing we need is a feedback about what features are
required the most. Feel free to create feature requests. We are also welcome to code contributions, required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues
especially in issues marked with marked with
[waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero) label. [waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero) label.

View File

@ -1,3 +1,7 @@
@file:Suppress("UNUSED_VARIABLE")
import space.kscience.kmath.benchmarks.addBenchmarkProperties
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
kotlin("plugin.allopen") kotlin("plugin.allopen")
@ -12,6 +16,7 @@ repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
maven("https://clojars.org/repo") maven("https://clojars.org/repo")
maven("https://jitpack.io") maven("https://jitpack.io")
maven("http://logicrunch.research.it.uu.se/maven") { maven("http://logicrunch.research.it.uu.se/maven") {
isAllowInsecureProtocol = true isAllowInsecureProtocol = true
} }
@ -30,7 +35,8 @@ kotlin {
implementation(project(":kmath-stat")) implementation(project(":kmath-stat"))
implementation(project(":kmath-dimensions")) implementation(project(":kmath-dimensions"))
implementation(project(":kmath-for-real")) implementation(project(":kmath-for-real"))
implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.0") implementation(project(":kmath-jafama"))
implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.1")
} }
} }
@ -41,8 +47,7 @@ kotlin {
implementation(project(":kmath-nd4j")) implementation(project(":kmath-nd4j"))
implementation(project(":kmath-kotlingrad")) implementation(project(":kmath-kotlingrad"))
implementation(project(":kmath-viktor")) implementation(project(":kmath-viktor"))
implementation("org.nd4j:nd4j-native:1.0.0-beta7") implementation("org.nd4j:nd4j-native:1.0.0-M1")
// uncomment if your system supports AVX2 // uncomment if your system supports AVX2
// val os = System.getProperty("os.name") // val os = System.getProperty("os.name")
// //
@ -95,6 +100,11 @@ benchmark {
commonConfiguration() commonConfiguration()
include("BigIntBenchmark") include("BigIntBenchmark")
} }
configurations.register("jafamaDouble") {
commonConfiguration()
include("JafamaBenchmark")
}
} }
// Fix kotlinx-benchmarks bug // Fix kotlinx-benchmarks bug
@ -124,3 +134,5 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
readme { readme {
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
} }
addBenchmarkProperties()

View File

@ -14,22 +14,51 @@ import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.math.sin
import kotlin.random.Random import kotlin.random.Random
@State(Scope.Benchmark) @State(Scope.Benchmark)
internal class ExpressionsInterpretersBenchmark { internal class ExpressionsInterpretersBenchmark {
/**
* Benchmark case for [Expression] created with [expressionInExtendedField].
*/
@Benchmark @Benchmark
fun functionalExpression(blackhole: Blackhole) = invokeAndSum(functional, blackhole) fun functionalExpression(blackhole: Blackhole) = invokeAndSum(functional, blackhole)
/**
* Benchmark case for [Expression] created with [toExpression].
*/
@Benchmark @Benchmark
fun mstExpression(blackhole: Blackhole) = invokeAndSum(mst, blackhole) fun mstExpression(blackhole: Blackhole) = invokeAndSum(mst, blackhole)
/**
* Benchmark case for [Expression] created with [compileToExpression].
*/
@Benchmark @Benchmark
fun asmExpression(blackhole: Blackhole) = invokeAndSum(asm, blackhole) fun asmExpression(blackhole: Blackhole) = invokeAndSum(asm, blackhole)
/**
* Benchmark case for [Expression] implemented manually with `kotlin.math` functions.
*/
@Benchmark @Benchmark
fun rawExpression(blackhole: Blackhole) = invokeAndSum(raw, blackhole) fun rawExpression(blackhole: Blackhole) = invokeAndSum(raw, blackhole)
/**
* Benchmark case for direct computation w/o [Expression].
*/
@Benchmark
fun justCalculate(blackhole: Blackhole) {
val random = Random(0)
var sum = 0.0
repeat(times) {
val x = random.nextDouble()
sum += x * 2.0 + 2.0 / x - 16.0 / sin(x)
}
blackhole.consume(sum)
}
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
@ -42,23 +71,24 @@ internal class ExpressionsInterpretersBenchmark {
} }
private companion object { private companion object {
private val x: Symbol by symbol private val x by symbol
private val algebra: DoubleField = DoubleField private val algebra = DoubleField
private const val times = 1_000_000 private const val times = 1_000_000
private val functional: Expression<Double> = DoubleField.expressionInExtendedField { private val functional = DoubleField.expressionInExtendedField {
bindSymbol(x) * number(2.0) + number(2.0) / bindSymbol(x) - number(16.0) / sin(bindSymbol(x)) bindSymbol(x) * number(2.0) + number(2.0) / bindSymbol(x) - number(16.0) / sin(bindSymbol(x))
} }
private val node = MstExtendedField { private val node = MstExtendedField {
bindSymbol(x) * 2.0 + number(2.0) / bindSymbol(x) - number(16.0) / sin(bindSymbol(x)) x * 2.0 + number(2.0) / x - number(16.0) / sin(x)
} }
private val mst: Expression<Double> = node.toExpression(DoubleField) private val mst = node.toExpression(DoubleField)
private val asm: Expression<Double> = node.compileToExpression(DoubleField) private val asm = node.compileToExpression(DoubleField)
private val raw: Expression<Double> = Expression { args -> private val raw = Expression<Double> { args ->
args.getValue(x) * 2.0 + 2.0 / args.getValue(x) - 16.0 / kotlin.math.sin(args.getValue(x)) val x = args[x]!!
x * 2.0 + 2.0 / x - 16.0 / sin(x)
} }
} }
} }

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.benchmarks
import kotlinx.benchmark.Blackhole
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State
import space.kscience.kmath.jafama.JafamaDoubleField
import space.kscience.kmath.jafama.StrictJafamaDoubleField
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.random.Random
@State(Scope.Benchmark)
internal class JafamaBenchmark {
@Benchmark
fun jafama(blackhole: Blackhole) = invokeBenchmarks(blackhole) { x ->
JafamaDoubleField { x * power(x, 4) * exp(x) / cos(x) + sin(x) }
}
@Benchmark
fun core(blackhole: Blackhole) = invokeBenchmarks(blackhole) { x ->
DoubleField { x * power(x, 4) * exp(x) / cos(x) + sin(x) }
}
@Benchmark
fun strictJafama(blackhole: Blackhole) = invokeBenchmarks(blackhole) { x ->
StrictJafamaDoubleField { x * power(x, 4) * exp(x) / cos(x) + sin(x) }
}
}
private inline fun invokeBenchmarks(blackhole: Blackhole, expr: (Double) -> Double) {
contract { callsInPlace(expr, InvocationKind.AT_LEAST_ONCE) }
val rng = Random(0)
repeat(1000000) { blackhole.consume(expr(rng.nextDouble())) }
}

View File

@ -14,8 +14,8 @@ import space.kscience.kmath.commons.linear.inverse
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.linear.InverseMatrixFeature import space.kscience.kmath.linear.InverseMatrixFeature
import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.inverseWithLup
import space.kscience.kmath.linear.invoke import space.kscience.kmath.linear.invoke
import space.kscience.kmath.linear.lupSolver
import space.kscience.kmath.nd.getFeature import space.kscience.kmath.nd.getFeature
import kotlin.random.Random import kotlin.random.Random
@ -35,19 +35,19 @@ internal class MatrixInverseBenchmark {
@Benchmark @Benchmark
fun kmathLupInversion(blackhole: Blackhole) { fun kmathLupInversion(blackhole: Blackhole) {
blackhole.consume(LinearSpace.double.inverseWithLup(matrix)) blackhole.consume(LinearSpace.double.lupSolver().inverse(matrix))
} }
@Benchmark @Benchmark
fun cmLUPInversion(blackhole: Blackhole) { fun cmLUPInversion(blackhole: Blackhole) {
with(CMLinearSpace) { CMLinearSpace {
blackhole.consume(inverse(matrix)) blackhole.consume(inverse(matrix))
} }
} }
@Benchmark @Benchmark
fun ejmlInverse(blackhole: Blackhole) { fun ejmlInverse(blackhole: Blackhole) {
with(EjmlLinearSpaceDDRM) { EjmlLinearSpaceDDRM {
blackhole.consume(matrix.getFeature<InverseMatrixFeature<Double>>()?.inverse) blackhole.consume(matrix.getFeature<InverseMatrixFeature<Double>>()?.inverse)
} }
} }

View File

@ -1,3 +1,5 @@
import java.net.URL
plugins { plugins {
id("ru.mipt.npm.gradle.project") id("ru.mipt.npm.gradle.project")
kotlin("jupyter.api") apply false kotlin("jupyter.api") apply false
@ -7,15 +9,17 @@ allprojects {
repositories { repositories {
maven("https://clojars.org/repo") maven("https://clojars.org/repo")
maven("https://jitpack.io") maven("https://jitpack.io")
maven("http://logicrunch.research.it.uu.se/maven") { maven("http://logicrunch.research.it.uu.se/maven") {
isAllowInsecureProtocol = true isAllowInsecureProtocol = true
} }
maven("https://oss.sonatype.org/content/repositories/snapshots") maven("https://oss.sonatype.org/content/repositories/snapshots")
mavenCentral() mavenCentral()
} }
group = "space.kscience" group = "space.kscience"
version = "0.3.0-dev-13" version = "0.3.0-dev-14"
} }
subprojects { subprojects {
@ -23,30 +27,46 @@ subprojects {
afterEvaluate { afterEvaluate {
tasks.withType<org.jetbrains.dokka.gradle.DokkaTaskPartial> { tasks.withType<org.jetbrains.dokka.gradle.DokkaTaskPartial> {
dependsOn(tasks.getByName("assemble")) dependsOn(tasks["assemble"])
dokkaSourceSets.all { dokkaSourceSets.all {
val readmeFile = File(this@subprojects.projectDir, "README.md") val readmeFile = this@subprojects.projectDir.resolve("README.md")
if (readmeFile.exists()) includes.setFrom(includes + readmeFile.absolutePath) if (readmeFile.exists()) includes.from(readmeFile)
externalDocumentationLink("http://ejml.org/javadoc/") val kotlinDirPath = "src/$name/kotlin"
val kotlinDir = file(kotlinDirPath)
if (kotlinDir.exists()) sourceLink {
localDirectory.set(kotlinDir)
remoteUrl.set(
URL("https://github.com/mipt-npm/${rootProject.name}/tree/master/${this@subprojects.name}/$kotlinDirPath")
)
}
externalDocumentationLink("https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/") externalDocumentationLink("https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/")
externalDocumentationLink("https://deeplearning4j.org/api/latest/") externalDocumentationLink("https://deeplearning4j.org/api/latest/")
externalDocumentationLink("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/") externalDocumentationLink("https://axelclk.bitbucket.io/symja/javadoc/")
externalDocumentationLink(
"https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/",
"https://kotlin.github.io/kotlinx.coroutines/package-list",
)
externalDocumentationLink(
"https://breandan.net/kotlingrad/kotlingrad/",
"https://breandan.net/kotlingrad/kotlingrad/kotlingrad/package-list",
)
} }
} }
} }
} }
readme { readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
readmeTemplate = file("docs/templates/README-TEMPLATE.md")
}
ksciencePublish { ksciencePublish {
github("kmath") vcs("https://github.com/mipt-npm/kmath")
space() space(publish = true)
sonatype() sonatype(publish = true)
} }
apiValidation { apiValidation.nonPublicMarkers.add("space.kscience.kmath.misc.UnstableKMathAPI")
nonPublicMarkers.add("space.kscience.kmath.misc.UnstableKMathAPI")
}

View File

@ -1,5 +1,20 @@
plugins { plugins {
`kotlin-dsl` `kotlin-dsl`
kotlin("plugin.serialization") version "1.4.31"
} }
repositories.mavenCentral() repositories {
maven("https://repo.kotlin.link")
mavenCentral()
gradlePluginPortal()
}
dependencies {
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
api("ru.mipt.npm:gradle-tools:0.10.2")
api("org.jetbrains.kotlinx:kotlinx-benchmark-plugin:0.3.1")
}
kotlin.sourceSets.all {
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalStdlibApi")
}

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.benchmarks
import kotlinx.serialization.Serializable
@Serializable
data class JmhReport(
val jmhVersion: String,
val benchmark: String,
val mode: String,
val threads: Int,
val forks: Int,
val jvm: String,
val jvmArgs: List<String>,
val jdkVersion: String,
val vmName: String,
val vmVersion: String,
val warmupIterations: Int,
val warmupTime: String,
val warmupBatchSize: Int,
val measurementIterations: Int,
val measurementTime: String,
val measurementBatchSize: Int,
val params: Map<String, String> = emptyMap(),
val primaryMetric: PrimaryMetric,
val secondaryMetrics: Map<String, SecondaryMetric>,
) {
interface Metric {
val score: Double
val scoreError: Double
val scoreConfidence: List<Double>
val scorePercentiles: Map<Double, Double>
val scoreUnit: String
}
@Serializable
data class PrimaryMetric(
override val score: Double,
override val scoreError: Double,
override val scoreConfidence: List<Double>,
override val scorePercentiles: Map<Double, Double>,
override val scoreUnit: String,
val rawDataHistogram: List<List<List<List<Double>>>>? = null,
val rawData: List<List<Double>>? = null,
) : Metric
@Serializable
data class SecondaryMetric(
override val score: Double,
override val scoreError: Double,
override val scoreConfidence: List<Double>,
override val scorePercentiles: Map<Double, Double>,
override val scoreUnit: String,
val rawData: List<List<Double>>,
) : Metric
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.benchmarks
import kotlinx.benchmark.gradle.BenchmarksExtension
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import org.gradle.api.Project
import ru.mipt.npm.gradle.KScienceReadmeExtension
import java.time.*
import java.time.format.*
import java.time.temporal.ChronoField.*
private val ISO_DATE_TIME: DateTimeFormatter = DateTimeFormatterBuilder().run {
parseCaseInsensitive()
appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
appendLiteral('-')
appendValue(MONTH_OF_YEAR, 2)
appendLiteral('-')
appendValue(DAY_OF_MONTH, 2)
appendLiteral('T')
appendValue(HOUR_OF_DAY, 2)
appendLiteral('.')
appendValue(MINUTE_OF_HOUR, 2)
optionalStart()
appendLiteral('.')
appendValue(SECOND_OF_MINUTE, 2)
optionalStart()
appendFraction(NANO_OF_SECOND, 0, 9, true)
optionalStart()
appendOffsetId()
optionalStart()
appendLiteral('[')
parseCaseSensitive()
appendZoneRegionId()
appendLiteral(']')
toFormatter()
}
private fun noun(number: Number, singular: String, plural: String) = if (number.toLong() == 1L) singular else plural
fun Project.addBenchmarkProperties() {
val benchmarksProject = this
rootProject.subprojects.forEach { p ->
p.extensions.findByType(KScienceReadmeExtension::class.java)?.run {
benchmarksProject.extensions.findByType(BenchmarksExtension::class.java)?.configurations?.forEach { cfg ->
property("benchmark${cfg.name.replaceFirstChar(Char::uppercase)}") {
val launches = benchmarksProject.buildDir.resolve("reports/benchmarks/${cfg.name}")
val resDirectory = launches.listFiles()?.maxByOrNull {
LocalDateTime.parse(it.name, ISO_DATE_TIME).atZone(ZoneId.systemDefault()).toInstant()
}
if (resDirectory == null) {
"> **Can't find appropriate benchmark data. Try generating readme files after running benchmarks**."
} else {
val reports =
Json.decodeFromString<List<JmhReport>>(resDirectory.resolve("jvm.json").readText())
buildString {
appendLine("<details>")
appendLine("<summary>")
appendLine("Report for benchmark configuration <code>${cfg.name}</code>")
appendLine("</summary>")
appendLine()
val first = reports.first()
appendLine("* Run on ${first.vmName} (build ${first.vmVersion}) with Java process:")
appendLine()
appendLine("```")
appendLine("${first.jvm} ${
first.jvmArgs.joinToString(" ")
}")
appendLine("```")
appendLine("* JMH ${first.jmhVersion} was used in `${first.mode}` mode with ${first.warmupIterations} warmup ${
noun(first.warmupIterations, "iteration", "iterations")
} by ${first.warmupTime} and ${first.measurementIterations} measurement ${
noun(first.measurementIterations, "iteration", "iterations")
} by ${first.measurementTime}.")
appendLine()
appendLine("| Benchmark | Score |")
appendLine("|:---------:|:-----:|")
reports.forEach { report ->
appendLine("|`${report.benchmark}`|${report.primaryMetric.score} &plusmn; ${report.primaryMetric.scoreError} ${report.primaryMetric.scoreUnit}|")
}
appendLine("</details>")
}
}
}
}
}
}
}

View File

@ -14,12 +14,12 @@ private fun Appendable.appendEjmlVector(type: String, ejmlMatrixType: String) {
@Language("kotlin") val text = """/** @Language("kotlin") val text = """/**
* [EjmlVector] specialization for [$type]. * [EjmlVector] specialization for [$type].
*/ */
public class Ejml${type}Vector<out M : $ejmlMatrixType>(public override val origin: M) : EjmlVector<$type, M>(origin) { public class Ejml${type}Vector<out M : $ejmlMatrixType>(override val origin: M) : EjmlVector<$type, M>(origin) {
init { init {
require(origin.numRows == 1) { "The origin matrix must have only one row to form a vector" } require(origin.numRows == 1) { "The origin matrix must have only one row to form a vector" }
} }
public override operator fun get(index: Int): $type = origin[0, index] override operator fun get(index: Int): $type = origin[0, index]
}""" }"""
appendLine(text) appendLine(text)
appendLine() appendLine()
@ -29,8 +29,8 @@ private fun Appendable.appendEjmlMatrix(type: String, ejmlMatrixType: String) {
val text = """/** val text = """/**
* [EjmlMatrix] specialization for [$type]. * [EjmlMatrix] specialization for [$type].
*/ */
public class Ejml${type}Matrix<out M : $ejmlMatrixType>(public override val origin: M) : EjmlMatrix<$type, M>(origin) { public class Ejml${type}Matrix<out M : $ejmlMatrixType>(override val origin: M) : EjmlMatrix<$type, M>(origin) {
public override operator fun get(i: Int, j: Int): $type = origin[i, j] override operator fun get(i: Int, j: Int): $type = origin[i, j]
}""" }"""
appendLine(text) appendLine(text)
appendLine() appendLine()
@ -54,23 +54,23 @@ public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra},
/** /**
* The [${kmathAlgebra}] reference. * The [${kmathAlgebra}] reference.
*/ */
public override val elementAlgebra: $kmathAlgebra get() = $kmathAlgebra override val elementAlgebra: $kmathAlgebra get() = $kmathAlgebra
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public override fun Matrix<${type}>.toEjml(): Ejml${type}Matrix<${ejmlMatrixType}> = when { override fun Matrix<${type}>.toEjml(): Ejml${type}Matrix<${ejmlMatrixType}> = when {
this is Ejml${type}Matrix<*> && origin is $ejmlMatrixType -> this as Ejml${type}Matrix<${ejmlMatrixType}> this is Ejml${type}Matrix<*> && origin is $ejmlMatrixType -> this as Ejml${type}Matrix<${ejmlMatrixType}>
else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) }
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public override fun Point<${type}>.toEjml(): Ejml${type}Vector<${ejmlMatrixType}> = when { override fun Point<${type}>.toEjml(): Ejml${type}Vector<${ejmlMatrixType}> = when {
this is Ejml${type}Vector<*> && origin is $ejmlMatrixType -> this as Ejml${type}Vector<${ejmlMatrixType}> this is Ejml${type}Vector<*> && origin is $ejmlMatrixType -> this as Ejml${type}Vector<${ejmlMatrixType}>
else -> Ejml${type}Vector(${ejmlMatrixType}(size, 1).also { else -> Ejml${type}Vector(${ejmlMatrixType}(size, 1).also {
(0 until it.numRows).forEach { row -> it[row, 0] = get(row) } (0 until it.numRows).forEach { row -> it[row, 0] = get(row) }
}) })
} }
public override fun buildMatrix( override fun buildMatrix(
rows: Int, rows: Int,
columns: Int, columns: Int,
initializer: ${kmathAlgebra}.(i: Int, j: Int) -> ${type}, initializer: ${kmathAlgebra}.(i: Int, j: Int) -> ${type},
@ -80,7 +80,7 @@ public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra},
} }
}.wrapMatrix() }.wrapMatrix()
public override fun buildVector( override fun buildVector(
size: Int, size: Int,
initializer: ${kmathAlgebra}.(Int) -> ${type}, initializer: ${kmathAlgebra}.(Int) -> ${type},
): Ejml${type}Vector<${ejmlMatrixType}> = Ejml${type}Vector(${ejmlMatrixType}(size, 1).also { ): Ejml${type}Vector<${ejmlMatrixType}> = Ejml${type}Vector(${ejmlMatrixType}(size, 1).also {
@ -90,21 +90,21 @@ public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra},
private fun <T : ${ejmlMatrixParentTypeMatrix}> T.wrapMatrix() = Ejml${type}Matrix(this) private fun <T : ${ejmlMatrixParentTypeMatrix}> T.wrapMatrix() = Ejml${type}Matrix(this)
private fun <T : ${ejmlMatrixParentTypeMatrix}> T.wrapVector() = Ejml${type}Vector(this) private fun <T : ${ejmlMatrixParentTypeMatrix}> T.wrapVector() = Ejml${type}Vector(this)
public override fun Matrix<${type}>.unaryMinus(): Matrix<${type}> = this * elementAlgebra { -one } override fun Matrix<${type}>.unaryMinus(): Matrix<${type}> = this * elementAlgebra { -one }
public override fun Matrix<${type}>.dot(other: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> { override fun Matrix<${type}>.dot(other: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> {
val out = ${ejmlMatrixType}(1, 1) val out = ${ejmlMatrixType}(1, 1)
CommonOps_${ops}.mult(toEjml().origin, other.toEjml().origin, out) CommonOps_${ops}.mult(toEjml().origin, other.toEjml().origin, out)
return out.wrapMatrix() return out.wrapMatrix()
} }
public override fun Matrix<${type}>.dot(vector: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> { override fun Matrix<${type}>.dot(vector: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> {
val out = ${ejmlMatrixType}(1, 1) val out = ${ejmlMatrixType}(1, 1)
CommonOps_${ops}.mult(toEjml().origin, vector.toEjml().origin, out) CommonOps_${ops}.mult(toEjml().origin, vector.toEjml().origin, out)
return out.wrapVector() return out.wrapVector()
} }
public override operator fun Matrix<${type}>.minus(other: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> { override operator fun Matrix<${type}>.minus(other: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> {
val out = ${ejmlMatrixType}(1, 1) val out = ${ejmlMatrixType}(1, 1)
CommonOps_${ops}.add( CommonOps_${ops}.add(
@ -123,19 +123,19 @@ public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra},
return out.wrapMatrix() return out.wrapMatrix()
} }
public override operator fun Matrix<${type}>.times(value: ${type}): Ejml${type}Matrix<${ejmlMatrixType}> { override operator fun Matrix<${type}>.times(value: ${type}): Ejml${type}Matrix<${ejmlMatrixType}> {
val res = ${ejmlMatrixType}(1, 1) val res = ${ejmlMatrixType}(1, 1)
CommonOps_${ops}.scale(value, toEjml().origin, res) CommonOps_${ops}.scale(value, toEjml().origin, res)
return res.wrapMatrix() return res.wrapMatrix()
} }
public override fun Point<${type}>.unaryMinus(): Ejml${type}Vector<${ejmlMatrixType}> { override fun Point<${type}>.unaryMinus(): Ejml${type}Vector<${ejmlMatrixType}> {
val res = ${ejmlMatrixType}(1, 1) val res = ${ejmlMatrixType}(1, 1)
CommonOps_${ops}.changeSign(toEjml().origin, res) CommonOps_${ops}.changeSign(toEjml().origin, res)
return res.wrapVector() return res.wrapVector()
} }
public override fun Matrix<${type}>.plus(other: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> { override fun Matrix<${type}>.plus(other: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> {
val out = ${ejmlMatrixType}(1, 1) val out = ${ejmlMatrixType}(1, 1)
CommonOps_${ops}.add( CommonOps_${ops}.add(
@ -154,7 +154,7 @@ public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra},
return out.wrapMatrix() return out.wrapMatrix()
} }
public override fun Point<${type}>.plus(other: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> { override fun Point<${type}>.plus(other: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> {
val out = ${ejmlMatrixType}(1, 1) val out = ${ejmlMatrixType}(1, 1)
CommonOps_${ops}.add( CommonOps_${ops}.add(
@ -173,7 +173,7 @@ public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra},
return out.wrapVector() return out.wrapVector()
} }
public override fun Point<${type}>.minus(other: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> { override fun Point<${type}>.minus(other: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> {
val out = ${ejmlMatrixType}(1, 1) val out = ${ejmlMatrixType}(1, 1)
CommonOps_${ops}.add( CommonOps_${ops}.add(
@ -192,18 +192,18 @@ public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra},
return out.wrapVector() return out.wrapVector()
} }
public override fun ${type}.times(m: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> = m * this override fun ${type}.times(m: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> = m * this
public override fun Point<${type}>.times(value: ${type}): Ejml${type}Vector<${ejmlMatrixType}> { override fun Point<${type}>.times(value: ${type}): Ejml${type}Vector<${ejmlMatrixType}> {
val res = ${ejmlMatrixType}(1, 1) val res = ${ejmlMatrixType}(1, 1)
CommonOps_${ops}.scale(value, toEjml().origin, res) CommonOps_${ops}.scale(value, toEjml().origin, res)
return res.wrapVector() return res.wrapVector()
} }
public override fun ${type}.times(v: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> = v * this override fun ${type}.times(v: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> = v * this
@UnstableKMathAPI @UnstableKMathAPI
public override fun <F : StructureFeature> computeFeature(structure: Matrix<${type}>, type: KClass<out F>): F? { override fun <F : StructureFeature> computeFeature(structure: Matrix<${type}>, type: KClass<out F>): F? {
structure.getFeature(type)?.let { return it } structure.getFeature(type)?.let { return it }
val origin = structure.toEjml().origin val origin = structure.toEjml().origin

View File

@ -2,16 +2,16 @@
The mathematical operations in KMath are generally separated from mathematical objects. This means that to perform an The mathematical operations in KMath are generally separated from mathematical objects. This means that to perform an
operation, say `+`, one needs two objects of a type `T` and an algebra context, which draws appropriate operation up, operation, say `+`, one needs two objects of a type `T` and an algebra context, which draws appropriate operation up,
say `Space<T>`. Next one needs to run the actual operation in the context: say `Group<T>`. Next one needs to run the actual operation in the context:
```kotlin ```kotlin
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
val a: T = ... val a: T = ...
val b: T = ... val b: T = ...
val space: Space<T> = ... val group: Group<T> = ...
val c = space { a + b } val c = group { a + b }
``` ```
At first glance, this distinction seems to be a needless complication, but in fact one needs to remember that in At first glance, this distinction seems to be a needless complication, but in fact one needs to remember that in
@ -20,66 +20,26 @@ geometry for vectors.
## Algebraic Structures ## Algebraic Structures
Mathematical contexts have the following hierarchy: Primary mathematical contexts have the following hierarchy:
**Algebra** ← **Space****Ring** ← **Field** `Field <: Ring <: Group <: Algebra`
These interfaces follow real algebraic structures: These interfaces follow real algebraic structures:
- [Space](https://mathworld.wolfram.com/VectorSpace.html) defines addition, its neutral element (i.e. 0) and scalar - [Group](https://mathworld.wolfram.com/Group.html) defines addition, its identity element (i.e., 0) and additive
multiplication; inverse (-x);
- [Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and its neutral element (i.e. 1); - [Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and its identity element (i.e., 1);
- [Field](http://mathworld.wolfram.com/Field.html) adds division operation. - [Field](http://mathworld.wolfram.com/Field.html) adds division operation.
A typical implementation of `Field<T>` is the `DoubleField` which works on doubles, and `VectorSpace` for `Space<T>`. A typical implementation of `Field<T>` is the `DoubleField` which works on doubles, and `VectorSpace` for `Space<T>`.
In some cases algebra context can hold additional operations like `exp` or `sin`, and then it inherits appropriate In some cases algebra context can hold additional operations like `exp` or `sin`, and then it inherits appropriate
interface. Also, contexts may have operations, which produce elements outside of the context. For example, `Matrix.dot` interface. Also, contexts may have operations, which produce elements outside the context. For example, `Matrix.dot`
operation produces a matrix with new dimensions, which can be incompatible with initial matrix in terms of linear operation produces a matrix with new dimensions, which can be incompatible with initial matrix in linear operations.
operations.
## Algebraic Element
To achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving
contexts KMath submits special type objects called `MathElement`. A `MathElement` is basically some object coupled to
a mathematical context. For example `Complex` is the pair of real numbers representing real and imaginary parts,
but it also holds reference to the `ComplexField` singleton, which allows performing direct operations on `Complex`
numbers without explicit involving the context like:
```kotlin
import space.kscience.kmath.operations.*
// Using elements
val c1 = Complex(1.0, 1.0)
val c2 = Complex(1.0, -1.0)
val c3 = c1 + c2 + 3.0.toComplex()
// Using context
val c4 = ComplexField { c1 + i - 2.0 }
```
Both notations have their pros and cons.
The hierarchy for algebraic elements follows the hierarchy for the corresponding algebraic structures.
**MathElement** ← **SpaceElement****RingElement** ← **FieldElement**
`MathElement<C>` is the generic common ancestor of the class with context.
One major distinction between algebraic elements and algebraic contexts is that elements have three type
parameters:
1. The type of elements, the field operates on.
2. The self-type of the element returned from operation (which has to be an algebraic element).
3. The type of the algebra over first type-parameter.
The middle type is needed for of algebra members do not store context. For example, it is impossible to add a context
to regular `Double`. The element performs automatic conversions from context types and back. One should use context
operations in all performance-critical places. The performance of element operations is not guaranteed.
## Spaces and Fields ## Spaces and Fields
KMath submits both contexts and elements for builtin algebraic structures: KMath introduces contexts for builtin algebraic structures:
```kotlin ```kotlin
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
@ -118,8 +78,9 @@ val element = NDElement.complex(shape = intArrayOf(2, 2)) { index: IntArray ->
``` ```
The `element` in this example is a member of the `Field` of 2D structures, each element of which is a member of its own The `element` in this example is a member of the `Field` of 2D structures, each element of which is a member of its own
`ComplexField`. It is important one does not need to create a special n-d class to hold complex `ComplexField`. It is important one does not need to create a special n-d class to hold complex numbers and implement
numbers and implement operations on it, one just needs to provide a field for its elements. operations on it, one just needs to provide a field for its elements.
**Note**: Fields themselves do not solve the problem of JVM boxing, but it is possible to solve with special contexts like **Note**: Fields themselves do not solve the problem of JVM boxing, but it is possible to solve with special contexts
like
`MemorySpec`. `MemorySpec`.

View File

@ -1,17 +1,20 @@
# Buffers # Buffers
Buffer is one of main building blocks of kmath. It is a basic interface allowing random-access read and write (with `MutableBuffer`). Buffer is one of main building blocks of kmath. It is a basic interface allowing random-access read and write (
There are different types of buffers: with `MutableBuffer`). There are different types of buffers:
* Primitive buffers wrapping like `RealBuffer` which are wrapping primitive arrays. * Primitive buffers wrapping like `DoubleBuffer` which are wrapping primitive arrays.
* Boxing `ListBuffer` wrapping a list * Boxing `ListBuffer` wrapping a list
* Functionally defined `VirtualBuffer` which does not hold a state itself, but provides a function to calculate value * Functionally defined `VirtualBuffer` which does not hold a state itself, but provides a function to calculate value
* `MemoryBuffer` allows direct allocation of objects in continuous memory block. * `MemoryBuffer` allows direct allocation of objects in continuous memory block.
Some kmath features require a `BufferFactory` class to operate properly. A general convention is to use functions defined in Some kmath features require a `BufferFactory` class to operate properly. A general convention is to use functions
`Buffer` and `MutableBuffer` companion classes. For example factory `Buffer.Companion::auto` in most cases creates the most suitable defined in
buffer for given reified type (for types with custom memory buffer it still better to use their own `MemoryBuffer.create()` factory). `Buffer` and `MutableBuffer` companion classes. For example factory `Buffer.Companion::auto` in most cases creates the
most suitable buffer for given reified type (for types with custom memory buffer it still better to use their
own `MemoryBuffer.create()` factory).
## Buffer performance ## Buffer performance
One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers
instead .

View File

@ -1,26 +1,20 @@
# Coding Conventions # Coding Conventions
KMath code follows general [Kotlin conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but Generally, KMath code follows general [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but with a number of small changes and clarifications.
with a number of small changes and clarifications.
## Utility Class Naming ## Utility Class Naming
Filename should coincide with a name of one of the classes contained in the file or start with small letter and Filename should coincide with a name of one of the classes contained in the file or start with small letter and describe its contents.
describe its contents.
The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that file names should start with a capital letter even if file does not contain classes. Yet starting utility classes and aggregators with a small letter seems to be a good way to visually separate those files.
file names should start with a capital letter even if file does not contain classes. Yet starting utility classes and
aggregators with a small letter seems to be a good way to visually separate those files.
This convention could be changed in future in a non-breaking way. This convention could be changed in future in a non-breaking way.
## Private Variable Naming ## Private Variable Naming
Private variables' names may start with underscore `_` for of the private mutable variable is shadowed by the public Private variables' names may start with underscore `_` for of the private mutable variable is shadowed by the public read-only value with the same meaning.
read-only value with the same meaning.
This rule does not permit underscores in names, but it is sometimes useful to "underscore" the fact that public and This rule does not permit underscores in names, but it is sometimes useful to "underscore" the fact that public and private versions draw up the same entity. It is allowed only for private variables.
private versions draw up the same entity. It is allowed only for private variables.
This convention could be changed in future in a non-breaking way. This convention could be changed in future in a non-breaking way.
@ -30,5 +24,4 @@ Use one-liners when they occupy single code window line both for functions and p
`val b: String get() = "fff"`. The same should be performed with multiline expressions when they could be `val b: String get() = "fff"`. The same should be performed with multiline expressions when they could be
cleanly separated. cleanly separated.
There is no universal consensus whenever use `fun a() = ...` or `fun a() { return ... }`. Yet from reader outlook There is no universal consensus whenever use `fun a() = ...` or `fun a() { return ... }`. Yet from reader outlook one-lines seem to better show that the property or function is easily calculated.
one-lines seem to better show that the property or function is easily calculated.

View File

@ -2,18 +2,17 @@
## The problem ## The problem
A known problem for implementing mathematics in statically-typed languages (but not only in them) is that different A known problem for implementing mathematics in statically-typed languages (but not only in them) is that different sets
sets of mathematical operators can be defined on the same mathematical objects. Sometimes there is no single way to of mathematical operators can be defined on the same mathematical objects. Sometimes there is no single way to treat
treat some operations, including basic arithmetic operations, on a Java/Kotlin `Number`. Sometimes there are different ways to some operations, including basic arithmetic operations, on a Java/Kotlin `Number`. Sometimes there are different ways to
define the same structure, such as Euclidean and elliptic geometry vector spaces over real vectors. Another problem arises when define the same structure, such as Euclidean and elliptic geometry vector spaces over real vectors. Another problem
one wants to add some kind of behavior to an existing entity. In dynamic languages those problems are usually solved arises when one wants to add some kind of behavior to an existing entity. In dynamic languages those problems are
by adding dynamic context-specific behaviors at runtime, but this solution has a lot of drawbacks. usually solved by adding dynamic context-specific behaviors at runtime, but this solution has a lot of drawbacks.
## Context-oriented approach ## Context-oriented approach
One possible solution to these problems is to divorce numerical representations from behaviors. One possible solution to these problems is to divorce numerical representations from behaviors. For example in Kotlin
For example in Kotlin one can define a separate class which represents some entity without any operations, one can define a separate class representing some entity without any operations, ex. a complex number:
ex. a complex number:
```kotlin ```kotlin
data class Complex(val re: Double, val im: Double) data class Complex(val re: Double, val im: Double)
@ -28,9 +27,10 @@ object ComplexOperations {
} }
``` ```
In Java, applying such external operations could be very cumbersome, but Kotlin has a unique feature which allows us In Java, applying such external operations could be cumbersome, but Kotlin has a unique feature that allows us
implement this naturally: [extensions with receivers](https://kotlinlang.org/docs/reference/extensions.html#extension-functions). implement this
In Kotlin, an operation on complex number could be implemented as: naturally: [extensions with receivers](https://kotlinlang.org/docs/reference/extensions.html#extension-functions). In
Kotlin, an operation on complex number could be implemented as:
```kotlin ```kotlin
with(ComplexOperations) { c1 + c2 - c3 } with(ComplexOperations) { c1 + c2 - c3 }
@ -52,20 +52,20 @@ In KMath, contexts are not only responsible for operations, but also for raw obj
### Type classes ### Type classes
An obvious candidate to get more or less the same functionality is the type class, which allows one to bind a behavior to An obvious candidate to get more or less the same functionality is the type class, which allows one to bind a behavior
a specific type without modifying the type itself. On the plus side, type classes do not require explicit context to a specific type without modifying the type itself. On the plus side, type classes do not require explicit context
declaration, so the code looks cleaner. On the minus side, if there are different sets of behaviors for the same types, declaration, so the code looks cleaner. On the minus side, if there are different sets of behaviors for the same types,
it is impossible to combine them into one module. Also, unlike type classes, context can have parameters or even it is impossible to combine them into one module. Also, unlike type classes, context can have parameters or even state.
state. For example in KMath, sizes and strides for `NDElement` or `Matrix` could be moved to context to optimize For example in KMath, sizes and strides for `NDElement` or `Matrix` could be moved to context to optimize performance in
performance in case of a large amount of structures. case of a large amount of structures.
### Wildcard imports and importing-on-demand ### Wildcard imports and importing-on-demand
Sometimes, one may wish to use a single context throughout a file. In this case, is possible to import all members Sometimes, one may wish to use a single context throughout a file. In this case, is possible to import all members from
from a package or file, via `import context.complex.*`. Effectively, this is the same as enclosing an entire file a package or file, via `import context.complex.*`. Effectively, this is the same as enclosing an entire file with a
with a single context. However when using multiple contexts, this technique can introduce operator ambiguity, due to single context. However, when using multiple contexts, this technique can introduce operator ambiguity, due to namespace
namespace pollution. If there are multiple scoped contexts which define the same operation, it is still possible to pollution. If there are multiple scoped contexts that define the same operation, it is still possible to import
to import specific operations as needed, without using an explicit context with extension functions, for example: specific operations as needed, without using an explicit context with extension functions, for example:
``` ```
import context.complex.op1 import context.complex.op1

View File

@ -1,26 +1,21 @@
# Expressions # Expressions
**Experimental: this API is in early stage and could change any time** Expressions is a feature, which allows constructing lazily or immediately calculated parametric mathematical expressions.
Expressions is an experimental feature which allows to construct lazily or immediately calculated parametric mathematical
expressions.
The potential use-cases for it (so far) are following: The potential use-cases for it (so far) are following:
* Lazy evaluation (in general simple lambda is better, but there are some border cases) * lazy evaluation (in general simple lambda is better, but there are some border cases);
* automatic differentiation in single-dimension and in multiple dimensions;
* generation of mathematical syntax trees with subsequent code generation for other languages;
* symbolic computations, especially differentiation (and some other actions with `kmath-symja` integration with Symja's `IExpr`&mdash;integration, simplification, and more);
* visualization with `kmath-jupyter`.
* Automatic differentiation in single-dimension and in multiple dimensions The workhorse of this API is `Expression` interface, which exposes single `operator fun invoke(arguments: Map<Symbol, T>): T`
method. `ExpressionAlgebra` is used to generate expressions and introduce variables.
* Generation of mathematical syntax trees with subsequent code generation for other languages
* Maybe symbolic computations (needs additional research)
The workhorse of this API is `Expression` interface which exposes single `operator fun invoke(arguments: Map<String, T>): T`
method. `ExpressionContext` is used to generate expressions and introduce variables.
Currently there are two implementations: Currently there are two implementations:
* Generic `ExpressionField` in `kmath-core` which allows construction of custom lazy expressions * Generic `ExpressionField` in `kmath-core` which allows construction of custom lazy expressions
* Auto-differentiation expression in `kmath-commons` module allows to use full power of `DerivativeStructure` * Auto-differentiation expression in `kmath-commons` module allows using full power of `DerivativeStructure`
from commons-math. **TODO: add example** from commons-math. **TODO: add example**

View File

@ -1,14 +0,0 @@
# Features
* [Algebra](algebra.md) - [Context-based](contexts.md) operations on different primitives and structures.
* [NDStructures](nd-structure.md)
* [Linear algebra](linear.md) - Matrices, operations and linear equations solving. To be moved to separate module. Currently supports basic
api and multiple library back-ends.
* [Histograms](histograms.md) - Multidimensional histogram calculation and operations.
* [Expressions](expressions.md)
* Commons math integration

View File

@ -1,19 +1,31 @@
## Basic linear algebra layout ## Basic linear algebra layout
KMath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared KMath support for linear algebra organized in a context-oriented way, which means that operations are in most cases declared in context classes, and are not the members of classes that store data. This allows more flexible approach to maintain multiple back-ends. The new operations added as extensions to contexts instead of being member functions of data structures.
in context classes, and are not the members of classes that store data. This allows more flexible approach to maintain multiple
back-ends. The new operations added as extensions to contexts instead of being member functions of data structures.
Two major contexts used for linear algebra and hyper-geometry: The main context for linear algebra over matrices and vectors is `LinearSpace`, which defines addition and dot products of matrices and vectors:
* `VectorSpace` forms a mathematical space on top of array-like structure (`Buffer` and its type alias `Point` used for geometry). ```kotlin
import space.kscience.kmath.linear.*
* `MatrixContext` forms a space-like context for 2d-structures. It does not store matrix size and therefore does not implement LinearSpace.Companion.real {
`Space` interface (it is impossible to create zero element without knowing the matrix size). val vec = buildVector(10) { i -> i.toDouble() }
val mat = buildMatrix(10, 10) { i, j -> i.toDouble() + j }
## Vector spaces // Addition
vec + vec
mat + mat
// Multiplication by scalar
vec * 2.0
mat * 2.0
## Matrix operations // Dot product
mat dot vec
mat dot mat
}
```
## Back-end overview ## Backends overview
### EJML
### Commons Math

View File

@ -11,16 +11,16 @@ Let us consider following contexts:
```kotlin ```kotlin
// automatically build context most suited for given type. // automatically build context most suited for given type.
val autoField = NDField.auto(DoubleField, dim, dim) val autoField = NDField.auto(DoubleField, dim, dim)
// specialized nd-field for Double. It works as generic Double field as well // specialized nd-field for Double. It works as generic Double field as well.
val specializedField = NDField.real(dim, dim) val specializedField = NDField.real(dim, dim)
//A generic boxing field. It should be used for objects, not primitives. //A generic boxing field. It should be used for objects, not primitives.
val genericField = NDField.buffered(DoubleField, dim, dim) val genericField = NDField.buffered(DoubleField, dim, dim)
``` ```
Now let us perform several tests and see which implementation is best suited for each case: Now let us perform several tests and see, which implementation is best suited for each case:
## Test case ## Test case
In order to test performance we will take 2d-structures with `dim = 1000` and add a structure filled with `1.0` To test performance we will take 2d-structures with `dim = 1000` and add a structure filled with `1.0`
to it `n = 1000` times. to it `n = 1000` times.
## Specialized ## Specialized
@ -35,8 +35,8 @@ The code to run this looks like:
``` ```
The performance of this code is the best of all tests since it inlines all operations and is specialized for operation The performance of this code is the best of all tests since it inlines all operations and is specialized for operation
with doubles. We will measure everything else relative to this one, so time for this test will be `1x` (real time with doubles. We will measure everything else relative to this one, so time for this test will be `1x` (real time
on my computer is about 4.5 seconds). The only problem with this approach is that it requires to specify type on my computer is about 4.5 seconds). The only problem with this approach is that it requires specifying type
from the beginning. Everyone do so anyway, so it is the recommended approach. from the beginning. Everyone does so anyway, so it is the recommended approach.
## Automatic ## Automatic
Let's do the same with automatic field inference: Let's do the same with automatic field inference:
@ -49,7 +49,7 @@ Let's do the same with automatic field inference:
} }
``` ```
Ths speed of this operation is approximately the same as for specialized case since `NDField.auto` just Ths speed of this operation is approximately the same as for specialized case since `NDField.auto` just
returns the same `RealNDField` in this case. Of course it is usually better to use specialized method to be sure. returns the same `RealNDField` in this case. Of course, it is usually better to use specialized method to be sure.
## Lazy ## Lazy
Lazy field does not produce a structure when asked, instead it generates an empty structure and fills it on-demand Lazy field does not produce a structure when asked, instead it generates an empty structure and fills it on-demand
@ -63,7 +63,7 @@ When one calls
} }
} }
``` ```
The result will be calculated almost immediately but the result will be empty. In order to get the full result The result will be calculated almost immediately but the result will be empty. To get the full result
structure one needs to call all its elements. In this case computation overhead will be huge. So this field never structure one needs to call all its elements. In this case computation overhead will be huge. So this field never
should be used if one expects to use the full result structure. Though if one wants only small fraction, it could should be used if one expects to use the full result structure. Though if one wants only small fraction, it could
save a lot of time. save a lot of time.
@ -94,7 +94,7 @@ The boxing field produced by
} }
} }
``` ```
obviously is the slowest one, because it requires to box and unbox the `double` on each operation. It takes about is the slowest one, because it requires boxing and unboxing the `double` on each operation. It takes about
`15x` time (**TODO: there seems to be a problem here, it should be slow, but not that slow**). This field should `15x` time (**TODO: there seems to be a problem here, it should be slow, but not that slow**). This field should
never be used for primitives. never be used for primitives.
@ -115,12 +115,14 @@ via extension function.
Usually it is bad idea to compare the direct numerical operation performance in different languages, but it hard to Usually it is bad idea to compare the direct numerical operation performance in different languages, but it hard to
work completely without frame of reference. In this case, simple numpy code: work completely without frame of reference. In this case, simple numpy code:
```python ```python
import numpy as np
res = np.ones((1000,1000)) res = np.ones((1000,1000))
for i in range(1000): for i in range(1000):
res = res + 1.0 res = res + 1.0
``` ```
gives the completion time of about `1.1x`, which means that specialized kotlin code in fact is working faster (I think it is gives the completion time of about `1.1x`, which means that specialized kotlin code in fact is working faster (I think it is
because better memory management). Of course if one writes `res += 1.0`, the performance will be different, because better memory management). Of course if one writes `res += 1.0`, the performance will be different,
but it would be differenc case, because numpy overrides `+=` with in-place operations. In-place operations are but it would be different case, because numpy overrides `+=` with in-place operations. In-place operations are
available in `kmath` with `MutableNDStructure` but there is no field for it (one can still work with mapping available in `kmath` with `MutableNDStructure` but there is no field for it (one can still work with mapping
functions). functions).

14
docs/readme.md Normal file
View File

@ -0,0 +1,14 @@
# Documentation
* [Algebra](algebra.md): [context-based](contexts.md) operations on different primitives and structures.
* [NDStructures](nd-structure.md)
* [Linear algebra](linear.md): matrices, operations and linear equations solving. To be moved to separate module.
Currently, supports basic API and multiple library back-ends.
* [Histograms](histograms.md): multidimensional histogram calculation and operations.
* [Expressions](expressions.md)
* Commons math integration

View File

@ -2,14 +2,14 @@
[![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382) [![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382)
![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg) ![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg)
[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22) [![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22)
[![Space](https://img.shields.io/maven-metadata/v?label=Space&metadataUrl=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fkscience%2Fkmath%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/) [![Space](https://img.shields.io/badge/dynamic/xml?color=orange&label=Space&query=//metadata/versioning/latest&url=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fspace%2Fkscience%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/)
# KMath # KMath
Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based analog to Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based
Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior architecture analog to Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior
designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like
be achieved with [kmath-for-real](/kmath-for-real) extension module. experience could be achieved with [kmath-for-real](/kmath-for-real) extension module.
[Documentation site (**WIP**)](https://mipt-npm.github.io/kmath/) [Documentation site (**WIP**)](https://mipt-npm.github.io/kmath/)
@ -21,26 +21,33 @@ be achieved with [kmath-for-real](/kmath-for-real) extension module.
# Goal # Goal
* Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS and Native). * Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS and Native)
.
* Provide basic multiplatform implementations for those abstractions (without significant performance optimization). * Provide basic multiplatform implementations for those abstractions (without significant performance optimization).
* Provide bindings and wrappers with those abstractions for popular optimized platform libraries. * Provide bindings and wrappers with those abstractions for popular optimized platform libraries.
## Non-goals ## Non-goals
* Be like NumPy. It was the idea at the beginning, but we decided that we can do better in terms of API. * Be like NumPy. It was the idea at the beginning, but we decided that we can do better in API.
* Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them. * Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them.
* Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually. * Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually.
* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like * Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like
for `Double` in the core. For that we will have specialization modules like `kmath-for-real`, which will give better for `Double` in the core. For that we will have specialization modules like `kmath-for-real`, which will give better
experience for those, who want to work with specific types. experience for those, who want to work with specific types.
## Features and stability ## Features and stability
KMath is a modular library. Different modules provide different features with different API stability guarantees. All core modules are released with the same version, but with different API change policy. The features are described in module definitions below. The module stability could have following levels: KMath is a modular library. Different modules provide different features with different API stability guarantees. All
core modules are released with the same version, but with different API change policy. The features are described in
module definitions below. The module stability could have the following levels:
* **PROTOTYPE**. On this level there are no compatibility guarantees. All methods and classes form those modules could break any moment. You can still use it, but be sure to fix the specific version. * **PROTOTYPE**. On this level there are no compatibility guarantees. All methods and classes form those modules could
* **EXPERIMENTAL**. The general API is decided, but some changes could be made. Volatile API is marked with `@UnstableKmathAPI` or other stability warning annotations. break any moment. You can still use it, but be sure to fix the specific version.
* **DEVELOPMENT**. API breaking generally follows semantic versioning ideology. There could be changes in minor versions, but not in patch versions. API is protected with [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator) tool. * **EXPERIMENTAL**. The general API is decided, but some changes could be made. Volatile API is marked
with `@UnstableKmathAPI` or other stability warning annotations.
* **DEVELOPMENT**. API breaking generally follows semantic versioning ideology. There could be changes in minor
versions, but not in patch versions. API is protected
with [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator) tool.
* **STABLE**. The API stabilized. Breaking changes are allowed only in major releases. * **STABLE**. The API stabilized. Breaking changes are allowed only in major releases.
<!--Current feature list is [here](/docs/features.md)--> <!--Current feature list is [here](/docs/features.md)-->
@ -86,8 +93,8 @@ feedback are also welcome.
## Performance ## Performance
Calculation performance is one of major goals of KMath in the future, but in some cases it is impossible to achieve Calculation performance is one of major goals of KMath in the future, but in some cases it is impossible to achieve both
both performance and flexibility. performance and flexibility.
We expect to focus on creating convenient universal API first and then work on increasing performance for specific We expect to focus on creating convenient universal API first and then work on increasing performance for specific
cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized
@ -96,12 +103,15 @@ better than SciPy.
## Requirements ## Requirements
KMath currently relies on JDK 11 for compilation and execution of Kotlin-JVM part. We recommend to use GraalVM-CE 11 for execution in order to get better performance. KMath currently relies on JDK 11 for compilation and execution of Kotlin-JVM part. We recommend to use GraalVM-CE 11 for
execution to get better performance.
### Repositories ### Repositories
Release and development artifacts are accessible from mipt-npm [Space](https://www.jetbrains.com/space/) repository `https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven` (see documentation of Release and development artifacts are accessible from mipt-npm [Space](https://www.jetbrains.com/space/)
[Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details). The repository could be reached through [repo.kotlin.link](https://repo.kotlin.link) proxy: repository `https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven` (see documentation of
[Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details). The repository could
be reached through [repo.kotlin.link](https://repo.kotlin.link) proxy:
```kotlin ```kotlin
repositories { repositories {
@ -119,6 +129,6 @@ Gradle `6.0+` is required for multiplatform artifacts.
## Contributing ## Contributing
The project requires a lot of additional work. The most important thing we need is a feedback about what features are The project requires a lot of additional work. The most important thing we need is a feedback about what features are
required the most. Feel free to create feature requests. We are also welcome to code contributions, required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues
especially in issues marked with marked with
[waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero) label. [waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero) label.

View File

@ -26,7 +26,7 @@ dependencies {
implementation(project(":kmath-ejml")) implementation(project(":kmath-ejml"))
implementation(project(":kmath-nd4j")) implementation(project(":kmath-nd4j"))
implementation(project(":kmath-tensors")) implementation(project(":kmath-tensors"))
implementation(project(":kmath-symja"))
implementation(project(":kmath-for-real")) implementation(project(":kmath-for-real"))
implementation("org.nd4j:nd4j-native:1.0.0-beta7") implementation("org.nd4j:nd4j-native:1.0.0-beta7")
@ -41,9 +41,11 @@ dependencies {
// } else // } else
implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7")
implementation("org.slf4j:slf4j-simple:1.7.30") implementation("org.slf4j:slf4j-simple:1.7.31")
// plotting // plotting
implementation("space.kscience:plotlykt-server:0.4.0") implementation("space.kscience:plotlykt-server:0.4.2")
//jafama
implementation(project(":kmath-jafama"))
} }
kotlin.sourceSets.all { kotlin.sourceSets.all {

View File

@ -10,7 +10,7 @@ import space.kscience.kmath.ast.rendering.LatexSyntaxRenderer
import space.kscience.kmath.ast.rendering.MathMLSyntaxRenderer import space.kscience.kmath.ast.rendering.MathMLSyntaxRenderer
import space.kscience.kmath.ast.rendering.renderWithStringBuilder import space.kscience.kmath.ast.rendering.renderWithStringBuilder
public fun main() { fun main() {
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath() val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath()
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst) val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
println("MathSyntax:") println("MathSyntax:")

View File

@ -9,12 +9,10 @@ import space.kscience.kmath.expressions.MstField
import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.interpret import space.kscience.kmath.expressions.interpret
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
fun main() { fun main() {
val expr = MstField { val expr = MstField {
val x = bindSymbol(x)
x * 2.0 + number(2.0) / x - 16.0 x * 2.0 + number(2.0) / x - 16.0
} }

View File

@ -5,25 +5,23 @@
package space.kscience.kmath.ast package space.kscience.kmath.ast
import space.kscience.kmath.asm.compileToExpression
import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.derivative
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.toExpression
import space.kscience.kmath.kotlingrad.toKotlingradExpression import space.kscience.kmath.kotlingrad.toKotlingradExpression
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
/** /**
* In this example, x^2-4*x-44 function is differentiated with Kotlin, and the autodiff result is compared with * In this example, *x<sup>2</sup> &minus; 4 x &minus; 44* function is differentiated with Kotlin, and the
* valid derivative. * derivation result is compared with valid derivative in a certain point.
*/ */
fun main() { fun main() {
val x by symbol val actualDerivative = "x^2-4*x-44"
.parseMath()
val actualDerivative = "x^2-4*x-44".parseMath()
.toKotlingradExpression(DoubleField) .toKotlingradExpression(DoubleField)
.derivative(x) .derivative(x)
val expectedDerivative = "2*x-4".parseMath().toExpression(DoubleField)
val expectedDerivative = "2*x-4".parseMath().compileToExpression(DoubleField) check(actualDerivative(x to 123.0) == expectedDerivative(x to 123.0))
assert(actualDerivative(x to 123.0) == expectedDerivative(x to 123.0))
} }

View File

@ -0,0 +1,27 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.ast
import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.derivative
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.expressions.toExpression
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.symja.toSymjaExpression
/**
* In this example, *x<sup>2</sup> &minus; 4 x &minus; 44* function is differentiated with Symja, and the
* derivation result is compared with valid derivative in a certain point.
*/
fun main() {
val actualDerivative = "x^2-4*x-44"
.parseMath()
.toSymjaExpression(DoubleField)
.derivative(x)
val expectedDerivative = "2*x-4".parseMath().toExpression(DoubleField)
check(actualDerivative(x to 123.0) == expectedDerivative(x to 123.0))
}

View File

@ -7,11 +7,15 @@ package space.kscience.kmath.commons.fit
import kotlinx.html.br import kotlinx.html.br
import kotlinx.html.h3 import kotlinx.html.h3
import space.kscience.kmath.commons.optimization.chiSquared import space.kscience.kmath.commons.expressions.DSProcessor
import space.kscience.kmath.commons.optimization.CMOptimizer
import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.expressions.chiSquaredExpression
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.optimization.FunctionOptimization import space.kscience.kmath.optimization.FunctionOptimizationTarget
import space.kscience.kmath.optimization.OptimizationResult import space.kscience.kmath.optimization.optimizeWith
import space.kscience.kmath.optimization.resultPoint
import space.kscience.kmath.optimization.resultValue
import space.kscience.kmath.real.DoubleVector import space.kscience.kmath.real.DoubleVector
import space.kscience.kmath.real.map import space.kscience.kmath.real.map
import space.kscience.kmath.real.step import space.kscience.kmath.real.step
@ -24,8 +28,7 @@ import space.kscience.plotly.models.TraceValues
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.sqrt import kotlin.math.sqrt
//Forward declaration of symbols that will be used in expressions. // Forward declaration of symbols that will be used in expressions.
// This declaration is required for
private val a by symbol private val a by symbol
private val b by symbol private val b by symbol
private val c by symbol private val c by symbol
@ -64,17 +67,21 @@ suspend fun main() {
val yErr = y.map { sqrt(it) }//RealVector.same(x.size, sigma) val yErr = y.map { sqrt(it) }//RealVector.same(x.size, sigma)
// compute differentiable chi^2 sum for given model ax^2 + bx + c // compute differentiable chi^2 sum for given model ax^2 + bx + c
val chi2 = FunctionOptimization.chiSquared(x, y, yErr) { x1 -> val chi2 = DSProcessor.chiSquaredExpression(x, y, yErr) { arg ->
//bind variables to autodiff context //bind variables to autodiff context
val a = bindSymbol(a) val a = bindSymbol(a)
val b = bindSymbol(b) val b = bindSymbol(b)
//Include default value for c if it is not provided as a parameter //Include default value for c if it is not provided as a parameter
val c = bindSymbolOrNull(c) ?: one val c = bindSymbolOrNull(c) ?: one
a * x1.pow(2) + b * x1 + c a * arg.pow(2) + b * arg + c
} }
//minimize the chi^2 in given starting point. Derivatives are not required, they are already included. //minimize the chi^2 in given starting point. Derivatives are not required, they are already included.
val result: OptimizationResult<Double> = chi2.minimize(a to 1.5, b to 0.9, c to 1.0) val result = chi2.optimizeWith(
CMOptimizer,
mapOf(a to 1.5, b to 0.9, c to 1.0),
FunctionOptimizationTarget.MINIMIZE
)
//display a page with plot and numerical results //display a page with plot and numerical results
val page = Plotly.page { val page = Plotly.page {
@ -91,7 +98,7 @@ suspend fun main() {
scatter { scatter {
mode = ScatterMode.lines mode = ScatterMode.lines
x(x) x(x)
y(x.map { result.point[a]!! * it.pow(2) + result.point[b]!! * it + 1 }) y(x.map { result.resultPoint[a]!! * it.pow(2) + result.resultPoint[b]!! * it + 1 })
name = "fit" name = "fit"
} }
} }
@ -100,7 +107,7 @@ suspend fun main() {
+"Fit result: $result" +"Fit result: $result"
} }
h3 { h3 {
+"Chi2/dof = ${result.value / (x.size - 3)}" +"Chi2/dof = ${result.resultValue / (x.size - 3)}"
} }
} }

View File

@ -0,0 +1,15 @@
/*
* 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.jafama
import space.kscience.kmath.operations.invoke
fun main() {
val a = 2.0
val b = StrictJafamaDoubleField { exp(a) }
println(JafamaDoubleField { b + a })
println(StrictJafamaDoubleField { ln(b) })
}

View File

@ -10,9 +10,6 @@ import space.kscience.kmath.chains.Chain
import space.kscience.kmath.chains.collectWithState import space.kscience.kmath.chains.collectWithState
import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.distributions.NormalDistribution
/**
* The state of distribution averager.
*/
private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0)
/** /**

View File

@ -32,7 +32,7 @@ fun main() {
// automatically build context most suited for given type. // automatically build context most suited for given type.
val autoField = AlgebraND.auto(DoubleField, dim, dim) val autoField = AlgebraND.auto(DoubleField, dim, dim)
// specialized nd-field for Double. It works as generic Double field as well // specialized nd-field for Double. It works as generic Double field as well.
val realField = AlgebraND.real(dim, dim) val realField = AlgebraND.real(dim, dim)
//A generic boxing field. It should be used for objects, not primitives. //A generic boxing field. It should be used for objects, not primitives.
val boxingField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim) val boxingField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim)

View File

@ -19,7 +19,7 @@ private fun DMatrixContext<Double, *>.simple() {
} }
private object D5 : Dimension { private object D5 : Dimension {
override val dim: UInt = 5u override val dim: Int = 5
} }
private fun DMatrixContext<Double, *>.custom() { private fun DMatrixContext<Double, *>.custom() {

View File

@ -17,7 +17,7 @@ fun main() = BroadcastDoubleTensorAlgebra { // work in context with broadcast m
dataset += fromArray( dataset += fromArray(
intArrayOf(5), intArrayOf(5),
doubleArrayOf(0.0, 1.0, 1.5, 3.0, 5.0) // rows means doubleArrayOf(0.0, 1.0, 1.5, 3.0, 5.0) // row means
) )
@ -28,7 +28,7 @@ fun main() = BroadcastDoubleTensorAlgebra { // work in context with broadcast m
println("Mean:\n$mean") println("Mean:\n$mean")
println("Standard deviation:\n$std") println("Standard deviation:\n$std")
// also we can calculate other statistic as minimum and maximum of rows // also, we can calculate other statistic as minimum and maximum of rows
println("Minimum:\n${dataset.min(0, false)}") println("Minimum:\n${dataset.min(0, false)}")
println("Maximum:\n${dataset.max(0, false)}") println("Maximum:\n${dataset.max(0, false)}")

View File

@ -42,7 +42,7 @@ fun main() = BroadcastDoubleTensorAlgebra {// work in context with linear operat
// get P, L, U such that PA = LU // get P, L, U such that PA = LU
val (p, l, u) = a.lu() val (p, l, u) = a.lu()
// check that P is permutation matrix // check P is permutation matrix
println("P:\n$p") println("P:\n$p")
// L is lower triangular matrix and U is upper triangular matrix // L is lower triangular matrix and U is upper triangular matrix
println("L:\n$l") println("L:\n$l")

View File

@ -186,7 +186,7 @@ fun main() = BroadcastDoubleTensorAlgebra {
x += fromArray( x += fromArray(
intArrayOf(5), intArrayOf(5),
doubleArrayOf(0.0, -1.0, -2.5, -3.0, 5.5) // rows means doubleArrayOf(0.0, -1.0, -2.5, -3.0, 5.5) // row means
) )

View File

@ -65,8 +65,8 @@ fun main(): Unit = BroadcastDoubleTensorAlgebra { // work in context with broad
val datasetReduced = v dot stack(listOf(xScaled, yScaled)) val datasetReduced = v dot stack(listOf(xScaled, yScaled))
println("Reduced data:\n$datasetReduced") println("Reduced data:\n$datasetReduced")
// we can restore original data from reduced data. // we can restore original data from reduced data;
// for example, find 7th element of dataset // for example, find 7th element of dataset.
val n = 7 val n = 7
val restored = (datasetReduced[n] dot v.view(intArrayOf(1, 2))) * std + mean val restored = (datasetReduced[n] dot v.view(intArrayOf(1, 2))) * std + mean
println("Original value:\n${dataset[n]}") println("Original value:\n${dataset[n]}")

View File

@ -7,7 +7,6 @@ kotlin.code.style=official
kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.stability.nowarn=true kotlin.mpp.stability.nowarn=true
kotlin.native.enableDependencyPropagation=false kotlin.native.enableDependencyPropagation=false
kotlin.parallel.tasks.in.project=true
org.gradle.configureondemand=true org.gradle.configureondemand=true
org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G org.gradle.jvmargs=-XX:MaxMetaspaceSize=2G
org.gradle.parallel=true org.gradle.parallel=true

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

2
gradlew vendored
View File

@ -72,7 +72,7 @@ case "`uname`" in
Darwin* ) Darwin* )
darwin=true darwin=true
;; ;;
MINGW* ) MSYS* | MINGW* )
msys=true msys=true
;; ;;
NONSTOP* ) NONSTOP* )

View File

@ -10,7 +10,7 @@ Performance and visualization extensions to MST API.
## Artifact: ## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-11`. The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-14`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -20,7 +20,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-ast:0.3.0-dev-11' implementation 'space.kscience:kmath-ast:0.3.0-dev-14'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -31,7 +31,7 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-ast:0.3.0-dev-11") implementation("space.kscience:kmath-ast:0.3.0-dev-14")
} }
``` ```
@ -45,11 +45,12 @@ special implementation of `Expression<T>` with implemented `invoke` function.
For example, the following builder: For example, the following builder:
```kotlin ```kotlin
import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.asm.* import space.kscience.kmath.asm.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { 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:
@ -89,11 +90,12 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
A similar feature is also available on JS. A similar feature is also available on JS.
```kotlin ```kotlin
import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.estree.* import space.kscience.kmath.estree.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { 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:
@ -104,15 +106,16 @@ var executable = function (constants, arguments) {
}; };
``` ```
JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation. JS also supports experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation.
Currently, only expressions inside `DoubleField` and `IntRing` are supported. Currently, only expressions inside `DoubleField` and `IntRing` are supported.
```kotlin ```kotlin
import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.wasm.* import space.kscience.kmath.wasm.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { x + 2 }.compileToExpression(DoubleField)
``` ```
An example of emitted Wasm IR in the form of WAT: An example of emitted Wasm IR in the form of WAT:
@ -158,7 +161,10 @@ public fun main() {
Result LaTeX: Result LaTeX:
<div style="background-color:white;">
![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{12}+x^{2/3}) ![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{12}+x^{2/3})
</div>
Result MathML (can be used with MathJax or other renderers): Result MathML (can be used with MathJax or other renderers):

View File

@ -37,17 +37,15 @@ kotlin.sourceSets {
jsMain { jsMain {
dependencies { dependencies {
implementation(npm("astring", "1.7.4")) implementation(npm("astring", "1.7.5"))
implementation(npm("binaryen", "100.0")) implementation(npm("binaryen", "101.0.0"))
implementation(npm("js-base64", "3.6.0")) implementation(npm("js-base64", "3.6.1"))
implementation(npm("webassembly", "0.11.0"))
} }
} }
jvmMain { jvmMain {
dependencies { dependencies {
implementation("org.ow2.asm:asm:9.1") implementation("org.ow2.asm:asm-commons:9.2")
implementation("org.ow2.asm:asm-commons:9.1")
} }
} }
} }
@ -63,25 +61,21 @@ readme {
feature( feature(
id = "expression-language", id = "expression-language",
description = "Expression language and its parser",
ref = "src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt" ref = "src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt"
) ) { "Expression language and its parser" }
feature( feature(
id = "mst-jvm-codegen", id = "mst-jvm-codegen",
description = "Dynamic MST to JVM bytecode compiler",
ref = "src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt" ref = "src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt"
) ) { "Dynamic MST to JVM bytecode compiler" }
feature( feature(
id = "mst-js-codegen", id = "mst-js-codegen",
description = "Dynamic MST to JS compiler",
ref = "src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt" ref = "src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt"
) ) { "Dynamic MST to JS compiler" }
feature( feature(
id = "rendering", id = "rendering",
description = "Extendable MST rendering",
ref = "src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt" ref = "src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt"
) ) { "Extendable MST rendering" }
} }

View File

@ -16,11 +16,12 @@ special implementation of `Expression<T>` with implemented `invoke` function.
For example, the following builder: For example, the following builder:
```kotlin ```kotlin
import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.asm.* import space.kscience.kmath.asm.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { 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:
@ -60,11 +61,12 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
A similar feature is also available on JS. A similar feature is also available on JS.
```kotlin ```kotlin
import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.estree.* import space.kscience.kmath.estree.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { 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,15 +77,16 @@ var executable = function (constants, arguments) {
}; };
``` ```
JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation. JS also supports experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation.
Currently, only expressions inside `DoubleField` and `IntRing` are supported. Currently, only expressions inside `DoubleField` and `IntRing` are supported.
```kotlin ```kotlin
import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.wasm.* import space.kscience.kmath.wasm.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { x + 2 }.compileToExpression(DoubleField)
``` ```
An example of emitted Wasm IR in the form of WAT: An example of emitted Wasm IR in the form of WAT:
@ -129,7 +132,10 @@ public fun main() {
Result LaTeX: Result LaTeX:
<div style="background-color:white;">
![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{12}+x^{2/3}) ![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{12}+x^{2/3})
</div>
Result MathML (can be used with MathJax or other renderers): Result MathML (can be used with MathJax or other renderers):

View File

@ -27,7 +27,7 @@ import space.kscience.kmath.misc.UnstableKMathAPI
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public object LatexSyntaxRenderer : SyntaxRenderer { public object LatexSyntaxRenderer : SyntaxRenderer {
public override fun render(node: MathSyntax, output: Appendable): Unit = output.run { override fun render(node: MathSyntax, output: Appendable): Unit = output.run {
fun render(syntax: MathSyntax) = render(syntax, output) fun render(syntax: MathSyntax) = render(syntax, output)
when (node) { when (node) {

View File

@ -16,7 +16,7 @@ import space.kscience.kmath.misc.UnstableKMathAPI
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public object MathMLSyntaxRenderer : SyntaxRenderer { public object MathMLSyntaxRenderer : SyntaxRenderer {
public override fun render(node: MathSyntax, output: Appendable) { override fun render(node: MathSyntax, output: Appendable) {
output.append("<math xmlns=\"https://www.w3.org/1998/Math/MathML\"><mrow>") output.append("<math xmlns=\"https://www.w3.org/1998/Math/MathML\"><mrow>")
renderPart(node, output) renderPart(node, output)
output.append("</mrow></math>") output.append("</mrow></math>")

View File

@ -29,7 +29,7 @@ public fun interface MathRenderer {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public open class FeaturedMathRenderer(public val features: List<RenderFeature>) : MathRenderer { public open class FeaturedMathRenderer(public val features: List<RenderFeature>) : MathRenderer {
public override fun render(mst: MST): MathSyntax { override fun render(mst: MST): MathSyntax {
for (feature in features) feature.render(this, mst)?.let { return it } for (feature in features) feature.render(this, mst)?.let { return it }
throw UnsupportedOperationException("Renderer $this has no appropriate feature to render node $mst.") throw UnsupportedOperationException("Renderer $this has no appropriate feature to render node $mst.")
} }
@ -54,9 +54,9 @@ public open class FeaturedMathRenderer(public val features: List<RenderFeature>)
@UnstableKMathAPI @UnstableKMathAPI
public open class FeaturedMathRendererWithPostProcess( public open class FeaturedMathRendererWithPostProcess(
features: List<RenderFeature>, features: List<RenderFeature>,
public val stages: List<PostProcessStage>, public val stages: List<PostProcessPhase>,
) : FeaturedMathRenderer(features) { ) : FeaturedMathRenderer(features) {
public override fun render(mst: MST): MathSyntax { override fun render(mst: MST): MathSyntax {
val res = super.render(mst) val res = super.render(mst)
for (stage in stages) stage.perform(res) for (stage in stages) stage.perform(res)
return res return res
@ -65,7 +65,7 @@ public open class FeaturedMathRendererWithPostProcess(
/** /**
* Logical unit of [MathSyntax] post-processing. * Logical unit of [MathSyntax] post-processing.
*/ */
public fun interface PostProcessStage { public fun interface PostProcessPhase {
/** /**
* Performs the specified action over [MathSyntax]. * Performs the specified action over [MathSyntax].
*/ */
@ -102,7 +102,7 @@ public open class FeaturedMathRendererWithPostProcess(
// Printing terminal nodes as string // Printing terminal nodes as string
PrintNumeric, PrintNumeric,
PrintSymbolic, PrintSymbol,
), ),
listOf( listOf(
BetterExponent, BetterExponent,

View File

@ -8,7 +8,7 @@ package space.kscience.kmath.ast.rendering
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
/** /**
* Mathematical typography syntax node. * Syntax node for mathematical typography.
* *
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@ -150,9 +150,9 @@ public data class OperandSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class UnaryOperatorSyntax( public data class UnaryOperatorSyntax(
public override val operation: String, override val operation: String,
public var prefix: MathSyntax, public var prefix: MathSyntax,
public override val operand: OperandSyntax, override val operand: OperandSyntax,
) : UnarySyntax() { ) : UnarySyntax() {
init { init {
operand.parent = this operand.parent = this
@ -166,8 +166,8 @@ public data class UnaryOperatorSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class UnaryPlusSyntax( public data class UnaryPlusSyntax(
public override val operation: String, override val operation: String,
public override val operand: OperandSyntax, override val operand: OperandSyntax,
) : UnarySyntax() { ) : UnarySyntax() {
init { init {
operand.parent = this operand.parent = this
@ -181,8 +181,8 @@ public data class UnaryPlusSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class UnaryMinusSyntax( public data class UnaryMinusSyntax(
public override val operation: String, override val operation: String,
public override val operand: OperandSyntax, override val operand: OperandSyntax,
) : UnarySyntax() { ) : UnarySyntax() {
init { init {
operand.parent = this operand.parent = this
@ -197,8 +197,8 @@ public data class UnaryMinusSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class RadicalSyntax( public data class RadicalSyntax(
public override val operation: String, override val operation: String,
public override val operand: MathSyntax, override val operand: MathSyntax,
) : UnarySyntax() { ) : UnarySyntax() {
init { init {
operand.parent = this operand.parent = this
@ -215,8 +215,8 @@ public data class RadicalSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class ExponentSyntax( public data class ExponentSyntax(
public override val operation: String, override val operation: String,
public override val operand: OperandSyntax, override val operand: OperandSyntax,
public var useOperatorForm: Boolean, public var useOperatorForm: Boolean,
) : UnarySyntax() { ) : UnarySyntax() {
init { init {
@ -233,9 +233,9 @@ public data class ExponentSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class SuperscriptSyntax( public data class SuperscriptSyntax(
public override val operation: String, override val operation: String,
public override val left: MathSyntax, override val left: MathSyntax,
public override val right: MathSyntax, override val right: MathSyntax,
) : BinarySyntax() { ) : BinarySyntax() {
init { init {
left.parent = this left.parent = this
@ -252,9 +252,9 @@ public data class SuperscriptSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class SubscriptSyntax( public data class SubscriptSyntax(
public override val operation: String, override val operation: String,
public override val left: MathSyntax, override val left: MathSyntax,
public override val right: MathSyntax, override val right: MathSyntax,
) : BinarySyntax() { ) : BinarySyntax() {
init { init {
left.parent = this left.parent = this
@ -270,10 +270,10 @@ public data class SubscriptSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class BinaryOperatorSyntax( public data class BinaryOperatorSyntax(
public override val operation: String, override val operation: String,
public var prefix: MathSyntax, public var prefix: MathSyntax,
public override val left: MathSyntax, override val left: MathSyntax,
public override val right: MathSyntax, override val right: MathSyntax,
) : BinarySyntax() { ) : BinarySyntax() {
init { init {
left.parent = this left.parent = this
@ -290,9 +290,9 @@ public data class BinaryOperatorSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class BinaryPlusSyntax( public data class BinaryPlusSyntax(
public override val operation: String, override val operation: String,
public override val left: OperandSyntax, override val left: OperandSyntax,
public override val right: OperandSyntax, override val right: OperandSyntax,
) : BinarySyntax() { ) : BinarySyntax() {
init { init {
left.parent = this left.parent = this
@ -301,7 +301,7 @@ public data class BinaryPlusSyntax(
} }
/** /**
* Represents binary, infix subtraction (*42 - 42*). * Represents binary, infix subtraction (*42 &minus; 42*).
* *
* @param left The minuend. * @param left The minuend.
* @param right The subtrahend. * @param right The subtrahend.
@ -309,9 +309,9 @@ public data class BinaryPlusSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class BinaryMinusSyntax( public data class BinaryMinusSyntax(
public override val operation: String, override val operation: String,
public override val left: OperandSyntax, override val left: OperandSyntax,
public override val right: OperandSyntax, override val right: OperandSyntax,
) : BinarySyntax() { ) : BinarySyntax() {
init { init {
left.parent = this left.parent = this
@ -329,9 +329,9 @@ public data class BinaryMinusSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class FractionSyntax( public data class FractionSyntax(
public override val operation: String, override val operation: String,
public override val left: OperandSyntax, override val left: OperandSyntax,
public override val right: OperandSyntax, override val right: OperandSyntax,
public var infix: Boolean, public var infix: Boolean,
) : BinarySyntax() { ) : BinarySyntax() {
init { init {
@ -349,9 +349,9 @@ public data class FractionSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class RadicalWithIndexSyntax( public data class RadicalWithIndexSyntax(
public override val operation: String, override val operation: String,
public override val left: MathSyntax, override val left: MathSyntax,
public override val right: MathSyntax, override val right: MathSyntax,
) : BinarySyntax() { ) : BinarySyntax() {
init { init {
left.parent = this left.parent = this
@ -369,9 +369,9 @@ public data class RadicalWithIndexSyntax(
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class MultiplicationSyntax( public data class MultiplicationSyntax(
public override val operation: String, override val operation: String,
public override val left: OperandSyntax, override val left: OperandSyntax,
public override val right: OperandSyntax, override val right: OperandSyntax,
public var times: Boolean, public var times: Boolean,
) : BinarySyntax() { ) : BinarySyntax() {
init { init {

View File

@ -9,7 +9,7 @@ import space.kscience.kmath.misc.UnstableKMathAPI
/** /**
* Abstraction of writing [MathSyntax] as a string of an actual markup language. Typical implementation should * Abstraction of writing [MathSyntax] as a string of an actual markup language. Typical implementation should
* involve traversal of MathSyntax with handling each its subtype. * involve traversal of MathSyntax with handling each subtype.
* *
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */

View File

@ -13,13 +13,12 @@ import space.kscience.kmath.operations.*
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
* Prints any [MST.Symbolic] as a [SymbolSyntax] containing the [MST.Symbolic.value] of it. * Prints any [Symbol] as a [SymbolSyntax] containing the [Symbol.identity] of it.
* *
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public object PrintSymbolic : RenderFeature { public val PrintSymbol: RenderFeature = RenderFeature { _, node ->
public override fun render(renderer: FeaturedMathRenderer, node: MST): SymbolSyntax? =
if (node !is Symbol) null if (node !is Symbol) null
else SymbolSyntax(string = node.identity) else SymbolSyntax(string = node.identity)
} }
@ -30,8 +29,8 @@ public object PrintSymbolic : RenderFeature {
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public object PrintNumeric : RenderFeature { public val PrintNumeric: RenderFeature = RenderFeature { _, node ->
public override fun render(renderer: FeaturedMathRenderer, node: MST): NumberSyntax? = if (node !is MST.Numeric) if (node !is MST.Numeric)
null null
else else
NumberSyntax(string = node.value.toString()) NumberSyntax(string = node.value.toString())
@ -50,7 +49,7 @@ else
NumberSyntax(string = s) NumberSyntax(string = s)
/** /**
* Special printing for numeric types which are printed in form of * Special printing for numeric types that are printed in form of
* *('-'? (DIGIT+ ('.' DIGIT+)? ('E' '-'? DIGIT+)? | 'Infinity')) | 'NaN'*. * *('-'? (DIGIT+ ('.' DIGIT+)? ('E' '-'? DIGIT+)? | 'Infinity')) | 'NaN'*.
* *
* @property types The suitable types. * @property types The suitable types.
@ -58,7 +57,7 @@ else
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : RenderFeature { public class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : RenderFeature {
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? { override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
if (node !is MST.Numeric || node.value::class !in types) return null if (node !is MST.Numeric || node.value::class !in types) return null
val toString = when (val v = node.value) { val toString = when (val v = node.value) {
@ -111,14 +110,14 @@ public class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : Rend
} }
/** /**
* Special printing for numeric types which are printed in form of *'-'? DIGIT+*. * Special printing for numeric types that are printed in form of *'-'? DIGIT+*.
* *
* @property types The suitable types. * @property types The suitable types.
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class PrettyPrintIntegers(public val types: Set<KClass<out Number>>) : RenderFeature { public class PrettyPrintIntegers(public val types: Set<KClass<out Number>>) : RenderFeature {
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? = override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? =
if (node !is MST.Numeric || node.value::class !in types) if (node !is MST.Numeric || node.value::class !in types)
null null
else else
@ -141,7 +140,7 @@ public class PrettyPrintIntegers(public val types: Set<KClass<out Number>>) : Re
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class PrettyPrintPi(public val symbols: Set<String>) : RenderFeature { public class PrettyPrintPi(public val symbols: Set<String>) : RenderFeature {
public override fun render(renderer: FeaturedMathRenderer, node: MST): SpecialSymbolSyntax? = override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? =
if (node !is Symbol || node.identity !in symbols) if (node !is Symbol || node.identity !in symbols)
null null
else else
@ -156,7 +155,7 @@ public class PrettyPrintPi(public val symbols: Set<String>) : RenderFeature {
} }
/** /**
* Abstract printing of unary operations which discards [MST] if their operation is not in [operations] or its type is * Abstract printing of unary operations that discards [MST] if their operation is not in [operations] or its type is
* not [MST.Unary]. * not [MST.Unary].
* *
* @param operations the allowed operations. If `null`, any operation is accepted. * @param operations the allowed operations. If `null`, any operation is accepted.
@ -177,7 +176,7 @@ public abstract class Unary(public val operations: Collection<String>?) : Render
} }
/** /**
* Abstract printing of unary operations which discards [MST] if their operation is not in [operations] or its type is * Abstract printing of unary operations that discards [MST] if their operation is not in [operations] or its type is
* not [MST.Binary]. * not [MST.Binary].
* *
* @property operations the allowed operations. If `null`, any operation is accepted. * @property operations the allowed operations. If `null`, any operation is accepted.
@ -203,7 +202,7 @@ public abstract class Binary(public val operations: Collection<String>?) : Rende
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class BinaryPlus(operations: Collection<String>?) : Binary(operations) { public class BinaryPlus(operations: Collection<String>?) : Binary(operations) {
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): BinaryPlusSyntax = override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
BinaryPlusSyntax( BinaryPlusSyntax(
operation = node.operation, operation = node.operation,
left = OperandSyntax(parent.render(node.left), true), left = OperandSyntax(parent.render(node.left), true),
@ -225,7 +224,7 @@ public class BinaryPlus(operations: Collection<String>?) : Binary(operations) {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class BinaryMinus(operations: Collection<String>?) : Binary(operations) { public class BinaryMinus(operations: Collection<String>?) : Binary(operations) {
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): BinaryMinusSyntax = override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
BinaryMinusSyntax( BinaryMinusSyntax(
operation = node.operation, operation = node.operation,
left = OperandSyntax(operand = parent.render(node.left), parentheses = true), left = OperandSyntax(operand = parent.render(node.left), parentheses = true),
@ -247,7 +246,7 @@ public class BinaryMinus(operations: Collection<String>?) : Binary(operations) {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class UnaryPlus(operations: Collection<String>?) : Unary(operations) { public class UnaryPlus(operations: Collection<String>?) : Unary(operations) {
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryPlusSyntax = UnaryPlusSyntax( override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryPlusSyntax(
operation = node.operation, operation = node.operation,
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true), operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
) )
@ -267,7 +266,7 @@ public class UnaryPlus(operations: Collection<String>?) : Unary(operations) {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class UnaryMinus(operations: Collection<String>?) : Unary(operations) { public class UnaryMinus(operations: Collection<String>?) : Unary(operations) {
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryMinusSyntax = UnaryMinusSyntax( override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryMinusSyntax(
operation = node.operation, operation = node.operation,
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true), operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
) )
@ -287,7 +286,7 @@ public class UnaryMinus(operations: Collection<String>?) : Unary(operations) {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class Fraction(operations: Collection<String>?) : Binary(operations) { public class Fraction(operations: Collection<String>?) : Binary(operations) {
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): FractionSyntax = FractionSyntax( override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = FractionSyntax(
operation = node.operation, operation = node.operation,
left = OperandSyntax(operand = parent.render(node.left), parentheses = true), left = OperandSyntax(operand = parent.render(node.left), parentheses = true),
right = OperandSyntax(operand = parent.render(node.right), parentheses = true), right = OperandSyntax(operand = parent.render(node.right), parentheses = true),
@ -309,7 +308,7 @@ public class Fraction(operations: Collection<String>?) : Binary(operations) {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class BinaryOperator(operations: Collection<String>?) : Binary(operations) { public class BinaryOperator(operations: Collection<String>?) : Binary(operations) {
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): BinaryOperatorSyntax = override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
BinaryOperatorSyntax( BinaryOperatorSyntax(
operation = node.operation, operation = node.operation,
prefix = OperatorNameSyntax(name = node.operation), prefix = OperatorNameSyntax(name = node.operation),
@ -332,7 +331,7 @@ public class BinaryOperator(operations: Collection<String>?) : Binary(operations
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class UnaryOperator(operations: Collection<String>?) : Unary(operations) { public class UnaryOperator(operations: Collection<String>?) : Unary(operations) {
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryOperatorSyntax = override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax =
UnaryOperatorSyntax( UnaryOperatorSyntax(
operation = node.operation, operation = node.operation,
prefix = OperatorNameSyntax(node.operation), prefix = OperatorNameSyntax(node.operation),
@ -354,7 +353,7 @@ public class UnaryOperator(operations: Collection<String>?) : Unary(operations)
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class Power(operations: Collection<String>?) : Binary(operations) { public class Power(operations: Collection<String>?) : Binary(operations) {
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): SuperscriptSyntax = override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
SuperscriptSyntax( SuperscriptSyntax(
operation = node.operation, operation = node.operation,
left = OperandSyntax(parent.render(node.left), true), left = OperandSyntax(parent.render(node.left), true),
@ -374,7 +373,7 @@ public class Power(operations: Collection<String>?) : Binary(operations) {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class SquareRoot(operations: Collection<String>?) : Unary(operations) { public class SquareRoot(operations: Collection<String>?) : Unary(operations) {
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): RadicalSyntax = override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax =
RadicalSyntax(operation = node.operation, operand = parent.render(node.value)) RadicalSyntax(operation = node.operation, operand = parent.render(node.value))
public companion object { public companion object {
@ -392,7 +391,7 @@ public class SquareRoot(operations: Collection<String>?) : Unary(operations) {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class Exponent(operations: Collection<String>?) : Unary(operations) { public class Exponent(operations: Collection<String>?) : Unary(operations) {
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): ExponentSyntax = ExponentSyntax( override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = ExponentSyntax(
operation = node.operation, operation = node.operation,
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true), operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
useOperatorForm = true, useOperatorForm = true,
@ -413,7 +412,7 @@ public class Exponent(operations: Collection<String>?) : Unary(operations) {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class Multiplication(operations: Collection<String>?) : Binary(operations) { public class Multiplication(operations: Collection<String>?) : Binary(operations) {
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MultiplicationSyntax = override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
MultiplicationSyntax( MultiplicationSyntax(
operation = node.operation, operation = node.operation,
left = OperandSyntax(operand = parent.render(node.left), parentheses = true), left = OperandSyntax(operand = parent.render(node.left), parentheses = true),
@ -436,7 +435,7 @@ public class Multiplication(operations: Collection<String>?) : Binary(operations
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class InverseTrigonometricOperations(operations: Collection<String>?) : Unary(operations) { public class InverseTrigonometricOperations(operations: Collection<String>?) : Unary(operations) {
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryOperatorSyntax = override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax =
UnaryOperatorSyntax( UnaryOperatorSyntax(
operation = node.operation, operation = node.operation,
prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "arc")), prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "arc")),
@ -463,7 +462,7 @@ public class InverseTrigonometricOperations(operations: Collection<String>?) : U
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class InverseHyperbolicOperations(operations: Collection<String>?) : Unary(operations) { public class InverseHyperbolicOperations(operations: Collection<String>?) : Unary(operations) {
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryOperatorSyntax = override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax =
UnaryOperatorSyntax( UnaryOperatorSyntax(
operation = node.operation, operation = node.operation,
prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "ar")), prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "ar")),

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.ast.rendering package space.kscience.kmath.ast.rendering
import space.kscience.kmath.ast.rendering.FeaturedMathRendererWithPostProcess.PostProcessPhase
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.FieldOperations import space.kscience.kmath.operations.FieldOperations
import space.kscience.kmath.operations.GroupOperations import space.kscience.kmath.operations.GroupOperations
@ -17,8 +18,8 @@ import space.kscience.kmath.operations.RingOperations
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostProcessStage { public val BetterMultiplication: PostProcessPhase = PostProcessPhase { node ->
public override fun perform(node: MathSyntax): Unit = when (node) { fun perform(node: MathSyntax): Unit = when (node) {
is NumberSyntax -> Unit is NumberSyntax -> Unit
is SymbolSyntax -> Unit is SymbolSyntax -> Unit
is OperatorNameSyntax -> Unit is OperatorNameSyntax -> Unit
@ -81,6 +82,8 @@ public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostPro
perform(node.right) perform(node.right)
} }
} }
perform(node)
} }
/** /**
@ -89,68 +92,68 @@ public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostPro
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public object BetterFraction : FeaturedMathRendererWithPostProcess.PostProcessStage { public val BetterFraction: PostProcessPhase = PostProcessPhase { node ->
private fun perform0(node: MathSyntax, infix: Boolean = false): Unit = when (node) { fun perform(node: MathSyntax, infix: Boolean = false): Unit = when (node) {
is NumberSyntax -> Unit is NumberSyntax -> Unit
is SymbolSyntax -> Unit is SymbolSyntax -> Unit
is OperatorNameSyntax -> Unit is OperatorNameSyntax -> Unit
is SpecialSymbolSyntax -> Unit is SpecialSymbolSyntax -> Unit
is OperandSyntax -> perform0(node.operand, infix) is OperandSyntax -> perform(node.operand, infix)
is UnaryOperatorSyntax -> { is UnaryOperatorSyntax -> {
perform0(node.prefix, infix) perform(node.prefix, infix)
perform0(node.operand, infix) perform(node.operand, infix)
} }
is UnaryPlusSyntax -> perform0(node.operand, infix) is UnaryPlusSyntax -> perform(node.operand, infix)
is UnaryMinusSyntax -> perform0(node.operand, infix) is UnaryMinusSyntax -> perform(node.operand, infix)
is RadicalSyntax -> perform0(node.operand, infix) is RadicalSyntax -> perform(node.operand, infix)
is ExponentSyntax -> perform0(node.operand, infix) is ExponentSyntax -> perform(node.operand, infix)
is SuperscriptSyntax -> { is SuperscriptSyntax -> {
perform0(node.left, true) perform(node.left, true)
perform0(node.right, true) perform(node.right, true)
} }
is SubscriptSyntax -> { is SubscriptSyntax -> {
perform0(node.left, true) perform(node.left, true)
perform0(node.right, true) perform(node.right, true)
} }
is BinaryOperatorSyntax -> { is BinaryOperatorSyntax -> {
perform0(node.prefix, infix) perform(node.prefix, infix)
perform0(node.left, infix) perform(node.left, infix)
perform0(node.right, infix) perform(node.right, infix)
} }
is BinaryPlusSyntax -> { is BinaryPlusSyntax -> {
perform0(node.left, infix) perform(node.left, infix)
perform0(node.right, infix) perform(node.right, infix)
} }
is BinaryMinusSyntax -> { is BinaryMinusSyntax -> {
perform0(node.left, infix) perform(node.left, infix)
perform0(node.right, infix) perform(node.right, infix)
} }
is FractionSyntax -> { is FractionSyntax -> {
node.infix = infix node.infix = infix
perform0(node.left, infix) perform(node.left, infix)
perform0(node.right, infix) perform(node.right, infix)
} }
is RadicalWithIndexSyntax -> { is RadicalWithIndexSyntax -> {
perform0(node.left, true) perform(node.left, true)
perform0(node.right, true) perform(node.right, true)
} }
is MultiplicationSyntax -> { is MultiplicationSyntax -> {
perform0(node.left, infix) perform(node.left, infix)
perform0(node.right, infix) perform(node.right, infix)
} }
} }
public override fun perform(node: MathSyntax): Unit = perform0(node) perform(node)
} }
/** /**
@ -160,39 +163,37 @@ public object BetterFraction : FeaturedMathRendererWithPostProcess.PostProcessSt
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public object BetterExponent : FeaturedMathRendererWithPostProcess.PostProcessStage { public val BetterExponent: PostProcessPhase = PostProcessPhase { node ->
private fun perform0(node: MathSyntax): Boolean { fun perform(node: MathSyntax): Boolean {
return when (node) { return when (node) {
is NumberSyntax -> false is NumberSyntax -> false
is SymbolSyntax -> false is SymbolSyntax -> false
is OperatorNameSyntax -> false is OperatorNameSyntax -> false
is SpecialSymbolSyntax -> false is SpecialSymbolSyntax -> false
is OperandSyntax -> perform0(node.operand) is OperandSyntax -> perform(node.operand)
is UnaryOperatorSyntax -> perform0(node.prefix) || perform0(node.operand) is UnaryOperatorSyntax -> perform(node.prefix) || perform(node.operand)
is UnaryPlusSyntax -> perform0(node.operand) is UnaryPlusSyntax -> perform(node.operand)
is UnaryMinusSyntax -> perform0(node.operand) is UnaryMinusSyntax -> perform(node.operand)
is RadicalSyntax -> true is RadicalSyntax -> true
is ExponentSyntax -> { is ExponentSyntax -> {
val r = perform0(node.operand) val r = perform(node.operand)
node.useOperatorForm = r node.useOperatorForm = r
r r
} }
is SuperscriptSyntax -> true is SuperscriptSyntax -> true
is SubscriptSyntax -> true is SubscriptSyntax -> true
is BinaryOperatorSyntax -> perform0(node.prefix) || perform0(node.left) || perform0(node.right) is BinaryOperatorSyntax -> perform(node.prefix) || perform(node.left) || perform(node.right)
is BinaryPlusSyntax -> perform0(node.left) || perform0(node.right) is BinaryPlusSyntax -> perform(node.left) || perform(node.right)
is BinaryMinusSyntax -> perform0(node.left) || perform0(node.right) is BinaryMinusSyntax -> perform(node.left) || perform(node.right)
is FractionSyntax -> true is FractionSyntax -> true
is RadicalWithIndexSyntax -> true is RadicalWithIndexSyntax -> true
is MultiplicationSyntax -> perform0(node.left) || perform0(node.right) is MultiplicationSyntax -> perform(node.left) || perform(node.right)
} }
} }
public override fun perform(node: MathSyntax) { perform(node)
perform0(node)
}
} }
/** /**
@ -203,8 +204,8 @@ public object BetterExponent : FeaturedMathRendererWithPostProcess.PostProcessSt
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) -> Int) : public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) -> Int) :
FeaturedMathRendererWithPostProcess.PostProcessStage { PostProcessPhase {
public override fun perform(node: MathSyntax): Unit = when (node) { override fun perform(node: MathSyntax): Unit = when (node) {
is NumberSyntax -> Unit is NumberSyntax -> Unit
is SymbolSyntax -> Unit is SymbolSyntax -> Unit
is OperatorNameSyntax -> Unit is OperatorNameSyntax -> Unit

View File

@ -11,7 +11,6 @@ import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.interpret import space.kscience.kmath.expressions.interpret
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.IntRing import space.kscience.kmath.operations.IntRing
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
@ -22,7 +21,7 @@ internal class TestCompilerConsistencyWithInterpreter {
val mst = MstRing { val mst = MstRing {
binaryOperationFunction("+")( binaryOperationFunction("+")(
unaryOperationFunction("+")( unaryOperationFunction("+")(
(bindSymbol(x) - (2.toByte() + (scale( (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()
@ -42,7 +41,7 @@ internal class TestCompilerConsistencyWithInterpreter {
fun doubleField() = runCompilerTest { fun doubleField() = runCompilerTest {
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 - (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

View File

@ -9,7 +9,6 @@ import space.kscience.kmath.expressions.MstExtendedField
import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
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
@ -47,19 +46,19 @@ internal class TestCompilerOperations {
@Test @Test
fun testSubtract() = runCompilerTest { fun testSubtract() = runCompilerTest {
val expr = MstExtendedField { bindSymbol(x) - bindSymbol(x) }.compileToExpression(DoubleField) val expr = MstExtendedField { x - x }.compileToExpression(DoubleField)
assertEquals(0.0, expr(x to 2.0)) assertEquals(0.0, expr(x to 2.0))
} }
@Test @Test
fun testDivide() = runCompilerTest { fun testDivide() = runCompilerTest {
val expr = MstExtendedField { bindSymbol(x) / bindSymbol(x) }.compileToExpression(DoubleField) val expr = MstExtendedField { x / x }.compileToExpression(DoubleField)
assertEquals(1.0, expr(x to 2.0)) assertEquals(1.0, expr(x to 2.0))
} }
@Test @Test
fun testPower() = runCompilerTest { fun testPower() = runCompilerTest {
val expr = MstExtendedField { bindSymbol(x) pow 2 }.compileToExpression(DoubleField) val expr = MstExtendedField { x pow 2 }.compileToExpression(DoubleField)
assertEquals(4.0, expr(x to 2.0)) assertEquals(4.0, expr(x to 2.0))
} }
} }

View File

@ -9,7 +9,6 @@ import space.kscience.kmath.expressions.MstRing
import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.IntRing import space.kscience.kmath.operations.IntRing
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
@ -18,13 +17,13 @@ import kotlin.test.assertFailsWith
internal class TestCompilerVariables { internal class TestCompilerVariables {
@Test @Test
fun testVariable() = runCompilerTest { fun testVariable() = runCompilerTest {
val expr = MstRing { bindSymbol(x) }.compileToExpression(IntRing) val expr = MstRing { x }.compileToExpression(IntRing)
assertEquals(1, expr(x to 1)) assertEquals(1, expr(x to 1))
} }
@Test @Test
fun testUndefinedVariableFails() = runCompilerTest { fun testUndefinedVariableFails() = runCompilerTest {
val expr = MstRing { bindSymbol(x) }.compileToExpression(IntRing) val expr = MstRing { x }.compileToExpression(IntRing)
assertFailsWith<NoSuchElementException> { expr() } assertFailsWith<NoSuchElementException> { expr() }
} }
} }

View File

@ -3,6 +3,8 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@file:Suppress("ClassName")
package space.kscience.kmath.internal.estree package space.kscience.kmath.internal.estree
import kotlin.js.RegExp import kotlin.js.RegExp

View File

@ -27,11 +27,7 @@ internal sealed class WasmBuilder<T>(
lateinit var ctx: BinaryenModule lateinit var ctx: BinaryenModule
open fun visitSymbolic(mst: Symbol): ExpressionRef { open fun visitSymbolic(mst: Symbol): ExpressionRef {
try { algebra.bindSymbolOrNull(mst)?.let { return visitNumeric(Numeric(it)) }
algebra.bindSymbol(mst)
} catch (ignored: Throwable) {
null
}?.let { return visitNumeric(Numeric(it)) }
var idx = keys.indexOf(mst) var idx = keys.indexOf(mst)

View File

@ -7,7 +7,6 @@ package space.kscience.kmath.ast
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.math.sin import kotlin.math.sin
@ -17,18 +16,19 @@ import kotlin.time.measureTime
import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression
import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression
// TODO move to benchmarks when https://github.com/Kotlin/kotlinx-benchmark/pull/38 or similar feature is merged
internal class TestExecutionTime { internal class TestExecutionTime {
private companion object { private companion object {
private const val times = 1_000_000 private const val times = 1_000_000
private val x by symbol private val x by symbol
private val algebra: ExtendedField<Double> = DoubleField private val algebra = DoubleField
private val functional = DoubleField.expressionInExtendedField { private val functional = DoubleField.expressionInExtendedField {
bindSymbol(x) * const(2.0) + const(2.0) / bindSymbol(x) - const(16.0) / sin(bindSymbol(x)) bindSymbol(x) * const(2.0) + const(2.0) / bindSymbol(x) - const(16.0) / sin(bindSymbol(x))
} }
private val node = MstExtendedField { private val node = MstExtendedField {
bindSymbol(x) * number(2.0) + number(2.0) / bindSymbol(x) - number(16.0) / sin(bindSymbol(x)) x * number(2.0) + number(2.0) / x - number(16.0) / sin(x)
} }
private val mst = node.toExpression(DoubleField) private val mst = node.toExpression(DoubleField)
@ -43,7 +43,13 @@ internal class TestExecutionTime {
// }; // };
private val raw = Expression<Double> { args -> private val raw = Expression<Double> { args ->
args.getValue(x) * 2.0 + 2.0 / args.getValue(x) - 16.0 / sin(args.getValue(x)) val x = args[x]!!
x * 2.0 + 2.0 / x - 16.0 / sin(x)
}
private val justCalculate = { args: dynamic ->
val x = args[x].unsafeCast<Double>()
x * 2.0 + 2.0 / x - 16.0 / sin(x)
} }
} }
@ -51,21 +57,56 @@ internal class TestExecutionTime {
println(name) println(name)
val rng = Random(0) val rng = Random(0)
var sum = 0.0 var sum = 0.0
measureTime { repeat(times) { sum += expr(x to rng.nextDouble()) } }.also(::println) measureTime {
repeat(times) { sum += expr(x to rng.nextDouble()) }
}.also(::println)
} }
/**
* [Expression] created with [expressionInExtendedField].
*/
@Test @Test
fun functionalExpression() = invokeAndSum("functional", functional) fun functionalExpression() = invokeAndSum("functional", functional)
/**
* [Expression] created with [mstExpression].
*/
@Test @Test
fun mstExpression() = invokeAndSum("mst", mst) fun mstExpression() = invokeAndSum("mst", mst)
/**
* [Expression] created with [wasmCompileToExpression].
*/
@Test @Test
fun wasmExpression() = invokeAndSum("wasm", wasm) fun wasmExpression() = invokeAndSum("wasm", wasm)
/**
* [Expression] created with [estreeCompileToExpression].
*/
@Test @Test
fun estreeExpression() = invokeAndSum("estree", wasm) fun estreeExpression() = invokeAndSum("estree", wasm)
/**
* [Expression] implemented manually with `kotlin.math`.
*/
@Test @Test
fun rawExpression() = invokeAndSum("raw", raw) fun rawExpression() = invokeAndSum("raw", raw)
/**
* Direct computation w/o [Expression].
*/
@Test
fun justCalculateExpression() {
println("justCalculate")
val rng = Random(0)
var sum = 0.0
measureTime {
repeat(times) {
val arg = rng.nextDouble()
val o = js("{}")
o["x"] = arg
sum += justCalculate(o)
}
}.also(::println)
}
} }

View File

@ -10,6 +10,8 @@ import space.kscience.kmath.expressions.MST
import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.IntRing import space.kscience.kmath.operations.IntRing
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import space.kscience.kmath.estree.compile as estreeCompile import space.kscience.kmath.estree.compile as estreeCompile
import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression
import space.kscience.kmath.wasm.compile as wasmCompile import space.kscience.kmath.wasm.compile as wasmCompile
@ -34,6 +36,7 @@ private object ESTreeCompilerTestContext : CompilerTestContext {
} }
internal actual inline fun runCompilerTest(action: CompilerTestContext.() -> Unit) { internal actual inline fun runCompilerTest(action: CompilerTestContext.() -> Unit) {
contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) }
action(WasmCompilerTestContext) action(WasmCompilerTestContext)
action(ESTreeCompilerTestContext) action(ESTreeCompilerTestContext)
} }

View File

@ -11,7 +11,6 @@ import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.IntRing import space.kscience.kmath.operations.IntRing
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
@ -31,7 +30,7 @@ internal class TestWasmSpecific {
@Test @Test
fun argsPassing() { fun argsPassing() {
val res = MstExtendedField { bindSymbol(y) + bindSymbol(x).pow(10) }.compile( val res = MstExtendedField { y + x.pow(10) }.compile(
DoubleField, DoubleField,
x to 2.0, x to 2.0,
y to 100000000.0, y to 100000000.0,
@ -42,7 +41,7 @@ internal class TestWasmSpecific {
@Test @Test
fun powFunction() { fun powFunction() {
val expr = MstExtendedField { bindSymbol(x).pow(1.0 / 6.0) }.compileToExpression(DoubleField) val expr = MstExtendedField { x.pow(1.0 / 6.0) }.compileToExpression(DoubleField)
assertEquals(0.9730585187140817, expr(x to 0.8488554755054833)) assertEquals(0.9730585187140817, expr(x to 0.8488554755054833))
} }

View File

@ -57,13 +57,13 @@ internal fun MethodVisitor.label(): Label = Label().also(::visitLabel)
/** /**
* Creates a class name for [Expression] subclassed to implement [mst] provided. * Creates a class name for [Expression] subclassed to implement [mst] provided.
* *
* This methods helps to avoid collisions of class name to prevent loading several classes with the same name. If there * These methods help to avoid collisions of class name to prevent loading several classes with the same name. If there
* is a colliding class, change [collision] parameter or leave it `0` to check existing classes recursively. * is a colliding class, change [collision] parameter or leave it `0` to check existing classes recursively.
* *
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
internal tailrec fun buildName(mst: MST, collision: Int = 0): String { internal tailrec fun buildName(mst: MST, collision: Int = 0): String {
val name = "kscience.kmath.asm.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" val name = "space.kscience.kmath.asm.generated.CompiledExpression_${mst.hashCode()}_$collision"
try { try {
Class.forName(name) Class.forName(name)

View File

@ -10,6 +10,8 @@ import space.kscience.kmath.expressions.MST
import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.IntRing import space.kscience.kmath.operations.IntRing
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import space.kscience.kmath.asm.compile as asmCompile import space.kscience.kmath.asm.compile as asmCompile
import space.kscience.kmath.asm.compileToExpression as asmCompileToExpression import space.kscience.kmath.asm.compileToExpression as asmCompileToExpression
@ -22,4 +24,7 @@ private object AsmCompilerTestContext : CompilerTestContext {
asmCompile(algebra, arguments) asmCompile(algebra, arguments)
} }
internal actual inline fun runCompilerTest(action: CompilerTestContext.() -> Unit) = action(AsmCompilerTestContext) internal actual inline fun runCompilerTest(action: CompilerTestContext.() -> Unit) {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
action(AsmCompilerTestContext)
}

View File

@ -15,7 +15,7 @@ import space.kscience.kmath.operations.NumbersAddOperations
* A field over commons-math [DerivativeStructure]. * A field over commons-math [DerivativeStructure].
* *
* @property order The derivation order. * @property order The derivation order.
* @property bindings The map of bindings values. All bindings are considered free parameters * @param bindings The map of bindings values. All bindings are considered free parameters
*/ */
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public class DerivativeStructureField( public class DerivativeStructureField(
@ -25,13 +25,13 @@ public class DerivativeStructureField(
NumbersAddOperations<DerivativeStructure> { NumbersAddOperations<DerivativeStructure> {
public val numberOfVariables: Int = bindings.size public val numberOfVariables: Int = bindings.size
public override val zero: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order) } override val zero: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order) }
public override val one: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order, 1.0) } override val one: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order, 1.0) }
public override fun number(value: Number): DerivativeStructure = const(value.toDouble()) override fun number(value: Number): DerivativeStructure = const(value.toDouble())
/** /**
* A class that implements both [DerivativeStructure] and a [Symbol] * A class implementing both [DerivativeStructure] and [Symbol].
*/ */
public inner class DerivativeStructureSymbol( public inner class DerivativeStructureSymbol(
size: Int, size: Int,
@ -39,10 +39,10 @@ public class DerivativeStructureField(
symbol: Symbol, symbol: Symbol,
value: Double, value: Double,
) : DerivativeStructure(size, order, index, value), Symbol { ) : DerivativeStructure(size, order, index, value), Symbol {
public override val identity: String = symbol.identity override val identity: String = symbol.identity
public override fun toString(): String = identity override fun toString(): String = identity
public override fun equals(other: Any?): Boolean = this.identity == (other as? Symbol)?.identity override fun equals(other: Any?): Boolean = this.identity == (other as? Symbol)?.identity
public override fun hashCode(): Int = identity.hashCode() override fun hashCode(): Int = identity.hashCode()
} }
/** /**
@ -52,10 +52,10 @@ public class DerivativeStructureField(
key.identity to DerivativeStructureSymbol(numberOfVariables, index, key, value) key.identity to DerivativeStructureSymbol(numberOfVariables, index, key, value)
}.toMap() }.toMap()
public override fun const(value: Double): DerivativeStructure = DerivativeStructure(numberOfVariables, order, value) override fun const(value: Double): DerivativeStructure = DerivativeStructure(numberOfVariables, order, value)
public override fun bindSymbolOrNull(value: String): DerivativeStructureSymbol? = variables[value] override fun bindSymbolOrNull(value: String): DerivativeStructureSymbol? = variables[value]
public override fun bindSymbol(value: String): DerivativeStructureSymbol = variables.getValue(value) override fun bindSymbol(value: String): DerivativeStructureSymbol = variables.getValue(value)
public fun bindSymbolOrNull(symbol: Symbol): DerivativeStructureSymbol? = variables[symbol.identity] public fun bindSymbolOrNull(symbol: Symbol): DerivativeStructureSymbol? = variables[symbol.identity]
public fun bindSymbol(symbol: Symbol): DerivativeStructureSymbol = variables.getValue(symbol.identity) public fun bindSymbol(symbol: Symbol): DerivativeStructureSymbol = variables.getValue(symbol.identity)
@ -68,45 +68,48 @@ public class DerivativeStructureField(
public fun DerivativeStructure.derivative(vararg symbols: Symbol): Double = derivative(symbols.toList()) public fun DerivativeStructure.derivative(vararg symbols: Symbol): Double = derivative(symbols.toList())
public override fun DerivativeStructure.unaryMinus(): DerivativeStructure = negate() override fun DerivativeStructure.unaryMinus(): DerivativeStructure = negate()
public override fun add(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.add(b) override fun add(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.add(b)
public override fun scale(a: DerivativeStructure, value: Double): DerivativeStructure = a.multiply(value) override fun scale(a: DerivativeStructure, value: Double): DerivativeStructure = a.multiply(value)
public override fun multiply(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.multiply(b) override fun multiply(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.multiply(b)
public override fun divide(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.divide(b) override fun divide(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.divide(b)
public override fun sin(arg: DerivativeStructure): DerivativeStructure = arg.sin() override fun sin(arg: DerivativeStructure): DerivativeStructure = arg.sin()
public override fun cos(arg: DerivativeStructure): DerivativeStructure = arg.cos() override fun cos(arg: DerivativeStructure): DerivativeStructure = arg.cos()
public override fun tan(arg: DerivativeStructure): DerivativeStructure = arg.tan() override fun tan(arg: DerivativeStructure): DerivativeStructure = arg.tan()
public override fun asin(arg: DerivativeStructure): DerivativeStructure = arg.asin() override fun asin(arg: DerivativeStructure): DerivativeStructure = arg.asin()
public override fun acos(arg: DerivativeStructure): DerivativeStructure = arg.acos() override fun acos(arg: DerivativeStructure): DerivativeStructure = arg.acos()
public override fun atan(arg: DerivativeStructure): DerivativeStructure = arg.atan() override fun atan(arg: DerivativeStructure): DerivativeStructure = arg.atan()
public override fun sinh(arg: DerivativeStructure): DerivativeStructure = arg.sinh() override fun sinh(arg: DerivativeStructure): DerivativeStructure = arg.sinh()
public override fun cosh(arg: DerivativeStructure): DerivativeStructure = arg.cosh() override fun cosh(arg: DerivativeStructure): DerivativeStructure = arg.cosh()
public override fun tanh(arg: DerivativeStructure): DerivativeStructure = arg.tanh() override fun tanh(arg: DerivativeStructure): DerivativeStructure = arg.tanh()
public override fun asinh(arg: DerivativeStructure): DerivativeStructure = arg.asinh() override fun asinh(arg: DerivativeStructure): DerivativeStructure = arg.asinh()
public override fun acosh(arg: DerivativeStructure): DerivativeStructure = arg.acosh() override fun acosh(arg: DerivativeStructure): DerivativeStructure = arg.acosh()
public override fun atanh(arg: DerivativeStructure): DerivativeStructure = arg.atanh() override fun atanh(arg: DerivativeStructure): DerivativeStructure = arg.atanh()
public override fun power(arg: DerivativeStructure, pow: Number): DerivativeStructure = when (pow) { override fun power(arg: DerivativeStructure, pow: Number): DerivativeStructure = when (pow) {
is Double -> arg.pow(pow) is Double -> arg.pow(pow)
is Int -> arg.pow(pow) is Int -> arg.pow(pow)
else -> arg.pow(pow.toDouble()) else -> arg.pow(pow.toDouble())
} }
public fun power(arg: DerivativeStructure, pow: DerivativeStructure): DerivativeStructure = arg.pow(pow) public fun power(arg: DerivativeStructure, pow: DerivativeStructure): DerivativeStructure = arg.pow(pow)
public override fun exp(arg: DerivativeStructure): DerivativeStructure = arg.exp() override fun exp(arg: DerivativeStructure): DerivativeStructure = arg.exp()
public override fun ln(arg: DerivativeStructure): DerivativeStructure = arg.log() override fun ln(arg: DerivativeStructure): DerivativeStructure = arg.log()
public override operator fun DerivativeStructure.plus(b: Number): DerivativeStructure = add(b.toDouble()) override operator fun DerivativeStructure.plus(b: Number): DerivativeStructure = add(b.toDouble())
public override operator fun DerivativeStructure.minus(b: Number): DerivativeStructure = subtract(b.toDouble()) override operator fun DerivativeStructure.minus(b: Number): DerivativeStructure = subtract(b.toDouble())
public override operator fun Number.plus(b: DerivativeStructure): DerivativeStructure = b + this override operator fun Number.plus(b: DerivativeStructure): DerivativeStructure = b + this
public override operator fun Number.minus(b: DerivativeStructure): DerivativeStructure = b - this override operator fun Number.minus(b: DerivativeStructure): DerivativeStructure = b - this
} }
/**
* Auto-diff processor based on Commons-math [DerivativeStructure]
*/
public object DSProcessor : AutoDiffProcessor<Double, DerivativeStructure, DerivativeStructureField> { public object DSProcessor : AutoDiffProcessor<Double, DerivativeStructure, DerivativeStructureField> {
public override fun differentiate( override fun differentiate(
function: DerivativeStructureField.() -> DerivativeStructure, function: DerivativeStructureField.() -> DerivativeStructure,
): DerivativeStructureExpression = DerivativeStructureExpression(function) ): DerivativeStructureExpression = DerivativeStructureExpression(function)
} }
@ -117,13 +120,13 @@ public object DSProcessor : AutoDiffProcessor<Double, DerivativeStructure, Deriv
public class DerivativeStructureExpression( public class DerivativeStructureExpression(
public val function: DerivativeStructureField.() -> DerivativeStructure, public val function: DerivativeStructureField.() -> DerivativeStructure,
) : DifferentiableExpression<Double> { ) : DifferentiableExpression<Double> {
public override operator fun invoke(arguments: Map<Symbol, Double>): Double = override operator fun invoke(arguments: Map<Symbol, Double>): Double =
DerivativeStructureField(0, arguments).function().value DerivativeStructureField(0, arguments).function().value
/** /**
* Get the derivative expression with given orders * Get the derivative expression with given orders
*/ */
public override fun derivativeOrNull(symbols: List<Symbol>): Expression<Double> = Expression { arguments -> override fun derivativeOrNull(symbols: List<Symbol>): Expression<Double> = Expression { arguments ->
with(DerivativeStructureField(symbols.size, arguments)) { function().derivative(symbols) } with(DerivativeStructureField(symbols.size, arguments)) { function().derivative(symbols) }
} }
} }

View File

@ -15,18 +15,18 @@ import kotlin.reflect.KClass
import kotlin.reflect.cast import kotlin.reflect.cast
public class CMMatrix(public val origin: RealMatrix) : Matrix<Double> { public class CMMatrix(public val origin: RealMatrix) : Matrix<Double> {
public override val rowNum: Int get() = origin.rowDimension override val rowNum: Int get() = origin.rowDimension
public override val colNum: Int get() = origin.columnDimension override val colNum: Int get() = origin.columnDimension
public override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j) override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j)
} }
public class CMVector(public val origin: RealVector) : Point<Double> { public class CMVector(public val origin: RealVector) : Point<Double> {
public override val size: Int get() = origin.dimension override val size: Int get() = origin.dimension
public override operator fun get(index: Int): Double = origin.getEntry(index) override operator fun get(index: Int): Double = origin.getEntry(index)
public override operator fun iterator(): Iterator<Double> = origin.toArray().iterator() override operator fun iterator(): Iterator<Double> = origin.toArray().iterator()
} }
public fun RealVector.toPoint(): CMVector = CMVector(this) public fun RealVector.toPoint(): CMVector = CMVector(this)
@ -34,7 +34,7 @@ public fun RealVector.toPoint(): CMVector = CMVector(this)
public object CMLinearSpace : LinearSpace<Double, DoubleField> { public object CMLinearSpace : LinearSpace<Double, DoubleField> {
override val elementAlgebra: DoubleField get() = DoubleField override val elementAlgebra: DoubleField get() = DoubleField
public override fun buildMatrix( override fun buildMatrix(
rows: Int, rows: Int,
columns: Int, columns: Int,
initializer: DoubleField.(i: Int, j: Int) -> Double, initializer: DoubleField.(i: Int, j: Int) -> Double,
@ -73,16 +73,16 @@ public object CMLinearSpace : LinearSpace<Double, DoubleField> {
override fun Point<Double>.minus(other: Point<Double>): CMVector = override fun Point<Double>.minus(other: Point<Double>): CMVector =
toCM().origin.subtract(other.toCM().origin).wrap() toCM().origin.subtract(other.toCM().origin).wrap()
public override fun Matrix<Double>.dot(other: Matrix<Double>): CMMatrix = override fun Matrix<Double>.dot(other: Matrix<Double>): CMMatrix =
toCM().origin.multiply(other.toCM().origin).wrap() toCM().origin.multiply(other.toCM().origin).wrap()
public override fun Matrix<Double>.dot(vector: Point<Double>): CMVector = override fun Matrix<Double>.dot(vector: Point<Double>): CMVector =
toCM().origin.preMultiply(vector.toCM().origin).wrap() toCM().origin.preMultiply(vector.toCM().origin).wrap()
public override operator fun Matrix<Double>.minus(other: Matrix<Double>): CMMatrix = override operator fun Matrix<Double>.minus(other: Matrix<Double>): CMMatrix =
toCM().origin.subtract(other.toCM().origin).wrap() toCM().origin.subtract(other.toCM().origin).wrap()
public override operator fun Matrix<Double>.times(value: Double): CMMatrix = override operator fun Matrix<Double>.times(value: Double): CMMatrix =
toCM().origin.scalarMultiply(value).wrap() toCM().origin.scalarMultiply(value).wrap()
override fun Double.times(m: Matrix<Double>): CMMatrix = override fun Double.times(m: Matrix<Double>): CMMatrix =

View File

@ -5,35 +5,43 @@
package space.kscience.kmath.commons.random package space.kscience.kmath.commons.random
import kotlinx.coroutines.runBlocking
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.samplers.GaussianSampler
import space.kscience.kmath.misc.toIntExact
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.next
public class CMRandomGeneratorWrapper( public class CMRandomGeneratorWrapper(
public val factory: (IntArray) -> RandomGenerator, public val factory: (IntArray) -> RandomGenerator,
) : org.apache.commons.math3.random.RandomGenerator { ) : org.apache.commons.math3.random.RandomGenerator {
private var generator: RandomGenerator = factory(intArrayOf()) private var generator: RandomGenerator = factory(intArrayOf())
public override fun nextBoolean(): Boolean = generator.nextBoolean() override fun nextBoolean(): Boolean = generator.nextBoolean()
public override fun nextFloat(): Float = generator.nextDouble().toFloat() override fun nextFloat(): Float = generator.nextDouble().toFloat()
public override fun setSeed(seed: Int) { override fun setSeed(seed: Int) {
generator = factory(intArrayOf(seed)) generator = factory(intArrayOf(seed))
} }
public override fun setSeed(seed: IntArray) { override fun setSeed(seed: IntArray) {
generator = factory(seed) generator = factory(seed)
} }
public override fun setSeed(seed: Long) { override fun setSeed(seed: Long) {
setSeed(seed.toInt()) setSeed(seed.toIntExact())
} }
public override fun nextBytes(bytes: ByteArray) { override fun nextBytes(bytes: ByteArray) {
generator.fillBytes(bytes) generator.fillBytes(bytes)
} }
public override fun nextInt(): Int = generator.nextInt() override fun nextInt(): Int = generator.nextInt()
public override fun nextInt(n: Int): Int = generator.nextInt(n) override fun nextInt(n: Int): Int = generator.nextInt(n)
public override fun nextGaussian(): Double = TODO()
public override fun nextDouble(): Double = generator.nextDouble() @PerformancePitfall
public override fun nextLong(): Long = generator.nextLong() override fun nextGaussian(): Double = runBlocking { GaussianSampler(0.0, 1.0).next(generator) }
override fun nextDouble(): Double = generator.nextDouble()
override fun nextLong(): Long = generator.nextLong()
} }

View File

@ -32,7 +32,7 @@ public object Transformations {
/** /**
* Create a virtual buffer on top of array * Create a virtual buffer on top of array
*/ */
private fun Array<org.apache.commons.math3.complex.Complex>.asBuffer() = VirtualBuffer<Complex>(size) { private fun Array<org.apache.commons.math3.complex.Complex>.asBuffer() = VirtualBuffer(size) {
val value = get(it) val value = get(it)
Complex(value.real, value.imaginary) Complex(value.real, value.imaginary)
} }

View File

@ -16,7 +16,7 @@ internal inline fun diff(
order: Int, order: Int,
vararg parameters: Pair<Symbol, Double>, vararg parameters: Pair<Symbol, Double>,
block: DerivativeStructureField.() -> Unit, block: DerivativeStructureField.() -> Unit,
): Unit { ) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
DerivativeStructureField(order, mapOf(*parameters)).run(block) DerivativeStructureField(order, mapOf(*parameters)).run(block)
} }
@ -34,7 +34,7 @@ internal class AutoDiffTest {
println(z.derivative(x)) println(z.derivative(x))
println(z.derivative(y, x)) println(z.derivative(y, x))
assertEquals(z.derivative(x, y), z.derivative(y, x)) assertEquals(z.derivative(x, y), z.derivative(y, x))
//check that improper order cause failure // check improper order cause failure
assertFails { z.derivative(x, x, y) } assertFails { z.derivative(x, x, y) }
} }
} }

View File

@ -11,9 +11,12 @@ import space.kscience.kmath.commons.expressions.DerivativeStructureExpression
import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.expressions.Symbol.Companion.y import space.kscience.kmath.expressions.Symbol.Companion.y
import space.kscience.kmath.expressions.chiSquaredExpression
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.optimization.* import space.kscience.kmath.optimization.*
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.structures.map
import kotlin.math.pow import kotlin.math.pow
import kotlin.test.Test import kotlin.test.Test
@ -49,26 +52,27 @@ internal class OptimizeTest {
val sigma = 1.0 val sigma = 1.0
val generator = NormalDistribution(0.0, sigma) val generator = NormalDistribution(0.0, sigma)
val chain = generator.sample(RandomGenerator.default(112667)) val chain = generator.sample(RandomGenerator.default(112667))
val x = (1..100).map(Int::toDouble) val x = (1..100).map(Int::toDouble).asBuffer()
val y = x.map { val y = x.map {
it.pow(2) + it + 1 + chain.next() it.pow(2) + it + 1 + chain.next()
} }
val yErr = List(x.size) { sigma } val yErr = List(x.size) { sigma }.asBuffer()
val model = DSProcessor.differentiate { x1 -> val chi2 = DSProcessor.chiSquaredExpression(
x, y, yErr
) { arg ->
val cWithDefault = bindSymbolOrNull(c) ?: one val cWithDefault = bindSymbolOrNull(c) ?: one
bindSymbol(a) * x1.pow(2) + bindSymbol(b) * x1 + cWithDefault bindSymbol(a) * arg.pow(2) + bindSymbol(b) * arg + cWithDefault
} }
val chi2 = FunctionOptimization.chiSquared(x, y, yErr) { x1 -> val result: FunctionOptimization<Double> = chi2.optimizeWith(
val cWithDefault = bindSymbolOrNull(c) ?: one CMOptimizer,
bindSymbol(a) * x1.pow(2) + bindSymbol(b) * x1 + cWithDefault mapOf(a to 1.5, b to 0.9, c to 1.0),
} FunctionOptimizationTarget.MINIMIZE
)
val result = chi2.minimize(a to 1.5, b to 0.9, c to 1.0)
println(result) println(result)
println("Chi2/dof = ${result.value / (x.size - 3)}") println("Chi2/dof = ${result.resultValue / (x.size - 3)}")
} }
} }

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-11`. The Maven coordinates of this project are `space.kscience:kmath-complex:0.3.0-dev-14`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -18,7 +18,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-complex:0.3.0-dev-11' implementation 'space.kscience:kmath-complex:0.3.0-dev-14'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -29,6 +29,6 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-complex:0.3.0-dev-11") implementation("space.kscience:kmath-complex:0.3.0-dev-14")
} }
``` ```

View File

@ -54,8 +54,8 @@ private val PI_DIV_2 = Complex(PI / 2, 0)
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex>, NumbersAddOperations<Complex>, public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex>, NumbersAddOperations<Complex>,
ScaleOperations<Complex> { ScaleOperations<Complex> {
public override val zero: Complex = 0.0.toComplex() override val zero: Complex = 0.0.toComplex()
public override val one: Complex = 1.0.toComplex() override val one: Complex = 1.0.toComplex()
/** /**
* The imaginary unit. * The imaginary unit.
@ -68,13 +68,13 @@ public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex>, Num
override fun scale(a: Complex, value: Double): Complex = Complex(a.re * value, a.im * value) override fun scale(a: Complex, value: Double): Complex = Complex(a.re * value, a.im * value)
public override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im) override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im)
// public override fun multiply(a: Complex, k: Number): Complex = Complex(a.re * k.toDouble(), a.im * k.toDouble()) // override fun multiply(a: Complex, k: Number): Complex = Complex(a.re * k.toDouble(), a.im * k.toDouble())
public override fun multiply(a: Complex, b: Complex): Complex = override fun multiply(a: Complex, b: Complex): Complex =
Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re) Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re)
public override fun divide(a: Complex, b: Complex): Complex = when { override fun divide(a: Complex, b: Complex): Complex = when {
abs(b.im) < abs(b.re) -> { abs(b.im) < abs(b.re) -> {
val wr = b.im / b.re val wr = b.im / b.re
val wd = b.re + wr * b.im val wd = b.re + wr * b.im
@ -100,31 +100,31 @@ public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex>, Num
override operator fun Complex.div(k: Number): Complex = Complex(re / k.toDouble(), im / k.toDouble()) override operator fun Complex.div(k: Number): Complex = Complex(re / k.toDouble(), im / k.toDouble())
public override fun sin(arg: Complex): Complex = i * (exp(-i * arg) - exp(i * arg)) / 2.0 override fun sin(arg: Complex): Complex = i * (exp(-i * arg) - exp(i * arg)) / 2.0
public override fun cos(arg: Complex): Complex = (exp(-i * arg) + exp(i * arg)) / 2.0 override fun cos(arg: Complex): Complex = (exp(-i * arg) + exp(i * arg)) / 2.0
public override fun tan(arg: Complex): Complex { override fun tan(arg: Complex): Complex {
val e1 = exp(-i * arg) val e1 = exp(-i * arg)
val e2 = exp(i * arg) val e2 = exp(i * arg)
return i * (e1 - e2) / (e1 + e2) return i * (e1 - e2) / (e1 + e2)
} }
public override fun asin(arg: Complex): Complex = -i * ln(sqrt(1 - (arg * arg)) + i * arg) override fun asin(arg: Complex): Complex = -i * ln(sqrt(1 - (arg * arg)) + i * arg)
public override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(1 - (arg * arg)) + i * arg) override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(1 - (arg * arg)) + i * arg)
public override fun atan(arg: Complex): Complex { override fun atan(arg: Complex): Complex {
val iArg = i * arg val iArg = i * arg
return i * (ln(1 - iArg) - ln(1 + iArg)) / 2 return i * (ln(1 - iArg) - ln(1 + iArg)) / 2
} }
public override fun power(arg: Complex, pow: Number): Complex = if (arg.im == 0.0) override fun power(arg: Complex, pow: Number): Complex = if (arg.im == 0.0)
arg.re.pow(pow.toDouble()).toComplex() arg.re.pow(pow.toDouble()).toComplex()
else else
exp(pow * ln(arg)) exp(pow * ln(arg))
public override fun exp(arg: Complex): Complex = exp(arg.re) * (cos(arg.im) + i * sin(arg.im)) override fun exp(arg: Complex): Complex = exp(arg.re) * (cos(arg.im) + i * sin(arg.im))
public override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re) override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re)
/** /**
* Adds complex number to real one. * Adds complex number to real one.
@ -171,9 +171,9 @@ public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex>, Num
*/ */
public operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) public operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this)
public override fun norm(arg: Complex): Complex = sqrt(arg.conjugate * arg) override fun norm(arg: Complex): Complex = sqrt(arg.conjugate * arg)
public override fun bindSymbolOrNull(value: String): Complex? = if (value == "i") i else null override fun bindSymbolOrNull(value: String): Complex? = if (value == "i") i else null
} }
/** /**
@ -187,16 +187,16 @@ public data class Complex(val re: Double, val im: Double) {
public constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble()) public constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble())
public constructor(re: Number) : this(re.toDouble(), 0.0) public constructor(re: Number) : this(re.toDouble(), 0.0)
public override fun toString(): String = "($re + i * $im)" override fun toString(): String = "($re + i * $im)"
public companion object : MemorySpec<Complex> { public companion object : MemorySpec<Complex> {
public override val objectSize: Int override val objectSize: Int
get() = 16 get() = 16
public override fun MemoryReader.read(offset: Int): Complex = override fun MemoryReader.read(offset: Int): Complex =
Complex(readDouble(offset), readDouble(offset + 8)) Complex(readDouble(offset), readDouble(offset + 8))
public override fun MemoryWriter.write(offset: Int, value: Complex) { override fun MemoryWriter.write(offset: Int, value: Complex) {
writeDouble(offset, value.re) writeDouble(offset, value.re)
writeDouble(offset + 8, value.im) writeDouble(offset + 8, value.im)
} }

View File

@ -27,10 +27,10 @@ public class ComplexFieldND(
NumbersAddOperations<StructureND<Complex>>, NumbersAddOperations<StructureND<Complex>>,
ExtendedField<StructureND<Complex>> { ExtendedField<StructureND<Complex>> {
public override val zero: BufferND<Complex> by lazy { produce { zero } } override val zero: BufferND<Complex> by lazy { produce { zero } }
public override val one: BufferND<Complex> by lazy { produce { one } } override val one: BufferND<Complex> by lazy { produce { one } }
public override fun number(value: Number): BufferND<Complex> { override fun number(value: Number): BufferND<Complex> {
val d = value.toComplex() // minimize conversions val d = value.toComplex() // minimize conversions
return produce { d } return produce { d }
} }
@ -81,25 +81,25 @@ public class ComplexFieldND(
// return BufferedNDFieldElement(this, buffer) // return BufferedNDFieldElement(this, buffer)
// } // }
public override fun power(arg: StructureND<Complex>, pow: Number): BufferND<Complex> = arg.map { power(it, pow) } override fun power(arg: StructureND<Complex>, pow: Number): BufferND<Complex> = arg.map { power(it, pow) }
public override fun exp(arg: StructureND<Complex>): BufferND<Complex> = arg.map { exp(it) } override fun exp(arg: StructureND<Complex>): BufferND<Complex> = arg.map { exp(it) }
public override fun ln(arg: StructureND<Complex>): BufferND<Complex> = arg.map { ln(it) } override fun ln(arg: StructureND<Complex>): BufferND<Complex> = arg.map { ln(it) }
public override fun sin(arg: StructureND<Complex>): BufferND<Complex> = arg.map { sin(it) } override fun sin(arg: StructureND<Complex>): BufferND<Complex> = arg.map { sin(it) }
public override fun cos(arg: StructureND<Complex>): BufferND<Complex> = arg.map { cos(it) } override fun cos(arg: StructureND<Complex>): BufferND<Complex> = arg.map { cos(it) }
public override fun tan(arg: StructureND<Complex>): BufferND<Complex> = arg.map { tan(it) } override fun tan(arg: StructureND<Complex>): BufferND<Complex> = arg.map { tan(it) }
public override fun asin(arg: StructureND<Complex>): BufferND<Complex> = arg.map { asin(it) } override fun asin(arg: StructureND<Complex>): BufferND<Complex> = arg.map { asin(it) }
public override fun acos(arg: StructureND<Complex>): BufferND<Complex> = arg.map { acos(it) } override fun acos(arg: StructureND<Complex>): BufferND<Complex> = arg.map { acos(it) }
public override fun atan(arg: StructureND<Complex>): BufferND<Complex> = arg.map { atan(it) } override fun atan(arg: StructureND<Complex>): BufferND<Complex> = arg.map { atan(it) }
public override fun sinh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { sinh(it) } override fun sinh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { sinh(it) }
public override fun cosh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { cosh(it) } override fun cosh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { cosh(it) }
public override fun tanh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { tanh(it) } override fun tanh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { tanh(it) }
public override fun asinh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { asinh(it) } override fun asinh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { asinh(it) }
public override fun acosh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { acosh(it) } override fun acosh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { acosh(it) }
public override fun atanh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { atanh(it) } override fun atanh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { atanh(it) }
} }

View File

@ -63,20 +63,20 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Quaternion>,
*/ */
public val k: Quaternion = Quaternion(0, 0, 0, 1) public val k: Quaternion = Quaternion(0, 0, 0, 1)
public override fun add(a: Quaternion, b: Quaternion): Quaternion = override fun add(a: Quaternion, b: Quaternion): Quaternion =
Quaternion(a.w + b.w, a.x + b.x, a.y + b.y, a.z + b.z) Quaternion(a.w + b.w, a.x + b.x, a.y + b.y, a.z + b.z)
public override fun scale(a: Quaternion, value: Double): Quaternion = override fun scale(a: Quaternion, value: Double): Quaternion =
Quaternion(a.w * value, a.x * value, a.y * value, a.z * value) Quaternion(a.w * value, a.x * value, a.y * value, a.z * value)
public override fun multiply(a: Quaternion, b: Quaternion): Quaternion = Quaternion( override fun multiply(a: Quaternion, b: Quaternion): Quaternion = Quaternion(
a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z, a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,
a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x, a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w, a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
) )
public override fun divide(a: Quaternion, b: Quaternion): Quaternion { override fun divide(a: Quaternion, b: Quaternion): Quaternion {
val s = b.w * b.w + b.x * b.x + b.y * b.y + b.z * b.z val s = b.w * b.w + b.x * b.x + b.y * b.y + b.z * b.z
return Quaternion( return Quaternion(
@ -87,7 +87,7 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Quaternion>,
) )
} }
public override fun power(arg: Quaternion, pow: Number): Quaternion { override fun power(arg: Quaternion, pow: Number): Quaternion {
if (pow is Int) return pwr(arg, pow) if (pow is Int) return pwr(arg, pow)
if (floor(pow.toDouble()) == pow.toDouble()) return pwr(arg, pow.toInt()) if (floor(pow.toDouble()) == pow.toDouble()) return pwr(arg, pow.toInt())
return exp(pow * ln(arg)) return exp(pow * ln(arg))
@ -131,7 +131,7 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Quaternion>,
return Quaternion(a2 * a2 - 6 * a2 * n1 + n1 * n1, x.x * n2, x.y * n2, x.z * n2) return Quaternion(a2 * a2 - 6 * a2 * n1 + n1 * n1, x.x * n2, x.y * n2, x.z * n2)
} }
public override fun exp(arg: Quaternion): Quaternion { override fun exp(arg: Quaternion): Quaternion {
val un = arg.x * arg.x + arg.y * arg.y + arg.z * arg.z val un = arg.x * arg.x + arg.y * arg.y + arg.z * arg.z
if (un == 0.0) return exp(arg.w).toQuaternion() if (un == 0.0) return exp(arg.w).toQuaternion()
val n1 = sqrt(un) val n1 = sqrt(un)
@ -140,14 +140,14 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Quaternion>,
return Quaternion(ea * cos(n1), n2 * arg.x, n2 * arg.y, n2 * arg.z) return Quaternion(ea * cos(n1), n2 * arg.x, n2 * arg.y, n2 * arg.z)
} }
public override fun ln(arg: Quaternion): Quaternion { override fun ln(arg: Quaternion): Quaternion {
val nu2 = arg.x * arg.x + arg.y * arg.y + arg.z * arg.z val nu2 = arg.x * arg.x + arg.y * arg.y + arg.z * arg.z
if (nu2 == 0.0) if (nu2 == 0.0)
return if (arg.w > 0) return if (arg.w > 0)
Quaternion(ln(arg.w), 0, 0, 0) Quaternion(ln(arg.w), 0, 0, 0)
else { else {
val l = ComplexField { ComplexField.ln(arg.w.toComplex()) } val l = ComplexField { ln(arg.w.toComplex()) }
Quaternion(l.re, l.im, 0, 0) Quaternion(l.re, l.im, 0, 0)
} }
@ -158,21 +158,21 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Quaternion>,
return Quaternion(ln(n), th * arg.x, th * arg.y, th * arg.z) return Quaternion(ln(n), th * arg.x, th * arg.y, th * arg.z)
} }
public override operator fun Number.plus(b: Quaternion): Quaternion = Quaternion(toDouble() + b.w, b.x, b.y, b.z) override operator fun Number.plus(b: Quaternion): Quaternion = Quaternion(toDouble() + b.w, b.x, b.y, b.z)
public override operator fun Number.minus(b: Quaternion): Quaternion = override operator fun Number.minus(b: Quaternion): Quaternion =
Quaternion(toDouble() - b.w, -b.x, -b.y, -b.z) Quaternion(toDouble() - b.w, -b.x, -b.y, -b.z)
public override operator fun Quaternion.plus(b: Number): Quaternion = Quaternion(w + b.toDouble(), x, y, z) override operator fun Quaternion.plus(b: Number): Quaternion = Quaternion(w + b.toDouble(), x, y, z)
public override operator fun Quaternion.minus(b: Number): Quaternion = Quaternion(w - b.toDouble(), x, y, z) override operator fun Quaternion.minus(b: Number): Quaternion = Quaternion(w - b.toDouble(), x, y, z)
public override operator fun Number.times(b: Quaternion): Quaternion = override operator fun Number.times(b: Quaternion): Quaternion =
Quaternion(toDouble() * b.w, toDouble() * b.x, toDouble() * b.y, toDouble() * b.z) Quaternion(toDouble() * b.w, toDouble() * b.x, toDouble() * b.y, toDouble() * b.z)
public override fun Quaternion.unaryMinus(): Quaternion = Quaternion(-w, -x, -y, -z) override fun Quaternion.unaryMinus(): Quaternion = Quaternion(-w, -x, -y, -z)
public override fun norm(arg: Quaternion): Quaternion = sqrt(arg.conjugate * arg) override fun norm(arg: Quaternion): Quaternion = sqrt(arg.conjugate * arg)
public override fun bindSymbolOrNull(value: String): Quaternion? = when (value) { override fun bindSymbolOrNull(value: String): Quaternion? = when (value) {
"i" -> i "i" -> i
"j" -> j "j" -> j
"k" -> k "k" -> k
@ -181,12 +181,12 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Quaternion>,
override fun number(value: Number): Quaternion = value.toQuaternion() override fun number(value: Number): Quaternion = value.toQuaternion()
public override fun sinh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / 2.0 override fun sinh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / 2.0
public override fun cosh(arg: Quaternion): Quaternion = (exp(arg) + exp(-arg)) / 2.0 override fun cosh(arg: Quaternion): Quaternion = (exp(arg) + exp(-arg)) / 2.0
public override fun tanh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) override fun tanh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg))
public override fun asinh(arg: Quaternion): Quaternion = ln(sqrt(arg * arg + one) + arg) override fun asinh(arg: Quaternion): Quaternion = ln(sqrt(arg * arg + one) + arg)
public override fun acosh(arg: Quaternion): Quaternion = ln(arg + sqrt((arg - one) * (arg + one))) override fun acosh(arg: Quaternion): Quaternion = ln(arg + sqrt((arg - one) * (arg + one)))
public override fun atanh(arg: Quaternion): Quaternion = (ln(arg + one) - ln(one - arg)) / 2.0 override fun atanh(arg: Quaternion): Quaternion = (ln(arg + one) - ln(one - arg)) / 2.0
} }
/** /**
@ -224,16 +224,16 @@ public data class Quaternion(
/** /**
* Returns a string representation of this quaternion. * Returns a string representation of this quaternion.
*/ */
public override fun toString(): String = "($w + $x * i + $y * j + $z * k)" override fun toString(): String = "($w + $x * i + $y * j + $z * k)"
public companion object : MemorySpec<Quaternion> { public companion object : MemorySpec<Quaternion> {
public override val objectSize: Int override val objectSize: Int
get() = 32 get() = 32
public override fun MemoryReader.read(offset: Int): Quaternion = override fun MemoryReader.read(offset: Int): Quaternion =
Quaternion(readDouble(offset), readDouble(offset + 8), readDouble(offset + 16), readDouble(offset + 24)) Quaternion(readDouble(offset), readDouble(offset + 8), readDouble(offset + 16), readDouble(offset + 24))
public override fun MemoryWriter.write(offset: Int, value: Quaternion) { override fun MemoryWriter.write(offset: Int, value: Quaternion) {
writeDouble(offset, value.w) writeDouble(offset, value.w)
writeDouble(offset + 8, value.x) writeDouble(offset + 8, value.x)
writeDouble(offset + 16, value.y) writeDouble(offset + 16, value.y)

View File

@ -10,12 +10,12 @@ The core interfaces of KMath.
objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high
performance calculations to code generation. performance calculations to code generation.
- [domains](src/commonMain/kotlin/space/kscience/kmath/domains) : Domains - [domains](src/commonMain/kotlin/space/kscience/kmath/domains) : Domains
- [autodif](src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation - [autodiff](src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation
## Artifact: ## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0-dev-11`. The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0-dev-14`.
**Gradle:** **Gradle:**
```gradle ```gradle
@ -25,7 +25,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'space.kscience:kmath-core:0.3.0-dev-11' implementation 'space.kscience:kmath-core:0.3.0-dev-14'
} }
``` ```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
@ -36,6 +36,6 @@ repositories {
} }
dependencies { dependencies {
implementation("space.kscience:kmath-core:0.3.0-dev-11") implementation("space.kscience:kmath-core:0.3.0-dev-14")
} }
``` ```

View File

@ -19,51 +19,42 @@ readme {
feature( feature(
id = "algebras", id = "algebras",
description = """ ref = "src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt",
Algebraic structures like rings, spaces and fields. ) { "Algebraic structures like rings, spaces and fields." }
""".trimIndent(),
ref = "src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt"
)
feature( feature(
id = "nd", id = "nd",
description = "Many-dimensional structures and operations on them.", ref = "src/commonMain/kotlin/space/kscience/kmath/structures/StructureND.kt",
ref = "src/commonMain/kotlin/space/kscience/kmath/structures/StructureND.kt" ) { "Many-dimensional structures and operations on them." }
)
feature( feature(
id = "linear", id = "linear",
description = """ ref = "src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt",
Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition. ) { "Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition." }
""".trimIndent(),
ref = "src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt"
)
feature( feature(
id = "buffers", id = "buffers",
description = "One-dimensional structure", ref = "src/commonMain/kotlin/space/kscience/kmath/structures/Buffers.kt",
ref = "src/commonMain/kotlin/space/kscience/kmath/structures/Buffers.kt" ) { "One-dimensional structure" }
)
feature( feature(
id = "expressions", id = "expressions",
description = """ ref = "src/commonMain/kotlin/space/kscience/kmath/expressions"
) {
"""
By writing a single mathematical expression once, users will be able to apply different types of By writing a single mathematical expression once, users will be able to apply different types of
objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high
performance calculations to code generation. performance calculations to code generation.
""".trimIndent(), """.trimIndent()
ref = "src/commonMain/kotlin/space/kscience/kmath/expressions" }
)
feature( feature(
id = "domains", id = "domains",
description = "Domains", ref = "src/commonMain/kotlin/space/kscience/kmath/domains",
ref = "src/commonMain/kotlin/space/kscience/kmath/domains" ) { "Domains" }
)
feature( feature(
id = "autodif", id = "autodiff",
description = "Automatic differentiation",
ref = "src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt" ref = "src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt"
) ) { "Automatic differentiation" }
} }

View File

@ -16,7 +16,7 @@ import kotlin.math.max
* The buffer of X values. * The buffer of X values.
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public interface XYColumnarData<T, out X : T, out Y : T> : ColumnarData<T> { public interface XYColumnarData<out T, out X : T, out Y : T> : ColumnarData<T> {
/** /**
* The buffer of X values * The buffer of X values
*/ */

View File

@ -14,7 +14,7 @@ import space.kscience.kmath.structures.Buffer
* Inherits [XYColumnarData]. * Inherits [XYColumnarData].
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public interface XYZColumnarData<T, out X : T, out Y : T, out Z : T> : XYColumnarData<T, X, Y> { public interface XYZColumnarData<out T, out X : T, out Y : T, out Z : T> : XYColumnarData<T, X, Y> {
public val z: Buffer<Z> public val z: Buffer<Z>
override fun get(symbol: Symbol): Buffer<T>? = when (symbol) { override fun get(symbol: Symbol): Buffer<T>? = when (symbol) {

View File

@ -12,7 +12,7 @@ import space.kscience.kmath.linear.Point
* *
* @param T the type of element of this domain. * @param T the type of element of this domain.
*/ */
public interface Domain<T : Any> { public interface Domain<in T : Any> {
/** /**
* Checks if the specified point is contained in this domain. * Checks if the specified point is contained in this domain.
*/ */

View File

@ -17,17 +17,17 @@ import space.kscience.kmath.structures.indices
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class HyperSquareDomain(private val lower: Buffer<Double>, private val upper: Buffer<Double>) : DoubleDomain { public class HyperSquareDomain(private val lower: Buffer<Double>, private val upper: Buffer<Double>) : DoubleDomain {
public override val dimension: Int get() = lower.size override val dimension: Int get() = lower.size
public override operator fun contains(point: Point<Double>): Boolean = point.indices.all { i -> override operator fun contains(point: Point<Double>): Boolean = point.indices.all { i ->
point[i] in lower[i]..upper[i] point[i] in lower[i]..upper[i]
} }
public override fun getLowerBound(num: Int): Double = lower[num] override fun getLowerBound(num: Int): Double = lower[num]
public override fun getUpperBound(num: Int): Double = upper[num] override fun getUpperBound(num: Int): Double = upper[num]
public override fun volume(): Double { override fun volume(): Double {
var res = 1.0 var res = 1.0
for (i in 0 until dimension) { for (i in 0 until dimension) {

View File

@ -8,12 +8,12 @@ import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
@UnstableKMathAPI @UnstableKMathAPI
public class UnconstrainedDomain(public override val dimension: Int) : DoubleDomain { public class UnconstrainedDomain(override val dimension: Int) : DoubleDomain {
public override operator fun contains(point: Point<Double>): Boolean = true override operator fun contains(point: Point<Double>): Boolean = true
public override fun getLowerBound(num: Int): Double = Double.NEGATIVE_INFINITY override fun getLowerBound(num: Int): Double = Double.NEGATIVE_INFINITY
public override fun getUpperBound(num: Int): Double = Double.POSITIVE_INFINITY override fun getUpperBound(num: Int): Double = Double.POSITIVE_INFINITY
public override fun volume(): Double = Double.POSITIVE_INFINITY override fun volume(): Double = Double.POSITIVE_INFINITY
} }

View File

@ -10,24 +10,24 @@ import space.kscience.kmath.misc.UnstableKMathAPI
@UnstableKMathAPI @UnstableKMathAPI
public class UnivariateDomain(public val range: ClosedFloatingPointRange<Double>) : DoubleDomain { public class UnivariateDomain(public val range: ClosedFloatingPointRange<Double>) : DoubleDomain {
public override val dimension: Int get() = 1 override val dimension: Int get() = 1
public operator fun contains(d: Double): Boolean = range.contains(d) public operator fun contains(d: Double): Boolean = range.contains(d)
public override operator fun contains(point: Point<Double>): Boolean { override operator fun contains(point: Point<Double>): Boolean {
require(point.size == 0) require(point.size == 0)
return contains(point[0]) return contains(point[0])
} }
public override fun getLowerBound(num: Int): Double { override fun getLowerBound(num: Int): Double {
require(num == 0) require(num == 0)
return range.start return range.start
} }
public override fun getUpperBound(num: Int): Double { override fun getUpperBound(num: Int): Double {
require(num == 0) require(num == 0)
return range.endInclusive return range.endInclusive
} }
public override fun volume(): Double = range.endInclusive - range.start override fun volume(): Double = range.endInclusive - range.start
} }

View File

@ -8,10 +8,9 @@ package space.kscience.kmath.expressions
import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.Algebra
/** /**
* Represents expression which structure can be differentiated. * Represents expression, which structure can be differentiated.
* *
* @param T the type this expression takes as argument and returns. * @param T the type this expression takes as argument and returns.
* @param R the type of expression this expression can be differentiated to.
*/ */
public interface DifferentiableExpression<T> : Expression<T> { public interface DifferentiableExpression<T> : Expression<T> {
/** /**
@ -33,9 +32,11 @@ public fun <T> DifferentiableExpression<T>.derivative(name: String): Expression
derivative(StringSymbol(name)) derivative(StringSymbol(name))
/** /**
* A special type of [DifferentiableExpression] which returns typed expressions as derivatives * A special type of [DifferentiableExpression] which returns typed expressions as derivatives.
*
* @param R the type of expression this expression can be differentiated to.
*/ */
public interface SpecialDifferentiableExpression<T, R: Expression<T>>: DifferentiableExpression<T> { public interface SpecialDifferentiableExpression<T, out R : Expression<T>> : DifferentiableExpression<T> {
override fun derivativeOrNull(symbols: List<Symbol>): R? override fun derivativeOrNull(symbols: List<Symbol>): R?
} }
@ -69,6 +70,6 @@ public abstract class FirstDerivativeExpression<T> : DifferentiableExpression<T>
* @param I type of the actual expression state * @param I type of the actual expression state
* @param A type of expression algebra * @param A type of expression algebra
*/ */
public fun interface AutoDiffProcessor<T, I, A : Algebra<I>> { public fun interface AutoDiffProcessor<T, I, out A : Algebra<I>> {
public fun differentiate(function: A.() -> I): DifferentiableExpression<T> public fun differentiate(function: A.() -> I): DifferentiableExpression<T>
} }

View File

@ -43,7 +43,7 @@ public operator fun <T> Expression<T>.invoke(vararg pairs: Pair<Symbol, T>): T =
/** /**
* Calls this expression from arguments. * Calls this expression from arguments.
* *
* @param pairs the pairs of arguments' names to values. * @param pairs the pairs of arguments' names to value.
* @return a value. * @return a value.
*/ */
@JvmName("callByString") @JvmName("callByString")
@ -60,7 +60,7 @@ public operator fun <T> Expression<T>.invoke(vararg pairs: Pair<String, T>): T =
public interface ExpressionAlgebra<in T, E> : Algebra<E> { public interface ExpressionAlgebra<in T, E> : Algebra<E> {
/** /**
* A constant expression which does not depend on arguments * A constant expression that does not depend on arguments.
*/ */
public fun const(value: T): E public fun const(value: T): E
} }

View File

@ -6,43 +6,39 @@
package space.kscience.kmath.expressions package space.kscience.kmath.expressions
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/** /**
* A context class for [Expression] construction. * A context class for [Expression] construction.
* *
* @param algebra The algebra to provide for Expressions built. * @param algebra The algebra to provide for Expressions built.
*/ */
public abstract class FunctionalExpressionAlgebra<T, A : Algebra<T>>( public abstract class FunctionalExpressionAlgebra<T, out A : Algebra<T>>(
public val algebra: A, public val algebra: A,
) : ExpressionAlgebra<T, Expression<T>> { ) : ExpressionAlgebra<T, Expression<T>> {
/** /**
* Builds an Expression of constant expression which does not depend on arguments. * Builds an Expression of constant expression that does not depend on arguments.
*/ */
public override fun const(value: T): Expression<T> = Expression { value } override fun const(value: T): Expression<T> = Expression { value }
/** /**
* Builds an Expression to access a variable. * Builds an Expression to access a variable.
*/ */
public override fun bindSymbolOrNull(value: String): Expression<T>? = Expression { arguments -> override fun bindSymbolOrNull(value: String): Expression<T>? = Expression { arguments ->
algebra.bindSymbolOrNull(value) algebra.bindSymbolOrNull(value)
?: arguments[StringSymbol(value)] ?: arguments[StringSymbol(value)]
?: error("Symbol '$value' is not supported in $this") ?: error("Symbol '$value' is not supported in $this")
} }
/** override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> =
* Builds an Expression of dynamic call of binary operation [operation] on [left] and [right].
*/
public override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> =
{ left, right -> { left, right ->
Expression { arguments -> Expression { arguments ->
algebra.binaryOperationFunction(operation)(left.invoke(arguments), right.invoke(arguments)) algebra.binaryOperationFunction(operation)(left.invoke(arguments), right.invoke(arguments))
} }
} }
/** override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> = { arg ->
* Builds an Expression of dynamic call of unary operation with name [operation] on [arg].
*/
public override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> = { arg ->
Expression { arguments -> algebra.unaryOperationFunction(operation)(arg.invoke(arguments)) } Expression { arguments -> algebra.unaryOperationFunction(operation)(arg.invoke(arguments)) }
} }
} }
@ -50,24 +46,24 @@ public abstract class FunctionalExpressionAlgebra<T, A : Algebra<T>>(
/** /**
* A context class for [Expression] construction for [Ring] algebras. * A context class for [Expression] construction for [Ring] algebras.
*/ */
public open class FunctionalExpressionGroup<T, A : Group<T>>( public open class FunctionalExpressionGroup<T, out A : Group<T>>(
algebra: A, algebra: A,
) : FunctionalExpressionAlgebra<T, A>(algebra), Group<Expression<T>> { ) : FunctionalExpressionAlgebra<T, A>(algebra), Group<Expression<T>> {
public override val zero: Expression<T> get() = const(algebra.zero) override val zero: Expression<T> get() = const(algebra.zero)
public override fun Expression<T>.unaryMinus(): Expression<T> = override fun Expression<T>.unaryMinus(): Expression<T> =
unaryOperation(GroupOperations.MINUS_OPERATION, this) unaryOperation(GroupOperations.MINUS_OPERATION, this)
/** /**
* Builds an Expression of addition of two another expressions. * Builds an Expression of addition of two another expressions.
*/ */
public override fun add(a: Expression<T>, b: Expression<T>): Expression<T> = override fun add(a: Expression<T>, b: Expression<T>): Expression<T> =
binaryOperation(GroupOperations.PLUS_OPERATION, a, b) binaryOperation(GroupOperations.PLUS_OPERATION, a, b)
// /** // /**
// * Builds an Expression of multiplication of expression by number. // * Builds an Expression of multiplication of expression by number.
// */ // */
// public override fun multiply(a: Expression<T>, k: Number): Expression<T> = Expression { arguments -> // override fun multiply(a: Expression<T>, k: Number): Expression<T> = Expression { arguments ->
// algebra.multiply(a.invoke(arguments), k) // algebra.multiply(a.invoke(arguments), k)
// } // }
@ -76,110 +72,122 @@ public open class FunctionalExpressionGroup<T, A : Group<T>>(
public operator fun T.plus(arg: Expression<T>): Expression<T> = arg + this public operator fun T.plus(arg: Expression<T>): Expression<T> = arg + this
public operator fun T.minus(arg: Expression<T>): Expression<T> = arg - this public operator fun T.minus(arg: Expression<T>): Expression<T> = arg - this
public override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> = override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> =
super<FunctionalExpressionAlgebra>.unaryOperationFunction(operation) super<FunctionalExpressionAlgebra>.unaryOperationFunction(operation)
public override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> = override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> =
super<FunctionalExpressionAlgebra>.binaryOperationFunction(operation) super<FunctionalExpressionAlgebra>.binaryOperationFunction(operation)
} }
public open class FunctionalExpressionRing<T, A : Ring<T>>( public open class FunctionalExpressionRing<T, out A : Ring<T>>(
algebra: A, algebra: A,
) : FunctionalExpressionGroup<T, A>(algebra), Ring<Expression<T>> { ) : FunctionalExpressionGroup<T, A>(algebra), Ring<Expression<T>> {
public override val one: Expression<T> get() = const(algebra.one) override val one: Expression<T> get() = const(algebra.one)
/** /**
* Builds an Expression of multiplication of two expressions. * Builds an Expression of multiplication of two expressions.
*/ */
public override fun multiply(a: Expression<T>, b: Expression<T>): Expression<T> = override fun multiply(a: Expression<T>, b: Expression<T>): Expression<T> =
binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, b) binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, b)
public operator fun Expression<T>.times(arg: T): Expression<T> = this * const(arg) public operator fun Expression<T>.times(arg: T): Expression<T> = this * const(arg)
public operator fun T.times(arg: Expression<T>): Expression<T> = arg * this public operator fun T.times(arg: Expression<T>): Expression<T> = arg * this
public override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> = override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> =
super<FunctionalExpressionGroup>.unaryOperationFunction(operation) super<FunctionalExpressionGroup>.unaryOperationFunction(operation)
public override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> = override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> =
super<FunctionalExpressionGroup>.binaryOperationFunction(operation) super<FunctionalExpressionGroup>.binaryOperationFunction(operation)
} }
public open class FunctionalExpressionField<T, A : Field<T>>( public open class FunctionalExpressionField<T, out A : Field<T>>(
algebra: A, algebra: A,
) : FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>>, ScaleOperations<Expression<T>> { ) : FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>>, ScaleOperations<Expression<T>> {
/** /**
* Builds an Expression of division an expression by another one. * Builds an Expression of division an expression by another one.
*/ */
public override fun divide(a: Expression<T>, b: Expression<T>): Expression<T> = override fun divide(a: Expression<T>, b: Expression<T>): Expression<T> =
binaryOperationFunction(FieldOperations.DIV_OPERATION)(a, b) binaryOperationFunction(FieldOperations.DIV_OPERATION)(a, b)
public operator fun Expression<T>.div(arg: T): Expression<T> = this / const(arg) public operator fun Expression<T>.div(arg: T): Expression<T> = this / const(arg)
public operator fun T.div(arg: Expression<T>): Expression<T> = arg / this public operator fun T.div(arg: Expression<T>): Expression<T> = arg / this
public override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> = override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> =
super<FunctionalExpressionRing>.unaryOperationFunction(operation) super<FunctionalExpressionRing>.unaryOperationFunction(operation)
public override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> = override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> =
super<FunctionalExpressionRing>.binaryOperationFunction(operation) super<FunctionalExpressionRing>.binaryOperationFunction(operation)
public override fun scale(a: Expression<T>, value: Double): Expression<T> = algebra { override fun scale(a: Expression<T>, value: Double): Expression<T> = algebra {
Expression { args -> a(args) * value } Expression { args -> a(args) * value }
} }
public override fun bindSymbolOrNull(value: String): Expression<T>? = override fun bindSymbolOrNull(value: String): Expression<T>? =
super<FunctionalExpressionRing>.bindSymbolOrNull(value) super<FunctionalExpressionRing>.bindSymbolOrNull(value)
} }
public open class FunctionalExpressionExtendedField<T, A : ExtendedField<T>>( public open class FunctionalExpressionExtendedField<T, out A : ExtendedField<T>>(
algebra: A, algebra: A,
) : FunctionalExpressionField<T, A>(algebra), ExtendedField<Expression<T>> { ) : FunctionalExpressionField<T, A>(algebra), ExtendedField<Expression<T>> {
public override fun number(value: Number): Expression<T> = const(algebra.number(value)) override fun number(value: Number): Expression<T> = const(algebra.number(value))
public override fun sqrt(arg: Expression<T>): Expression<T> = override fun sqrt(arg: Expression<T>): Expression<T> =
unaryOperationFunction(PowerOperations.SQRT_OPERATION)(arg) unaryOperationFunction(PowerOperations.SQRT_OPERATION)(arg)
public override fun sin(arg: Expression<T>): Expression<T> = override fun sin(arg: Expression<T>): Expression<T> =
unaryOperationFunction(TrigonometricOperations.SIN_OPERATION)(arg) unaryOperationFunction(TrigonometricOperations.SIN_OPERATION)(arg)
public override fun cos(arg: Expression<T>): Expression<T> = override fun cos(arg: Expression<T>): Expression<T> =
unaryOperationFunction(TrigonometricOperations.COS_OPERATION)(arg) unaryOperationFunction(TrigonometricOperations.COS_OPERATION)(arg)
public override fun asin(arg: Expression<T>): Expression<T> = override fun asin(arg: Expression<T>): Expression<T> =
unaryOperationFunction(TrigonometricOperations.ASIN_OPERATION)(arg) unaryOperationFunction(TrigonometricOperations.ASIN_OPERATION)(arg)
public override fun acos(arg: Expression<T>): Expression<T> = override fun acos(arg: Expression<T>): Expression<T> =
unaryOperationFunction(TrigonometricOperations.ACOS_OPERATION)(arg) unaryOperationFunction(TrigonometricOperations.ACOS_OPERATION)(arg)
public override fun atan(arg: Expression<T>): Expression<T> = override fun atan(arg: Expression<T>): Expression<T> =
unaryOperationFunction(TrigonometricOperations.ATAN_OPERATION)(arg) unaryOperationFunction(TrigonometricOperations.ATAN_OPERATION)(arg)
public override fun power(arg: Expression<T>, pow: Number): Expression<T> = override fun power(arg: Expression<T>, pow: Number): Expression<T> =
binaryOperationFunction(PowerOperations.POW_OPERATION)(arg, number(pow)) binaryOperationFunction(PowerOperations.POW_OPERATION)(arg, number(pow))
public override fun exp(arg: Expression<T>): Expression<T> = override fun exp(arg: Expression<T>): Expression<T> =
unaryOperationFunction(ExponentialOperations.EXP_OPERATION)(arg) unaryOperationFunction(ExponentialOperations.EXP_OPERATION)(arg)
public override fun ln(arg: Expression<T>): Expression<T> = override fun ln(arg: Expression<T>): Expression<T> =
unaryOperationFunction(ExponentialOperations.LN_OPERATION)(arg) unaryOperationFunction(ExponentialOperations.LN_OPERATION)(arg)
public override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> = override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> =
super<FunctionalExpressionField>.unaryOperationFunction(operation) super<FunctionalExpressionField>.unaryOperationFunction(operation)
public override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> = override fun binaryOperationFunction(operation: String): (left: Expression<T>, right: Expression<T>) -> Expression<T> =
super<FunctionalExpressionField>.binaryOperationFunction(operation) super<FunctionalExpressionField>.binaryOperationFunction(operation)
public override fun bindSymbol(value: String): Expression<T> = super<FunctionalExpressionField>.bindSymbol(value) override fun bindSymbol(value: String): Expression<T> = super<FunctionalExpressionField>.bindSymbol(value)
} }
public inline fun <T, A : Ring<T>> A.expressionInSpace(block: FunctionalExpressionGroup<T, A>.() -> Expression<T>): Expression<T> = public inline fun <T, A : Group<T>> A.expressionInGroup(
FunctionalExpressionGroup(this).block() block: FunctionalExpressionGroup<T, A>.() -> Expression<T>,
): Expression<T> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return FunctionalExpressionGroup(this).block()
}
public inline fun <T, A : Ring<T>> A.expressionInRing(block: FunctionalExpressionRing<T, A>.() -> Expression<T>): Expression<T> = public inline fun <T, A : Ring<T>> A.expressionInRing(
FunctionalExpressionRing(this).block() block: FunctionalExpressionRing<T, A>.() -> Expression<T>,
): Expression<T> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return FunctionalExpressionRing(this).block()
}
public inline fun <T, A : Field<T>> A.expressionInField(block: FunctionalExpressionField<T, A>.() -> Expression<T>): Expression<T> = public inline fun <T, A : Field<T>> A.expressionInField(
FunctionalExpressionField(this).block() block: FunctionalExpressionField<T, A>.() -> Expression<T>,
): Expression<T> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return FunctionalExpressionField(this).block()
}
public inline fun <T, A : ExtendedField<T>> A.expressionInExtendedField( public inline fun <T, A : ExtendedField<T>> A.expressionInExtendedField(
block: FunctionalExpressionExtendedField<T, A>.() -> Expression<T>, block: FunctionalExpressionExtendedField<T, A>.() -> Expression<T>,

View File

@ -12,14 +12,14 @@ import space.kscience.kmath.operations.*
* [Algebra] over [MST] nodes. * [Algebra] over [MST] nodes.
*/ */
public object MstNumericAlgebra : NumericAlgebra<MST> { public object MstNumericAlgebra : NumericAlgebra<MST> {
public override fun number(value: Number): MST.Numeric = MST.Numeric(value) override fun number(value: Number): MST.Numeric = MST.Numeric(value)
public override fun bindSymbolOrNull(value: String): Symbol = StringSymbol(value) override fun bindSymbolOrNull(value: String): Symbol = StringSymbol(value)
override fun bindSymbol(value: String): Symbol = bindSymbolOrNull(value) override fun bindSymbol(value: String): Symbol = bindSymbolOrNull(value)
public override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary = override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary =
{ arg -> MST.Unary(operation, arg) } { arg -> MST.Unary(operation, arg) }
public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary =
{ left, right -> MST.Binary(operation, left, right) } { left, right -> MST.Binary(operation, left, right) }
} }
@ -27,27 +27,27 @@ public object MstNumericAlgebra : NumericAlgebra<MST> {
* [Group] over [MST] nodes. * [Group] over [MST] nodes.
*/ */
public object MstGroup : Group<MST>, NumericAlgebra<MST>, ScaleOperations<MST> { public object MstGroup : Group<MST>, NumericAlgebra<MST>, ScaleOperations<MST> {
public override val zero: MST.Numeric = number(0.0) override val zero: MST.Numeric = number(0.0)
public override fun number(value: Number): MST.Numeric = MstNumericAlgebra.number(value) override fun number(value: Number): MST.Numeric = MstNumericAlgebra.number(value)
public override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value) override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value)
public override fun add(a: MST, b: MST): MST.Binary = binaryOperationFunction(GroupOperations.PLUS_OPERATION)(a, b) override fun add(a: MST, b: MST): MST.Binary = binaryOperationFunction(GroupOperations.PLUS_OPERATION)(a, b)
public override operator fun MST.unaryPlus(): MST.Unary = override operator fun MST.unaryPlus(): MST.Unary =
unaryOperationFunction(GroupOperations.PLUS_OPERATION)(this) unaryOperationFunction(GroupOperations.PLUS_OPERATION)(this)
public override operator fun MST.unaryMinus(): MST.Unary = override operator fun MST.unaryMinus(): MST.Unary =
unaryOperationFunction(GroupOperations.MINUS_OPERATION)(this) unaryOperationFunction(GroupOperations.MINUS_OPERATION)(this)
public override operator fun MST.minus(b: MST): MST.Binary = override operator fun MST.minus(b: MST): MST.Binary =
binaryOperationFunction(GroupOperations.MINUS_OPERATION)(this, b) binaryOperationFunction(GroupOperations.MINUS_OPERATION)(this, b)
public override fun scale(a: MST, value: Double): MST.Binary = override fun scale(a: MST, value: Double): MST.Binary =
binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, number(value)) binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, number(value))
public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary =
MstNumericAlgebra.binaryOperationFunction(operation) MstNumericAlgebra.binaryOperationFunction(operation)
public override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary = override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary =
MstNumericAlgebra.unaryOperationFunction(operation) MstNumericAlgebra.unaryOperationFunction(operation)
} }
@ -57,27 +57,27 @@ public object MstGroup : Group<MST>, NumericAlgebra<MST>, ScaleOperations<MST> {
@Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public object MstRing : Ring<MST>, NumbersAddOperations<MST>, ScaleOperations<MST> { public object MstRing : Ring<MST>, NumbersAddOperations<MST>, ScaleOperations<MST> {
public override inline val zero: MST.Numeric get() = MstGroup.zero override inline val zero: MST.Numeric get() = MstGroup.zero
public override val one: MST.Numeric = number(1.0) override val one: MST.Numeric = number(1.0)
public override fun number(value: Number): MST.Numeric = MstGroup.number(value) override fun number(value: Number): MST.Numeric = MstGroup.number(value)
public override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value) override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value)
public override fun add(a: MST, b: MST): MST.Binary = MstGroup.add(a, b) override fun add(a: MST, b: MST): MST.Binary = MstGroup.add(a, b)
public override fun scale(a: MST, value: Double): MST.Binary = override fun scale(a: MST, value: Double): MST.Binary =
MstGroup.binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, MstGroup.number(value)) MstGroup.binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, MstGroup.number(value))
public override fun multiply(a: MST, b: MST): MST.Binary = override fun multiply(a: MST, b: MST): MST.Binary =
binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, b) binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, b)
public override operator fun MST.unaryPlus(): MST.Unary = MstGroup { +this@unaryPlus } override operator fun MST.unaryPlus(): MST.Unary = MstGroup { +this@unaryPlus }
public override operator fun MST.unaryMinus(): MST.Unary = MstGroup { -this@unaryMinus } override operator fun MST.unaryMinus(): MST.Unary = MstGroup { -this@unaryMinus }
public override operator fun MST.minus(b: MST): MST.Binary = MstGroup { this@minus - b } override operator fun MST.minus(b: MST): MST.Binary = MstGroup { this@minus - b }
public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary =
MstGroup.binaryOperationFunction(operation) MstGroup.binaryOperationFunction(operation)
public override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary = override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary =
MstNumericAlgebra.unaryOperationFunction(operation) MstNumericAlgebra.unaryOperationFunction(operation)
} }
@ -87,28 +87,28 @@ public object MstRing : Ring<MST>, NumbersAddOperations<MST>, ScaleOperations<MS
@Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public object MstField : Field<MST>, NumbersAddOperations<MST>, ScaleOperations<MST> { public object MstField : Field<MST>, NumbersAddOperations<MST>, ScaleOperations<MST> {
public override inline val zero: MST.Numeric get() = MstRing.zero override inline val zero: MST.Numeric get() = MstRing.zero
public override inline val one: MST.Numeric get() = MstRing.one override inline val one: MST.Numeric get() = MstRing.one
public override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value) override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value)
public override fun number(value: Number): MST.Numeric = MstRing.number(value) override fun number(value: Number): MST.Numeric = MstRing.number(value)
public override fun add(a: MST, b: MST): MST.Binary = MstRing.add(a, b) override fun add(a: MST, b: MST): MST.Binary = MstRing.add(a, b)
public override fun scale(a: MST, value: Double): MST.Binary = override fun scale(a: MST, value: Double): MST.Binary =
MstGroup.binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, MstGroup.number(value)) MstGroup.binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, MstGroup.number(value))
public override fun multiply(a: MST, b: MST): MST.Binary = MstRing.multiply(a, b) override fun multiply(a: MST, b: MST): MST.Binary = MstRing.multiply(a, b)
public override fun divide(a: MST, b: MST): MST.Binary = override fun divide(a: MST, b: MST): MST.Binary =
binaryOperationFunction(FieldOperations.DIV_OPERATION)(a, b) binaryOperationFunction(FieldOperations.DIV_OPERATION)(a, b)
public override operator fun MST.unaryPlus(): MST.Unary = MstRing { +this@unaryPlus } override operator fun MST.unaryPlus(): MST.Unary = MstRing { +this@unaryPlus }
public override operator fun MST.unaryMinus(): MST.Unary = MstRing { -this@unaryMinus } override operator fun MST.unaryMinus(): MST.Unary = MstRing { -this@unaryMinus }
public override operator fun MST.minus(b: MST): MST.Binary = MstRing { this@minus - b } override operator fun MST.minus(b: MST): MST.Binary = MstRing { this@minus - b }
public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary =
MstRing.binaryOperationFunction(operation) MstRing.binaryOperationFunction(operation)
public override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary = override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary =
MstRing.unaryOperationFunction(operation) MstRing.unaryOperationFunction(operation)
} }
@ -117,45 +117,45 @@ public object MstField : Field<MST>, NumbersAddOperations<MST>, ScaleOperations<
*/ */
@Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
public object MstExtendedField : ExtendedField<MST>, NumericAlgebra<MST> { public object MstExtendedField : ExtendedField<MST>, NumericAlgebra<MST> {
public override inline val zero: MST.Numeric get() = MstField.zero override inline val zero: MST.Numeric get() = MstField.zero
public override inline val one: MST.Numeric get() = MstField.one override inline val one: MST.Numeric get() = MstField.one
public override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value) override fun bindSymbolOrNull(value: String): Symbol = MstNumericAlgebra.bindSymbolOrNull(value)
public override fun number(value: Number): MST.Numeric = MstRing.number(value) override fun number(value: Number): MST.Numeric = MstRing.number(value)
public override fun sin(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.SIN_OPERATION)(arg) override fun sin(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.SIN_OPERATION)(arg)
public override fun cos(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.COS_OPERATION)(arg) override fun cos(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.COS_OPERATION)(arg)
public override fun tan(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.TAN_OPERATION)(arg) override fun tan(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.TAN_OPERATION)(arg)
public override fun asin(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.ASIN_OPERATION)(arg) override fun asin(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.ASIN_OPERATION)(arg)
public override fun acos(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.ACOS_OPERATION)(arg) override fun acos(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.ACOS_OPERATION)(arg)
public override fun atan(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.ATAN_OPERATION)(arg) override fun atan(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.ATAN_OPERATION)(arg)
public override fun sinh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.SINH_OPERATION)(arg) override fun sinh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.SINH_OPERATION)(arg)
public override fun cosh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.COSH_OPERATION)(arg) override fun cosh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.COSH_OPERATION)(arg)
public override fun tanh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.TANH_OPERATION)(arg) override fun tanh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.TANH_OPERATION)(arg)
public override fun asinh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ASINH_OPERATION)(arg) override fun asinh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ASINH_OPERATION)(arg)
public override fun acosh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ACOSH_OPERATION)(arg) override fun acosh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ACOSH_OPERATION)(arg)
public override fun atanh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ATANH_OPERATION)(arg) override fun atanh(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.ATANH_OPERATION)(arg)
public override fun add(a: MST, b: MST): MST.Binary = MstField.add(a, b) override fun add(a: MST, b: MST): MST.Binary = MstField.add(a, b)
public override fun sqrt(arg: MST): MST = unaryOperationFunction(PowerOperations.SQRT_OPERATION)(arg) override fun sqrt(arg: MST): MST = unaryOperationFunction(PowerOperations.SQRT_OPERATION)(arg)
public override fun scale(a: MST, value: Double): MST = override fun scale(a: MST, value: Double): MST =
binaryOperation(GroupOperations.PLUS_OPERATION, a, number(value)) binaryOperation(GroupOperations.PLUS_OPERATION, a, number(value))
public override fun multiply(a: MST, b: MST): MST.Binary = MstField.multiply(a, b) override fun multiply(a: MST, b: MST): MST.Binary = MstField.multiply(a, b)
public override fun divide(a: MST, b: MST): MST.Binary = MstField.divide(a, b) override fun divide(a: MST, b: MST): MST.Binary = MstField.divide(a, b)
public override operator fun MST.unaryPlus(): MST.Unary = MstField { +this@unaryPlus } override operator fun MST.unaryPlus(): MST.Unary = MstField { +this@unaryPlus }
public override operator fun MST.unaryMinus(): MST.Unary = MstField { -this@unaryMinus } override operator fun MST.unaryMinus(): MST.Unary = MstField { -this@unaryMinus }
public override operator fun MST.minus(b: MST): MST.Binary = MstField { this@minus - b } override operator fun MST.minus(b: MST): MST.Binary = MstField { this@minus - b }
public override fun power(arg: MST, pow: Number): MST.Binary = override fun power(arg: MST, pow: Number): MST.Binary =
binaryOperationFunction(PowerOperations.POW_OPERATION)(arg, number(pow)) binaryOperationFunction(PowerOperations.POW_OPERATION)(arg, number(pow))
public override fun exp(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.EXP_OPERATION)(arg) override fun exp(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.EXP_OPERATION)(arg)
public override fun ln(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.LN_OPERATION)(arg) override fun ln(arg: MST): MST.Unary = unaryOperationFunction(ExponentialOperations.LN_OPERATION)(arg)
public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary =
MstField.binaryOperationFunction(operation) MstField.binaryOperationFunction(operation)
public override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary = override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary =
MstField.unaryOperationFunction(operation) MstField.unaryOperationFunction(operation)
} }
@ -164,7 +164,7 @@ public object MstExtendedField : ExtendedField<MST>, NumericAlgebra<MST> {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public object MstLogicAlgebra : LogicAlgebra<MST> { public object MstLogicAlgebra : LogicAlgebra<MST> {
public override fun bindSymbolOrNull(value: String): MST = super.bindSymbolOrNull(value) ?: StringSymbol(value) override fun bindSymbolOrNull(value: String): MST = super.bindSymbolOrNull(value) ?: StringSymbol(value)
override fun const(boolean: Boolean): Symbol = if (boolean) { override fun const(boolean: Boolean): Symbol = if (boolean) {
LogicAlgebra.TRUE LogicAlgebra.TRUE

View File

@ -60,8 +60,8 @@ public open class SimpleAutoDiffField<T : Any, F : Field<T>>(
public val context: F, public val context: F,
bindings: Map<Symbol, T>, bindings: Map<Symbol, T>,
) : Field<AutoDiffValue<T>>, ExpressionAlgebra<T, AutoDiffValue<T>>, NumbersAddOperations<AutoDiffValue<T>> { ) : Field<AutoDiffValue<T>>, ExpressionAlgebra<T, AutoDiffValue<T>>, NumbersAddOperations<AutoDiffValue<T>> {
public override val zero: AutoDiffValue<T> get() = const(context.zero) override val zero: AutoDiffValue<T> get() = const(context.zero)
public override val one: AutoDiffValue<T> get() = const(context.one) override val one: AutoDiffValue<T> get() = const(context.one)
// this stack contains pairs of blocks and values to apply them to // this stack contains pairs of blocks and values to apply them to
private var stack: Array<Any?> = arrayOfNulls<Any?>(8) private var stack: Array<Any?> = arrayOfNulls<Any?>(8)
@ -119,8 +119,6 @@ public open class SimpleAutoDiffField<T : Any, F : Field<T>>(
get() = getDerivative(this) get() = getDerivative(this)
set(value) = setDerivative(this, value) set(value) = setDerivative(this, value)
public inline fun const(block: F.() -> T): AutoDiffValue<T> = const(context.block())
/** /**
* Performs update of derivative after the rest of the formula in the back-pass. * Performs update of derivative after the rest of the formula in the back-pass.
* *
@ -151,17 +149,17 @@ public open class SimpleAutoDiffField<T : Any, F : Field<T>>(
// // Overloads for Double constants // // Overloads for Double constants
// //
// public override operator fun Number.plus(b: AutoDiffValue<T>): AutoDiffValue<T> = // override operator fun Number.plus(b: AutoDiffValue<T>): AutoDiffValue<T> =
// derive(const { this@plus.toDouble() * one + b.value }) { z -> // derive(const { this@plus.toDouble() * one + b.value }) { z ->
// b.d += z.d // b.d += z.d
// } // }
// //
// public override operator fun AutoDiffValue<T>.plus(b: Number): AutoDiffValue<T> = b.plus(this) // override operator fun AutoDiffValue<T>.plus(b: Number): AutoDiffValue<T> = b.plus(this)
// //
// public override operator fun Number.minus(b: AutoDiffValue<T>): AutoDiffValue<T> = // override operator fun Number.minus(b: AutoDiffValue<T>): AutoDiffValue<T> =
// derive(const { this@minus.toDouble() * one - b.value }) { z -> b.d -= z.d } // derive(const { this@minus.toDouble() * one - b.value }) { z -> b.d -= z.d }
// //
// public override operator fun AutoDiffValue<T>.minus(b: Number): AutoDiffValue<T> = // override operator fun AutoDiffValue<T>.minus(b: Number): AutoDiffValue<T> =
// derive(const { this@minus.value - one * b.toDouble() }) { z -> d += z.d } // derive(const { this@minus.value - one * b.toDouble() }) { z -> d += z.d }
@ -170,30 +168,35 @@ public open class SimpleAutoDiffField<T : Any, F : Field<T>>(
// Basic math (+, -, *, /) // Basic math (+, -, *, /)
public override fun add(a: AutoDiffValue<T>, b: AutoDiffValue<T>): AutoDiffValue<T> = override fun add(a: AutoDiffValue<T>, b: AutoDiffValue<T>): AutoDiffValue<T> =
derive(const { a.value + b.value }) { z -> derive(const { a.value + b.value }) { z ->
a.d += z.d a.d += z.d
b.d += z.d b.d += z.d
} }
public override fun multiply(a: AutoDiffValue<T>, b: AutoDiffValue<T>): AutoDiffValue<T> = override fun multiply(a: AutoDiffValue<T>, b: AutoDiffValue<T>): AutoDiffValue<T> =
derive(const { a.value * b.value }) { z -> derive(const { a.value * b.value }) { z ->
a.d += z.d * b.value a.d += z.d * b.value
b.d += z.d * a.value b.d += z.d * a.value
} }
public override fun divide(a: AutoDiffValue<T>, b: AutoDiffValue<T>): AutoDiffValue<T> = override fun divide(a: AutoDiffValue<T>, b: AutoDiffValue<T>): AutoDiffValue<T> =
derive(const { a.value / b.value }) { z -> derive(const { a.value / b.value }) { z ->
a.d += z.d / b.value a.d += z.d / b.value
b.d -= z.d * a.value / (b.value * b.value) b.d -= z.d * a.value / (b.value * b.value)
} }
public override fun scale(a: AutoDiffValue<T>, value: Double): AutoDiffValue<T> = override fun scale(a: AutoDiffValue<T>, value: Double): AutoDiffValue<T> =
derive(const { value * a.value }) { z -> derive(const { value * a.value }) { z ->
a.d += z.d * value a.d += z.d * value
} }
} }
public inline fun <T : Any, F : Field<T>> SimpleAutoDiffField<T, F>.const(block: F.() -> T): AutoDiffValue<T> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return const(context.block())
}
/** /**
* Runs differentiation and establishes [SimpleAutoDiffField] context inside the block of code. * Runs differentiation and establishes [SimpleAutoDiffField] context inside the block of code.
@ -208,7 +211,7 @@ public open class SimpleAutoDiffField<T : Any, F : Field<T>>(
* assertEquals(9.0, x.d) // dy/dx * assertEquals(9.0, x.d) // dy/dx
* ``` * ```
* *
* @param body the action in [SimpleAutoDiffField] context returning [AutoDiffVariable] to differentiate with respect to. * @param body the action in [SimpleAutoDiffField] context returning [AutoDiffValue] to differentiate with respect to.
* @return the result of differentiation. * @return the result of differentiation.
*/ */
public fun <T : Any, F : Field<T>> F.simpleAutoDiff( public fun <T : Any, F : Field<T>> F.simpleAutoDiff(
@ -233,12 +236,12 @@ public class SimpleAutoDiffExpression<T : Any, F : Field<T>>(
public val field: F, public val field: F,
public val function: SimpleAutoDiffField<T, F>.() -> AutoDiffValue<T>, public val function: SimpleAutoDiffField<T, F>.() -> AutoDiffValue<T>,
) : FirstDerivativeExpression<T>() { ) : FirstDerivativeExpression<T>() {
public override operator fun invoke(arguments: Map<Symbol, T>): T { override operator fun invoke(arguments: Map<Symbol, T>): T {
//val bindings = arguments.entries.map { it.key.bind(it.value) } //val bindings = arguments.entries.map { it.key.bind(it.value) }
return SimpleAutoDiffField(field, arguments).function().value return SimpleAutoDiffField(field, arguments).function().value
} }
public override fun derivativeOrNull(symbol: Symbol): Expression<T> = Expression { arguments -> override fun derivativeOrNull(symbol: Symbol): Expression<T> = Expression { arguments ->
//val bindings = arguments.entries.map { it.key.bind(it.value) } //val bindings = arguments.entries.map { it.key.bind(it.value) }
val derivationResult = SimpleAutoDiffField(field, arguments).differentiate(function) val derivationResult = SimpleAutoDiffField(field, arguments).differentiate(function)
derivationResult.derivative(symbol) derivationResult.derivative(symbol)
@ -248,7 +251,9 @@ public class SimpleAutoDiffExpression<T : Any, F : Field<T>>(
/** /**
* Generate [AutoDiffProcessor] for [SimpleAutoDiffExpression] * Generate [AutoDiffProcessor] for [SimpleAutoDiffExpression]
*/ */
public fun <T : Any, F : Field<T>> simpleAutoDiff(field: F): AutoDiffProcessor<T, AutoDiffValue<T>, SimpleAutoDiffField<T, F>, Expression<T>> = public fun <T : Any, F : Field<T>> simpleAutoDiff(
field: F
): AutoDiffProcessor<T, AutoDiffValue<T>, SimpleAutoDiffField<T, F>> =
AutoDiffProcessor { function -> AutoDiffProcessor { function ->
SimpleAutoDiffExpression(field, function) SimpleAutoDiffExpression(field, function)
} }
@ -343,28 +348,28 @@ public class SimpleAutoDiffExtendedField<T : Any, F : ExtendedField<T>>(
override fun bindSymbol(value: String): AutoDiffValue<T> = super<SimpleAutoDiffField>.bindSymbol(value) override fun bindSymbol(value: String): AutoDiffValue<T> = super<SimpleAutoDiffField>.bindSymbol(value)
public override fun number(value: Number): AutoDiffValue<T> = const { number(value) } override fun number(value: Number): AutoDiffValue<T> = const { number(value) }
public override fun scale(a: AutoDiffValue<T>, value: Double): AutoDiffValue<T> = a * number(value) override fun scale(a: AutoDiffValue<T>, value: Double): AutoDiffValue<T> = a * number(value)
// x ^ 2 // x ^ 2
public fun sqr(x: AutoDiffValue<T>): AutoDiffValue<T> = public fun sqr(x: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).sqr(x) (this as SimpleAutoDiffField<T, F>).sqr(x)
// x ^ 1/2 // x ^ 1/2
public override fun sqrt(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun sqrt(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).sqrt(arg) (this as SimpleAutoDiffField<T, F>).sqrt(arg)
// x ^ y (const) // x ^ y (const)
public override fun power(arg: AutoDiffValue<T>, pow: Number): AutoDiffValue<T> = override fun power(arg: AutoDiffValue<T>, pow: Number): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).pow(arg, pow.toDouble()) (this as SimpleAutoDiffField<T, F>).pow(arg, pow.toDouble())
// exp(x) // exp(x)
public override fun exp(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun exp(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).exp(arg) (this as SimpleAutoDiffField<T, F>).exp(arg)
// ln(x) // ln(x)
public override fun ln(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun ln(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).ln(arg) (this as SimpleAutoDiffField<T, F>).ln(arg)
// x ^ y (any) // x ^ y (any)
@ -374,40 +379,40 @@ public class SimpleAutoDiffExtendedField<T : Any, F : ExtendedField<T>>(
): AutoDiffValue<T> = exp(y * ln(x)) ): AutoDiffValue<T> = exp(y * ln(x))
// sin(x) // sin(x)
public override fun sin(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun sin(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).sin(arg) (this as SimpleAutoDiffField<T, F>).sin(arg)
// cos(x) // cos(x)
public override fun cos(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun cos(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).cos(arg) (this as SimpleAutoDiffField<T, F>).cos(arg)
public override fun tan(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun tan(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).tan(arg) (this as SimpleAutoDiffField<T, F>).tan(arg)
public override fun asin(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun asin(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).asin(arg) (this as SimpleAutoDiffField<T, F>).asin(arg)
public override fun acos(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun acos(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).acos(arg) (this as SimpleAutoDiffField<T, F>).acos(arg)
public override fun atan(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun atan(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).atan(arg) (this as SimpleAutoDiffField<T, F>).atan(arg)
public override fun sinh(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun sinh(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).sinh(arg) (this as SimpleAutoDiffField<T, F>).sinh(arg)
public override fun cosh(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun cosh(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).cosh(arg) (this as SimpleAutoDiffField<T, F>).cosh(arg)
public override fun tanh(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun tanh(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).tanh(arg) (this as SimpleAutoDiffField<T, F>).tanh(arg)
public override fun asinh(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun asinh(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).asinh(arg) (this as SimpleAutoDiffField<T, F>).asinh(arg)
public override fun acosh(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun acosh(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).acosh(arg) (this as SimpleAutoDiffField<T, F>).acosh(arg)
public override fun atanh(arg: AutoDiffValue<T>): AutoDiffValue<T> = override fun atanh(arg: AutoDiffValue<T>): AutoDiffValue<T> =
(this as SimpleAutoDiffField<T, F>).atanh(arg) (this as SimpleAutoDiffField<T, F>).atanh(arg)
} }

View File

@ -10,6 +10,8 @@ import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
/** /**
@ -75,9 +77,13 @@ public value class SimpleSymbolIndexer(override val symbols: List<Symbol>) : Sym
* Execute the block with symbol indexer based on given symbol order * Execute the block with symbol indexer based on given symbol order
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public inline fun <R> withSymbols(vararg symbols: Symbol, block: SymbolIndexer.() -> R): R = public inline fun <R> withSymbols(vararg symbols: Symbol, block: SymbolIndexer.() -> R): R {
with(SimpleSymbolIndexer(symbols.toList()), block) contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return with(SimpleSymbolIndexer(symbols.toList()), block)
}
@UnstableKMathAPI @UnstableKMathAPI
public inline fun <R> withSymbols(symbols: Collection<Symbol>, block: SymbolIndexer.() -> R): R = public inline fun <R> withSymbols(symbols: Collection<Symbol>, block: SymbolIndexer.() -> R): R {
with(SimpleSymbolIndexer(symbols.toList()), block) contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return with(SimpleSymbolIndexer(symbols.toList()), block)
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.expressions
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.indices
/**
* Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic
* differentiation.
*/
public fun <T : Any, I : Any, A> AutoDiffProcessor<T, I, A>.chiSquaredExpression(
x: Buffer<T>,
y: Buffer<T>,
yErr: Buffer<T>,
model: A.(I) -> I,
): DifferentiableExpression<T> where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
require(x.size == y.size) { "X and y buffers should be of the same size" }
require(y.size == yErr.size) { "Y and yErr buffer should of the same size" }
return differentiate {
var sum = zero
x.indices.forEach {
val xValue = const(x[it])
val yValue = const(y[it])
val yErrValue = const(yErr[it])
val modelValue = model(xValue)
sum += ((yValue - modelValue) / yErrValue).pow(2)
}
sum
}
}

View File

@ -15,7 +15,7 @@ import space.kscience.kmath.structures.VirtualBuffer
import space.kscience.kmath.structures.indices import space.kscience.kmath.structures.indices
public class BufferedLinearSpace<T : Any, A : Ring<T>>( public class BufferedLinearSpace<T : Any, out A : Ring<T>>(
override val elementAlgebra: A, override val elementAlgebra: A,
private val bufferFactory: BufferFactory<T>, private val bufferFactory: BufferFactory<T>,
) : LinearSpace<T, A> { ) : LinearSpace<T, A> {

View File

@ -6,8 +6,8 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
/** /**
* A group of methods to solve for *X* in equation *X = A <sup>-1</sup> &middot; B*, where *A* and *B* are matrices or * A group of methods to solve for *X* in equation *X = A<sup>&minus;1</sup> &middot; B*, where *A* and *B* are
* vectors. * matrices or vectors.
* *
* @param T the type of items. * @param T the type of items.
*/ */

View File

@ -29,10 +29,10 @@ public typealias MutableMatrix<T> = MutableStructure2D<T>
public typealias Point<T> = Buffer<T> public typealias Point<T> = Buffer<T>
/** /**
* Basic operations on matrices and vectors. Operates on [Matrix]. * Basic operations on matrices and vectors.
* *
* @param T the type of items in the matrices. * @param T the type of items in the matrices.
* @param M the type of operated matrices. * @param A the type of ring over [T].
*/ */
public interface LinearSpace<T : Any, out A : Ring<T>> { public interface LinearSpace<T : Any, out A : Ring<T>> {
public val elementAlgebra: A public val elementAlgebra: A
@ -164,7 +164,7 @@ public interface LinearSpace<T : Any, out A : Ring<T>> {
public operator fun T.times(v: Point<T>): Point<T> = v * this public operator fun T.times(v: Point<T>): Point<T> = v * this
/** /**
* Compute a feature of the structure in this scope. Structure features take precedence other context features * Compute a feature of the structure in this scope. Structure features take precedence other context features.
* *
* @param F the type of feature. * @param F the type of feature.
* @param structure the structure. * @param structure the structure.
@ -195,7 +195,7 @@ public interface LinearSpace<T : Any, out A : Ring<T>> {
} }
/** /**
* Get a feature of the structure in this scope. Structure features take precedence other context features * Get a feature of the structure in this scope. Structure features take precedence other context features.
* *
* @param T the type of items in the matrices. * @param T the type of items in the matrices.
* @param F the type of feature. * @param F the type of feature.

View File

@ -115,7 +115,7 @@ public fun <T : Comparable<T>> LinearSpace<T, Field<T>>.lup(
for (i in 0 until col) sum -= luRow[i] * lu[i, col] for (i in 0 until col) sum -= luRow[i] * lu[i, col]
luRow[col] = sum luRow[col] = sum
// maintain best permutation choice // maintain the best permutation choice
if (abs(sum) > largest) { if (abs(sum) > largest) {
largest = abs(sum) largest = abs(sum)
max = row max = row

View File

@ -31,11 +31,11 @@ public object ZeroFeature : DiagonalFeature
public object UnitFeature : DiagonalFeature public object UnitFeature : DiagonalFeature
/** /**
* Matrices with this feature can be inverted: [inverse] = `a`<sup>-1</sup> where `a` is the owning matrix. * Matrices with this feature can be inverted: *[inverse] = a<sup>&minus;1</sup>* where *a* is the owning matrix.
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
*/ */
public interface InverseMatrixFeature<T : Any> : MatrixFeature { public interface InverseMatrixFeature<out T : Any> : MatrixFeature {
/** /**
* The inverse matrix of the matrix that owns this feature. * The inverse matrix of the matrix that owns this feature.
*/ */
@ -47,7 +47,7 @@ public interface InverseMatrixFeature<T : Any> : MatrixFeature {
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
*/ */
public interface DeterminantFeature<T : Any> : MatrixFeature { public interface DeterminantFeature<out T : Any> : MatrixFeature {
/** /**
* The determinant of the matrix that owns this feature. * The determinant of the matrix that owns this feature.
*/ */
@ -80,7 +80,7 @@ public object UFeature : MatrixFeature
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
*/ */
public interface LUDecompositionFeature<T : Any> : MatrixFeature { public interface LUDecompositionFeature<out T : Any> : MatrixFeature {
/** /**
* The lower triangular matrix in this decomposition. It may have [LFeature]. * The lower triangular matrix in this decomposition. It may have [LFeature].
*/ */
@ -98,7 +98,7 @@ public interface LUDecompositionFeature<T : Any> : MatrixFeature {
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
*/ */
public interface LupDecompositionFeature<T : Any> : MatrixFeature { public interface LupDecompositionFeature<out T : Any> : MatrixFeature {
/** /**
* The lower triangular matrix in this decomposition. It may have [LFeature]. * The lower triangular matrix in this decomposition. It may have [LFeature].
*/ */
@ -126,7 +126,7 @@ public object OrthogonalFeature : MatrixFeature
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
*/ */
public interface QRDecompositionFeature<T : Any> : MatrixFeature { public interface QRDecompositionFeature<out T : Any> : MatrixFeature {
/** /**
* The orthogonal matrix in this decomposition. It may have [OrthogonalFeature]. * The orthogonal matrix in this decomposition. It may have [OrthogonalFeature].
*/ */
@ -144,7 +144,7 @@ public interface QRDecompositionFeature<T : Any> : MatrixFeature {
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
*/ */
public interface CholeskyDecompositionFeature<T : Any> : MatrixFeature { public interface CholeskyDecompositionFeature<out T : Any> : MatrixFeature {
/** /**
* The triangular matrix in this decomposition. It may have either [UFeature] or [LFeature]. * The triangular matrix in this decomposition. It may have either [UFeature] or [LFeature].
*/ */
@ -157,7 +157,7 @@ public interface CholeskyDecompositionFeature<T : Any> : MatrixFeature {
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
*/ */
public interface SingularValueDecompositionFeature<T : Any> : MatrixFeature { public interface SingularValueDecompositionFeature<out T : Any> : MatrixFeature {
/** /**
* The matrix in this decomposition. It is unitary, and it consists from left singular vectors. * The matrix in this decomposition. It is unitary, and it consists from left singular vectors.
*/ */

View File

@ -23,14 +23,15 @@ public class MatrixWrapper<out T : Any> internal constructor(
) : Matrix<T> by origin { ) : Matrix<T> by origin {
/** /**
* Get the first feature matching given class. Does not guarantee that matrix has only one feature matching the criteria * Get the first feature matching given class. Does not guarantee that matrix has only one feature matching the
* criteria.
*/ */
@UnstableKMathAPI @UnstableKMathAPI
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public override fun <F : StructureFeature> getFeature(type: KClass<out F>): F? = override fun <F : StructureFeature> getFeature(type: KClass<out F>): F? =
features.getFeature(type) ?: origin.getFeature(type) features.getFeature(type) ?: origin.getFeature(type)
public override fun toString(): String = "MatrixWrapper(matrix=$origin, features=$features)" override fun toString(): String = "MatrixWrapper(matrix=$origin, features=$features)"
} }
/** /**
@ -64,7 +65,7 @@ public fun <T : Any> Matrix<T>.withFeatures(newFeatures: Iterable<MatrixFeature>
} }
/** /**
* Diagonal matrix of ones. The matrix is virtual no actual matrix is created * Diagonal matrix of ones. The matrix is virtual no actual matrix is created.
*/ */
public fun <T : Any> LinearSpace<T, Ring<T>>.one( public fun <T : Any> LinearSpace<T, Ring<T>>.one(
rows: Int, rows: Int,

View File

@ -7,8 +7,8 @@ package space.kscience.kmath.misc
/** /**
* Marks declarations that are still experimental in the KMath APIs, which means that the design of the corresponding * Marks declarations that are still experimental in the KMath APIs, which means that the design of the corresponding
* declarations has open issues which may (or may not) lead to their changes in the future. Roughly speaking, there is * declarations has open issues that may (or may not) lead to their changes in the future. Roughly speaking, there is
* a chance that those declarations will be deprecated in the near future or the semantics of their behavior may change * a chance of those declarations will be deprecated in the near future or the semantics of their behavior may change
* in some way that may break some code. * in some way that may break some code.
*/ */
@MustBeDocumented @MustBeDocumented
@ -17,7 +17,7 @@ package space.kscience.kmath.misc
public annotation class UnstableKMathAPI public annotation class UnstableKMathAPI
/** /**
* Marks API which could cause performance problems. The code, marked by this API is not necessary slow, but could cause * Marks API that could cause performance problems. The code marked by this API is unnecessary slow but could cause
* slow-down in some cases. Refer to the documentation and benchmark it to be sure. * slow-down in some cases. Refer to the documentation and benchmark it to be sure.
*/ */
@MustBeDocumented @MustBeDocumented

View File

@ -34,7 +34,7 @@ public inline fun <T, R> Iterable<T>.cumulative(initial: R, crossinline operatio
public inline fun <T, R> Sequence<T>.cumulative(initial: R, crossinline operation: (R, T) -> R): Sequence<R> = public inline fun <T, R> Sequence<T>.cumulative(initial: R, crossinline operation: (R, T) -> R): Sequence<R> =
Sequence { this@cumulative.iterator().cumulative(initial, operation) } Sequence { this@cumulative.iterator().cumulative(initial, operation) }
public fun <T, R> List<T>.cumulative(initial: R, operation: (R, T) -> R): List<R> = public inline fun <T, R> List<T>.cumulative(initial: R, crossinline operation: (R, T) -> R): List<R> =
iterator().cumulative(initial, operation).asSequence().toList() iterator().cumulative(initial, operation).asSequence().toList()
//Cumulative sum //Cumulative sum

View File

@ -3,3 +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.
*/ */
package space.kscience.kmath.misc
public expect fun Long.toIntExact(): Int

View File

@ -11,7 +11,7 @@ import space.kscience.kmath.structures.*
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
* An exception is thrown when the expected ans actual shape of NDArray differs. * An exception is thrown when the expected and actual shape of NDArray differ.
* *
* @property expected the expected shape. * @property expected the expected shape.
* @property actual the actual shape. * @property actual the actual shape.
@ -24,7 +24,6 @@ public class ShapeMismatchException(public val expected: IntArray, public val ac
* *
* @param T the type of ND-structure element. * @param T the type of ND-structure element.
* @param C the type of the element context. * @param C the type of the element context.
* @param N the type of the structure.
*/ */
public interface AlgebraND<T, out C : Algebra<T>> { public interface AlgebraND<T, out C : Algebra<T>> {
/** /**
@ -64,7 +63,7 @@ public interface AlgebraND<T, out C : Algebra<T>> {
structure.map { value -> this@invoke(value) } structure.map { value -> this@invoke(value) }
/** /**
* Get a feature of the structure in this scope. Structure features take precedence other context features * Get a feature of the structure in this scope. Structure features take precedence other context features.
* *
* @param F the type of feature. * @param F the type of feature.
* @param structure the structure. * @param structure the structure.
@ -80,7 +79,7 @@ public interface AlgebraND<T, out C : Algebra<T>> {
/** /**
* Get a feature of the structure in this scope. Structure features take precedence other context features * Get a feature of the structure in this scope. Structure features take precedence other context features.
* *
* @param T the type of items in the matrices. * @param T the type of items in the matrices.
* @param F the type of feature. * @param F the type of feature.
@ -118,8 +117,7 @@ internal fun <T, C : Algebra<T>> AlgebraND<T, C>.checkShape(element: StructureND
* Space of [StructureND]. * Space of [StructureND].
* *
* @param T the type of the element contained in ND structure. * @param T the type of the element contained in ND structure.
* @param N the type of ND structure. * @param S the type of group over structure elements.
* @param S the type of space of structure elements.
*/ */
public interface GroupND<T, out S : Group<T>> : Group<StructureND<T>>, AlgebraND<T, S> { public interface GroupND<T, out S : Group<T>> : Group<StructureND<T>>, AlgebraND<T, S> {
/** /**
@ -129,18 +127,9 @@ public interface GroupND<T, out S : Group<T>> : Group<StructureND<T>>, AlgebraND
* @param b the addend. * @param b the addend.
* @return the sum. * @return the sum.
*/ */
public override fun add(a: StructureND<T>, b: StructureND<T>): StructureND<T> = override fun add(a: StructureND<T>, b: StructureND<T>): StructureND<T> =
combine(a, b) { aValue, bValue -> add(aValue, bValue) } combine(a, b) { aValue, bValue -> add(aValue, bValue) }
// /**
// * Element-wise multiplication by scalar.
// *
// * @param a the multiplicand.
// * @param k the multiplier.
// * @return the product.
// */
// public override fun multiply(a: NDStructure<T>, k: Number): NDStructure<T> = a.map { multiply(it, k) }
// TODO move to extensions after KEEP-176 // TODO move to extensions after KEEP-176
/** /**
@ -186,8 +175,7 @@ public interface GroupND<T, out S : Group<T>> : Group<StructureND<T>>, AlgebraND
* Ring of [StructureND]. * Ring of [StructureND].
* *
* @param T the type of the element contained in ND structure. * @param T the type of the element contained in ND structure.
* @param N the type of ND structure. * @param R the type of ring over structure elements.
* @param R the type of ring of structure elements.
*/ */
public interface RingND<T, out R : Ring<T>> : Ring<StructureND<T>>, GroupND<T, R> { public interface RingND<T, out R : Ring<T>> : Ring<StructureND<T>>, GroupND<T, R> {
/** /**
@ -197,7 +185,7 @@ public interface RingND<T, out R : Ring<T>> : Ring<StructureND<T>>, GroupND<T, R
* @param b the multiplier. * @param b the multiplier.
* @return the product. * @return the product.
*/ */
public override fun multiply(a: StructureND<T>, b: StructureND<T>): StructureND<T> = override fun multiply(a: StructureND<T>, b: StructureND<T>): StructureND<T> =
combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } combine(a, b) { aValue, bValue -> multiply(aValue, bValue) }
//TODO move to extensions after KEEP-176 //TODO move to extensions after KEEP-176
@ -227,9 +215,9 @@ public interface RingND<T, out R : Ring<T>> : Ring<StructureND<T>>, GroupND<T, R
* Field of [StructureND]. * Field of [StructureND].
* *
* @param T the type of the element contained in ND structure. * @param T the type of the element contained in ND structure.
* @param F the type field of structure elements. * @param F the type field over structure elements.
*/ */
public interface FieldND<T, out F : Field<T>> : Field<StructureND<T>>, RingND<T, F>, ScaleOperations<StructureND<T>> { public interface FieldND<T, out F : Field<T>> : Field<StructureND<T>>, RingND<T, F> {
/** /**
* Element-wise division. * Element-wise division.
* *
@ -237,7 +225,7 @@ public interface FieldND<T, out F : Field<T>> : Field<StructureND<T>>, RingND<T,
* @param b the divisor. * @param b the divisor.
* @return the quotient. * @return the quotient.
*/ */
public override fun divide(a: StructureND<T>, b: StructureND<T>): StructureND<T> = override fun divide(a: StructureND<T>, b: StructureND<T>): StructureND<T> =
combine(a, b) { aValue, bValue -> divide(aValue, bValue) } combine(a, b) { aValue, bValue -> divide(aValue, bValue) }
//TODO move to extensions after KEEP-176 //TODO move to extensions after KEEP-176
@ -259,6 +247,15 @@ public interface FieldND<T, out F : Field<T>> : Field<StructureND<T>>, RingND<T,
*/ */
public operator fun T.div(arg: StructureND<T>): StructureND<T> = arg.map { divide(it, this@div) } public operator fun T.div(arg: StructureND<T>): StructureND<T> = arg.map { divide(it, this@div) }
/**
* Element-wise scaling.
*
* @param a the multiplicand.
* @param value the multiplier.
* @return the product.
*/
override fun scale(a: StructureND<T>, value: Double): StructureND<T> = a.map { scale(it, value) }
// @ThreadLocal // @ThreadLocal
// public companion object { // public companion object {
// private val realNDFieldCache: MutableMap<IntArray, RealNDField> = hashMapOf() // private val realNDFieldCache: MutableMap<IntArray, RealNDField> = hashMapOf()

View File

@ -57,7 +57,7 @@ public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
} }
} }
public open class BufferedGroupND<T, A : Group<T>>( public open class BufferedGroupND<T, out A : Group<T>>(
final override val shape: IntArray, final override val shape: IntArray,
final override val elementContext: A, final override val elementContext: A,
final override val bufferFactory: BufferFactory<T>, final override val bufferFactory: BufferFactory<T>,
@ -67,7 +67,7 @@ public open class BufferedGroupND<T, A : Group<T>>(
override fun StructureND<T>.unaryMinus(): StructureND<T> = produce { -get(it) } override fun StructureND<T>.unaryMinus(): StructureND<T> = produce { -get(it) }
} }
public open class BufferedRingND<T, R : Ring<T>>( public open class BufferedRingND<T, out R : Ring<T>>(
shape: IntArray, shape: IntArray,
elementContext: R, elementContext: R,
bufferFactory: BufferFactory<T>, bufferFactory: BufferFactory<T>,
@ -75,7 +75,7 @@ public open class BufferedRingND<T, R : Ring<T>>(
override val one: BufferND<T> by lazy { produce { one } } override val one: BufferND<T> by lazy { produce { one } }
} }
public open class BufferedFieldND<T, R : Field<T>>( public open class BufferedFieldND<T, out R : Field<T>>(
shape: IntArray, shape: IntArray,
elementContext: R, elementContext: R,
bufferFactory: BufferFactory<T>, bufferFactory: BufferFactory<T>,

View File

@ -22,15 +22,15 @@ public class DoubleFieldND(
ScaleOperations<StructureND<Double>>, ScaleOperations<StructureND<Double>>,
ExtendedField<StructureND<Double>> { ExtendedField<StructureND<Double>> {
public override val zero: BufferND<Double> by lazy { produce { zero } } override val zero: BufferND<Double> by lazy { produce { zero } }
public override val one: BufferND<Double> by lazy { produce { one } } override val one: BufferND<Double> by lazy { produce { one } }
public override fun number(value: Number): BufferND<Double> { override fun number(value: Number): BufferND<Double> {
val d = value.toDouble() // minimize conversions val d = value.toDouble() // minimize conversions
return produce { d } return produce { d }
} }
public override val StructureND<Double>.buffer: DoubleBuffer override val StructureND<Double>.buffer: DoubleBuffer
get() = when { get() = when {
!shape.contentEquals(this@DoubleFieldND.shape) -> throw ShapeMismatchException( !shape.contentEquals(this@DoubleFieldND.shape) -> throw ShapeMismatchException(
this@DoubleFieldND.shape, this@DoubleFieldND.shape,
@ -41,7 +41,7 @@ public class DoubleFieldND(
} }
@Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
public override inline fun StructureND<Double>.map( override inline fun StructureND<Double>.map(
transform: DoubleField.(Double) -> Double, transform: DoubleField.(Double) -> Double,
): BufferND<Double> { ): BufferND<Double> {
val buffer = DoubleBuffer(strides.linearSize) { offset -> DoubleField.transform(buffer.array[offset]) } val buffer = DoubleBuffer(strides.linearSize) { offset -> DoubleField.transform(buffer.array[offset]) }
@ -49,7 +49,7 @@ public class DoubleFieldND(
} }
@Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
public override inline fun produce(initializer: DoubleField.(IntArray) -> Double): BufferND<Double> { override inline fun produce(initializer: DoubleField.(IntArray) -> Double): BufferND<Double> {
val array = DoubleArray(strides.linearSize) { offset -> val array = DoubleArray(strides.linearSize) { offset ->
val index = strides.index(offset) val index = strides.index(offset)
DoubleField.initializer(index) DoubleField.initializer(index)
@ -58,7 +58,7 @@ public class DoubleFieldND(
} }
@Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
public override inline fun StructureND<Double>.mapIndexed( override inline fun StructureND<Double>.mapIndexed(
transform: DoubleField.(index: IntArray, Double) -> Double, transform: DoubleField.(index: IntArray, Double) -> Double,
): BufferND<Double> = BufferND( ): BufferND<Double> = BufferND(
strides, strides,
@ -70,7 +70,7 @@ public class DoubleFieldND(
}) })
@Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
public override inline fun combine( override inline fun combine(
a: StructureND<Double>, a: StructureND<Double>,
b: StructureND<Double>, b: StructureND<Double>,
transform: DoubleField.(Double, Double) -> Double, transform: DoubleField.(Double, Double) -> Double,
@ -81,26 +81,26 @@ public class DoubleFieldND(
return BufferND(strides, buffer) return BufferND(strides, buffer)
} }
public override fun scale(a: StructureND<Double>, value: Double): StructureND<Double> = a.map { it * value } override fun scale(a: StructureND<Double>, value: Double): StructureND<Double> = a.map { it * value }
public override fun power(arg: StructureND<Double>, pow: Number): BufferND<Double> = arg.map { power(it, pow) } override fun power(arg: StructureND<Double>, pow: Number): BufferND<Double> = arg.map { power(it, pow) }
public override fun exp(arg: StructureND<Double>): BufferND<Double> = arg.map { exp(it) } override fun exp(arg: StructureND<Double>): BufferND<Double> = arg.map { exp(it) }
public override fun ln(arg: StructureND<Double>): BufferND<Double> = arg.map { ln(it) } override fun ln(arg: StructureND<Double>): BufferND<Double> = arg.map { ln(it) }
public override fun sin(arg: StructureND<Double>): BufferND<Double> = arg.map { sin(it) } override fun sin(arg: StructureND<Double>): BufferND<Double> = arg.map { sin(it) }
public override fun cos(arg: StructureND<Double>): BufferND<Double> = arg.map { cos(it) } override fun cos(arg: StructureND<Double>): BufferND<Double> = arg.map { cos(it) }
public override fun tan(arg: StructureND<Double>): BufferND<Double> = arg.map { tan(it) } override fun tan(arg: StructureND<Double>): BufferND<Double> = arg.map { tan(it) }
public override fun asin(arg: StructureND<Double>): BufferND<Double> = arg.map { asin(it) } override fun asin(arg: StructureND<Double>): BufferND<Double> = arg.map { asin(it) }
public override fun acos(arg: StructureND<Double>): BufferND<Double> = arg.map { acos(it) } override fun acos(arg: StructureND<Double>): BufferND<Double> = arg.map { acos(it) }
public override fun atan(arg: StructureND<Double>): BufferND<Double> = arg.map { atan(it) } override fun atan(arg: StructureND<Double>): BufferND<Double> = arg.map { atan(it) }
public override fun sinh(arg: StructureND<Double>): BufferND<Double> = arg.map { sinh(it) } override fun sinh(arg: StructureND<Double>): BufferND<Double> = arg.map { sinh(it) }
public override fun cosh(arg: StructureND<Double>): BufferND<Double> = arg.map { cosh(it) } override fun cosh(arg: StructureND<Double>): BufferND<Double> = arg.map { cosh(it) }
public override fun tanh(arg: StructureND<Double>): BufferND<Double> = arg.map { tanh(it) } override fun tanh(arg: StructureND<Double>): BufferND<Double> = arg.map { tanh(it) }
public override fun asinh(arg: StructureND<Double>): BufferND<Double> = arg.map { asinh(it) } override fun asinh(arg: StructureND<Double>): BufferND<Double> = arg.map { asinh(it) }
public override fun acosh(arg: StructureND<Double>): BufferND<Double> = arg.map { acosh(it) } override fun acosh(arg: StructureND<Double>): BufferND<Double> = arg.map { acosh(it) }
public override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) } override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
} }
public fun AlgebraND.Companion.real(vararg shape: Int): DoubleFieldND = DoubleFieldND(shape) public fun AlgebraND.Companion.real(vararg shape: Int): DoubleFieldND = DoubleFieldND(shape)

View File

@ -31,9 +31,8 @@ public class ShortRingND(
/** /**
* Fast element production using function inlining. * Fast element production using function inlining.
*/ */
public inline fun BufferedRingND<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): BufferND<Short> { public inline fun BufferedRingND<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): BufferND<Short> =
return BufferND(strides, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) })) BufferND(strides, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) }))
}
public inline fun <R> ShortRing.nd(vararg shape: Int, action: ShortRingND.() -> R): R { public inline fun <R> ShortRing.nd(vararg shape: Int, action: ShortRingND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }

Some files were not shown because too many files have changed in this diff Show More