Merge branch 'dev' into feature/torch
# Conflicts: # .github/workflows/build.yml # kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt # kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/TensorLinearStructure.kt
122
.github/workflows/build.yml
vendored
@ -1,110 +1,42 @@
|
||||
name: Gradle build
|
||||
|
||||
on: [ push ]
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
types: [opened, edited]
|
||||
|
||||
jobs:
|
||||
build-ubuntu:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macOS-latest, windows-latest ]
|
||||
runs-on: ${{matrix.os}}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout the repo
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
uses: DeLaGuardo/setup-graalvm@4.0
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Install build-essential
|
||||
run: |
|
||||
sudo apt install -y build-essential
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Install Chrome
|
||||
run: |
|
||||
sudo apt install -y libappindicator1 fonts-liberation
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
sudo dpkg -i google-chrome*.deb
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
.gradle
|
||||
build
|
||||
~/.gradle
|
||||
key: gradle
|
||||
restore-keys: gradle
|
||||
|
||||
- name: Cache konan
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.konan/dependencies
|
||||
~/.konan/kotlin-native-prebuilt-linux-*
|
||||
key: ${{ runner.os }}-konan
|
||||
restore-keys: ${{ runner.os }}-konan
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew -Dorg.gradle.daemon=false --build-cache build
|
||||
|
||||
build-osx:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
.gradle
|
||||
build
|
||||
~/.gradle
|
||||
key: gradle
|
||||
restore-keys: gradle
|
||||
|
||||
- name: Cache konan
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.konan/dependencies
|
||||
~/.konan/kotlin-native-prebuilt-macos-*
|
||||
key: ${{ runner.os }}-konan
|
||||
restore-keys: ${{ runner.os }}-konan
|
||||
- name: Build with Gradle
|
||||
run: sudo ./gradlew -Dorg.gradle.daemon=false --build-cache build
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
graalvm: 21.1.0
|
||||
java: java11
|
||||
arch: amd64
|
||||
- name: Add msys to path
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: SETX PATH "%PATH%;C:\msys64\mingw64\bin"
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
.gradle
|
||||
build
|
||||
~/.gradle
|
||||
key: ${{ runner.os }}-gradle
|
||||
restore-keys: ${{ runner.os }}-gradle
|
||||
|
||||
path: ~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Cache konan
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.konan/dependencies
|
||||
~/.konan/kotlin-native-prebuilt-mingw-*
|
||||
key: ${{ runner.os }}-konan
|
||||
restore-keys: ${{ runner.os }}-konan
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew --build-cache build
|
||||
path: ~/.konan
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Build
|
||||
run: ./gradlew build --no-daemon --stacktrace
|
||||
|
24
.github/workflows/pages.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: Dokka publication
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout the repo
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Build
|
||||
run: ./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: JamesIves/github-pages-deploy-action@4.1.0
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: build/dokka/htmlMultiModule
|
61
.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
name: Gradle publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
environment:
|
||||
name: publish
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macOS-latest, windows-latest ]
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- name: Checkout the repo
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: DeLaGuardo/setup-graalvm@4.0
|
||||
with:
|
||||
graalvm: 21.1.0
|
||||
java: java11
|
||||
arch: amd64
|
||||
- name: Add msys to path
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: SETX PATH "%PATH%;C:\msys64\mingw64\bin"
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Cache konan
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.konan
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Publish Windows Artifacts
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: >
|
||||
./gradlew release --no-daemon
|
||||
-Ppublishing.enabled=true
|
||||
-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 }}
|
||||
- name: Publish Mac Artifacts
|
||||
if: matrix.os == 'macOS-latest'
|
||||
run: >
|
||||
./gradlew release --no-daemon
|
||||
-Ppublishing.enabled=true
|
||||
-Ppublishing.platform=macosX64
|
||||
-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 }}
|
117
.github/workflows/release.yml
vendored
@ -1,117 +0,0 @@
|
||||
name: Gradle release
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
jobs:
|
||||
build-ubuntu:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Install Chrome
|
||||
run: |
|
||||
sudo apt install -y libappindicator1 fonts-liberation
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
sudo dpkg -i google-chrome*.deb
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
.gradle
|
||||
build
|
||||
~/.gradle
|
||||
key: gradle
|
||||
restore-keys: gradle
|
||||
|
||||
- name: Cache konan
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.konan/dependencies
|
||||
~/.konan/kotlin-native-prebuilt-linux-*
|
||||
key: ${{ runner.os }}-konan
|
||||
restore-keys: ${{ runner.os }}-konan
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew -Dorg.gradle.daemon=false --build-cache build
|
||||
- name: Run release task
|
||||
run: ./gradlew release -PbintrayUser=${{ secrets.BINTRAY_USER }} -PbintrayApiKey=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
build-osx:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
.gradle
|
||||
build
|
||||
~/.gradle
|
||||
key: gradle
|
||||
restore-keys: gradle
|
||||
|
||||
- name: Cache konan
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.konan/dependencies
|
||||
~/.konan/kotlin-native-prebuilt-macos-*
|
||||
key: ${{ runner.os }}-konan
|
||||
restore-keys: ${{ runner.os }}-konan
|
||||
- name: Build with Gradle
|
||||
run: sudo ./gradlew -Dorg.gradle.daemon=false --build-cache build
|
||||
- name: Run release task
|
||||
run: ./gradlew release -PbintrayUser=${{ secrets.BINTRAY_USER }} -PbintrayApiKey=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Add msys to path
|
||||
run: SETX PATH "%PATH%;C:\msys64\mingw64\bin"
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
.gradle
|
||||
build
|
||||
~/.gradle
|
||||
key: ${{ runner.os }}-gradle
|
||||
restore-keys: ${{ runner.os }}-gradle
|
||||
|
||||
- name: Cache konan
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.konan/dependencies
|
||||
~/.konan/kotlin-native-prebuilt-mingw-*
|
||||
key: ${{ runner.os }}-konan
|
||||
restore-keys: ${{ runner.os }}-konan
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew --build-cache build
|
||||
- name: Run release task
|
||||
run: ./gradlew release -PbintrayUser=${{ secrets.BINTRAY_USER }} -PbintrayApiKey=${{ secrets.BINTRAY_KEY }}
|
||||
|
5
.gitignore
vendored
@ -1,7 +1,12 @@
|
||||
.gradle
|
||||
build/
|
||||
out/
|
||||
|
||||
.idea/
|
||||
|
||||
!.idea/copyright/
|
||||
!.idea/scopes/
|
||||
|
||||
.vscode/
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
|
6
.idea/copyright/kmath.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="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." />
|
||||
<option name="myName" value="kmath" />
|
||||
</copyright>
|
||||
</component>
|
21
.idea/copyright/profiles_settings.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings default="kmath">
|
||||
<module2copyright>
|
||||
<element module="Apply copyright" copyright="kmath" />
|
||||
</module2copyright>
|
||||
<LanguageOptions name="Groovy">
|
||||
<option name="fileTypeOverride" value="1" />
|
||||
</LanguageOptions>
|
||||
<LanguageOptions name="HTML">
|
||||
<option name="fileTypeOverride" value="1" />
|
||||
<option name="prefixLines" value="false" />
|
||||
</LanguageOptions>
|
||||
<LanguageOptions name="Properties">
|
||||
<option name="fileTypeOverride" value="1" />
|
||||
</LanguageOptions>
|
||||
<LanguageOptions name="XML">
|
||||
<option name="fileTypeOverride" value="1" />
|
||||
<option name="prefixLines" value="false" />
|
||||
</LanguageOptions>
|
||||
</settings>
|
||||
</component>
|
4
.idea/scopes/Apply_copyright.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="Apply copyright"
|
||||
pattern="!file[*]:*//testData//*&&!file[*]:testData//*&&!file[*]:*.gradle.kts&&!file[*]:*.gradle&&!file[group:kotlin-ultimate]:*/&&!file[kotlin.libraries]:stdlib/api//*"/>
|
||||
</component>
|
42
CHANGELOG.md
@ -2,14 +2,55 @@
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- `ScaleOperations` interface
|
||||
- `Field` extends `ScaleOperations`
|
||||
- Basic integration API
|
||||
- Basic MPP distributions and samplers
|
||||
- `bindSymbolOrNull`
|
||||
- Blocking chains and Statistics
|
||||
- Multiplatform integration
|
||||
- Integration for any Field element
|
||||
- Extended operations for ND4J fields
|
||||
- Jupyter Notebook integration module (kmath-jupyter)
|
||||
- `@PerformancePitfall` annotation to mark possibly slow API
|
||||
- `BigInt` operation performance improvement and fixes by @zhelenskiy (#328)
|
||||
- Integration between `MST` and Symja `IExpr`
|
||||
|
||||
### Changed
|
||||
- Exponential operations merged with hyperbolic functions
|
||||
- Space is replaced by Group. Space is reserved for vector spaces.
|
||||
- VectorSpace is now a vector space
|
||||
- Buffer factories for primitives moved to MutableBuffer.Companion
|
||||
- Rename `NDStructure` and `NDAlgebra` to `StructureND` and `AlgebraND` respectively
|
||||
- `Real` -> `Double`
|
||||
- DataSets are moved from functions to core
|
||||
- Redesign advanced Chain API
|
||||
- Redesign `MST`. Remove `MstExpression`.
|
||||
- Move `MST` to core
|
||||
- Separated benchmarks and examples
|
||||
- Rewrite `kmath-ejml` without `ejml-simple` artifact, support sparse matrices
|
||||
- Promote stability of kmath-ast and kmath-kotlingrad to EXPERIMENTAL.
|
||||
- ColumnarData returns nullable column
|
||||
- `MST` is made sealed interface
|
||||
- Replace `MST.Symbolic` by `Symbol`, `Symbol` now implements MST
|
||||
- Remove Any restriction on polynomials
|
||||
- Add `out` variance to type parameters of `StructureND` and its implementations where possible
|
||||
- Rename `DifferentiableMstExpression` to `KotlingradExpression`
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
- Nearest in Domain. To be implemented in geometry package.
|
||||
- Number multiplication and division in main Algebra chain
|
||||
- `contentEquals` from Buffer. It moved to the companion.
|
||||
- MSTExpression
|
||||
- Expression algebra builders
|
||||
- Complex and Quaternion no longer are elements.
|
||||
- Second generic from DifferentiableExpression
|
||||
|
||||
### Fixed
|
||||
- Ring inherits RingOperations, not GroupOperations
|
||||
- Univariate histogram filling
|
||||
|
||||
### Security
|
||||
|
||||
@ -65,6 +106,7 @@
|
||||
- `toGrid` method.
|
||||
- Public visibility of `BufferAccessor2D`
|
||||
- `Real` class
|
||||
- StructureND identity and equals
|
||||
|
||||
### Fixed
|
||||
- `symbol` method in `MstExtendedField` (https://github.com/mipt-npm/kmath/pull/140)
|
||||
|
201
LICENSE
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
131
README.md
@ -1,11 +1,8 @@
|
||||
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||
[![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)
|
||||
|
||||
Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-core/_latestVersion)
|
||||
|
||||
Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion)
|
||||
[![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/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
|
||||
|
||||
@ -14,6 +11,8 @@ Python's NumPy library. Later we found that kotlin is much more flexible languag
|
||||
designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could
|
||||
be achieved with [kmath-for-real](/kmath-for-real) extension module.
|
||||
|
||||
[Documentation site (**WIP**)](https://mipt-npm.github.io/kmath/)
|
||||
|
||||
## Publications and talks
|
||||
|
||||
* [A conceptual article about context-oriented design](https://proandroiddev.com/an-introduction-context-oriented-programming-in-kotlin-2e79d316b0a2)
|
||||
@ -41,7 +40,7 @@ KMath is a modular library. Different modules provide different features with di
|
||||
|
||||
* **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.
|
||||
* **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 genrally 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.
|
||||
* **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.
|
||||
|
||||
<!--Current feature list is [here](/docs/features.md)-->
|
||||
@ -77,6 +76,12 @@ KMath is a modular library. Different modules provide different features with di
|
||||
|
||||
<hr/>
|
||||
|
||||
* ### [benchmarks](benchmarks)
|
||||
>
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
<hr/>
|
||||
|
||||
* ### [examples](examples)
|
||||
>
|
||||
>
|
||||
@ -86,15 +91,13 @@ KMath is a modular library. Different modules provide different features with di
|
||||
* ### [kmath-ast](kmath-ast)
|
||||
>
|
||||
>
|
||||
> **Maturity**: PROTOTYPE
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
>
|
||||
> **Features:**
|
||||
> - [expression-language](kmath-ast/src/jvmMain/kotlin/kscience/kmath/ast/parser.kt) : Expression language and its parser
|
||||
> - [mst](kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation
|
||||
> - [mst-building](kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure
|
||||
> - [mst-interpreter](kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST interpreter
|
||||
> - [mst-jvm-codegen](kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler
|
||||
> - [mst-js-codegen](kmath-ast/src/jsMain/kotlin/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
|
||||
> - [expression-language](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser
|
||||
> - [mst-jvm-codegen](kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler
|
||||
> - [mst-js-codegen](kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
|
||||
> - [rendering](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt) : Extendable MST rendering
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -110,8 +113,8 @@ KMath is a modular library. Different modules provide different features with di
|
||||
> **Maturity**: PROTOTYPE
|
||||
>
|
||||
> **Features:**
|
||||
> - [complex](kmath-complex/src/commonMain/kotlin/kscience/kmath/complex/Complex.kt) : Complex Numbers
|
||||
> - [quaternion](kmath-complex/src/commonMain/kotlin/kscience/kmath/complex/Quaternion.kt) : Quaternions
|
||||
> - [complex](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt) : Complex Numbers
|
||||
> - [quaternion](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt) : Quaternions
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -121,15 +124,15 @@ KMath is a modular library. Different modules provide different features with di
|
||||
> **Maturity**: DEVELOPMENT
|
||||
>
|
||||
> **Features:**
|
||||
> - [algebras](kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : Algebraic structures like rings, spaces and fields.
|
||||
> - [nd](kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Many-dimensional structures and operations on them.
|
||||
> - [linear](kmath-core/src/commonMain/kotlin/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.
|
||||
> - [buffers](kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt) : One-dimensional structure
|
||||
> - [expressions](kmath-core/src/commonMain/kotlin/kscience/kmath/expressions) : By writing a single mathematical expression once, users will be able to apply different types of
|
||||
> - [algebras](kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Algebraic structures like rings, spaces and fields.
|
||||
> - [nd](kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/StructureND.kt) : Many-dimensional structures and operations on them.
|
||||
> - [linear](kmath-core/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.
|
||||
> - [buffers](kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffers.kt) : One-dimensional structure
|
||||
> - [expressions](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions) : 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
|
||||
performance calculations to code generation.
|
||||
> - [domains](kmath-core/src/commonMain/kotlin/kscience/kmath/domains) : Domains
|
||||
> - [autodif](kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation
|
||||
> - [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
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -149,6 +152,12 @@ performance calculations to code generation.
|
||||
>
|
||||
>
|
||||
> **Maturity**: PROTOTYPE
|
||||
>
|
||||
> **Features:**
|
||||
> - [ejml-vector](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : Point implementations.
|
||||
> - [ejml-matrix](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : Matrix implementation.
|
||||
> - [ejml-linear-space](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : LinearSpace implementations.
|
||||
|
||||
<hr/>
|
||||
|
||||
* ### [kmath-for-real](kmath-for-real)
|
||||
@ -159,22 +168,23 @@ One can still use generic algebras though.
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
>
|
||||
> **Features:**
|
||||
> - [RealVector](kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt) : Numpy-like operations for Buffers/Points
|
||||
> - [RealMatrix](kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt) : Numpy-like operations for 2d real structures
|
||||
> - [grids](kmath-for-real/src/commonMain/kotlin/kscience/kmath/structures/grids.kt) : Uniform grid generators
|
||||
> - [DoubleVector](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt) : Numpy-like operations for Buffers/Points
|
||||
> - [DoubleMatrix](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleMatrix.kt) : Numpy-like operations for 2d real structures
|
||||
> - [grids](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/structures/grids.kt) : Uniform grid generators
|
||||
|
||||
<hr/>
|
||||
|
||||
* ### [kmath-functions](kmath-functions)
|
||||
> Functions and interpolation
|
||||
>
|
||||
> **Maturity**: PROTOTYPE
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
>
|
||||
> **Features:**
|
||||
> - [piecewise](kmath-functions/Piecewise functions.) : src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt
|
||||
> - [polynomials](kmath-functions/Polynomial functions.) : src/commonMain/kotlin/kscience/kmath/functions/Polynomial.kt
|
||||
> - [linear interpolation](kmath-functions/Linear XY interpolator.) : src/commonMain/kotlin/kscience/kmath/interpolation/LinearInterpolator.kt
|
||||
> - [spline interpolation](kmath-functions/Cubic spline XY interpolator.) : src/commonMain/kotlin/kscience/kmath/interpolation/SplineInterpolator.kt
|
||||
> - [piecewise](kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Piecewise.kt) : Piecewise functions.
|
||||
> - [polynomials](kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt) : Polynomial functions.
|
||||
> - [linear interpolation](kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt) : Linear XY interpolator.
|
||||
> - [spline interpolation](kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt) : Cubic spline XY interpolator.
|
||||
> - [integration](kmath-functions/#) : Univariate and multivariate quadratures
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -190,27 +200,48 @@ One can still use generic algebras though.
|
||||
> **Maturity**: PROTOTYPE
|
||||
<hr/>
|
||||
|
||||
* ### [kmath-kotlingrad](kmath-kotlingrad)
|
||||
* ### [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)
|
||||
>
|
||||
>
|
||||
> **Maturity**: PROTOTYPE
|
||||
<hr/>
|
||||
|
||||
* ### [kmath-kotlingrad](kmath-kotlingrad)
|
||||
>
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
>
|
||||
> **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/DifferentiableMstExpression.kt) : Conversions between Kotlin∇'s SFun and MST
|
||||
|
||||
<hr/>
|
||||
|
||||
* ### [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
|
||||
<hr/>
|
||||
|
||||
* ### [kmath-nd4j](kmath-nd4j)
|
||||
> ND4J NDStructure implementation and according NDAlgebra classes
|
||||
>
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
>
|
||||
> **Features:**
|
||||
> - [nd4jarraystructure](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : NDStructure wrapper for INDArray
|
||||
> - [nd4jarrayrings](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Rings over Nd4jArrayStructure of Int and Long
|
||||
> - [nd4jarrayfields](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt) : Fields over Nd4jArrayStructure of Float and Double
|
||||
> - [nd4jarraystructure](kmath-nd4j/#) : NDStructure wrapper for INDArray
|
||||
> - [nd4jarrayrings](kmath-nd4j/#) : Rings over Nd4jArrayStructure of Int and Long
|
||||
> - [nd4jarrayfields](kmath-nd4j/#) : Fields over Nd4jArrayStructure of Float and Double
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -220,6 +251,24 @@ One can still use generic algebras though.
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
<hr/>
|
||||
|
||||
* ### [kmath-symja](kmath-symja)
|
||||
>
|
||||
>
|
||||
> **Maturity**: PROTOTYPE
|
||||
<hr/>
|
||||
|
||||
* ### [kmath-tensors](kmath-tensors)
|
||||
>
|
||||
>
|
||||
> **Maturity**: PROTOTYPE
|
||||
>
|
||||
> **Features:**
|
||||
> - [tensor algebra](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt) : Basic linear algebra operations on tensors (plus, dot, etc.)
|
||||
> - [tensor algebra with broadcasting](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/BroadcastDoubleTensorAlgebra.kt) : Basic linear algebra operations implemented with broadcasting.
|
||||
> - [linear algebra operations](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt) : Advanced linear algebra operations like LU decomposition, SVD, etc.
|
||||
|
||||
<hr/>
|
||||
|
||||
* ### [kmath-viktor](kmath-viktor)
|
||||
>
|
||||
>
|
||||
@ -245,6 +294,10 @@ cases. We expect the worst KMath benchmarks will perform better than native Pyth
|
||||
native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be
|
||||
better than SciPy.
|
||||
|
||||
## 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.
|
||||
|
||||
### 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
|
||||
@ -256,8 +309,8 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api("kscience.kmath:kmath-core:() -> kotlin.Any")
|
||||
// api("kscience.kmath:kmath-core-jvm:() -> kotlin.Any") for jvm-specific version
|
||||
api("space.kscience:kmath-core:0.3.0-dev-14")
|
||||
// api("space.kscience:kmath-core-jvm:0.3.0-dev-14") for jvm-specific version
|
||||
}
|
||||
```
|
||||
|
||||
|
138
benchmarks/build.gradle.kts
Normal file
@ -0,0 +1,138 @@
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
import space.kscience.kmath.benchmarks.addBenchmarkProperties
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
kotlin("plugin.allopen")
|
||||
id("org.jetbrains.kotlinx.benchmark")
|
||||
}
|
||||
|
||||
allOpen.annotation("org.openjdk.jmh.annotations.State")
|
||||
sourceSets.register("benchmarks")
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.kotlin.link")
|
||||
maven("https://clojars.org/repo")
|
||||
maven("https://jitpack.io")
|
||||
|
||||
maven("http://logicrunch.research.it.uu.se/maven") {
|
||||
isAllowInsecureProtocol = true
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(project(":kmath-ast"))
|
||||
implementation(project(":kmath-core"))
|
||||
implementation(project(":kmath-coroutines"))
|
||||
implementation(project(":kmath-complex"))
|
||||
implementation(project(":kmath-stat"))
|
||||
implementation(project(":kmath-dimensions"))
|
||||
implementation(project(":kmath-for-real"))
|
||||
implementation(project(":kmath-jafama"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.1")
|
||||
}
|
||||
}
|
||||
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(project(":kmath-commons"))
|
||||
implementation(project(":kmath-ejml"))
|
||||
implementation(project(":kmath-nd4j"))
|
||||
implementation(project(":kmath-kotlingrad"))
|
||||
implementation(project(":kmath-viktor"))
|
||||
implementation("org.nd4j:nd4j-native:1.0.0-M1")
|
||||
// uncomment if your system supports AVX2
|
||||
// val os = System.getProperty("os.name")
|
||||
//
|
||||
// if (System.getProperty("os.arch") in arrayOf("x86_64", "amd64")) when {
|
||||
// os.startsWith("Windows") -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:windows-x86_64-avx2")
|
||||
// os == "Linux" -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:linux-x86_64-avx2")
|
||||
// os == "Mac OS X" -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:macosx-x86_64-avx2")
|
||||
// } else
|
||||
// implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure benchmark
|
||||
benchmark {
|
||||
// Setup configurations
|
||||
targets {
|
||||
register("jvm")
|
||||
}
|
||||
|
||||
fun kotlinx.benchmark.gradle.BenchmarkConfiguration.commonConfiguration() {
|
||||
warmups = 1
|
||||
iterations = 5
|
||||
iterationTime = 1000
|
||||
iterationTimeUnit = "ms"
|
||||
}
|
||||
|
||||
configurations.register("buffer") {
|
||||
commonConfiguration()
|
||||
include("BufferBenchmark")
|
||||
}
|
||||
|
||||
configurations.register("dot") {
|
||||
commonConfiguration()
|
||||
include("DotBenchmark")
|
||||
}
|
||||
|
||||
configurations.register("expressions") {
|
||||
commonConfiguration()
|
||||
include("ExpressionsInterpretersBenchmark")
|
||||
}
|
||||
|
||||
configurations.register("matrixInverse") {
|
||||
commonConfiguration()
|
||||
include("MatrixInverseBenchmark")
|
||||
}
|
||||
|
||||
configurations.register("bigInt") {
|
||||
commonConfiguration()
|
||||
include("BigIntBenchmark")
|
||||
}
|
||||
|
||||
configurations.register("jafamaDouble") {
|
||||
commonConfiguration()
|
||||
include("JafamaBenchmark")
|
||||
}
|
||||
}
|
||||
|
||||
// Fix kotlinx-benchmarks bug
|
||||
afterEvaluate {
|
||||
val jvmBenchmarkJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
kotlin.sourceSets.all {
|
||||
with(languageSettings) {
|
||||
useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
|
||||
useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||
useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
readme {
|
||||
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
|
||||
}
|
||||
|
||||
addBenchmarkProperties()
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import kotlinx.benchmark.Blackhole
|
||||
import kotlinx.benchmark.Scope
|
||||
import kotlinx.benchmark.State
|
||||
import java.nio.IntBuffer
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class ArrayBenchmark {
|
||||
@Benchmark
|
||||
fun benchmarkArrayRead(blackhole: Blackhole) {
|
||||
var res = 0
|
||||
for (i in 1..size) res += array[size - i]
|
||||
blackhole.consume(res)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun benchmarkBufferRead(blackhole: Blackhole) {
|
||||
var res = 0
|
||||
for (i in 1..size) res += arrayBuffer[size - i]
|
||||
blackhole.consume(res)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun nativeBufferRead(blackhole: Blackhole) {
|
||||
var res = 0
|
||||
for (i in 1..size) res += nativeBuffer[size - i]
|
||||
blackhole.consume(res)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val size = 1000
|
||||
private val array = IntArray(size) { it }
|
||||
private val arrayBuffer = IntBuffer.wrap(array)
|
||||
private val nativeBuffer = IntBuffer.allocate(size).also { for (i in 0 until size) it.put(i, i) }
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.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.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.*
|
||||
import java.math.BigInteger
|
||||
|
||||
|
||||
@UnstableKMathAPI
|
||||
@State(Scope.Benchmark)
|
||||
internal class BigIntBenchmark {
|
||||
|
||||
val kmNumber = BigIntField.number(Int.MAX_VALUE)
|
||||
val jvmNumber = JBigIntegerField.number(Int.MAX_VALUE)
|
||||
val largeKmNumber = BigIntField { number(11).pow(100_000U) }
|
||||
val largeJvmNumber: BigInteger = JBigIntegerField { number(11).pow(100_000) }
|
||||
val bigExponent = 50_000
|
||||
|
||||
@Benchmark
|
||||
fun kmAdd(blackhole: Blackhole) = BigIntField {
|
||||
blackhole.consume(kmNumber + kmNumber + kmNumber)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun jvmAdd(blackhole: Blackhole) = JBigIntegerField {
|
||||
blackhole.consume(jvmNumber + jvmNumber + jvmNumber)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun kmAddLarge(blackhole: Blackhole) = BigIntField {
|
||||
blackhole.consume(largeKmNumber + largeKmNumber + largeKmNumber)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun jvmAddLarge(blackhole: Blackhole) = JBigIntegerField {
|
||||
blackhole.consume(largeJvmNumber + largeJvmNumber + largeJvmNumber)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun kmMultiply(blackhole: Blackhole) = BigIntField {
|
||||
blackhole.consume(kmNumber * kmNumber * kmNumber)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun kmMultiplyLarge(blackhole: Blackhole) = BigIntField {
|
||||
blackhole.consume(largeKmNumber*largeKmNumber)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun jvmMultiply(blackhole: Blackhole) = JBigIntegerField {
|
||||
blackhole.consume(jvmNumber * jvmNumber * jvmNumber)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun jvmMultiplyLarge(blackhole: Blackhole) = JBigIntegerField {
|
||||
blackhole.consume(largeJvmNumber*largeJvmNumber)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun kmPower(blackhole: Blackhole) = BigIntField {
|
||||
blackhole.consume(kmNumber.pow(bigExponent.toUInt()))
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun jvmPower(blackhole: Blackhole) = JBigIntegerField {
|
||||
blackhole.consume(jvmNumber.pow(bigExponent))
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun kmParsing16(blackhole: Blackhole) = JBigIntegerField {
|
||||
blackhole.consume("0x7f57ed8b89c29a3b9a85c7a5b84ca3929c7b7488593".parseBigInteger())
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun kmParsing10(blackhole: Blackhole) = JBigIntegerField {
|
||||
blackhole.consume("236656783929183747565738292847574838922010".parseBigInteger())
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun jvmParsing10(blackhole: Blackhole) = JBigIntegerField {
|
||||
blackhole.consume("236656783929183747565738292847574838922010".toBigInteger(10))
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun jvmParsing16(blackhole: Blackhole) = JBigIntegerField {
|
||||
blackhole.consume("7f57ed8b89c29a3b9a85c7a5b84ca3929c7b7488593".toBigInteger(16))
|
||||
}
|
||||
}
|
@ -1,18 +1,23 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark
|
||||
import org.openjdk.jmh.annotations.Scope
|
||||
import org.openjdk.jmh.annotations.State
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import kotlinx.benchmark.Scope
|
||||
import kotlinx.benchmark.State
|
||||
import space.kscience.kmath.complex.Complex
|
||||
import space.kscience.kmath.complex.complex
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import space.kscience.kmath.structures.MutableBuffer
|
||||
import space.kscience.kmath.structures.RealBuffer
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class BufferBenchmark {
|
||||
@Benchmark
|
||||
fun genericRealBufferReadWrite() {
|
||||
val buffer = RealBuffer(size) { it.toDouble() }
|
||||
fun genericDoubleBufferReadWrite() {
|
||||
val buffer = DoubleBuffer(size) { it.toDouble() }
|
||||
|
||||
(0 until size).forEach {
|
||||
buffer[it]
|
||||
@ -28,7 +33,7 @@ internal class BufferBenchmark {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val size: Int = 100
|
||||
private companion object {
|
||||
private const val size = 100
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import kotlinx.benchmark.Blackhole
|
||||
import kotlinx.benchmark.Scope
|
||||
import kotlinx.benchmark.State
|
||||
import space.kscience.kmath.commons.linear.CMLinearSpace
|
||||
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
|
||||
import space.kscience.kmath.linear.LinearSpace
|
||||
import space.kscience.kmath.linear.invoke
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import kotlin.random.Random
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class DotBenchmark {
|
||||
companion object {
|
||||
val random = Random(12224)
|
||||
const val dim = 1000
|
||||
|
||||
//creating invertible matrix
|
||||
val matrix1 = LinearSpace.real.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
|
||||
val matrix2 = LinearSpace.real.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
|
||||
|
||||
val cmMatrix1 = CMLinearSpace { matrix1.toCM() }
|
||||
val cmMatrix2 = CMLinearSpace { matrix2.toCM() }
|
||||
|
||||
val ejmlMatrix1 = EjmlLinearSpaceDDRM { matrix1.toEjml() }
|
||||
val ejmlMatrix2 = EjmlLinearSpaceDDRM { matrix2.toEjml() }
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun cmDot(blackhole: Blackhole) {
|
||||
CMLinearSpace.run {
|
||||
blackhole.consume(cmMatrix1 dot cmMatrix2)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun ejmlDot(blackhole: Blackhole) {
|
||||
EjmlLinearSpaceDDRM {
|
||||
blackhole.consume(ejmlMatrix1 dot ejmlMatrix2)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun ejmlDotWithConversion(blackhole: Blackhole) {
|
||||
EjmlLinearSpaceDDRM {
|
||||
blackhole.consume(matrix1 dot matrix2)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun bufferedDot(blackhole: Blackhole) {
|
||||
LinearSpace.auto(DoubleField).invoke {
|
||||
blackhole.consume(matrix1 dot matrix2)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun realDot(blackhole: Blackhole) {
|
||||
LinearSpace.real {
|
||||
blackhole.consume(matrix1 dot matrix2)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import kotlinx.benchmark.Blackhole
|
||||
import kotlinx.benchmark.Scope
|
||||
import kotlinx.benchmark.State
|
||||
import space.kscience.kmath.asm.compileToExpression
|
||||
import space.kscience.kmath.expressions.*
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.bindSymbol
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class ExpressionsInterpretersBenchmark {
|
||||
/**
|
||||
* Benchmark case for [Expression] created with [expressionInExtendedField].
|
||||
*/
|
||||
@Benchmark
|
||||
fun functionalExpression(blackhole: Blackhole) = invokeAndSum(functional, blackhole)
|
||||
|
||||
/**
|
||||
* Benchmark case for [Expression] created with [toExpression].
|
||||
*/
|
||||
@Benchmark
|
||||
fun mstExpression(blackhole: Blackhole) = invokeAndSum(mst, blackhole)
|
||||
|
||||
/**
|
||||
* Benchmark case for [Expression] created with [compileToExpression].
|
||||
*/
|
||||
@Benchmark
|
||||
fun asmExpression(blackhole: Blackhole) = invokeAndSum(asm, blackhole)
|
||||
|
||||
/**
|
||||
* Benchmark case for [Expression] implemented manually with `kotlin.math` functions.
|
||||
*/
|
||||
@Benchmark
|
||||
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) {
|
||||
val random = Random(0)
|
||||
var sum = 0.0
|
||||
|
||||
repeat(times) {
|
||||
sum += expr(x to random.nextDouble())
|
||||
}
|
||||
|
||||
blackhole.consume(sum)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val x by symbol
|
||||
private val algebra = DoubleField
|
||||
private const val times = 1_000_000
|
||||
|
||||
private val functional = DoubleField.expressionInExtendedField {
|
||||
bindSymbol(x) * number(2.0) + number(2.0) / bindSymbol(x) - number(16.0) / sin(bindSymbol(x))
|
||||
}
|
||||
|
||||
private val node = MstExtendedField {
|
||||
x * 2.0 + number(2.0) / x - number(16.0) / sin(x)
|
||||
}
|
||||
|
||||
private val mst = node.toExpression(DoubleField)
|
||||
private val asm = node.compileToExpression(DoubleField)
|
||||
|
||||
private val raw = Expression<Double> { args ->
|
||||
val x = args[x]!!
|
||||
x * 2.0 + 2.0 / x - 16.0 / sin(x)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.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.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) {
|
||||
val rng = Random(0)
|
||||
repeat(1000000) { blackhole.consume(expr(rng.nextDouble())) }
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import kotlinx.benchmark.Blackhole
|
||||
import kotlinx.benchmark.Scope
|
||||
import kotlinx.benchmark.State
|
||||
import space.kscience.kmath.commons.linear.CMLinearSpace
|
||||
import space.kscience.kmath.commons.linear.inverse
|
||||
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
|
||||
import space.kscience.kmath.linear.InverseMatrixFeature
|
||||
import space.kscience.kmath.linear.LinearSpace
|
||||
import space.kscience.kmath.linear.inverseWithLup
|
||||
import space.kscience.kmath.linear.invoke
|
||||
import space.kscience.kmath.nd.getFeature
|
||||
import kotlin.random.Random
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class MatrixInverseBenchmark {
|
||||
private companion object {
|
||||
private val random = Random(1224)
|
||||
private const val dim = 100
|
||||
|
||||
private val space = LinearSpace.real
|
||||
|
||||
//creating invertible matrix
|
||||
private val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
|
||||
private val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 }
|
||||
private val matrix = space { l dot u }
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun kmathLupInversion(blackhole: Blackhole) {
|
||||
blackhole.consume(LinearSpace.real.inverseWithLup(matrix))
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun cmLUPInversion(blackhole: Blackhole) {
|
||||
with(CMLinearSpace) {
|
||||
blackhole.consume(inverse(matrix))
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun ejmlInverse(blackhole: Blackhole) {
|
||||
with(EjmlLinearSpaceDDRM) {
|
||||
blackhole.consume(matrix.getFeature<InverseMatrixFeature<Double>>()?.inverse)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import kotlinx.benchmark.Blackhole
|
||||
import kotlinx.benchmark.Scope
|
||||
import kotlinx.benchmark.State
|
||||
import space.kscience.kmath.nd.*
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class NDFieldBenchmark {
|
||||
@Benchmark
|
||||
fun autoFieldAdd(blackhole: Blackhole) {
|
||||
with(autoField) {
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += one }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun specializedFieldAdd(blackhole: Blackhole) {
|
||||
with(specializedField) {
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Benchmark
|
||||
fun boxingFieldAdd(blackhole: Blackhole) {
|
||||
with(genericField) {
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val dim = 1000
|
||||
private const val n = 100
|
||||
private val autoField = AlgebraND.auto(DoubleField, dim, dim)
|
||||
private val specializedField = AlgebraND.real(dim, dim)
|
||||
private val genericField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim)
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import kotlinx.benchmark.Blackhole
|
||||
import kotlinx.benchmark.Scope
|
||||
import kotlinx.benchmark.State
|
||||
import org.jetbrains.bio.viktor.F64Array
|
||||
import space.kscience.kmath.nd.AlgebraND
|
||||
import space.kscience.kmath.nd.StructureND
|
||||
import space.kscience.kmath.nd.auto
|
||||
import space.kscience.kmath.nd.real
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.viktor.ViktorNDField
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class ViktorBenchmark {
|
||||
@Benchmark
|
||||
fun automaticFieldAddition(blackhole: Blackhole) {
|
||||
with(autoField) {
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun realFieldAddition(blackhole: Blackhole) {
|
||||
with(realField) {
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun viktorFieldAddition(blackhole: Blackhole) {
|
||||
with(viktorField) {
|
||||
var res = one
|
||||
repeat(n) { res += 1.0 }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun rawViktor(blackhole: Blackhole) {
|
||||
val one = F64Array.full(init = 1.0, shape = intArrayOf(dim, dim))
|
||||
var res = one
|
||||
repeat(n) { res = res + one }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val dim = 1000
|
||||
private const val n = 100
|
||||
|
||||
// automatically build context most suited for given type.
|
||||
private val autoField = AlgebraND.auto(DoubleField, dim, dim)
|
||||
private val realField = AlgebraND.real(dim, dim)
|
||||
private val viktorField = ViktorNDField(dim, dim)
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import kotlinx.benchmark.Blackhole
|
||||
import kotlinx.benchmark.Scope
|
||||
import kotlinx.benchmark.State
|
||||
import org.jetbrains.bio.viktor.F64Array
|
||||
import space.kscience.kmath.nd.AlgebraND
|
||||
import space.kscience.kmath.nd.auto
|
||||
import space.kscience.kmath.nd.real
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.viktor.ViktorFieldND
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class ViktorLogBenchmark {
|
||||
@Benchmark
|
||||
fun realFieldLog(blackhole: Blackhole) {
|
||||
with(realNdField) {
|
||||
val fortyTwo = produce { 42.0 }
|
||||
var res = one
|
||||
repeat(n) { res = ln(fortyTwo) }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun viktorFieldLog(blackhole: Blackhole) {
|
||||
with(viktorField) {
|
||||
val fortyTwo = produce { 42.0 }
|
||||
var res = one
|
||||
repeat(n) { res = ln(fortyTwo) }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun rawViktorLog(blackhole: Blackhole) {
|
||||
val fortyTwo = F64Array.full(dim, dim, init = 42.0)
|
||||
lateinit var res: F64Array
|
||||
repeat(n) { res = fortyTwo.log() }
|
||||
blackhole.consume(res)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val dim = 1000
|
||||
private const val n = 100
|
||||
|
||||
// automatically build context most suited for given type.
|
||||
private val autoField = AlgebraND.auto(DoubleField, dim, dim)
|
||||
private val realNdField = AlgebraND.real(dim, dim)
|
||||
private val viktorField = ViktorFieldND(intArrayOf(dim, dim))
|
||||
}
|
||||
}
|
@ -1,28 +1,46 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.project")
|
||||
kotlin("jupyter.api") apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven("https://clojars.org/repo")
|
||||
maven("https://dl.bintray.com/egor-bogomolov/astminer/")
|
||||
maven("https://dl.bintray.com/hotkeytlt/maven")
|
||||
maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
maven("https://dl.bintray.com/kotlin/kotlinx")
|
||||
maven("https://dl.bintray.com/mipt-npm/dev")
|
||||
maven("https://dl.bintray.com/mipt-npm/kscience")
|
||||
maven("https://jitpack.io")
|
||||
maven("http://logicrunch.research.it.uu.se/maven/")
|
||||
maven("http://logicrunch.research.it.uu.se/maven") {
|
||||
isAllowInsecureProtocol = true
|
||||
}
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
group = "space.kscience"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0-dev-14"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
if (name.startsWith("kmath")) apply<ru.mipt.npm.gradle.KSciencePublishingPlugin>()
|
||||
if (name.startsWith("kmath")) apply<MavenPublishPlugin>()
|
||||
|
||||
afterEvaluate {
|
||||
tasks.withType<org.jetbrains.dokka.gradle.DokkaTaskPartial> {
|
||||
dependsOn(tasks.getByName("assemble"))
|
||||
|
||||
dokkaSourceSets.all {
|
||||
val readmeFile = File(this@subprojects.projectDir, "README.md")
|
||||
if (readmeFile.exists()) includes.from(readmeFile.absolutePath)
|
||||
externalDocumentationLink("https://ejml.org/javadoc/")
|
||||
externalDocumentationLink("https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/")
|
||||
externalDocumentationLink("https://deeplearning4j.org/api/latest/")
|
||||
externalDocumentationLink("https://axelclk.bitbucket.io/symja/javadoc/")
|
||||
externalDocumentationLink("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/")
|
||||
|
||||
externalDocumentationLink(
|
||||
"https://breandan.net/kotlingrad/kotlingrad/",
|
||||
"https://breandan.net/kotlingrad/kotlingrad/kotlingrad/package-list",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readme {
|
||||
@ -30,9 +48,9 @@ readme {
|
||||
}
|
||||
|
||||
ksciencePublish {
|
||||
spaceRepo = "https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven"
|
||||
bintrayRepo = "kscience"
|
||||
githubProject = "kmath"
|
||||
github("kmath")
|
||||
space()
|
||||
sonatype()
|
||||
}
|
||||
|
||||
apiValidation {
|
||||
|
20
buildSrc/build.gradle.kts
Normal file
@ -0,0 +1,20 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
kotlin("plugin.serialization") version "1.4.31"
|
||||
}
|
||||
|
||||
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.0")
|
||||
api("org.jetbrains.kotlinx:kotlinx-benchmark-plugin:0.3.1")
|
||||
}
|
||||
|
||||
kotlin.sourceSets.all {
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalStdlibApi")
|
||||
}
|
@ -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
|
||||
}
|
@ -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} ± ${report.primaryMetric.scoreError} ${report.primaryMetric.scoreUnit}|")
|
||||
}
|
||||
|
||||
appendLine("</details>")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("KDocUnresolvedReference")
|
||||
|
||||
package space.kscience.kmath.ejml.codegen
|
||||
|
||||
import org.intellij.lang.annotations.Language
|
||||
import java.io.File
|
||||
|
||||
private fun Appendable.appendEjmlVector(type: String, ejmlMatrixType: String) {
|
||||
@Language("kotlin") val text = """/**
|
||||
* [EjmlVector] specialization for [$type].
|
||||
*/
|
||||
public class Ejml${type}Vector<out M : $ejmlMatrixType>(public override val origin: M) : EjmlVector<$type, M>(origin) {
|
||||
init {
|
||||
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]
|
||||
}"""
|
||||
appendLine(text)
|
||||
appendLine()
|
||||
}
|
||||
|
||||
private fun Appendable.appendEjmlMatrix(type: String, ejmlMatrixType: String) {
|
||||
val text = """/**
|
||||
* [EjmlMatrix] specialization for [$type].
|
||||
*/
|
||||
public class Ejml${type}Matrix<out M : $ejmlMatrixType>(public override val origin: M) : EjmlMatrix<$type, M>(origin) {
|
||||
public override operator fun get(i: Int, j: Int): $type = origin[i, j]
|
||||
}"""
|
||||
appendLine(text)
|
||||
appendLine()
|
||||
}
|
||||
|
||||
private fun Appendable.appendEjmlLinearSpace(
|
||||
type: String,
|
||||
kmathAlgebra: String,
|
||||
ejmlMatrixParentTypeMatrix: String,
|
||||
ejmlMatrixType: String,
|
||||
ejmlMatrixDenseType: String,
|
||||
ops: String,
|
||||
denseOps: String,
|
||||
isDense: Boolean,
|
||||
) {
|
||||
@Language("kotlin") val text = """/**
|
||||
* [EjmlLinearSpace] implementation based on [CommonOps_$ops], [DecompositionFactory_${ops}] operations and
|
||||
* [${ejmlMatrixType}] matrices.
|
||||
*/
|
||||
public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra}, $ejmlMatrixType>() {
|
||||
/**
|
||||
* The [${kmathAlgebra}] reference.
|
||||
*/
|
||||
public override val elementAlgebra: $kmathAlgebra get() = $kmathAlgebra
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public override fun Matrix<${type}>.toEjml(): Ejml${type}Matrix<${ejmlMatrixType}> = when {
|
||||
this is Ejml${type}Matrix<*> && origin is $ejmlMatrixType -> this as Ejml${type}Matrix<${ejmlMatrixType}>
|
||||
else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) }
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public override fun Point<${type}>.toEjml(): Ejml${type}Vector<${ejmlMatrixType}> = when {
|
||||
this is Ejml${type}Vector<*> && origin is $ejmlMatrixType -> this as Ejml${type}Vector<${ejmlMatrixType}>
|
||||
else -> Ejml${type}Vector(${ejmlMatrixType}(size, 1).also {
|
||||
(0 until it.numRows).forEach { row -> it[row, 0] = get(row) }
|
||||
})
|
||||
}
|
||||
|
||||
public override fun buildMatrix(
|
||||
rows: Int,
|
||||
columns: Int,
|
||||
initializer: ${kmathAlgebra}.(i: Int, j: Int) -> ${type},
|
||||
): Ejml${type}Matrix<${ejmlMatrixType}> = ${ejmlMatrixType}(rows, columns).also {
|
||||
(0 until rows).forEach { row ->
|
||||
(0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) }
|
||||
}
|
||||
}.wrapMatrix()
|
||||
|
||||
public override fun buildVector(
|
||||
size: Int,
|
||||
initializer: ${kmathAlgebra}.(Int) -> ${type},
|
||||
): Ejml${type}Vector<${ejmlMatrixType}> = Ejml${type}Vector(${ejmlMatrixType}(size, 1).also {
|
||||
(0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) }
|
||||
})
|
||||
|
||||
private fun <T : ${ejmlMatrixParentTypeMatrix}> T.wrapMatrix() = Ejml${type}Matrix(this)
|
||||
private fun <T : ${ejmlMatrixParentTypeMatrix}> T.wrapVector() = Ejml${type}Vector(this)
|
||||
|
||||
public override fun Matrix<${type}>.unaryMinus(): Matrix<${type}> = this * elementAlgebra { -one }
|
||||
|
||||
public override fun Matrix<${type}>.dot(other: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> {
|
||||
val out = ${ejmlMatrixType}(1, 1)
|
||||
CommonOps_${ops}.mult(toEjml().origin, other.toEjml().origin, out)
|
||||
return out.wrapMatrix()
|
||||
}
|
||||
|
||||
public override fun Matrix<${type}>.dot(vector: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> {
|
||||
val out = ${ejmlMatrixType}(1, 1)
|
||||
CommonOps_${ops}.mult(toEjml().origin, vector.toEjml().origin, out)
|
||||
return out.wrapVector()
|
||||
}
|
||||
|
||||
public override operator fun Matrix<${type}>.minus(other: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> {
|
||||
val out = ${ejmlMatrixType}(1, 1)
|
||||
|
||||
CommonOps_${ops}.add(
|
||||
elementAlgebra.one,
|
||||
toEjml().origin,
|
||||
elementAlgebra { -one },
|
||||
other.toEjml().origin,
|
||||
out,${
|
||||
if (isDense) "" else
|
||||
"""
|
||||
null,
|
||||
null,"""
|
||||
}
|
||||
)
|
||||
|
||||
return out.wrapMatrix()
|
||||
}
|
||||
|
||||
public override operator fun Matrix<${type}>.times(value: ${type}): Ejml${type}Matrix<${ejmlMatrixType}> {
|
||||
val res = ${ejmlMatrixType}(1, 1)
|
||||
CommonOps_${ops}.scale(value, toEjml().origin, res)
|
||||
return res.wrapMatrix()
|
||||
}
|
||||
|
||||
public override fun Point<${type}>.unaryMinus(): Ejml${type}Vector<${ejmlMatrixType}> {
|
||||
val res = ${ejmlMatrixType}(1, 1)
|
||||
CommonOps_${ops}.changeSign(toEjml().origin, res)
|
||||
return res.wrapVector()
|
||||
}
|
||||
|
||||
public override fun Matrix<${type}>.plus(other: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> {
|
||||
val out = ${ejmlMatrixType}(1, 1)
|
||||
|
||||
CommonOps_${ops}.add(
|
||||
elementAlgebra.one,
|
||||
toEjml().origin,
|
||||
elementAlgebra.one,
|
||||
other.toEjml().origin,
|
||||
out,${
|
||||
if (isDense) "" else
|
||||
"""
|
||||
null,
|
||||
null,"""
|
||||
}
|
||||
)
|
||||
|
||||
return out.wrapMatrix()
|
||||
}
|
||||
|
||||
public override fun Point<${type}>.plus(other: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> {
|
||||
val out = ${ejmlMatrixType}(1, 1)
|
||||
|
||||
CommonOps_${ops}.add(
|
||||
elementAlgebra.one,
|
||||
toEjml().origin,
|
||||
elementAlgebra.one,
|
||||
other.toEjml().origin,
|
||||
out,${
|
||||
if (isDense) "" else
|
||||
"""
|
||||
null,
|
||||
null,"""
|
||||
}
|
||||
)
|
||||
|
||||
return out.wrapVector()
|
||||
}
|
||||
|
||||
public override fun Point<${type}>.minus(other: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> {
|
||||
val out = ${ejmlMatrixType}(1, 1)
|
||||
|
||||
CommonOps_${ops}.add(
|
||||
elementAlgebra.one,
|
||||
toEjml().origin,
|
||||
elementAlgebra { -one },
|
||||
other.toEjml().origin,
|
||||
out,${
|
||||
if (isDense) "" else
|
||||
"""
|
||||
null,
|
||||
null,"""
|
||||
}
|
||||
)
|
||||
|
||||
return out.wrapVector()
|
||||
}
|
||||
|
||||
public 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}> {
|
||||
val res = ${ejmlMatrixType}(1, 1)
|
||||
CommonOps_${ops}.scale(value, toEjml().origin, res)
|
||||
return res.wrapVector()
|
||||
}
|
||||
|
||||
public override fun ${type}.times(v: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> = v * this
|
||||
|
||||
@UnstableKMathAPI
|
||||
public override fun <F : StructureFeature> getFeature(structure: Matrix<${type}>, type: KClass<out F>): F? {
|
||||
structure.getFeature(type)?.let { return it }
|
||||
val origin = structure.toEjml().origin
|
||||
|
||||
return when (type) {
|
||||
${
|
||||
if (isDense)
|
||||
""" InverseMatrixFeature::class -> object : InverseMatrixFeature<${type}> {
|
||||
override val inverse: Matrix<${type}> by lazy {
|
||||
val res = origin.copy()
|
||||
CommonOps_${ops}.invert(res)
|
||||
res.wrapMatrix()
|
||||
}
|
||||
}
|
||||
|
||||
DeterminantFeature::class -> object : DeterminantFeature<${type}> {
|
||||
override val determinant: $type by lazy { CommonOps_${ops}.det(origin) }
|
||||
}
|
||||
|
||||
SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature<${type}> {
|
||||
private val svd by lazy {
|
||||
DecompositionFactory_${ops}.svd(origin.numRows, origin.numCols, true, true, false)
|
||||
.apply { decompose(origin.copy()) }
|
||||
}
|
||||
|
||||
override val u: Matrix<${type}> by lazy { svd.getU(null, false).wrapMatrix() }
|
||||
override val s: Matrix<${type}> by lazy { svd.getW(null).wrapMatrix() }
|
||||
override val v: Matrix<${type}> by lazy { svd.getV(null, false).wrapMatrix() }
|
||||
override val singularValues: Point<${type}> by lazy { ${type}Buffer(svd.singularValues) }
|
||||
}
|
||||
|
||||
QRDecompositionFeature::class -> object : QRDecompositionFeature<${type}> {
|
||||
private val qr by lazy {
|
||||
DecompositionFactory_${ops}.qr().apply { decompose(origin.copy()) }
|
||||
}
|
||||
|
||||
override val q: Matrix<${type}> by lazy {
|
||||
qr.getQ(null, false).wrapMatrix() + OrthogonalFeature
|
||||
}
|
||||
|
||||
override val r: Matrix<${type}> by lazy { qr.getR(null, false).wrapMatrix() + UFeature }
|
||||
}
|
||||
|
||||
CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature<${type}> {
|
||||
override val l: Matrix<${type}> by lazy {
|
||||
val cholesky =
|
||||
DecompositionFactory_${ops}.chol(structure.rowNum, true).apply { decompose(origin.copy()) }
|
||||
|
||||
cholesky.getT(null).wrapMatrix() + LFeature
|
||||
}
|
||||
}
|
||||
|
||||
LupDecompositionFeature::class -> object : LupDecompositionFeature<${type}> {
|
||||
private val lup by lazy {
|
||||
DecompositionFactory_${ops}.lu(origin.numRows, origin.numCols).apply { decompose(origin.copy()) }
|
||||
}
|
||||
|
||||
override val l: Matrix<${type}> by lazy {
|
||||
lup.getLower(null).wrapMatrix() + LFeature
|
||||
}
|
||||
|
||||
override val u: Matrix<${type}> by lazy {
|
||||
lup.getUpper(null).wrapMatrix() + UFeature
|
||||
}
|
||||
|
||||
override val p: Matrix<${type}> by lazy { lup.getRowPivot(null).wrapMatrix() }
|
||||
}""" else """ QRDecompositionFeature::class -> object : QRDecompositionFeature<$type> {
|
||||
private val qr by lazy {
|
||||
DecompositionFactory_${ops}.qr(FillReducing.NONE).apply { decompose(origin.copy()) }
|
||||
}
|
||||
|
||||
override val q: Matrix<${type}> by lazy {
|
||||
qr.getQ(null, false).wrapMatrix() + OrthogonalFeature
|
||||
}
|
||||
|
||||
override val r: Matrix<${type}> by lazy { qr.getR(null, false).wrapMatrix() + UFeature }
|
||||
}
|
||||
|
||||
CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature<${type}> {
|
||||
override val l: Matrix<${type}> by lazy {
|
||||
val cholesky =
|
||||
DecompositionFactory_${ops}.cholesky().apply { decompose(origin.copy()) }
|
||||
|
||||
(cholesky.getT(null) as ${ejmlMatrixParentTypeMatrix}).wrapMatrix() + LFeature
|
||||
}
|
||||
}
|
||||
|
||||
LUDecompositionFeature::class, DeterminantFeature::class, InverseMatrixFeature::class -> object :
|
||||
LUDecompositionFeature<${type}>, DeterminantFeature<${type}>, InverseMatrixFeature<${type}> {
|
||||
private val lu by lazy {
|
||||
DecompositionFactory_${ops}.lu(FillReducing.NONE).apply { decompose(origin.copy()) }
|
||||
}
|
||||
|
||||
override val l: Matrix<${type}> by lazy {
|
||||
lu.getLower(null).wrapMatrix() + LFeature
|
||||
}
|
||||
|
||||
override val u: Matrix<${type}> by lazy {
|
||||
lu.getUpper(null).wrapMatrix() + UFeature
|
||||
}
|
||||
|
||||
override val inverse: Matrix<${type}> by lazy {
|
||||
var a = origin
|
||||
val inverse = ${ejmlMatrixDenseType}(1, 1)
|
||||
val solver = LinearSolverFactory_${ops}.lu(FillReducing.NONE)
|
||||
if (solver.modifiesA()) a = a.copy()
|
||||
val i = CommonOps_${denseOps}.identity(a.numRows)
|
||||
solver.solve(i, inverse)
|
||||
inverse.wrapMatrix()
|
||||
}
|
||||
|
||||
override val determinant: $type by lazy { elementAlgebra.number(lu.computeDeterminant().real) }
|
||||
}"""
|
||||
}
|
||||
|
||||
else -> null
|
||||
}?.let(type::cast)
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves for *x* in the following equation: *x = [a] <sup>-1</sup> · [b]*.
|
||||
*
|
||||
* @param a the base matrix.
|
||||
* @param b n by p matrix.
|
||||
* @return the solution for *x* that is n by p.
|
||||
*/
|
||||
public fun solve(a: Matrix<${type}>, b: Matrix<${type}>): Ejml${type}Matrix<${ejmlMatrixType}> {
|
||||
val res = ${ejmlMatrixType}(1, 1)
|
||||
CommonOps_${ops}.solve(${ejmlMatrixType}(a.toEjml().origin), ${ejmlMatrixType}(b.toEjml().origin), res)
|
||||
return res.wrapMatrix()
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves for *x* in the following equation: *x = [a] <sup>-1</sup> · [b]*.
|
||||
*
|
||||
* @param a the base matrix.
|
||||
* @param b n by p vector.
|
||||
* @return the solution for *x* that is n by p.
|
||||
*/
|
||||
public fun solve(a: Matrix<${type}>, b: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> {
|
||||
val res = ${ejmlMatrixType}(1, 1)
|
||||
CommonOps_${ops}.solve(${ejmlMatrixType}(a.toEjml().origin), ${ejmlMatrixType}(b.toEjml().origin), res)
|
||||
return Ejml${type}Vector(res)
|
||||
}
|
||||
}"""
|
||||
appendLine(text)
|
||||
appendLine()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates routine EJML classes.
|
||||
*/
|
||||
fun ejmlCodegen(outputFile: String): Unit = File(outputFile).run {
|
||||
parentFile.mkdirs()
|
||||
|
||||
writer().use {
|
||||
it.appendLine("/*")
|
||||
it.appendLine(" * Copyright 2018-2021 KMath contributors.")
|
||||
it.appendLine(" * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.")
|
||||
it.appendLine(" */")
|
||||
it.appendLine()
|
||||
it.appendLine("/* This file is generated with buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt */")
|
||||
it.appendLine()
|
||||
it.appendLine("package space.kscience.kmath.ejml")
|
||||
it.appendLine()
|
||||
it.appendLine("""import org.ejml.data.*
|
||||
import org.ejml.dense.row.CommonOps_DDRM
|
||||
import org.ejml.dense.row.CommonOps_FDRM
|
||||
import org.ejml.dense.row.factory.DecompositionFactory_DDRM
|
||||
import org.ejml.dense.row.factory.DecompositionFactory_FDRM
|
||||
import org.ejml.sparse.FillReducing
|
||||
import org.ejml.sparse.csc.CommonOps_DSCC
|
||||
import org.ejml.sparse.csc.CommonOps_FSCC
|
||||
import org.ejml.sparse.csc.factory.DecompositionFactory_DSCC
|
||||
import org.ejml.sparse.csc.factory.DecompositionFactory_FSCC
|
||||
import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC
|
||||
import org.ejml.sparse.csc.factory.LinearSolverFactory_FSCC
|
||||
import space.kscience.kmath.linear.*
|
||||
import space.kscience.kmath.linear.Matrix
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.nd.StructureFeature
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.FloatField
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import space.kscience.kmath.structures.FloatBuffer
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.cast""")
|
||||
it.appendLine()
|
||||
it.appendEjmlVector("Double", "DMatrix")
|
||||
it.appendEjmlVector("Float", "FMatrix")
|
||||
it.appendEjmlMatrix("Double", "DMatrix")
|
||||
it.appendEjmlMatrix("Float", "FMatrix")
|
||||
it.appendEjmlLinearSpace("Double", "DoubleField", "DMatrix", "DMatrixRMaj", "DMatrixRMaj", "DDRM", "DDRM", true)
|
||||
it.appendEjmlLinearSpace("Float", "FloatField", "FMatrix", "FMatrixRMaj", "FMatrixRMaj", "FDRM", "FDRM", true)
|
||||
|
||||
it.appendEjmlLinearSpace(
|
||||
type = "Double",
|
||||
kmathAlgebra = "DoubleField",
|
||||
ejmlMatrixParentTypeMatrix = "DMatrix",
|
||||
ejmlMatrixType = "DMatrixSparseCSC",
|
||||
ejmlMatrixDenseType = "DMatrixRMaj",
|
||||
ops = "DSCC",
|
||||
denseOps = "DDRM",
|
||||
isDense = false,
|
||||
)
|
||||
|
||||
it.appendEjmlLinearSpace(
|
||||
type = "Float",
|
||||
kmathAlgebra = "FloatField",
|
||||
ejmlMatrixParentTypeMatrix = "FMatrix",
|
||||
ejmlMatrixType = "FMatrixSparseCSC",
|
||||
ejmlMatrixDenseType = "FMatrixRMaj",
|
||||
ops = "FSCC",
|
||||
denseOps = "FDRM",
|
||||
isDense = false,
|
||||
)
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ multiplication;
|
||||
- [Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and its neutral element (i.e. 1);
|
||||
- [Field](http://mathworld.wolfram.com/Field.html) adds division operation.
|
||||
|
||||
A typical implementation of `Field<T>` is the `RealField` 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
|
||||
interface. Also, contexts may have operations, which produce elements outside of the context. For example, `Matrix.dot`
|
||||
|
@ -13,16 +13,19 @@
|
||||
version="1.1"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata>
|
||||
<defs
|
||||
id="defs6"><clipPath
|
||||
id="clipPath24"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path22"
|
||||
d="M 0,1590 H 6720 V 4400 H 0 Z" /></clipPath><clipPath
|
||||
d="M 0,1590 H 6720 V 4400 H 0 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath36"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path34"
|
||||
d="M 3410,0 H 6720 V 1590 H 3410 Z" /></clipPath></defs><g
|
||||
d="M 3410,0 H 6720 V 1590 H 3410 Z" /></clipPath></defs>
|
||||
<g
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,0,586.66667)"
|
||||
id="g10"><g
|
||||
transform="scale(0.1)"
|
||||
|
Before Width: | Height: | Size: 248 KiB After Width: | Height: | Size: 248 KiB |
@ -13,12 +13,14 @@
|
||||
version="1.1"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata>
|
||||
<defs
|
||||
id="defs6"><clipPath
|
||||
id="clipPath32"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path30"
|
||||
d="M 1780,1750 H 3830 V 3800 H 1780 Z" /></clipPath></defs><g
|
||||
d="M 1780,1750 H 3830 V 3800 H 1780 Z" /></clipPath></defs>
|
||||
<g
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,0,633.33333)"
|
||||
id="g10"><g
|
||||
transform="scale(0.1)"
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@ -13,24 +13,29 @@
|
||||
version="1.1"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata>
|
||||
<defs
|
||||
id="defs6"><clipPath
|
||||
id="clipPath24"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path22"
|
||||
d="M 0,3010 H 6470 V 4280 H 0 Z" /></clipPath><clipPath
|
||||
d="M 0,3010 H 6470 V 4280 H 0 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath36"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path34"
|
||||
d="M 0,1590 H 7000 V 3010 H 0 Z" /></clipPath><clipPath
|
||||
d="M 0,1590 H 7000 V 3010 H 0 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath48"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path46"
|
||||
d="m 0,1580 h 6470 v 10 H 0 Z" /></clipPath><clipPath
|
||||
d="m 0,1580 h 6470 v 10 H 0 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath60"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path58"
|
||||
d="M 3280,0 H 6460 V 1580 H 3280 Z" /></clipPath></defs><g
|
||||
d="M 3280,0 H 6460 V 1580 H 3280 Z" /></clipPath></defs>
|
||||
<g
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,0,570.66667)"
|
||||
id="g10"><g
|
||||
transform="scale(0.1)"
|
||||
|
Before Width: | Height: | Size: 278 KiB After Width: | Height: | Size: 278 KiB |
@ -13,88 +13,109 @@
|
||||
version="1.1"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata>
|
||||
<defs
|
||||
id="defs6"><clipPath
|
||||
id="clipPath40"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path38"
|
||||
d="m 2370,2780 h 1370 v 10 H 2370 Z" /></clipPath><clipPath
|
||||
d="m 2370,2780 h 1370 v 10 H 2370 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath52"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path50"
|
||||
d="m 5630,2780 h 860 v 10 h -860 z" /></clipPath><clipPath
|
||||
d="m 5630,2780 h 860 v 10 h -860 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath64"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path62"
|
||||
d="m 970,2690 h 1300 v 90 H 970 Z" /></clipPath><clipPath
|
||||
d="m 970,2690 h 1300 v 90 H 970 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath76"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path74"
|
||||
d="m 2370,2690 h 1370 v 90 H 2370 Z" /></clipPath><clipPath
|
||||
d="m 2370,2690 h 1370 v 90 H 2370 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath88"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path86"
|
||||
d="m 5630,2690 h 860 v 90 h -860 z" /></clipPath><clipPath
|
||||
d="m 5630,2690 h 860 v 90 h -860 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath100"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path98"
|
||||
d="m 970,2460 h 1300 v 230 H 970 Z" /></clipPath><clipPath
|
||||
d="m 970,2460 h 1300 v 230 H 970 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath112"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path110"
|
||||
d="m 2370,2460 h 1370 v 230 H 2370 Z" /></clipPath><clipPath
|
||||
d="m 2370,2460 h 1370 v 230 H 2370 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath124"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path122"
|
||||
d="m 4900,2460 h 620 v 230 h -620 z" /></clipPath><clipPath
|
||||
d="m 4900,2460 h 620 v 230 h -620 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath136"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path134"
|
||||
d="m 5630,2460 h 860 v 230 h -860 z" /></clipPath><clipPath
|
||||
d="m 5630,2460 h 860 v 230 h -860 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath148"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path146"
|
||||
d="m 970,1480 h 1300 v 980 H 970 Z" /></clipPath><clipPath
|
||||
d="m 970,1480 h 1300 v 980 H 970 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath160"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path158"
|
||||
d="m 2370,1480 h 1370 v 980 H 2370 Z" /></clipPath><clipPath
|
||||
d="m 2370,1480 h 1370 v 980 H 2370 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath172"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path170"
|
||||
d="m 3920,1480 h 860 v 980 h -860 z" /></clipPath><clipPath
|
||||
d="m 3920,1480 h 860 v 980 h -860 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath184"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path182"
|
||||
d="m 4900,1480 h 620 v 980 h -620 z" /></clipPath><clipPath
|
||||
d="m 4900,1480 h 620 v 980 h -620 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath196"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path194"
|
||||
d="m 5630,1480 h 860 v 980 h -860 z" /></clipPath><clipPath
|
||||
d="m 5630,1480 h 860 v 980 h -860 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath208"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path206"
|
||||
d="m 2370,1470 h 1370 v 10 H 2370 Z" /></clipPath><clipPath
|
||||
d="m 2370,1470 h 1370 v 10 H 2370 Z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath220"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path218"
|
||||
d="m 3920,1470 h 860 v 10 h -860 z" /></clipPath><clipPath
|
||||
d="m 3920,1470 h 860 v 10 h -860 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath232"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path230"
|
||||
d="m 4900,1470 h 620 v 10 h -620 z" /></clipPath><clipPath
|
||||
d="m 4900,1470 h 620 v 10 h -620 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath244"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path242"
|
||||
d="m 5630,1470 h 860 v 10 h -860 z" /></clipPath><clipPath
|
||||
d="m 5630,1470 h 860 v 10 h -860 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath256"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path254"
|
||||
d="m 3920,1450 h 860 v 20 h -860 z" /></clipPath><clipPath
|
||||
d="m 3920,1450 h 860 v 20 h -860 z" /></clipPath>
|
||||
<clipPath
|
||||
id="clipPath268"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path266"
|
||||
d="m 4900,1450 h 620 v 20 h -620 z" /></clipPath></defs><g
|
||||
d="m 4900,1450 h 620 v 20 h -620 z" /></clipPath></defs>
|
||||
<g
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,0,529.33333)"
|
||||
id="g10"><g
|
||||
transform="scale(0.1)"
|
||||
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
@ -10,11 +10,11 @@ structures. In `kmath` performance depends on which particular context was used
|
||||
Let us consider following contexts:
|
||||
```kotlin
|
||||
// automatically build context most suited for given type.
|
||||
val autoField = NDField.auto(RealField, dim, dim)
|
||||
val autoField = NDField.auto(DoubleField, dim, dim)
|
||||
// specialized nd-field for Double. It works as generic Double field as well
|
||||
val specializedField = NDField.real(dim, dim)
|
||||
//A generic boxing field. It should be used for objects, not primitives.
|
||||
val genericField = NDField.buffered(RealField, 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:
|
||||
|
||||
|
66
docs/templates/ARTIFACT-TEMPLATE.md
vendored
@ -1,40 +1,26 @@
|
||||
> #### Artifact:
|
||||
>
|
||||
> This module artifact: `${group}:${name}:${version}`.
|
||||
>
|
||||
> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/${name}/images/download.svg) ](https://bintray.com/mipt-npm/kscience/${name}/_latestVersion)
|
||||
>
|
||||
> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/${name}/images/download.svg) ](https://bintray.com/mipt-npm/dev/${name}/_latestVersion)
|
||||
>
|
||||
> **Gradle:**
|
||||
>
|
||||
> ```gradle
|
||||
> repositories {
|
||||
> maven { url 'https://repo.kotlin.link' }
|
||||
> maven { url 'https://dl.bintray.com/hotkeytlt/maven' }
|
||||
> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap
|
||||
>// Uncomment if repo.kotlin.link is unavailable
|
||||
>// maven { url 'https://dl.bintray.com/mipt-npm/kscience' }
|
||||
>// maven { url 'https://dl.bintray.com/mipt-npm/dev' }
|
||||
> }
|
||||
>
|
||||
> dependencies {
|
||||
> implementation '${group}:${name}:${version}'
|
||||
> }
|
||||
> ```
|
||||
> **Gradle Kotlin DSL:**
|
||||
>
|
||||
> ```kotlin
|
||||
> repositories {
|
||||
> maven("https://repo.kotlin.link")
|
||||
> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap
|
||||
> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a
|
||||
>// Uncomment if repo.kotlin.link is unavailable
|
||||
>// maven("https://dl.bintray.com/mipt-npm/kscience")
|
||||
>// maven("https://dl.bintray.com/mipt-npm/dev")
|
||||
> }
|
||||
>
|
||||
> dependencies {
|
||||
> implementation("${group}:${name}:${version}")
|
||||
> }
|
||||
> ```
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `${group}:${name}:${version}`.
|
||||
|
||||
**Gradle:**
|
||||
```gradle
|
||||
repositories {
|
||||
maven { url 'https://repo.kotlin.link' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation '${group}:${name}:${version}'
|
||||
}
|
||||
```
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("${group}:${name}:${version}")
|
||||
}
|
||||
```
|
19
docs/templates/README-TEMPLATE.md
vendored
@ -1,11 +1,8 @@
|
||||
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||
[![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)
|
||||
|
||||
Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-core/_latestVersion)
|
||||
|
||||
Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion)
|
||||
[![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/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
|
||||
|
||||
@ -14,6 +11,8 @@ Python's NumPy library. Later we found that kotlin is much more flexible languag
|
||||
designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could
|
||||
be achieved with [kmath-for-real](/kmath-for-real) extension module.
|
||||
|
||||
[Documentation site (**WIP**)](https://mipt-npm.github.io/kmath/)
|
||||
|
||||
## Publications and talks
|
||||
|
||||
* [A conceptual article about context-oriented design](https://proandroiddev.com/an-introduction-context-oriented-programming-in-kotlin-2e79d316b0a2)
|
||||
@ -41,7 +40,7 @@ KMath is a modular library. Different modules provide different features with di
|
||||
|
||||
* **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.
|
||||
* **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 genrally 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.
|
||||
* **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.
|
||||
|
||||
<!--Current feature list is [here](/docs/features.md)-->
|
||||
@ -95,6 +94,10 @@ cases. We expect the worst KMath benchmarks will perform better than native Pyth
|
||||
native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be
|
||||
better than SciPy.
|
||||
|
||||
## 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.
|
||||
|
||||
### 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
|
||||
@ -106,8 +109,8 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api("kscience.kmath:kmath-core:$version")
|
||||
// api("kscience.kmath:kmath-core-jvm:$version") for jvm-specific version
|
||||
api("${group}:kmath-core:$version")
|
||||
// api("${group}:kmath-core-jvm:$version") for jvm-specific version
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1,27 +1,16 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.allopen")
|
||||
id("kotlinx.benchmark")
|
||||
}
|
||||
|
||||
allOpen.annotation("org.openjdk.jmh.annotations.State")
|
||||
sourceSets.register("benchmarks")
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven("https://repo.kotlin.link")
|
||||
maven("https://clojars.org/repo")
|
||||
maven("https://dl.bintray.com/egor-bogomolov/astminer/")
|
||||
maven("https://dl.bintray.com/hotkeytlt/maven")
|
||||
maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
maven("https://dl.bintray.com/kotlin/kotlinx")
|
||||
maven("https://dl.bintray.com/mipt-npm/dev")
|
||||
maven("https://dl.bintray.com/mipt-npm/kscience")
|
||||
maven("https://jitpack.io")
|
||||
maven("http://logicrunch.research.it.uu.se/maven/")
|
||||
mavenCentral()
|
||||
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers")
|
||||
maven("http://logicrunch.research.it.uu.se/maven") {
|
||||
isAllowInsecureProtocol = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@ -36,10 +25,10 @@ dependencies {
|
||||
implementation(project(":kmath-dimensions"))
|
||||
implementation(project(":kmath-ejml"))
|
||||
implementation(project(":kmath-nd4j"))
|
||||
|
||||
implementation(project(":kmath-tensors"))
|
||||
implementation(project(":kmath-symja"))
|
||||
implementation(project(":kmath-for-real"))
|
||||
|
||||
implementation("org.deeplearning4j:deeplearning4j-core:1.0.0-beta7")
|
||||
implementation("org.nd4j:nd4j-native:1.0.0-beta7")
|
||||
|
||||
// uncomment if your system supports AVX2
|
||||
@ -52,57 +41,26 @@ dependencies {
|
||||
// } else
|
||||
implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-io:0.2.0-npm-dev-11")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20")
|
||||
implementation("org.slf4j:slf4j-simple:1.7.30")
|
||||
|
||||
// plotting
|
||||
implementation("kscience.plotlykt:plotlykt-server:0.3.1-dev")
|
||||
|
||||
"benchmarksImplementation"("org.jetbrains.kotlinx:kotlinx.benchmark.runtime-jvm:0.2.0-dev-20")
|
||||
"benchmarksImplementation"(sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath)
|
||||
}
|
||||
|
||||
// Configure benchmark
|
||||
benchmark {
|
||||
// Setup configurations
|
||||
targets.register("benchmarks")
|
||||
// This one matches sourceSet name above
|
||||
|
||||
configurations.register("buffer") {
|
||||
warmups = 1 // number of warmup iterations
|
||||
iterations = 3 // number of iterations
|
||||
iterationTime = 500 // time in seconds per iteration
|
||||
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
|
||||
include("BufferBenchmark")
|
||||
}
|
||||
|
||||
configurations.register("dot") {
|
||||
warmups = 1 // number of warmup iterations
|
||||
iterations = 3 // number of iterations
|
||||
iterationTime = 500 // time in seconds per iteration
|
||||
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
|
||||
include("DotBenchmark")
|
||||
}
|
||||
|
||||
configurations.register("expressions") {
|
||||
warmups = 1 // number of warmup iterations
|
||||
iterations = 3 // number of iterations
|
||||
iterationTime = 500 // time in seconds per iteration
|
||||
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
|
||||
include("ExpressionsInterpretersBenchmark")
|
||||
}
|
||||
implementation("space.kscience:plotlykt-server:0.4.0")
|
||||
//jafama
|
||||
implementation(project(":kmath-jafama"))
|
||||
}
|
||||
|
||||
kotlin.sourceSets.all {
|
||||
with(languageSettings) {
|
||||
useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
|
||||
useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||
useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "11"
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
kotlinOptions{
|
||||
jvmTarget = "11"
|
||||
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" + "-Xopt-in=kotlin.RequiresOptIn"
|
||||
}
|
||||
}
|
||||
|
||||
readme {
|
||||
|
@ -1,34 +0,0 @@
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark
|
||||
import org.openjdk.jmh.annotations.Scope
|
||||
import org.openjdk.jmh.annotations.State
|
||||
import java.nio.IntBuffer
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class ArrayBenchmark {
|
||||
@Benchmark
|
||||
fun benchmarkArrayRead() {
|
||||
var res = 0
|
||||
for (i in 1..space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.size) res += space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.array[space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.size - i]
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun benchmarkBufferRead() {
|
||||
var res = 0
|
||||
for (i in 1..space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.size) res += space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.arrayBuffer[space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.size - i]
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun nativeBufferRead() {
|
||||
var res = 0
|
||||
for (i in 1..space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.size) res += space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.nativeBuffer[space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.size - i]
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val size: Int = 1000
|
||||
val array: IntArray = IntArray(space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.size) { it }
|
||||
val arrayBuffer: IntBuffer = IntBuffer.wrap(space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.array)
|
||||
val nativeBuffer: IntBuffer = IntBuffer.allocate(space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.size).also { for (i in 0 until space.kscience.kmath.benchmarks.ArrayBenchmark.Companion.size) it.put(i, i) }
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import org.openjdk.jmh.annotations.Scope
|
||||
import org.openjdk.jmh.annotations.State
|
||||
import space.kscience.kmath.commons.linear.CMMatrixContext
|
||||
import space.kscience.kmath.ejml.EjmlMatrixContext
|
||||
import space.kscience.kmath.linear.BufferMatrixContext
|
||||
import space.kscience.kmath.linear.Matrix
|
||||
import space.kscience.kmath.linear.RealMatrixContext
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import kotlin.random.Random
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class DotBenchmark {
|
||||
companion object {
|
||||
val random = Random(12224)
|
||||
val dim = 1000
|
||||
|
||||
//creating invertible matrix
|
||||
val matrix1 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
|
||||
val matrix2 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
|
||||
|
||||
val cmMatrix1 = CMMatrixContext { matrix1.toCM() }
|
||||
val cmMatrix2 = CMMatrixContext { matrix2.toCM() }
|
||||
|
||||
val ejmlMatrix1 = EjmlMatrixContext { matrix1.toEjml() }
|
||||
val ejmlMatrix2 = EjmlMatrixContext { matrix2.toEjml() }
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun cmDot() {
|
||||
CMMatrixContext {
|
||||
cmMatrix1 dot cmMatrix2
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun ejmlDot() {
|
||||
EjmlMatrixContext {
|
||||
ejmlMatrix1 dot ejmlMatrix2
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun ejmlDotWithConversion() {
|
||||
EjmlMatrixContext {
|
||||
matrix1 dot matrix2
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun bufferedDot() {
|
||||
BufferMatrixContext(RealField, Buffer.Companion::real).invoke {
|
||||
matrix1 dot matrix2
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun realDot() {
|
||||
RealMatrixContext {
|
||||
matrix1 dot matrix2
|
||||
}
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark
|
||||
import org.openjdk.jmh.annotations.Scope
|
||||
import org.openjdk.jmh.annotations.State
|
||||
import space.kscience.kmath.asm.compile
|
||||
import space.kscience.kmath.ast.mstInField
|
||||
import space.kscience.kmath.expressions.Expression
|
||||
import space.kscience.kmath.expressions.expressionInField
|
||||
import space.kscience.kmath.expressions.invoke
|
||||
import space.kscience.kmath.expressions.symbol
|
||||
import space.kscience.kmath.operations.Field
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.operations.bindSymbol
|
||||
import kotlin.random.Random
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class ExpressionsInterpretersBenchmark {
|
||||
private val algebra: Field<Double> = RealField
|
||||
val x by symbol
|
||||
|
||||
@Benchmark
|
||||
fun functionalExpression() {
|
||||
val expr = algebra.expressionInField {
|
||||
val x = bindSymbol(x)
|
||||
x * const(2.0) + const(2.0) / x - const(16.0)
|
||||
}
|
||||
|
||||
invokeAndSum(expr)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun mstExpression() {
|
||||
val expr = algebra.mstInField {
|
||||
val x = bindSymbol(x)
|
||||
x * 2.0 + 2.0 / x - 16.0
|
||||
}
|
||||
|
||||
invokeAndSum(expr)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun asmExpression() {
|
||||
val expr = algebra.mstInField {
|
||||
val x = bindSymbol(x)
|
||||
x * 2.0 + 2.0 / x - 16.0
|
||||
}.compile()
|
||||
|
||||
invokeAndSum(expr)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun rawExpression() {
|
||||
val expr = Expression<Double> { args ->
|
||||
val x = args.getValue(x)
|
||||
x * 2.0 + 2.0 / x - 16.0
|
||||
}
|
||||
invokeAndSum(expr)
|
||||
}
|
||||
|
||||
private fun invokeAndSum(expr: Expression<Double>) {
|
||||
val random = Random(0)
|
||||
var sum = 0.0
|
||||
|
||||
repeat(1000000) {
|
||||
sum += expr(x to random.nextDouble())
|
||||
}
|
||||
|
||||
println(sum)
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import kotlinx.benchmark.Benchmark
|
||||
import org.openjdk.jmh.annotations.Scope
|
||||
import org.openjdk.jmh.annotations.State
|
||||
import space.kscience.kmath.commons.linear.CMMatrixContext
|
||||
import space.kscience.kmath.commons.linear.CMMatrixContext.dot
|
||||
import space.kscience.kmath.commons.linear.inverse
|
||||
import space.kscience.kmath.ejml.EjmlMatrixContext
|
||||
import space.kscience.kmath.ejml.inverse
|
||||
import space.kscience.kmath.linear.Matrix
|
||||
import space.kscience.kmath.linear.MatrixContext
|
||||
import space.kscience.kmath.linear.inverseWithLup
|
||||
import space.kscience.kmath.linear.real
|
||||
import kotlin.random.Random
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class LinearAlgebraBenchmark {
|
||||
companion object {
|
||||
val random = Random(1224)
|
||||
val dim = 100
|
||||
|
||||
//creating invertible matrix
|
||||
val u = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
|
||||
val l = Matrix.real(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 }
|
||||
val matrix = l dot u
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun kmathLupInversion() {
|
||||
MatrixContext.real.inverseWithLup(matrix)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun cmLUPInversion() {
|
||||
with(CMMatrixContext) {
|
||||
inverse(matrix)
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun ejmlInverse() {
|
||||
with(EjmlMatrixContext) {
|
||||
inverse(matrix)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark
|
||||
import org.openjdk.jmh.annotations.Scope
|
||||
import org.openjdk.jmh.annotations.State
|
||||
import space.kscience.kmath.nd.*
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class NDFieldBenchmark {
|
||||
@Benchmark
|
||||
fun autoFieldAdd() {
|
||||
with(autoField) {
|
||||
var res: NDStructure<Double> = one
|
||||
repeat(n) { res += one }
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun specializedFieldAdd() {
|
||||
with(specializedField) {
|
||||
var res: NDStructure<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Benchmark
|
||||
fun boxingFieldAdd() {
|
||||
with(genericField) {
|
||||
var res: NDStructure<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val dim: Int = 1000
|
||||
const val n: Int = 100
|
||||
val autoField = NDAlgebra.auto(RealField, dim, dim)
|
||||
val specializedField: RealNDField = NDAlgebra.real(dim, dim)
|
||||
val genericField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim)
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import org.jetbrains.bio.viktor.F64Array
|
||||
import org.openjdk.jmh.annotations.Benchmark
|
||||
import org.openjdk.jmh.annotations.Scope
|
||||
import org.openjdk.jmh.annotations.State
|
||||
import space.kscience.kmath.nd.*
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.viktor.ViktorNDField
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class ViktorBenchmark {
|
||||
final val dim: Int = 1000
|
||||
final val n: Int = 100
|
||||
|
||||
// automatically build context most suited for given type.
|
||||
final val autoField: NDField<Double, RealField> = NDAlgebra.auto(RealField, dim, dim)
|
||||
final val realField: RealNDField = NDAlgebra.real(dim, dim)
|
||||
final val viktorField: ViktorNDField = ViktorNDField(dim, dim)
|
||||
|
||||
@Benchmark
|
||||
fun automaticFieldAddition() {
|
||||
with(autoField) {
|
||||
var res: NDStructure<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun realFieldAddition() {
|
||||
with(realField) {
|
||||
var res: NDStructure<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun viktorFieldAddition() {
|
||||
with(viktorField) {
|
||||
var res = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun rawViktor() {
|
||||
val one = F64Array.full(init = 1.0, shape = intArrayOf(dim, dim))
|
||||
var res = one
|
||||
repeat(n) { res = res + one }
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package space.kscience.kmath.benchmarks
|
||||
|
||||
import org.jetbrains.bio.viktor.F64Array
|
||||
import org.openjdk.jmh.annotations.Benchmark
|
||||
import org.openjdk.jmh.annotations.Scope
|
||||
import org.openjdk.jmh.annotations.State
|
||||
import space.kscience.kmath.nd.*
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.viktor.ViktorNDField
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
internal class ViktorLogBenchmark {
|
||||
final val dim: Int = 1000
|
||||
final val n: Int = 100
|
||||
|
||||
// automatically build context most suited for given type.
|
||||
final val autoField: NDField<Double, RealField> = NDAlgebra.auto(RealField, dim, dim)
|
||||
final val realField: RealNDField = NDAlgebra.real(dim, dim)
|
||||
final val viktorField: ViktorNDField = ViktorNDField(intArrayOf(dim, dim))
|
||||
|
||||
|
||||
@Benchmark
|
||||
fun realFieldLog() {
|
||||
with(realField) {
|
||||
val fortyTwo = produce { 42.0 }
|
||||
var res = one
|
||||
repeat(n) { res = ln(fortyTwo) }
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun viktorFieldLog() {
|
||||
with(viktorField) {
|
||||
val fortyTwo = produce { 42.0 }
|
||||
var res = one
|
||||
repeat(n) { res = ln(fortyTwo) }
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
fun rawViktorLog() {
|
||||
val fortyTwo = F64Array.full(dim, dim, init = 42.0)
|
||||
var res: F64Array
|
||||
repeat(n) {
|
||||
res = fortyTwo.log()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.ast.rendering.FeaturedMathRendererWithPostProcess
|
||||
import space.kscience.kmath.ast.rendering.LatexSyntaxRenderer
|
||||
import space.kscience.kmath.ast.rendering.MathMLSyntaxRenderer
|
||||
import space.kscience.kmath.ast.rendering.renderWithStringBuilder
|
||||
|
||||
public fun main() {
|
||||
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath()
|
||||
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
|
||||
println("MathSyntax:")
|
||||
println(syntax)
|
||||
println()
|
||||
val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||
println("LaTeX:")
|
||||
println(latex)
|
||||
println()
|
||||
val mathML = MathMLSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||
println("MathML:")
|
||||
println(mathML)
|
||||
}
|
@ -1,15 +1,22 @@
|
||||
/*
|
||||
* 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.invoke
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.expressions.MstField
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.interpret
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.invoke
|
||||
|
||||
fun main() {
|
||||
val expr = RealField.mstInField {
|
||||
val x = bindSymbol("x")
|
||||
x * 2.0 + 2.0 / x - 16.0
|
||||
val expr = MstField {
|
||||
x * 2.0 + number(2.0) / x - 16.0
|
||||
}
|
||||
|
||||
repeat(10000000) {
|
||||
expr.invoke("x" to 1.0)
|
||||
expr.interpret(DoubleField, x to 1.0)
|
||||
}
|
||||
}
|
@ -1,24 +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.asm.compile
|
||||
import space.kscience.kmath.expressions.derivative
|
||||
import space.kscience.kmath.expressions.invoke
|
||||
import space.kscience.kmath.expressions.symbol
|
||||
import space.kscience.kmath.kotlingrad.differentiable
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.toExpression
|
||||
import space.kscience.kmath.kotlingrad.toKotlingradExpression
|
||||
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
|
||||
* valid derivative.
|
||||
* valid derivative in a certain point.
|
||||
*/
|
||||
fun main() {
|
||||
val x by symbol
|
||||
|
||||
val actualDerivative = MstExpression(RealField, "x^2-4*x-44".parseMath())
|
||||
.differentiable()
|
||||
val actualDerivative = "x^2-4*x-44"
|
||||
.parseMath()
|
||||
.toKotlingradExpression(DoubleField)
|
||||
.derivative(x)
|
||||
.compile()
|
||||
|
||||
val expectedDerivative = MstExpression(RealField, "2*x-4".parseMath()).compile()
|
||||
assert(actualDerivative("x" to 123.0) == expectedDerivative("x" to 123.0))
|
||||
val expectedDerivative = "2*x-4".parseMath().toExpression(DoubleField)
|
||||
check(actualDerivative(x to 123.0) == expectedDerivative(x to 123.0))
|
||||
}
|
||||
|
@ -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^2-4*x-44 function is differentiated with Symja, and the autodiff 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))
|
||||
}
|
@ -1,19 +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.commons.fit
|
||||
|
||||
import kotlinx.html.br
|
||||
import kotlinx.html.h3
|
||||
import kscience.plotly.*
|
||||
import kscience.plotly.models.ScatterMode
|
||||
import kscience.plotly.models.TraceValues
|
||||
import space.kscience.kmath.commons.optimization.chiSquared
|
||||
import space.kscience.kmath.commons.optimization.minimize
|
||||
import space.kscience.kmath.distributions.NormalDistribution
|
||||
import space.kscience.kmath.expressions.symbol
|
||||
import space.kscience.kmath.real.RealVector
|
||||
import space.kscience.kmath.optimization.FunctionOptimization
|
||||
import space.kscience.kmath.optimization.OptimizationResult
|
||||
import space.kscience.kmath.real.DoubleVector
|
||||
import space.kscience.kmath.real.map
|
||||
import space.kscience.kmath.real.step
|
||||
import space.kscience.kmath.stat.*
|
||||
import space.kscience.kmath.stat.RandomGenerator
|
||||
import space.kscience.kmath.structures.asIterable
|
||||
import space.kscience.kmath.structures.toList
|
||||
import space.kscience.plotly.*
|
||||
import space.kscience.plotly.models.ScatterMode
|
||||
import space.kscience.plotly.models.TraceValues
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@ -26,17 +34,16 @@ private val c by symbol
|
||||
/**
|
||||
* Shortcut to use buffers in plotly
|
||||
*/
|
||||
operator fun TraceValues.invoke(vector: RealVector) {
|
||||
operator fun TraceValues.invoke(vector: DoubleVector) {
|
||||
numbers = vector.asIterable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Least squares fie with auto-differentiation. Uses `kmath-commons` and `kmath-for-real` modules.
|
||||
*/
|
||||
fun main() {
|
||||
|
||||
suspend fun main() {
|
||||
//A generator for a normally distributed values
|
||||
val generator = Distribution.normal()
|
||||
val generator = NormalDistribution(2.0, 7.0)
|
||||
|
||||
//A chain/flow of random values with the given seed
|
||||
val chain = generator.sample(RandomGenerator.default(112667))
|
||||
@ -49,7 +56,7 @@ fun main() {
|
||||
//Perform an operation on each x value (much more effective, than numpy)
|
||||
val y = x.map {
|
||||
val value = it.pow(2) + it + 1
|
||||
value + chain.nextDouble() * sqrt(value)
|
||||
value + chain.next() * sqrt(value)
|
||||
}
|
||||
// this will also work, but less effective:
|
||||
// val y = x.pow(2)+ x + 1 + chain.nextDouble()
|
||||
@ -58,10 +65,10 @@ fun main() {
|
||||
val yErr = y.map { sqrt(it) }//RealVector.same(x.size, sigma)
|
||||
|
||||
// compute differentiable chi^2 sum for given model ax^2 + bx + c
|
||||
val chi2 = Fitting.chiSquared(x, y, yErr) { x1 ->
|
||||
val chi2 = FunctionOptimization.chiSquared(x, y, yErr) { x1 ->
|
||||
//bind variables to autodiff context
|
||||
val a = bind(a)
|
||||
val b = bind(b)
|
||||
val a = bindSymbol(a)
|
||||
val b = bindSymbol(b)
|
||||
//Include default value for c if it is not provided as a parameter
|
||||
val c = bindSymbolOrNull(c) ?: one
|
||||
a * x1.pow(2) + b * x1 + c
|
||||
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.integration.gaussIntegrator
|
||||
import space.kscience.kmath.integration.integrate
|
||||
import space.kscience.kmath.integration.value
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import kotlin.math.pow
|
||||
|
||||
fun main() {
|
||||
//Define a function
|
||||
val function: UnivariateFunction<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 }
|
||||
|
||||
//get the result of the integration
|
||||
val result = DoubleField.gaussIntegrator.integrate(0.0..10.0, function = function)
|
||||
|
||||
//the value is nullable because in some cases the integration could not succeed
|
||||
println(result.value)
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.interpolation.SplineInterpolator
|
||||
import space.kscience.kmath.interpolation.interpolatePolynomials
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import space.kscience.plotly.Plotly
|
||||
import space.kscience.plotly.UnstablePlotlyAPI
|
||||
import space.kscience.plotly.makeFile
|
||||
import space.kscience.plotly.models.functionXY
|
||||
import space.kscience.plotly.scatter
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.sin
|
||||
|
||||
@OptIn(UnstablePlotlyAPI::class)
|
||||
fun main() {
|
||||
val data = (0..10).map {
|
||||
val x = it.toDouble() / 5 * PI
|
||||
x to sin(x)
|
||||
}
|
||||
|
||||
val polynomial: PiecewisePolynomial<Double> = SplineInterpolator(
|
||||
DoubleField, ::DoubleBuffer
|
||||
).interpolatePolynomials(data)
|
||||
|
||||
val function = polynomial.asFunction(DoubleField, 0.0)
|
||||
|
||||
val cmInterpolate = org.apache.commons.math3.analysis.interpolation.SplineInterpolator().interpolate(
|
||||
data.map { it.first }.toDoubleArray(),
|
||||
data.map { it.second }.toDoubleArray()
|
||||
)
|
||||
|
||||
Plotly.plot {
|
||||
scatter {
|
||||
name = "interpolated"
|
||||
x.numbers = data.map { it.first }
|
||||
y.numbers = x.doubles.map { function(it) }
|
||||
}
|
||||
scatter {
|
||||
name = "original"
|
||||
functionXY(0.0..(2 * PI), 0.1) { sin(it) }
|
||||
}
|
||||
scatter {
|
||||
name = "cm"
|
||||
x.numbers = data.map { it.first }
|
||||
y.numbers = x.doubles.map { cmInterpolate.value(it) }
|
||||
}
|
||||
}.makeFile()
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.interpolation.SplineInterpolator
|
||||
import space.kscience.kmath.interpolation.interpolatePolynomials
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.real.step
|
||||
import space.kscience.kmath.structures.map
|
||||
import space.kscience.plotly.Plotly
|
||||
import space.kscience.plotly.UnstablePlotlyAPI
|
||||
import space.kscience.plotly.makeFile
|
||||
import space.kscience.plotly.models.functionXY
|
||||
import space.kscience.plotly.scatter
|
||||
|
||||
@OptIn(UnstablePlotlyAPI::class)
|
||||
fun main() {
|
||||
val function: UnivariateFunction<Double> = { x ->
|
||||
if (x in 30.0..50.0) {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
val xs = 0.0..100.0 step 0.5
|
||||
val ys = xs.map(function)
|
||||
|
||||
val polynomial: PiecewisePolynomial<Double> = SplineInterpolator.double.interpolatePolynomials(xs, ys)
|
||||
|
||||
val polyFunction = polynomial.asFunction(DoubleField, 0.0)
|
||||
|
||||
Plotly.plot {
|
||||
scatter {
|
||||
name = "interpolated"
|
||||
functionXY(25.0..55.0, 0.1) { polyFunction(it) }
|
||||
}
|
||||
scatter {
|
||||
name = "original"
|
||||
functionXY(25.0..55.0, 0.1) { function(it) }
|
||||
}
|
||||
}.makeFile()
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.integration.gaussIntegrator
|
||||
import space.kscience.kmath.integration.integrate
|
||||
import space.kscience.kmath.integration.value
|
||||
import space.kscience.kmath.nd.StructureND
|
||||
import space.kscience.kmath.nd.nd
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.invoke
|
||||
|
||||
fun main(): Unit = DoubleField {
|
||||
nd(2, 2) {
|
||||
|
||||
//Produce a diagonal StructureND
|
||||
fun diagonal(v: Double) = produce { (i, j) ->
|
||||
if (i == j) v else 0.0
|
||||
}
|
||||
|
||||
//Define a function in a nd space
|
||||
val function: (Double) -> StructureND<Double> = { x: Double -> 3 * number(x).pow(2) + 2 * diagonal(x) + 1 }
|
||||
|
||||
//get the result of the integration
|
||||
val result = gaussIntegrator.integrate(0.0..10.0, function = function)
|
||||
|
||||
//the value is nullable because in some cases the integration could not succeed
|
||||
println(result.value)
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.jafama
|
||||
|
||||
import net.jafama.FastMath
|
||||
|
||||
|
||||
fun main(){
|
||||
val a = JafamaDoubleField.number(2.0)
|
||||
val b = StrictJafamaDoubleField.power(FastMath.E,a)
|
||||
|
||||
println(JafamaDoubleField.add(b,a))
|
||||
println(StrictJafamaDoubleField.ln(b))
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.linear
|
||||
|
||||
import space.kscience.kmath.real.*
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
|
||||
fun main() {
|
||||
val x0 = DoubleVector(0.0, 0.0, 0.0)
|
||||
val sigma = DoubleVector(1.0, 1.0, 1.0)
|
||||
|
||||
val gaussian: (Point<Double>) -> Double = { x ->
|
||||
require(x.size == x0.size)
|
||||
kotlin.math.exp(-((x - x0) / sigma).square().sum())
|
||||
}
|
||||
|
||||
fun ((Point<Double>) -> Double).grad(x: Point<Double>): Point<Double> {
|
||||
require(x.size == x0.size)
|
||||
return DoubleBuffer(x.size) { i ->
|
||||
val h = sigma[i] / 5
|
||||
val dVector = DoubleBuffer(x.size) { if (it == i) h else 0.0 }
|
||||
val f1 = this(x + dVector / 2)
|
||||
val f0 = this(x - dVector / 2)
|
||||
(f1 - f0) / h
|
||||
}
|
||||
}
|
||||
|
||||
println(gaussian.grad(x0))
|
||||
|
||||
}
|
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.operations
|
||||
|
||||
fun main() {
|
||||
|
@ -1,18 +1,23 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.operations
|
||||
|
||||
import space.kscience.kmath.complex.Complex
|
||||
import space.kscience.kmath.complex.complex
|
||||
import space.kscience.kmath.nd.NDAlgebra
|
||||
import space.kscience.kmath.nd.AlgebraND
|
||||
|
||||
fun main() {
|
||||
// 2d element
|
||||
val element = NDAlgebra.complex(2, 2).produce { (i, j) ->
|
||||
val element = AlgebraND.complex(2, 2).produce { (i, j) ->
|
||||
Complex(i.toDouble() - j.toDouble(), i.toDouble() + j.toDouble())
|
||||
}
|
||||
println(element)
|
||||
|
||||
// 1d element operation
|
||||
val result = with(NDAlgebra.complex(8)) {
|
||||
val result = with(AlgebraND.complex(8)) {
|
||||
val a = produce { (it) -> i * it - it.toDouble() }
|
||||
val b = 3
|
||||
val c = Complex(1.0, 1.0)
|
||||
|
@ -1,23 +1,29 @@
|
||||
package kscience.kmath.commons.prob
|
||||
/*
|
||||
* 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.stat
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler
|
||||
import org.apache.commons.rng.sampling.distribution.BoxMullerNormalizedGaussianSampler
|
||||
import org.apache.commons.rng.simple.RandomSource
|
||||
import space.kscience.kmath.stat.*
|
||||
import space.kscience.kmath.samplers.GaussianSampler
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import org.apache.commons.rng.sampling.distribution.GaussianSampler as CMGaussianSampler
|
||||
|
||||
private fun runChain(): Duration {
|
||||
private suspend fun runKMathChained(): Duration {
|
||||
val generator = RandomGenerator.fromSource(RandomSource.MT, 123L)
|
||||
val normal = Distribution.normal(NormalSamplerMethod.Ziggurat)
|
||||
val normal = GaussianSampler(7.0, 2.0)
|
||||
val chain = normal.sample(generator)
|
||||
val startTime = Instant.now()
|
||||
var sum = 0.0
|
||||
|
||||
repeat(10000001) { counter ->
|
||||
sum += chain.nextDouble()
|
||||
sum += chain.next()
|
||||
|
||||
if (counter % 100000 == 0) {
|
||||
val duration = Duration.between(startTime, Instant.now())
|
||||
@ -29,9 +35,15 @@ private fun runChain(): Duration {
|
||||
return Duration.between(startTime, Instant.now())
|
||||
}
|
||||
|
||||
private fun runDirect(): Duration {
|
||||
val provider = RandomSource.create(RandomSource.MT, 123L)
|
||||
val sampler = ZigguratNormalizedGaussianSampler(provider)
|
||||
private fun runApacheDirect(): Duration {
|
||||
val rng = RandomSource.create(RandomSource.MT, 123L)
|
||||
|
||||
val sampler = CMGaussianSampler.of(
|
||||
BoxMullerNormalizedGaussianSampler.of(rng),
|
||||
7.0,
|
||||
2.0
|
||||
)
|
||||
|
||||
val startTime = Instant.now()
|
||||
var sum = 0.0
|
||||
|
||||
@ -51,11 +63,9 @@ private fun runDirect(): Duration {
|
||||
/**
|
||||
* Comparing chain sampling performance with direct sampling performance
|
||||
*/
|
||||
fun main() {
|
||||
runBlocking(Dispatchers.Default) {
|
||||
val chainJob = async { runChain() }
|
||||
val directJob = async { runDirect() }
|
||||
println("Chain: ${chainJob.await()}")
|
||||
println("Direct: ${directJob.await()}")
|
||||
}
|
||||
fun main(): Unit = runBlocking(Dispatchers.Default) {
|
||||
val directJob = async { runApacheDirect() }
|
||||
val chainJob = async { runKMathChained() }
|
||||
println("KMath Chained: ${chainJob.await()}")
|
||||
println("Apache Direct: ${directJob.await()}")
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
/*
|
||||
* 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.stat
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import space.kscience.kmath.chains.Chain
|
||||
import space.kscience.kmath.chains.collectWithState
|
||||
import space.kscience.kmath.distributions.NormalDistribution
|
||||
|
||||
/**
|
||||
* The state of distribution averager
|
||||
* The state of distribution averager.
|
||||
*/
|
||||
private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0)
|
||||
|
||||
/**
|
||||
* Averaging
|
||||
* Averaging.
|
||||
*/
|
||||
private fun Chain<Double>.mean(): Chain<Double> = collectWithState(AveragingChainState(), { it.copy() }) { chain ->
|
||||
val next = chain.next()
|
||||
@ -21,7 +27,7 @@ private fun Chain<Double>.mean(): Chain<Double> = collectWithState(AveragingChai
|
||||
|
||||
|
||||
fun main() {
|
||||
val normal = Distribution.normal()
|
||||
val normal = NormalDistribution(0.0, 2.0)
|
||||
val chain = normal.sample(RandomGenerator.default).mean()
|
||||
|
||||
runBlocking {
|
||||
|
@ -1,11 +1,16 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package space.kscience.kmath.structures
|
||||
|
||||
import space.kscience.kmath.complex.*
|
||||
import space.kscience.kmath.linear.transpose
|
||||
import space.kscience.kmath.nd.NDAlgebra
|
||||
import space.kscience.kmath.nd.NDStructure
|
||||
import space.kscience.kmath.nd.AlgebraND
|
||||
import space.kscience.kmath.nd.StructureND
|
||||
import space.kscience.kmath.nd.as2D
|
||||
import space.kscience.kmath.nd.real
|
||||
import space.kscience.kmath.operations.invoke
|
||||
@ -15,12 +20,12 @@ fun main() {
|
||||
val dim = 1000
|
||||
val n = 1000
|
||||
|
||||
val realField = NDAlgebra.real(dim, dim)
|
||||
val complexField: ComplexNDField = NDAlgebra.complex(dim, dim)
|
||||
val realField = AlgebraND.real(dim, dim)
|
||||
val complexField: ComplexFieldND = AlgebraND.complex(dim, dim)
|
||||
|
||||
val realTime = measureTimeMillis {
|
||||
realField {
|
||||
var res: NDStructure<Double> = one
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) {
|
||||
res += 1.0
|
||||
}
|
||||
@ -31,7 +36,7 @@ fun main() {
|
||||
|
||||
val complexTime = measureTimeMillis {
|
||||
complexField {
|
||||
var res: NDStructure<Complex> = one
|
||||
var res: StructureND<Complex> = one
|
||||
repeat(n) {
|
||||
res += 1.0
|
||||
}
|
||||
|
@ -1,10 +1,16 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.structures
|
||||
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import org.nd4j.linalg.factory.Nd4j
|
||||
import space.kscience.kmath.nd.*
|
||||
import space.kscience.kmath.nd4j.Nd4jArrayField
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.viktor.ViktorNDField
|
||||
import kotlin.contracts.InvocationKind
|
||||
@ -17,6 +23,7 @@ internal inline fun measureAndPrint(title: String, block: () -> Unit) {
|
||||
println("$title completed in $time millis")
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun main() {
|
||||
// initializing Nd4j
|
||||
Nd4j.zeros(0)
|
||||
@ -24,56 +31,56 @@ fun main() {
|
||||
val n = 1000
|
||||
|
||||
// automatically build context most suited for given type.
|
||||
val autoField = NDAlgebra.auto(RealField, dim, dim)
|
||||
val autoField = AlgebraND.auto(DoubleField, dim, dim)
|
||||
// specialized nd-field for Double. It works as generic Double field as well
|
||||
val realField = NDAlgebra.real(dim, dim)
|
||||
val realField = AlgebraND.real(dim, dim)
|
||||
//A generic boxing field. It should be used for objects, not primitives.
|
||||
val boxingField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim)
|
||||
val boxingField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim)
|
||||
// Nd4j specialized field.
|
||||
val nd4jField = Nd4jArrayField.real(dim, dim)
|
||||
//viktor field
|
||||
val viktorField = ViktorNDField(dim, dim)
|
||||
//parallel processing based on Java Streams
|
||||
val parallelField = NDAlgebra.realWithStream(dim,dim)
|
||||
val parallelField = AlgebraND.realWithStream(dim, dim)
|
||||
|
||||
measureAndPrint("Boxing addition") {
|
||||
boxingField {
|
||||
var res: NDStructure<Double> = one
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
measureAndPrint("Specialized addition") {
|
||||
realField {
|
||||
var res: NDStructure<Double> = one
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
measureAndPrint("Nd4j specialized addition") {
|
||||
nd4jField {
|
||||
var res: NDStructure<Double> = one
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
measureAndPrint("Viktor addition") {
|
||||
viktorField {
|
||||
var res: NDStructure<Double> = one
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
measureAndPrint("Parallel stream addition") {
|
||||
parallelField {
|
||||
var res: NDStructure<Double> = one
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
measureAndPrint("Automatic field addition") {
|
||||
autoField {
|
||||
var res: NDStructure<Double> = one
|
||||
var res: StructureND<Double> = one
|
||||
repeat(n) { res += 1.0 }
|
||||
}
|
||||
}
|
||||
|
@ -1,103 +0,0 @@
|
||||
package space.kscience.kmath.structures
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.nd.*
|
||||
import space.kscience.kmath.operations.ExtendedField
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.operations.RingWithNumbers
|
||||
import java.util.*
|
||||
import java.util.stream.IntStream
|
||||
|
||||
/**
|
||||
* A demonstration implementation of NDField over Real using Java [DoubleStream] for parallel execution
|
||||
*/
|
||||
@OptIn(UnstableKMathAPI::class)
|
||||
class StreamRealNDField(
|
||||
override val shape: IntArray,
|
||||
) : NDField<Double, RealField>,
|
||||
RingWithNumbers<NDStructure<Double>>,
|
||||
ExtendedField<NDStructure<Double>> {
|
||||
|
||||
private val strides = DefaultStrides(shape)
|
||||
override val elementContext: RealField get() = RealField
|
||||
override val zero: NDBuffer<Double> by lazy { produce { zero } }
|
||||
override val one: NDBuffer<Double> by lazy { produce { one } }
|
||||
|
||||
override fun number(value: Number): NDBuffer<Double> {
|
||||
val d = value.toDouble() // minimize conversions
|
||||
return produce { d }
|
||||
}
|
||||
|
||||
private val NDStructure<Double>.buffer: RealBuffer
|
||||
get() = when {
|
||||
!shape.contentEquals(this@StreamRealNDField.shape) -> throw ShapeMismatchException(
|
||||
this@StreamRealNDField.shape,
|
||||
shape
|
||||
)
|
||||
this is NDBuffer && this.strides == this@StreamRealNDField.strides -> this.buffer as RealBuffer
|
||||
else -> RealBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
|
||||
}
|
||||
|
||||
|
||||
override fun produce(initializer: RealField.(IntArray) -> Double): NDBuffer<Double> {
|
||||
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
|
||||
val index = strides.index(offset)
|
||||
RealField.initializer(index)
|
||||
}.toArray()
|
||||
|
||||
return NDBuffer(strides, array.asBuffer())
|
||||
}
|
||||
|
||||
override fun NDStructure<Double>.map(
|
||||
transform: RealField.(Double) -> Double,
|
||||
): NDBuffer<Double> {
|
||||
val array = Arrays.stream(buffer.array).parallel().map { RealField.transform(it) }.toArray()
|
||||
return NDBuffer(strides, array.asBuffer())
|
||||
}
|
||||
|
||||
override fun NDStructure<Double>.mapIndexed(
|
||||
transform: RealField.(index: IntArray, Double) -> Double,
|
||||
): NDBuffer<Double> {
|
||||
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
|
||||
RealField.transform(
|
||||
strides.index(offset),
|
||||
buffer.array[offset]
|
||||
)
|
||||
}.toArray()
|
||||
|
||||
return NDBuffer(strides, array.asBuffer())
|
||||
}
|
||||
|
||||
override fun combine(
|
||||
a: NDStructure<Double>,
|
||||
b: NDStructure<Double>,
|
||||
transform: RealField.(Double, Double) -> Double,
|
||||
): NDBuffer<Double> {
|
||||
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
|
||||
RealField.transform(a.buffer.array[offset], b.buffer.array[offset])
|
||||
}.toArray()
|
||||
return NDBuffer(strides, array.asBuffer())
|
||||
}
|
||||
|
||||
override fun power(arg: NDStructure<Double>, pow: Number): NDBuffer<Double> = arg.map() { power(it, pow) }
|
||||
|
||||
override fun exp(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { exp(it) }
|
||||
|
||||
override fun ln(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { ln(it) }
|
||||
|
||||
override fun sin(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { sin(it) }
|
||||
override fun cos(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { cos(it) }
|
||||
override fun tan(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { tan(it) }
|
||||
override fun asin(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { asin(it) }
|
||||
override fun acos(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { acos(it) }
|
||||
override fun atan(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { atan(it) }
|
||||
|
||||
override fun sinh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { sinh(it) }
|
||||
override fun cosh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { cosh(it) }
|
||||
override fun tanh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { tanh(it) }
|
||||
override fun asinh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { asinh(it) }
|
||||
override fun acosh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { acosh(it) }
|
||||
override fun atanh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map() { atanh(it) }
|
||||
}
|
||||
|
||||
fun NDAlgebra.Companion.realWithStream(vararg shape: Int): StreamRealNDField = StreamRealNDField(shape)
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.structures
|
||||
|
||||
import space.kscience.kmath.nd.*
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.ExtendedField
|
||||
import space.kscience.kmath.operations.NumbersAddOperations
|
||||
import java.util.*
|
||||
import java.util.stream.IntStream
|
||||
|
||||
/**
|
||||
* A demonstration implementation of NDField over Real using Java [java.util.stream.DoubleStream] for parallel
|
||||
* execution.
|
||||
*/
|
||||
class StreamDoubleFieldND(override val shape: IntArray) : FieldND<Double, DoubleField>,
|
||||
NumbersAddOperations<StructureND<Double>>,
|
||||
ExtendedField<StructureND<Double>> {
|
||||
|
||||
private val strides = DefaultStrides(shape)
|
||||
override val elementContext: DoubleField get() = DoubleField
|
||||
override val zero: BufferND<Double> by lazy { produce { zero } }
|
||||
override val one: BufferND<Double> by lazy { produce { one } }
|
||||
|
||||
override fun number(value: Number): BufferND<Double> {
|
||||
val d = value.toDouble() // minimize conversions
|
||||
return produce { d }
|
||||
}
|
||||
|
||||
private val StructureND<Double>.buffer: DoubleBuffer
|
||||
get() = when {
|
||||
!shape.contentEquals(this@StreamDoubleFieldND.shape) -> throw ShapeMismatchException(
|
||||
this@StreamDoubleFieldND.shape,
|
||||
shape
|
||||
)
|
||||
this is BufferND && this.strides == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer
|
||||
else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
|
||||
}
|
||||
|
||||
override fun produce(initializer: DoubleField.(IntArray) -> Double): BufferND<Double> {
|
||||
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
|
||||
val index = strides.index(offset)
|
||||
DoubleField.initializer(index)
|
||||
}.toArray()
|
||||
|
||||
return BufferND(strides, array.asBuffer())
|
||||
}
|
||||
|
||||
override fun StructureND<Double>.map(
|
||||
transform: DoubleField.(Double) -> Double,
|
||||
): BufferND<Double> {
|
||||
val array = Arrays.stream(buffer.array).parallel().map { DoubleField.transform(it) }.toArray()
|
||||
return BufferND(strides, array.asBuffer())
|
||||
}
|
||||
|
||||
override fun StructureND<Double>.mapIndexed(
|
||||
transform: DoubleField.(index: IntArray, Double) -> Double,
|
||||
): BufferND<Double> {
|
||||
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
|
||||
DoubleField.transform(
|
||||
strides.index(offset),
|
||||
buffer.array[offset]
|
||||
)
|
||||
}.toArray()
|
||||
|
||||
return BufferND(strides, array.asBuffer())
|
||||
}
|
||||
|
||||
override fun combine(
|
||||
a: StructureND<Double>,
|
||||
b: StructureND<Double>,
|
||||
transform: DoubleField.(Double, Double) -> Double,
|
||||
): BufferND<Double> {
|
||||
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
|
||||
DoubleField.transform(a.buffer.array[offset], b.buffer.array[offset])
|
||||
}.toArray()
|
||||
return BufferND(strides, array.asBuffer())
|
||||
}
|
||||
|
||||
override fun StructureND<Double>.unaryMinus(): StructureND<Double> = map { -it }
|
||||
|
||||
override fun scale(a: StructureND<Double>, value: Double): StructureND<Double> = a.map { it * value }
|
||||
|
||||
override fun power(arg: StructureND<Double>, pow: Number): BufferND<Double> = arg.map { power(it, pow) }
|
||||
|
||||
override fun exp(arg: StructureND<Double>): BufferND<Double> = arg.map { exp(it) }
|
||||
|
||||
override fun ln(arg: StructureND<Double>): BufferND<Double> = arg.map { ln(it) }
|
||||
|
||||
override fun sin(arg: StructureND<Double>): BufferND<Double> = arg.map { sin(it) }
|
||||
override fun cos(arg: StructureND<Double>): BufferND<Double> = arg.map { cos(it) }
|
||||
override fun tan(arg: StructureND<Double>): BufferND<Double> = arg.map { tan(it) }
|
||||
override fun asin(arg: StructureND<Double>): BufferND<Double> = arg.map { asin(it) }
|
||||
override fun acos(arg: StructureND<Double>): BufferND<Double> = arg.map { acos(it) }
|
||||
override fun atan(arg: StructureND<Double>): BufferND<Double> = arg.map { atan(it) }
|
||||
|
||||
override fun sinh(arg: StructureND<Double>): BufferND<Double> = arg.map { sinh(it) }
|
||||
override fun cosh(arg: StructureND<Double>): BufferND<Double> = arg.map { cosh(it) }
|
||||
override fun tanh(arg: StructureND<Double>): BufferND<Double> = arg.map { tanh(it) }
|
||||
override fun asinh(arg: StructureND<Double>): BufferND<Double> = arg.map { asinh(it) }
|
||||
override fun acosh(arg: StructureND<Double>): BufferND<Double> = arg.map { acosh(it) }
|
||||
override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
|
||||
}
|
||||
|
||||
fun AlgebraND.Companion.realWithStream(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(shape)
|
@ -1,16 +1,21 @@
|
||||
/*
|
||||
* 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.structures
|
||||
|
||||
import space.kscience.kmath.nd.BufferND
|
||||
import space.kscience.kmath.nd.DefaultStrides
|
||||
import space.kscience.kmath.nd.NDBuffer
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||
fun main() {
|
||||
val n = 6000
|
||||
val array = DoubleArray(n * n) { 1.0 }
|
||||
val buffer = RealBuffer(array)
|
||||
val buffer = DoubleBuffer(array)
|
||||
val strides = DefaultStrides(intArrayOf(n, n))
|
||||
val structure = NDBuffer(strides, buffer)
|
||||
val structure = BufferND(strides, buffer)
|
||||
|
||||
measureTimeMillis {
|
||||
var res = 0.0
|
||||
|
@ -1,13 +1,18 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.structures
|
||||
|
||||
import space.kscience.kmath.nd.NDStructure
|
||||
import space.kscience.kmath.nd.StructureND
|
||||
import space.kscience.kmath.nd.mapToBuffer
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
fun main() {
|
||||
val n = 6000
|
||||
val structure = NDStructure.build(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 }
|
||||
val structure = StructureND.buffered(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 }
|
||||
structure.mapToBuffer { it + 1 } // warm-up
|
||||
val time1 = measureTimeMillis { val res = structure.mapToBuffer { it + 1 } }
|
||||
println("Structure mapping finished in $time1 millis")
|
||||
@ -20,10 +25,10 @@ fun main() {
|
||||
|
||||
println("Array mapping finished in $time2 millis")
|
||||
|
||||
val buffer = RealBuffer(DoubleArray(n * n) { 1.0 })
|
||||
val buffer = DoubleBuffer(DoubleArray(n * n) { 1.0 })
|
||||
|
||||
val time3 = measureTimeMillis {
|
||||
val target = RealBuffer(DoubleArray(n * n))
|
||||
val target = DoubleBuffer(DoubleArray(n * n))
|
||||
val res = array.forEachIndexed { index, value ->
|
||||
target[index] = value + 1
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.structures
|
||||
|
||||
import space.kscience.kmath.dimensions.D2
|
||||
@ -5,7 +10,7 @@ import space.kscience.kmath.dimensions.D3
|
||||
import space.kscience.kmath.dimensions.DMatrixContext
|
||||
import space.kscience.kmath.dimensions.Dimension
|
||||
|
||||
private fun DMatrixContext<Double>.simple() {
|
||||
private fun DMatrixContext<Double, *>.simple() {
|
||||
val m1 = produce<D2, D3> { i, j -> (i + j).toDouble() }
|
||||
val m2 = produce<D3, D2> { i, j -> (i + j).toDouble() }
|
||||
|
||||
@ -17,7 +22,7 @@ private object D5 : Dimension {
|
||||
override val dim: UInt = 5u
|
||||
}
|
||||
|
||||
private fun DMatrixContext<Double>.custom() {
|
||||
private fun DMatrixContext<Double, *>.custom() {
|
||||
val m1 = produce<D2, D5> { i, j -> (i + j).toDouble() }
|
||||
val m2 = produce<D5, D2> { i, j -> (i - j).toDouble() }
|
||||
val m3 = produce<D2, D2> { i, j -> (i - j).toDouble() }
|
||||
|
@ -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.tensors
|
||||
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra
|
||||
|
||||
|
||||
// Dataset normalization
|
||||
|
||||
fun main() = BroadcastDoubleTensorAlgebra { // work in context with broadcast methods
|
||||
// take dataset of 5-element vectors from normal distribution
|
||||
val dataset = randomNormal(intArrayOf(100, 5)) * 1.5 // all elements from N(0, 1.5)
|
||||
|
||||
dataset += fromArray(
|
||||
intArrayOf(5),
|
||||
doubleArrayOf(0.0, 1.0, 1.5, 3.0, 5.0) // rows means
|
||||
)
|
||||
|
||||
|
||||
// find out mean and standard deviation of each column
|
||||
val mean = dataset.mean(0, false)
|
||||
val std = dataset.std(0, false)
|
||||
|
||||
println("Mean:\n$mean")
|
||||
println("Standard deviation:\n$std")
|
||||
|
||||
// also we can calculate other statistic as minimum and maximum of rows
|
||||
println("Minimum:\n${dataset.min(0, false)}")
|
||||
println("Maximum:\n${dataset.max(0, false)}")
|
||||
|
||||
// now we can scale dataset with mean normalization
|
||||
val datasetScaled = (dataset - mean) / std
|
||||
|
||||
// find out mean and std of scaled dataset
|
||||
|
||||
println("Mean of scaled:\n${datasetScaled.mean(0, false)}")
|
||||
println("Mean of scaled:\n${datasetScaled.std(0, false)}")
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.tensors
|
||||
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra
|
||||
import space.kscience.kmath.tensors.core.DoubleTensor
|
||||
|
||||
// solving linear system with LUP decomposition
|
||||
|
||||
fun main() = BroadcastDoubleTensorAlgebra {// work in context with linear operations
|
||||
|
||||
// set true value of x
|
||||
val trueX = fromArray(
|
||||
intArrayOf(4),
|
||||
doubleArrayOf(-2.0, 1.5, 6.8, -2.4)
|
||||
)
|
||||
|
||||
// and A matrix
|
||||
val a = fromArray(
|
||||
intArrayOf(4, 4),
|
||||
doubleArrayOf(
|
||||
0.5, 10.5, 4.5, 1.0,
|
||||
8.5, 0.9, 12.8, 0.1,
|
||||
5.56, 9.19, 7.62, 5.45,
|
||||
1.0, 2.0, -3.0, -2.5
|
||||
)
|
||||
)
|
||||
|
||||
// calculate y value
|
||||
val b = a dot trueX
|
||||
|
||||
// check out A and b
|
||||
println("A:\n$a")
|
||||
println("b:\n$b")
|
||||
|
||||
// solve `Ax = b` system using LUP decomposition
|
||||
|
||||
// get P, L, U such that PA = LU
|
||||
val (p, l, u) = a.lu()
|
||||
|
||||
// check that P is permutation matrix
|
||||
println("P:\n$p")
|
||||
// L is lower triangular matrix and U is upper triangular matrix
|
||||
println("L:\n$l")
|
||||
println("U:\n$u")
|
||||
// and PA = LU
|
||||
println("PA:\n${p dot a}")
|
||||
println("LU:\n${l dot u}")
|
||||
|
||||
/* Ax = b;
|
||||
PAx = Pb;
|
||||
LUx = Pb;
|
||||
let y = Ux, then
|
||||
Ly = Pb -- this system can be easily solved, since the matrix L is lower triangular;
|
||||
Ux = y can be solved the same way, since the matrix L is upper triangular
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// this function returns solution x of a system lx = b, l should be lower triangular
|
||||
fun solveLT(l: DoubleTensor, b: DoubleTensor): DoubleTensor {
|
||||
val n = l.shape[0]
|
||||
val x = zeros(intArrayOf(n))
|
||||
for (i in 0 until n) {
|
||||
x[intArrayOf(i)] = (b[intArrayOf(i)] - l[i].dot(x).value()) / l[intArrayOf(i, i)]
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
val y = solveLT(l, p dot b)
|
||||
|
||||
// solveLT(l, b) function can be easily adapted for upper triangular matrix by the permutation matrix revMat
|
||||
// create it by placing ones on side diagonal
|
||||
val revMat = u.zeroesLike()
|
||||
val n = revMat.shape[0]
|
||||
for (i in 0 until n) {
|
||||
revMat[intArrayOf(i, n - 1 - i)] = 1.0
|
||||
}
|
||||
|
||||
// solution of system ux = b, u should be upper triangular
|
||||
fun solveUT(u: DoubleTensor, b: DoubleTensor): DoubleTensor = revMat dot solveLT(
|
||||
revMat dot u dot revMat, revMat dot b
|
||||
)
|
||||
|
||||
val x = solveUT(u, y)
|
||||
|
||||
println("True x:\n$trueX")
|
||||
println("x founded with LU method:\n$x")
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.tensors
|
||||
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra
|
||||
import space.kscience.kmath.tensors.core.DoubleTensor
|
||||
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
||||
import space.kscience.kmath.tensors.core.toDoubleArray
|
||||
import kotlin.math.sqrt
|
||||
|
||||
const val seed = 100500L
|
||||
|
||||
// Simple feedforward neural network with backpropagation training
|
||||
|
||||
// interface of network layer
|
||||
interface Layer {
|
||||
fun forward(input: DoubleTensor): DoubleTensor
|
||||
fun backward(input: DoubleTensor, outputError: DoubleTensor): DoubleTensor
|
||||
}
|
||||
|
||||
// activation layer
|
||||
open class Activation(
|
||||
val activation: (DoubleTensor) -> DoubleTensor,
|
||||
val activationDer: (DoubleTensor) -> DoubleTensor,
|
||||
) : Layer {
|
||||
override fun forward(input: DoubleTensor): DoubleTensor {
|
||||
return activation(input)
|
||||
}
|
||||
|
||||
override fun backward(input: DoubleTensor, outputError: DoubleTensor): DoubleTensor {
|
||||
return DoubleTensorAlgebra { outputError * activationDer(input) }
|
||||
}
|
||||
}
|
||||
|
||||
fun relu(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
||||
x.map { if (it > 0) it else 0.0 }
|
||||
}
|
||||
|
||||
fun reluDer(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
||||
x.map { if (it > 0) 1.0 else 0.0 }
|
||||
}
|
||||
|
||||
// activation layer with relu activator
|
||||
class ReLU : Activation(::relu, ::reluDer)
|
||||
|
||||
fun sigmoid(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
||||
1.0 / (1.0 + (-x).exp())
|
||||
}
|
||||
|
||||
fun sigmoidDer(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
||||
sigmoid(x) * (1.0 - sigmoid(x))
|
||||
}
|
||||
|
||||
// activation layer with sigmoid activator
|
||||
class Sigmoid : Activation(::sigmoid, ::sigmoidDer)
|
||||
|
||||
// dense layer
|
||||
class Dense(
|
||||
private val inputUnits: Int,
|
||||
private val outputUnits: Int,
|
||||
private val learningRate: Double = 0.1,
|
||||
) : Layer {
|
||||
|
||||
private val weights: DoubleTensor = DoubleTensorAlgebra {
|
||||
randomNormal(
|
||||
intArrayOf(inputUnits, outputUnits),
|
||||
seed
|
||||
) * sqrt(2.0 / (inputUnits + outputUnits))
|
||||
}
|
||||
|
||||
private val bias: DoubleTensor = DoubleTensorAlgebra { zeros(intArrayOf(outputUnits)) }
|
||||
|
||||
override fun forward(input: DoubleTensor): DoubleTensor = BroadcastDoubleTensorAlgebra {
|
||||
(input dot weights) + bias
|
||||
}
|
||||
|
||||
override fun backward(input: DoubleTensor, outputError: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
||||
val gradInput = outputError dot weights.transpose()
|
||||
|
||||
val gradW = input.transpose() dot outputError
|
||||
val gradBias = outputError.mean(dim = 0, keepDim = false) * input.shape[0].toDouble()
|
||||
|
||||
weights -= learningRate * gradW
|
||||
bias -= learningRate * gradBias
|
||||
|
||||
gradInput
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// simple accuracy equal to the proportion of correct answers
|
||||
fun accuracy(yPred: DoubleTensor, yTrue: DoubleTensor): Double {
|
||||
check(yPred.shape contentEquals yTrue.shape)
|
||||
val n = yPred.shape[0]
|
||||
var correctCnt = 0
|
||||
for (i in 0 until n) {
|
||||
if (yPred[intArrayOf(i, 0)] == yTrue[intArrayOf(i, 0)]) {
|
||||
correctCnt += 1
|
||||
}
|
||||
}
|
||||
return correctCnt.toDouble() / n.toDouble()
|
||||
}
|
||||
|
||||
// neural network class
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
class NeuralNetwork(private val layers: List<Layer>) {
|
||||
private fun softMaxLoss(yPred: DoubleTensor, yTrue: DoubleTensor): DoubleTensor = BroadcastDoubleTensorAlgebra {
|
||||
|
||||
val onesForAnswers = yPred.zeroesLike()
|
||||
yTrue.toDoubleArray().forEachIndexed { index, labelDouble ->
|
||||
val label = labelDouble.toInt()
|
||||
onesForAnswers[intArrayOf(index, label)] = 1.0
|
||||
}
|
||||
|
||||
val softmaxValue = yPred.exp() / yPred.exp().sum(dim = 1, keepDim = true)
|
||||
|
||||
(-onesForAnswers + softmaxValue) / (yPred.shape[0].toDouble())
|
||||
}
|
||||
|
||||
|
||||
private fun forward(x: DoubleTensor): List<DoubleTensor> {
|
||||
var input = x
|
||||
|
||||
return buildList {
|
||||
layers.forEach { layer ->
|
||||
val output = layer.forward(input)
|
||||
add(output)
|
||||
input = output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun train(xTrain: DoubleTensor, yTrain: DoubleTensor) {
|
||||
val layerInputs = buildList {
|
||||
add(xTrain)
|
||||
addAll(forward(xTrain))
|
||||
}
|
||||
|
||||
var lossGrad = softMaxLoss(layerInputs.last(), yTrain)
|
||||
|
||||
layers.zip(layerInputs).reversed().forEach { (layer, input) ->
|
||||
lossGrad = layer.backward(input, lossGrad)
|
||||
}
|
||||
}
|
||||
|
||||
fun fit(xTrain: DoubleTensor, yTrain: DoubleTensor, batchSize: Int, epochs: Int) = DoubleTensorAlgebra {
|
||||
fun iterBatch(x: DoubleTensor, y: DoubleTensor): Sequence<Pair<DoubleTensor, DoubleTensor>> = sequence {
|
||||
val n = x.shape[0]
|
||||
val shuffledIndices = (0 until n).shuffled()
|
||||
for (i in 0 until n step batchSize) {
|
||||
val excerptIndices = shuffledIndices.drop(i).take(batchSize).toIntArray()
|
||||
val batch = x.rowsByIndices(excerptIndices) to y.rowsByIndices(excerptIndices)
|
||||
yield(batch)
|
||||
}
|
||||
}
|
||||
|
||||
for (epoch in 0 until epochs) {
|
||||
println("Epoch ${epoch + 1}/$epochs")
|
||||
for ((xBatch, yBatch) in iterBatch(xTrain, yTrain)) {
|
||||
train(xBatch, yBatch)
|
||||
}
|
||||
println("Accuracy:${accuracy(yTrain, predict(xTrain).argMax(1, true))}")
|
||||
}
|
||||
}
|
||||
|
||||
fun predict(x: DoubleTensor): DoubleTensor {
|
||||
return forward(x).last()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
fun main() = BroadcastDoubleTensorAlgebra {
|
||||
val features = 5
|
||||
val sampleSize = 250
|
||||
val trainSize = 180
|
||||
//val testSize = sampleSize - trainSize
|
||||
|
||||
// take sample of features from normal distribution
|
||||
val x = randomNormal(intArrayOf(sampleSize, features), seed) * 2.5
|
||||
|
||||
x += fromArray(
|
||||
intArrayOf(5),
|
||||
doubleArrayOf(0.0, -1.0, -2.5, -3.0, 5.5) // rows means
|
||||
)
|
||||
|
||||
|
||||
// define class like '1' if the sum of features > 0 and '0' otherwise
|
||||
val y = fromArray(
|
||||
intArrayOf(sampleSize, 1),
|
||||
DoubleArray(sampleSize) { i ->
|
||||
if (x[i].sum() > 0.0) {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// split train ans test
|
||||
val trainIndices = (0 until trainSize).toList().toIntArray()
|
||||
val testIndices = (trainSize until sampleSize).toList().toIntArray()
|
||||
|
||||
val xTrain = x.rowsByIndices(trainIndices)
|
||||
val yTrain = y.rowsByIndices(trainIndices)
|
||||
|
||||
val xTest = x.rowsByIndices(testIndices)
|
||||
val yTest = y.rowsByIndices(testIndices)
|
||||
|
||||
// build model
|
||||
val layers = buildList {
|
||||
add(Dense(features, 64))
|
||||
add(ReLU())
|
||||
add(Dense(64, 16))
|
||||
add(ReLU())
|
||||
add(Dense(16, 2))
|
||||
add(Sigmoid())
|
||||
}
|
||||
val model = NeuralNetwork(layers)
|
||||
|
||||
// fit it with train data
|
||||
model.fit(xTrain, yTrain, batchSize = 20, epochs = 10)
|
||||
|
||||
// make prediction
|
||||
val prediction = model.predict(xTest)
|
||||
|
||||
// process raw prediction via argMax
|
||||
val predictionLabels = prediction.argMax(1, true)
|
||||
|
||||
// find out accuracy
|
||||
val acc = accuracy(yTest, predictionLabels)
|
||||
println("Test accuracy:$acc")
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.tensors
|
||||
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.tensors.core.DoubleTensor
|
||||
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
||||
|
||||
import kotlin.math.abs
|
||||
|
||||
// OLS estimator using SVD
|
||||
|
||||
fun main() {
|
||||
//seed for random
|
||||
val randSeed = 100500L
|
||||
|
||||
// work in context with linear operations
|
||||
DoubleTensorAlgebra {
|
||||
// take coefficient vector from normal distribution
|
||||
val alpha = randomNormal(
|
||||
intArrayOf(5),
|
||||
randSeed
|
||||
) + fromArray(
|
||||
intArrayOf(5),
|
||||
doubleArrayOf(1.0, 2.5, 3.4, 5.0, 10.1)
|
||||
)
|
||||
|
||||
println("Real alpha:\n$alpha")
|
||||
|
||||
// also take sample of size 20 from normal distribution for x
|
||||
val x = randomNormal(
|
||||
intArrayOf(20, 5),
|
||||
randSeed
|
||||
)
|
||||
|
||||
// calculate y and add gaussian noise (N(0, 0.05))
|
||||
val y = x dot alpha
|
||||
y += y.randomNormalLike(randSeed) * 0.05
|
||||
|
||||
// now restore the coefficient vector with OSL estimator with SVD
|
||||
val (u, singValues, v) = x.svd()
|
||||
|
||||
// we have to make sure the singular values of the matrix are not close to zero
|
||||
println("Singular values:\n$singValues")
|
||||
|
||||
|
||||
// inverse Sigma matrix can be restored from singular values with diagonalEmbedding function
|
||||
val sigma = diagonalEmbedding(singValues.map{ if (abs(it) < 1e-3) 0.0 else 1.0/it })
|
||||
|
||||
val alphaOLS = v dot sigma dot u.transpose() dot y
|
||||
println("Estimated alpha:\n" +
|
||||
"$alphaOLS")
|
||||
|
||||
// figure out MSE of approximation
|
||||
fun mse(yTrue: DoubleTensor, yPred: DoubleTensor): Double {
|
||||
require(yTrue.shape.size == 1)
|
||||
require(yTrue.shape contentEquals yPred.shape)
|
||||
|
||||
val diff = yTrue - yPred
|
||||
return diff.dot(diff).sqrt().value()
|
||||
}
|
||||
|
||||
println("MSE: ${mse(alpha, alphaOLS)}")
|
||||
}
|
||||
}
|
74
examples/src/main/kotlin/space/kscience/kmath/tensors/PCA.kt
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.tensors
|
||||
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra
|
||||
|
||||
|
||||
// simple PCA
|
||||
|
||||
fun main(): Unit = BroadcastDoubleTensorAlgebra { // work in context with broadcast methods
|
||||
val seed = 100500L
|
||||
|
||||
// assume x is range from 0 until 10
|
||||
val x = fromArray(
|
||||
intArrayOf(10),
|
||||
(0 until 10).toList().map { it.toDouble() }.toDoubleArray()
|
||||
)
|
||||
|
||||
// take y dependent on x with noise
|
||||
val y = 2.0 * x + (3.0 + x.randomNormalLike(seed) * 1.5)
|
||||
|
||||
println("x:\n$x")
|
||||
println("y:\n$y")
|
||||
|
||||
// stack them into single dataset
|
||||
val dataset = stack(listOf(x, y)).transpose()
|
||||
|
||||
// normalize both x and y
|
||||
val xMean = x.mean()
|
||||
val yMean = y.mean()
|
||||
|
||||
val xStd = x.std()
|
||||
val yStd = y.std()
|
||||
|
||||
val xScaled = (x - xMean) / xStd
|
||||
val yScaled = (y - yMean) / yStd
|
||||
|
||||
// save means ans standard deviations for further recovery
|
||||
val mean = fromArray(
|
||||
intArrayOf(2),
|
||||
doubleArrayOf(xMean, yMean)
|
||||
)
|
||||
println("Means:\n$mean")
|
||||
|
||||
val std = fromArray(
|
||||
intArrayOf(2),
|
||||
doubleArrayOf(xStd, yStd)
|
||||
)
|
||||
println("Standard deviations:\n$std")
|
||||
|
||||
// calculate the covariance matrix of scaled x and y
|
||||
val covMatrix = cov(listOf(xScaled, yScaled))
|
||||
println("Covariance matrix:\n$covMatrix")
|
||||
|
||||
// and find out eigenvector of it
|
||||
val (_, evecs) = covMatrix.symEig()
|
||||
val v = evecs[0]
|
||||
println("Eigenvector:\n$v")
|
||||
|
||||
// reduce dimension of dataset
|
||||
val datasetReduced = v dot stack(listOf(xScaled, yScaled))
|
||||
println("Reduced data:\n$datasetReduced")
|
||||
|
||||
// we can restore original data from reduced data.
|
||||
// for example, find 7th element of dataset
|
||||
val n = 7
|
||||
val restored = (datasetReduced[n] dot v.view(intArrayOf(1, 2))) * std + mean
|
||||
println("Original value:\n${dataset[n]}")
|
||||
println("Restored value:\n$restored")
|
||||
}
|
@ -1,8 +1,13 @@
|
||||
#
|
||||
# Copyright 2018-2021 KMath contributors.
|
||||
# Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
#
|
||||
|
||||
kotlin.code.style=official
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.mpp.stability.nowarn=true
|
||||
kotlin.native.enableDependencyPropagation=false
|
||||
kotlin.parallel.tasks.in.project=true
|
||||
org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.jvmargs=-XX:MaxMetaspaceSize=2G
|
||||
org.gradle.parallel=true
|
||||
systemProp.org.gradle.internal.publish.checksums.insecure=true
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
2
gradlew
vendored
@ -72,7 +72,7 @@ case "`uname`" in
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
MSYS* | MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
|
@ -1,75 +1,65 @@
|
||||
# Abstract Syntax Tree Expression Representation and Operations (`kmath-ast`)
|
||||
# Module kmath-ast
|
||||
|
||||
This subproject implements the following features:
|
||||
Performance and visualization extensions to MST API.
|
||||
|
||||
- [expression-language](src/jvmMain/kotlin/kscience/kmath/ast/parser.kt) : Expression language and its parser
|
||||
- [mst](src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation
|
||||
- [mst-building](src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure
|
||||
- [mst-interpreter](src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST interpreter
|
||||
- [mst-jvm-codegen](src/jvmMain/kotlin/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler
|
||||
- [mst-js-codegen](src/jsMain/kotlin/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
|
||||
- [expression-language](src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser
|
||||
- [mst-jvm-codegen](src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler
|
||||
- [mst-js-codegen](src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
|
||||
- [rendering](src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt) : Extendable MST rendering
|
||||
|
||||
|
||||
> #### Artifact:
|
||||
>
|
||||
> This module artifact: `space.kscience:kmath-ast:0.2.0`.
|
||||
>
|
||||
> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-ast/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-ast/_latestVersion)
|
||||
>
|
||||
> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-ast/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-ast/_latestVersion)
|
||||
>
|
||||
> **Gradle:**
|
||||
>
|
||||
> ```gradle
|
||||
> repositories {
|
||||
> maven { url 'https://repo.kotlin.link' }
|
||||
> maven { url 'https://dl.bintray.com/hotkeytlt/maven' }
|
||||
> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap
|
||||
>// Uncomment if repo.kotlin.link is unavailable
|
||||
>// maven { url 'https://dl.bintray.com/mipt-npm/kscience' }
|
||||
>// maven { url 'https://dl.bintray.com/mipt-npm/dev' }
|
||||
> }
|
||||
>
|
||||
> dependencies {
|
||||
> implementation 'space.kscience:kmath-ast:0.2.0'
|
||||
> }
|
||||
> ```
|
||||
> **Gradle Kotlin DSL:**
|
||||
>
|
||||
> ```kotlin
|
||||
> repositories {
|
||||
> maven("https://repo.kotlin.link")
|
||||
> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap
|
||||
> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a
|
||||
>// Uncomment if repo.kotlin.link is unavailable
|
||||
>// maven("https://dl.bintray.com/mipt-npm/kscience")
|
||||
>// maven("https://dl.bintray.com/mipt-npm/dev")
|
||||
> }
|
||||
>
|
||||
> dependencies {
|
||||
> implementation("space.kscience:kmath-ast:0.2.0")
|
||||
> }
|
||||
> ```
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-14`.
|
||||
|
||||
**Gradle:**
|
||||
```gradle
|
||||
repositories {
|
||||
maven { url 'https://repo.kotlin.link' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'space.kscience:kmath-ast:0.3.0-dev-14'
|
||||
}
|
||||
```
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:kmath-ast:0.3.0-dev-14")
|
||||
}
|
||||
```
|
||||
|
||||
## Dynamic expression code generation
|
||||
|
||||
### On JVM
|
||||
|
||||
`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds
|
||||
a special implementation of `Expression<T>` with implemented `invoke` function.
|
||||
`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a
|
||||
special implementation of `Expression<T>` with implemented `invoke` function.
|
||||
|
||||
For example, the following builder:
|
||||
|
||||
```kotlin
|
||||
RealField.mstInField { symbol("x") + 2 }.compile()
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.*
|
||||
import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.asm.*
|
||||
|
||||
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:
|
||||
|
||||
```java
|
||||
package space.kscience.kmath.asm.generated;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import kotlin.jvm.functions.Function2;
|
||||
import space.kscience.kmath.asm.internal.MapIntrinsics;
|
||||
import space.kscience.kmath.expressions.Expression;
|
||||
@ -89,19 +79,10 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
|
||||
|
||||
```
|
||||
|
||||
### Example Usage
|
||||
|
||||
This API extends MST and MstExpression, so you may optimize as both of them:
|
||||
|
||||
```kotlin
|
||||
RealField.mstInField { symbol("x") + 2 }.compile()
|
||||
RealField.expression("x+2".parseMath())
|
||||
```
|
||||
|
||||
#### Known issues
|
||||
|
||||
- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid
|
||||
class loading overhead.
|
||||
- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid class
|
||||
loading overhead.
|
||||
- This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders.
|
||||
|
||||
### On JS
|
||||
@ -109,7 +90,12 @@ RealField.expression("x+2".parseMath())
|
||||
A similar feature is also available on JS.
|
||||
|
||||
```kotlin
|
||||
RealField.mstInField { symbol("x") + 2 }.compile()
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.*
|
||||
import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.estree.*
|
||||
|
||||
MstField { x + 2 }.compileToExpression(DoubleField)
|
||||
```
|
||||
|
||||
The code above returns expression implemented with such a JS function:
|
||||
@ -120,6 +106,133 @@ var executable = function (constants, arguments) {
|
||||
};
|
||||
```
|
||||
|
||||
JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation.
|
||||
Currently, only expressions inside `DoubleField` and `IntRing` are supported.
|
||||
|
||||
```kotlin
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.*
|
||||
import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.wasm.*
|
||||
|
||||
MstField { x + 2 }.compileToExpression(DoubleField)
|
||||
```
|
||||
|
||||
An example of emitted Wasm IR in the form of WAT:
|
||||
|
||||
```lisp
|
||||
(func $executable (param $0 f64) (result f64)
|
||||
(f64.add
|
||||
(local.get $0)
|
||||
(f64.const 2)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
#### Known issues
|
||||
|
||||
- This feature uses `eval` which can be unavailable in several environments.
|
||||
- ESTree expression compilation uses `eval` which can be unavailable in several environments.
|
||||
- WebAssembly isn't supported by old versions of browsers (see https://webassembly.org/roadmap/).
|
||||
|
||||
## Rendering expressions
|
||||
|
||||
kmath-ast also includes an extensible engine to display expressions in LaTeX or MathML syntax.
|
||||
|
||||
Example usage:
|
||||
|
||||
```kotlin
|
||||
import space.kscience.kmath.ast.*
|
||||
import space.kscience.kmath.ast.rendering.*
|
||||
import space.kscience.kmath.misc.*
|
||||
|
||||
@OptIn(UnstableKMathAPI::class)
|
||||
public fun main() {
|
||||
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(12)+x^(2/3)".parseMath()
|
||||
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
|
||||
val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||
println("LaTeX:")
|
||||
println(latex)
|
||||
println()
|
||||
val mathML = MathMLSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||
println("MathML:")
|
||||
println(mathML)
|
||||
}
|
||||
```
|
||||
|
||||
Result LaTeX:
|
||||
|
||||
![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{12}+x^{2/3})
|
||||
|
||||
Result MathML (can be used with MathJax or other renderers):
|
||||
|
||||
<details>
|
||||
|
||||
```html
|
||||
<math xmlns="https://www.w3.org/1998/Math/MathML">
|
||||
<mrow>
|
||||
<mo>exp</mo>
|
||||
<mspace width="0.167em"></mspace>
|
||||
<mfenced open="(" close=")" separators="">
|
||||
<msqrt>
|
||||
<mi>x</mi>
|
||||
</msqrt>
|
||||
</mfenced>
|
||||
<mo>-</mo>
|
||||
<mfrac>
|
||||
<mrow>
|
||||
<mfrac>
|
||||
<mrow>
|
||||
<mo>arcsin</mo>
|
||||
<mspace width="0.167em"></mspace>
|
||||
<mfenced open="(" close=")" separators="">
|
||||
<mn>2</mn>
|
||||
<mspace width="0.167em"></mspace>
|
||||
<mi>x</mi>
|
||||
</mfenced>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>2</mn>
|
||||
<mo>×</mo>
|
||||
<msup>
|
||||
<mrow>
|
||||
<mn>10</mn>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>10</mn>
|
||||
</mrow>
|
||||
</msup>
|
||||
<mo>+</mo>
|
||||
<msup>
|
||||
<mrow>
|
||||
<mi>x</mi>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>3</mn>
|
||||
</mrow>
|
||||
</msup>
|
||||
</mrow>
|
||||
</mfrac>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>12</mn>
|
||||
</mrow>
|
||||
</mfrac>
|
||||
<mo>+</mo>
|
||||
<msup>
|
||||
<mrow>
|
||||
<mi>x</mi>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>2</mn>
|
||||
<mo>/</mo>
|
||||
<mn>3</mn>
|
||||
</mrow>
|
||||
</msup>
|
||||
</mrow>
|
||||
</math>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
It is also possible to create custom algorithms of render, and even add support of other markup languages
|
||||
(see API reference).
|
||||
|
@ -1,7 +1,6 @@
|
||||
import ru.mipt.npm.gradle.Maturity
|
||||
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.mpp")
|
||||
kotlin("multiplatform")
|
||||
id("ru.mipt.npm.gradle.common")
|
||||
}
|
||||
|
||||
kotlin.js {
|
||||
@ -19,8 +18,13 @@ kotlin.js {
|
||||
}
|
||||
|
||||
kotlin.sourceSets {
|
||||
filter { it.name.contains("test", true) }
|
||||
.map(org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet::languageSettings)
|
||||
.forEach { it.useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI") }
|
||||
|
||||
commonMain {
|
||||
dependencies {
|
||||
api("com.github.h0tk3y.betterParse:better-parse:0.4.2")
|
||||
api(project(":kmath-core"))
|
||||
}
|
||||
}
|
||||
@ -33,15 +37,15 @@ kotlin.sourceSets {
|
||||
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(npm("astring", "1.7.0"))
|
||||
implementation(npm("astring", "1.7.5"))
|
||||
implementation(npm("binaryen", "101.0.0"))
|
||||
implementation(npm("js-base64", "3.6.1"))
|
||||
}
|
||||
}
|
||||
|
||||
jvmMain {
|
||||
dependencies {
|
||||
api("com.github.h0tk3y.betterParse:better-parse:0.4.1")
|
||||
implementation("org.ow2.asm:asm:9.1")
|
||||
implementation("org.ow2.asm:asm-commons:9.1")
|
||||
implementation("org.ow2.asm:asm-commons:9.2")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52,42 +56,26 @@ tasks.dokkaHtml {
|
||||
}
|
||||
|
||||
readme {
|
||||
maturity = Maturity.PROTOTYPE
|
||||
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
|
||||
propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
|
||||
|
||||
feature(
|
||||
id = "expression-language",
|
||||
description = "Expression language and its parser",
|
||||
ref = "src/jvmMain/kotlin/kscience/kmath/ast/parser.kt"
|
||||
)
|
||||
|
||||
feature(
|
||||
id = "mst",
|
||||
description = "MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation",
|
||||
ref = "src/commonMain/kotlin/kscience/kmath/ast/MST.kt"
|
||||
)
|
||||
|
||||
feature(
|
||||
id = "mst-building",
|
||||
description = "MST building algebraic structure",
|
||||
ref = "src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt"
|
||||
)
|
||||
|
||||
feature(
|
||||
id = "mst-interpreter",
|
||||
description = "MST interpreter",
|
||||
ref = "src/commonMain/kotlin/kscience/kmath/ast/MST.kt"
|
||||
)
|
||||
ref = "src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt"
|
||||
) { "Expression language and its parser" }
|
||||
|
||||
feature(
|
||||
id = "mst-jvm-codegen",
|
||||
description = "Dynamic MST to JVM bytecode compiler",
|
||||
ref = "src/jvmMain/kotlin/kscience/kmath/asm/asm.kt"
|
||||
)
|
||||
ref = "src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt"
|
||||
) { "Dynamic MST to JVM bytecode compiler" }
|
||||
|
||||
feature(
|
||||
id = "mst-js-codegen",
|
||||
description = "Dynamic MST to JS compiler",
|
||||
ref = "src/jsMain/kotlin/kscience/kmath/estree/estree.kt"
|
||||
)
|
||||
ref = "src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt"
|
||||
) { "Dynamic MST to JS compiler" }
|
||||
|
||||
feature(
|
||||
id = "rendering",
|
||||
ref = "src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt"
|
||||
) { "Extendable MST rendering" }
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Abstract Syntax Tree Expression Representation and Operations (`kmath-ast`)
|
||||
# Module kmath-ast
|
||||
|
||||
This subproject implements the following features:
|
||||
Performance and visualization extensions to MST API.
|
||||
|
||||
${features}
|
||||
|
||||
@ -10,21 +10,27 @@ ${artifact}
|
||||
|
||||
### On JVM
|
||||
|
||||
`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds
|
||||
a special implementation of `Expression<T>` with implemented `invoke` function.
|
||||
`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a
|
||||
special implementation of `Expression<T>` with implemented `invoke` function.
|
||||
|
||||
For example, the following builder:
|
||||
|
||||
```kotlin
|
||||
RealField.mstInField { symbol("x") + 2 }.compile()
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.*
|
||||
import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.asm.*
|
||||
|
||||
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:
|
||||
|
||||
```java
|
||||
package space.kscience.kmath.asm.generated;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import kotlin.jvm.functions.Function2;
|
||||
import space.kscience.kmath.asm.internal.MapIntrinsics;
|
||||
import space.kscience.kmath.expressions.Expression;
|
||||
@ -44,19 +50,10 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
|
||||
|
||||
```
|
||||
|
||||
### Example Usage
|
||||
|
||||
This API extends MST and MstExpression, so you may optimize as both of them:
|
||||
|
||||
```kotlin
|
||||
RealField.mstInField { symbol("x") + 2 }.compile()
|
||||
RealField.expression("x+2".parseMath())
|
||||
```
|
||||
|
||||
#### Known issues
|
||||
|
||||
- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid
|
||||
class loading overhead.
|
||||
- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid class
|
||||
loading overhead.
|
||||
- This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders.
|
||||
|
||||
### On JS
|
||||
@ -64,7 +61,12 @@ RealField.expression("x+2".parseMath())
|
||||
A similar feature is also available on JS.
|
||||
|
||||
```kotlin
|
||||
RealField.mstInField { symbol("x") + 2 }.compile()
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.*
|
||||
import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.estree.*
|
||||
|
||||
MstField { x + 2 }.compileToExpression(DoubleField)
|
||||
```
|
||||
|
||||
The code above returns expression implemented with such a JS function:
|
||||
@ -75,6 +77,133 @@ var executable = function (constants, arguments) {
|
||||
};
|
||||
```
|
||||
|
||||
JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation.
|
||||
Currently, only expressions inside `DoubleField` and `IntRing` are supported.
|
||||
|
||||
```kotlin
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.*
|
||||
import space.kscience.kmath.operations.*
|
||||
import space.kscience.kmath.wasm.*
|
||||
|
||||
MstField { x + 2 }.compileToExpression(DoubleField)
|
||||
```
|
||||
|
||||
An example of emitted Wasm IR in the form of WAT:
|
||||
|
||||
```lisp
|
||||
(func \$executable (param \$0 f64) (result f64)
|
||||
(f64.add
|
||||
(local.get \$0)
|
||||
(f64.const 2)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
#### Known issues
|
||||
|
||||
- This feature uses `eval` which can be unavailable in several environments.
|
||||
- ESTree expression compilation uses `eval` which can be unavailable in several environments.
|
||||
- WebAssembly isn't supported by old versions of browsers (see https://webassembly.org/roadmap/).
|
||||
|
||||
## Rendering expressions
|
||||
|
||||
kmath-ast also includes an extensible engine to display expressions in LaTeX or MathML syntax.
|
||||
|
||||
Example usage:
|
||||
|
||||
```kotlin
|
||||
import space.kscience.kmath.ast.*
|
||||
import space.kscience.kmath.ast.rendering.*
|
||||
import space.kscience.kmath.misc.*
|
||||
|
||||
@OptIn(UnstableKMathAPI::class)
|
||||
public fun main() {
|
||||
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(12)+x^(2/3)".parseMath()
|
||||
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
|
||||
val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||
println("LaTeX:")
|
||||
println(latex)
|
||||
println()
|
||||
val mathML = MathMLSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||
println("MathML:")
|
||||
println(mathML)
|
||||
}
|
||||
```
|
||||
|
||||
Result LaTeX:
|
||||
|
||||
![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{12}+x^{2/3})
|
||||
|
||||
Result MathML (can be used with MathJax or other renderers):
|
||||
|
||||
<details>
|
||||
|
||||
```html
|
||||
<math xmlns="https://www.w3.org/1998/Math/MathML">
|
||||
<mrow>
|
||||
<mo>exp</mo>
|
||||
<mspace width="0.167em"></mspace>
|
||||
<mfenced open="(" close=")" separators="">
|
||||
<msqrt>
|
||||
<mi>x</mi>
|
||||
</msqrt>
|
||||
</mfenced>
|
||||
<mo>-</mo>
|
||||
<mfrac>
|
||||
<mrow>
|
||||
<mfrac>
|
||||
<mrow>
|
||||
<mo>arcsin</mo>
|
||||
<mspace width="0.167em"></mspace>
|
||||
<mfenced open="(" close=")" separators="">
|
||||
<mn>2</mn>
|
||||
<mspace width="0.167em"></mspace>
|
||||
<mi>x</mi>
|
||||
</mfenced>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>2</mn>
|
||||
<mo>×</mo>
|
||||
<msup>
|
||||
<mrow>
|
||||
<mn>10</mn>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>10</mn>
|
||||
</mrow>
|
||||
</msup>
|
||||
<mo>+</mo>
|
||||
<msup>
|
||||
<mrow>
|
||||
<mi>x</mi>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>3</mn>
|
||||
</mrow>
|
||||
</msup>
|
||||
</mrow>
|
||||
</mfrac>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>12</mn>
|
||||
</mrow>
|
||||
</mfrac>
|
||||
<mo>+</mo>
|
||||
<msup>
|
||||
<mrow>
|
||||
<mi>x</mi>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mn>2</mn>
|
||||
<mo>/</mo>
|
||||
<mn>3</mn>
|
||||
</mrow>
|
||||
</msup>
|
||||
</mrow>
|
||||
</math>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
It is also possible to create custom algorithms of render, and even add support of other markup languages
|
||||
(see API reference).
|
||||
|
@ -1,136 +0,0 @@
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.expressions.*
|
||||
import space.kscience.kmath.operations.*
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than
|
||||
* ASM-generated expressions.
|
||||
*
|
||||
* @property algebra the algebra that provides operations.
|
||||
* @property mst the [MST] node.
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public class MstExpression<T, out A : Algebra<T>>(public val algebra: A, public val mst: MST) : Expression<T> {
|
||||
private inner class InnerAlgebra(val arguments: Map<Symbol, T>) : NumericAlgebra<T> {
|
||||
override fun bindSymbol(value: String): T = try {
|
||||
algebra.bindSymbol(value)
|
||||
} catch (ignored: IllegalStateException) {
|
||||
null
|
||||
} ?: arguments.getValue(StringSymbol(value))
|
||||
|
||||
override fun unaryOperation(operation: String, arg: T): T =
|
||||
algebra.unaryOperation(operation, arg)
|
||||
|
||||
override fun binaryOperation(operation: String, left: T, right: T): T =
|
||||
algebra.binaryOperation(operation, left, right)
|
||||
|
||||
override fun unaryOperationFunction(operation: String): (arg: T) -> T =
|
||||
algebra.unaryOperationFunction(operation)
|
||||
|
||||
override fun binaryOperationFunction(operation: String): (left: T, right: T) -> T =
|
||||
algebra.binaryOperationFunction(operation)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun number(value: Number): T = if (algebra is NumericAlgebra<*>)
|
||||
(algebra as NumericAlgebra<T>).number(value)
|
||||
else
|
||||
error("Numeric nodes are not supported by $this")
|
||||
}
|
||||
|
||||
override operator fun invoke(arguments: Map<Symbol, T>): T = InnerAlgebra(arguments).evaluate(mst)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds [MstExpression] over [Algebra].
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public inline fun <reified T : Any, A : Algebra<T>, E : Algebra<MST>> A.mst(
|
||||
mstAlgebra: E,
|
||||
block: E.() -> MST,
|
||||
): MstExpression<T, A> = MstExpression(this, mstAlgebra.block())
|
||||
|
||||
/**
|
||||
* Builds [MstExpression] over [Space].
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public inline fun <reified T : Any, A : Space<T>> A.mstInSpace(block: MstSpace.() -> MST): MstExpression<T, A> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return MstExpression(this, MstSpace.block())
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds [MstExpression] over [Ring].
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public inline fun <reified T : Any, A : Ring<T>> A.mstInRing(block: MstRing.() -> MST): MstExpression<T, A> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return MstExpression(this, MstRing.block())
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds [MstExpression] over [Field].
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public inline fun <reified T : Any, A : Field<T>> A.mstInField(block: MstField.() -> MST): MstExpression<T, A> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return MstExpression(this, MstField.block())
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds [MstExpression] over [ExtendedField].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
public inline fun <reified T : Any, A : ExtendedField<T>> A.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression<T, A> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return MstExpression(this, MstExtendedField.block())
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds [MstExpression] over [FunctionalExpressionSpace].
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public inline fun <reified T : Any, A : Space<T>> FunctionalExpressionSpace<T, A>.mstInSpace(block: MstSpace.() -> MST): MstExpression<T, A> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return algebra.mstInSpace(block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds [MstExpression] over [FunctionalExpressionRing].
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public inline fun <reified T : Any, A : Ring<T>> FunctionalExpressionRing<T, A>.mstInRing(block: MstRing.() -> MST): MstExpression<T, A> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return algebra.mstInRing(block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds [MstExpression] over [FunctionalExpressionField].
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public inline fun <reified T : Any, A : Field<T>> FunctionalExpressionField<T, A>.mstInField(block: MstField.() -> MST): MstExpression<T, A> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return algebra.mstInField(block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds [MstExpression] over [FunctionalExpressionExtendedField].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
public inline fun <reified T : Any, A : ExtendedField<T>> FunctionalExpressionExtendedField<T, A>.mstInExtendedField(
|
||||
block: MstExtendedField.() -> MST,
|
||||
): MstExpression<T, A> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return algebra.mstInExtendedField(block)
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
// TODO move to common when https://github.com/h0tk3y/better-parse/pull/37 is merged
|
||||
/*
|
||||
* 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
|
||||
|
||||
@ -13,18 +16,20 @@ import com.github.h0tk3y.betterParse.lexer.literalToken
|
||||
import com.github.h0tk3y.betterParse.lexer.regexToken
|
||||
import com.github.h0tk3y.betterParse.parser.ParseResult
|
||||
import com.github.h0tk3y.betterParse.parser.Parser
|
||||
import space.kscience.kmath.expressions.MST
|
||||
import space.kscience.kmath.expressions.StringSymbol
|
||||
import space.kscience.kmath.operations.FieldOperations
|
||||
import space.kscience.kmath.operations.GroupOperations
|
||||
import space.kscience.kmath.operations.PowerOperations
|
||||
import space.kscience.kmath.operations.RingOperations
|
||||
import space.kscience.kmath.operations.SpaceOperations
|
||||
|
||||
/**
|
||||
* better-parse implementation of grammar defined in the ArithmeticsEvaluator.g4.
|
||||
*
|
||||
* @author Alexander Nozik and Iaroslav Postovalov
|
||||
* @author Alexander Nozik
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
public object ArithmeticsEvaluator : Grammar<MST>() {
|
||||
// TODO replace with "...".toRegex() when better-parse 0.4.1 is released
|
||||
private val num: Token by regexToken("[\\d.]+(?:[eE][-+]?\\d+)?".toRegex())
|
||||
private val id: Token by regexToken("[a-z_A-Z][\\da-z_A-Z]*".toRegex())
|
||||
private val lpar: Token by literalToken("(")
|
||||
@ -38,7 +43,7 @@ public object ArithmeticsEvaluator : Grammar<MST>() {
|
||||
private val ws: Token by regexToken("\\s+".toRegex(), ignore = true)
|
||||
|
||||
private val number: Parser<MST> by num use { MST.Numeric(text.toDouble()) }
|
||||
private val singular: Parser<MST> by id use { MST.Symbolic(text) }
|
||||
private val singular: Parser<MST> by id use { StringSymbol(text) }
|
||||
|
||||
private val unaryFunction: Parser<MST> by (id and -lpar and parser(ArithmeticsEvaluator::subSumChain) and -rpar)
|
||||
.map { (id, term) -> MST.Unary(id.text, term) }
|
||||
@ -55,7 +60,7 @@ public object ArithmeticsEvaluator : Grammar<MST>() {
|
||||
.or(binaryFunction)
|
||||
.or(unaryFunction)
|
||||
.or(singular)
|
||||
.or(-minus and parser(ArithmeticsEvaluator::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) })
|
||||
.or(-minus and parser(ArithmeticsEvaluator::term) map { MST.Unary(GroupOperations.MINUS_OPERATION, it) })
|
||||
.or(-lpar and parser(ArithmeticsEvaluator::subSumChain) and -rpar)
|
||||
|
||||
private val powChain: Parser<MST> by leftAssociative(term = term, operator = pow) { a, _, b ->
|
||||
@ -77,9 +82,9 @@ public object ArithmeticsEvaluator : Grammar<MST>() {
|
||||
operator = plus or minus use TokenMatch::type
|
||||
) { a, op, b ->
|
||||
if (op == plus)
|
||||
MST.Binary(SpaceOperations.PLUS_OPERATION, a, b)
|
||||
MST.Binary(GroupOperations.PLUS_OPERATION, a, b)
|
||||
else
|
||||
MST.Binary(SpaceOperations.MINUS_OPERATION, a, b)
|
||||
MST.Binary(GroupOperations.MINUS_OPERATION, a, b)
|
||||
}
|
||||
|
||||
override val rootParser: Parser<MST> by subSumChain
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
|
||||
/**
|
||||
* [SyntaxRenderer] implementation for LaTeX.
|
||||
*
|
||||
* The generated string is a valid LaTeX fragment to be used in the Math Mode.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* ```
|
||||
* \documentclass{article}
|
||||
* \begin{document}
|
||||
* \begin{equation}
|
||||
* %code generated by the syntax renderer
|
||||
* \end{equation}
|
||||
* \end{document}
|
||||
* ```
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public object LatexSyntaxRenderer : SyntaxRenderer {
|
||||
public override fun render(node: MathSyntax, output: Appendable): Unit = output.run {
|
||||
fun render(syntax: MathSyntax) = render(syntax, output)
|
||||
|
||||
when (node) {
|
||||
is NumberSyntax -> append(node.string)
|
||||
is SymbolSyntax -> append(node.string)
|
||||
|
||||
is OperatorNameSyntax -> {
|
||||
append("\\operatorname{")
|
||||
append(node.name)
|
||||
append('}')
|
||||
}
|
||||
|
||||
is SpecialSymbolSyntax -> when (node.kind) {
|
||||
SpecialSymbolSyntax.Kind.INFINITY -> append("\\infty")
|
||||
SpecialSymbolSyntax.Kind.SMALL_PI -> append("\\pi")
|
||||
}
|
||||
|
||||
is OperandSyntax -> {
|
||||
if (node.parentheses) append("\\left(")
|
||||
render(node.operand)
|
||||
if (node.parentheses) append("\\right)")
|
||||
}
|
||||
|
||||
is UnaryOperatorSyntax -> {
|
||||
render(node.prefix)
|
||||
append("\\,")
|
||||
render(node.operand)
|
||||
}
|
||||
|
||||
is UnaryPlusSyntax -> {
|
||||
append('+')
|
||||
render(node.operand)
|
||||
}
|
||||
|
||||
is UnaryMinusSyntax -> {
|
||||
append('-')
|
||||
render(node.operand)
|
||||
}
|
||||
|
||||
is RadicalSyntax -> {
|
||||
append("\\sqrt")
|
||||
append('{')
|
||||
render(node.operand)
|
||||
append('}')
|
||||
}
|
||||
|
||||
is ExponentSyntax -> if (node.useOperatorForm) {
|
||||
append("\\operatorname{exp}\\,")
|
||||
render(node.operand)
|
||||
} else {
|
||||
append("e^{")
|
||||
render(node.operand)
|
||||
append('}')
|
||||
}
|
||||
|
||||
is SuperscriptSyntax -> {
|
||||
render(node.left)
|
||||
append("^{")
|
||||
render(node.right)
|
||||
append('}')
|
||||
}
|
||||
|
||||
is SubscriptSyntax -> {
|
||||
render(node.left)
|
||||
append("_{")
|
||||
render(node.right)
|
||||
append('}')
|
||||
}
|
||||
|
||||
is BinaryOperatorSyntax -> {
|
||||
render(node.prefix)
|
||||
append("\\left(")
|
||||
render(node.left)
|
||||
append(',')
|
||||
render(node.right)
|
||||
append("\\right)")
|
||||
}
|
||||
|
||||
is BinaryPlusSyntax -> {
|
||||
render(node.left)
|
||||
append('+')
|
||||
render(node.right)
|
||||
}
|
||||
|
||||
is BinaryMinusSyntax -> {
|
||||
render(node.left)
|
||||
append('-')
|
||||
render(node.right)
|
||||
}
|
||||
|
||||
is FractionSyntax -> if (node.infix) {
|
||||
render(node.left)
|
||||
append('/')
|
||||
render(node.right)
|
||||
} else {
|
||||
append("\\frac{")
|
||||
render(node.left)
|
||||
append("}{")
|
||||
render(node.right)
|
||||
append('}')
|
||||
}
|
||||
|
||||
is RadicalWithIndexSyntax -> {
|
||||
append("\\sqrt")
|
||||
append('[')
|
||||
render(node.left)
|
||||
append(']')
|
||||
append('{')
|
||||
render(node.right)
|
||||
append('}')
|
||||
}
|
||||
|
||||
is MultiplicationSyntax -> {
|
||||
render(node.left)
|
||||
append(if (node.times) "\\times" else "\\,")
|
||||
render(node.right)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
|
||||
/**
|
||||
* [SyntaxRenderer] implementation for MathML.
|
||||
*
|
||||
* The generated XML string is a valid MathML instance.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public object MathMLSyntaxRenderer : SyntaxRenderer {
|
||||
public override fun render(node: MathSyntax, output: Appendable) {
|
||||
output.append("<math xmlns=\"https://www.w3.org/1998/Math/MathML\"><mrow>")
|
||||
renderPart(node, output)
|
||||
output.append("</mrow></math>")
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a part of syntax returning a correct MathML tag not the whole MathML instance.
|
||||
*/
|
||||
public fun renderPart(node: MathSyntax, output: Appendable): Unit = output.run {
|
||||
fun tag(tagName: String, vararg attr: Pair<String, String>, block: () -> Unit = {}) {
|
||||
append('<')
|
||||
append(tagName)
|
||||
|
||||
if (attr.isNotEmpty()) {
|
||||
append(' ')
|
||||
var count = 0
|
||||
|
||||
for ((name, value) in attr) {
|
||||
if (++count > 1) append(' ')
|
||||
append(name)
|
||||
append("=\"")
|
||||
append(value)
|
||||
append('"')
|
||||
}
|
||||
}
|
||||
|
||||
append('>')
|
||||
block()
|
||||
append("</")
|
||||
append(tagName)
|
||||
append('>')
|
||||
}
|
||||
|
||||
fun render(syntax: MathSyntax) = renderPart(syntax, output)
|
||||
|
||||
when (node) {
|
||||
is NumberSyntax -> tag("mn") { append(node.string) }
|
||||
is SymbolSyntax -> tag("mi") { append(node.string) }
|
||||
is OperatorNameSyntax -> tag("mo") { append(node.name) }
|
||||
|
||||
is SpecialSymbolSyntax -> when (node.kind) {
|
||||
SpecialSymbolSyntax.Kind.INFINITY -> tag("mo") { append("∞") }
|
||||
SpecialSymbolSyntax.Kind.SMALL_PI -> tag("mo") { append("π") }
|
||||
}
|
||||
|
||||
is OperandSyntax -> if (node.parentheses) {
|
||||
tag("mfenced", "open" to "(", "close" to ")", "separators" to "") {
|
||||
render(node.operand)
|
||||
}
|
||||
} else {
|
||||
render(node.operand)
|
||||
}
|
||||
|
||||
is UnaryOperatorSyntax -> {
|
||||
render(node.prefix)
|
||||
tag("mspace", "width" to "0.167em")
|
||||
render(node.operand)
|
||||
}
|
||||
|
||||
is UnaryPlusSyntax -> {
|
||||
tag("mo") { append('+') }
|
||||
render(node.operand)
|
||||
}
|
||||
|
||||
is UnaryMinusSyntax -> {
|
||||
tag("mo") { append("-") }
|
||||
render(node.operand)
|
||||
}
|
||||
|
||||
is RadicalSyntax -> tag("msqrt") { render(node.operand) }
|
||||
|
||||
is ExponentSyntax -> if (node.useOperatorForm) {
|
||||
tag("mo") { append("exp") }
|
||||
tag("mspace", "width" to "0.167em")
|
||||
render(node.operand)
|
||||
} else {
|
||||
tag("msup") {
|
||||
tag("mrow") {
|
||||
tag("mi") { append("e") }
|
||||
}
|
||||
tag("mrow") { render(node.operand) }
|
||||
}
|
||||
}
|
||||
|
||||
is SuperscriptSyntax -> tag("msup") {
|
||||
tag("mrow") { render(node.left) }
|
||||
tag("mrow") { render(node.right) }
|
||||
}
|
||||
|
||||
is SubscriptSyntax -> tag("msub") {
|
||||
tag("mrow") { render(node.left) }
|
||||
tag("mrow") { render(node.right) }
|
||||
}
|
||||
|
||||
is BinaryOperatorSyntax -> {
|
||||
render(node.prefix)
|
||||
|
||||
tag("mfenced", "open" to "(", "close" to ")", "separators" to "") {
|
||||
render(node.left)
|
||||
tag("mo") { append(',') }
|
||||
render(node.right)
|
||||
}
|
||||
}
|
||||
|
||||
is BinaryPlusSyntax -> {
|
||||
render(node.left)
|
||||
tag("mo") { append('+') }
|
||||
render(node.right)
|
||||
}
|
||||
|
||||
is BinaryMinusSyntax -> {
|
||||
render(node.left)
|
||||
tag("mo") { append('-') }
|
||||
render(node.right)
|
||||
}
|
||||
|
||||
is FractionSyntax -> if (node.infix) {
|
||||
render(node.left)
|
||||
tag("mo") { append('/') }
|
||||
render(node.right)
|
||||
} else tag("mfrac") {
|
||||
tag("mrow") { render(node.left) }
|
||||
tag("mrow") { render(node.right) }
|
||||
}
|
||||
|
||||
is RadicalWithIndexSyntax -> tag("mroot") {
|
||||
tag("mrow") { render(node.right) }
|
||||
tag("mrow") { render(node.left) }
|
||||
}
|
||||
|
||||
is MultiplicationSyntax -> {
|
||||
render(node.left)
|
||||
if (node.times) tag("mo") { append("×") } else tag("mspace", "width" to "0.167em")
|
||||
render(node.right)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.expressions.MST
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
|
||||
/**
|
||||
* Renders [MST] to [MathSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun interface MathRenderer {
|
||||
/**
|
||||
* Renders [MST] to [MathSyntax].
|
||||
*/
|
||||
public fun render(mst: MST): MathSyntax
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [MST] render process with sequence of features.
|
||||
*
|
||||
* @property features The applied features.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public open class FeaturedMathRenderer(public val features: List<RenderFeature>) : MathRenderer {
|
||||
public override fun render(mst: MST): MathSyntax {
|
||||
for (feature in features) feature.render(this, mst)?.let { return it }
|
||||
throw UnsupportedOperationException("Renderer $this has no appropriate feature to render node $mst.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Logical unit of [MST] rendering.
|
||||
*/
|
||||
public fun interface RenderFeature {
|
||||
/**
|
||||
* Renders [MST] to [MathSyntax] in the context of owning renderer.
|
||||
*/
|
||||
public fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends [FeaturedMathRenderer] by adding post-processing stages.
|
||||
*
|
||||
* @property stages The applied stages.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public open class FeaturedMathRendererWithPostProcess(
|
||||
features: List<RenderFeature>,
|
||||
public val stages: List<PostProcessPhase>,
|
||||
) : FeaturedMathRenderer(features) {
|
||||
public override fun render(mst: MST): MathSyntax {
|
||||
val res = super.render(mst)
|
||||
for (stage in stages) stage.perform(res)
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Logical unit of [MathSyntax] post-processing.
|
||||
*/
|
||||
public fun interface PostProcessPhase {
|
||||
/**
|
||||
* Performs the specified action over [MathSyntax].
|
||||
*/
|
||||
public fun perform(node: MathSyntax)
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default setup of [FeaturedMathRendererWithPostProcess].
|
||||
*/
|
||||
public val Default: FeaturedMathRendererWithPostProcess = FeaturedMathRendererWithPostProcess(
|
||||
listOf(
|
||||
// Printing known operations
|
||||
BinaryPlus.Default,
|
||||
BinaryMinus.Default,
|
||||
UnaryPlus.Default,
|
||||
UnaryMinus.Default,
|
||||
Multiplication.Default,
|
||||
Fraction.Default,
|
||||
Power.Default,
|
||||
SquareRoot.Default,
|
||||
Exponent.Default,
|
||||
InverseTrigonometricOperations.Default,
|
||||
InverseHyperbolicOperations.Default,
|
||||
|
||||
// Fallback option for unknown operations - printing them as operator
|
||||
BinaryOperator.Default,
|
||||
UnaryOperator.Default,
|
||||
|
||||
// Pretty printing for some objects
|
||||
PrettyPrintFloats.Default,
|
||||
PrettyPrintIntegers.Default,
|
||||
PrettyPrintPi.Default,
|
||||
|
||||
// Printing terminal nodes as string
|
||||
PrintNumeric,
|
||||
PrintSymbol,
|
||||
),
|
||||
listOf(
|
||||
BetterExponent,
|
||||
BetterFraction,
|
||||
SimplifyParentheses.Default,
|
||||
BetterMultiplication,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
|
||||
/**
|
||||
* Mathematical typography syntax node.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public sealed class MathSyntax {
|
||||
/**
|
||||
* The parent node of this syntax node.
|
||||
*/
|
||||
public var parent: MathSyntax? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminal node, which should not have any children nodes.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public sealed class TerminalSyntax : MathSyntax()
|
||||
|
||||
/**
|
||||
* Node containing a certain operation.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public sealed class OperationSyntax : MathSyntax() {
|
||||
/**
|
||||
* The operation token.
|
||||
*/
|
||||
public abstract val operation: String
|
||||
}
|
||||
|
||||
/**
|
||||
* Unary node, which has only one child.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public sealed class UnarySyntax : OperationSyntax() {
|
||||
/**
|
||||
* The operand of this node.
|
||||
*/
|
||||
public abstract val operand: MathSyntax
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary node, which has only two children.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public sealed class BinarySyntax : OperationSyntax() {
|
||||
/**
|
||||
* The left-hand side operand.
|
||||
*/
|
||||
public abstract val left: MathSyntax
|
||||
|
||||
/**
|
||||
* The right-hand side operand.
|
||||
*/
|
||||
public abstract val right: MathSyntax
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a number.
|
||||
*
|
||||
* @property string The digits of number.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class NumberSyntax(public var string: String) : TerminalSyntax()
|
||||
|
||||
/**
|
||||
* Represents a symbol.
|
||||
*
|
||||
* @property string The symbol.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class SymbolSyntax(public var string: String) : TerminalSyntax()
|
||||
|
||||
/**
|
||||
* Represents special typing for operator name.
|
||||
*
|
||||
* @property name The operator name.
|
||||
* @see BinaryOperatorSyntax
|
||||
* @see UnaryOperatorSyntax
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class OperatorNameSyntax(public var name: String) : TerminalSyntax()
|
||||
|
||||
/**
|
||||
* Represents a usage of special symbols (e.g., *∞*).
|
||||
*
|
||||
* @property kind The kind of symbol.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class SpecialSymbolSyntax(public var kind: Kind) : TerminalSyntax() {
|
||||
/**
|
||||
* The kind of symbol.
|
||||
*/
|
||||
public enum class Kind {
|
||||
/**
|
||||
* The infinity (∞) symbol.
|
||||
*/
|
||||
INFINITY,
|
||||
|
||||
/**
|
||||
* The Pi (π) symbol.
|
||||
*/
|
||||
SMALL_PI;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents operand of a certain operator wrapped with parentheses or not.
|
||||
*
|
||||
* @property operand The operand.
|
||||
* @property parentheses Whether the operand should be wrapped with parentheses.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class OperandSyntax(
|
||||
public val operand: MathSyntax,
|
||||
public var parentheses: Boolean,
|
||||
) : MathSyntax() {
|
||||
init {
|
||||
operand.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents unary, prefix operator syntax (like *f(x)*).
|
||||
*
|
||||
* @property prefix The prefix.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class UnaryOperatorSyntax(
|
||||
public override val operation: String,
|
||||
public var prefix: MathSyntax,
|
||||
public override val operand: OperandSyntax,
|
||||
) : UnarySyntax() {
|
||||
init {
|
||||
operand.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents prefix, unary plus operator (*+x*).
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class UnaryPlusSyntax(
|
||||
public override val operation: String,
|
||||
public override val operand: OperandSyntax,
|
||||
) : UnarySyntax() {
|
||||
init {
|
||||
operand.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents prefix, unary minus operator (*-x*).
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class UnaryMinusSyntax(
|
||||
public override val operation: String,
|
||||
public override val operand: OperandSyntax,
|
||||
) : UnarySyntax() {
|
||||
init {
|
||||
operand.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents radical with a node inside it (*√x*).
|
||||
*
|
||||
* @property operand The radicand.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class RadicalSyntax(
|
||||
public override val operation: String,
|
||||
public override val operand: MathSyntax,
|
||||
) : UnarySyntax() {
|
||||
init {
|
||||
operand.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents exponential function.
|
||||
*
|
||||
* @property operand The argument of function.
|
||||
* @property useOperatorForm `true` if operator form is used (*exp (x)*), `false` if exponentiation form is used
|
||||
* (*e<sup>x</sup>*).
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class ExponentSyntax(
|
||||
public override val operation: String,
|
||||
public override val operand: OperandSyntax,
|
||||
public var useOperatorForm: Boolean,
|
||||
) : UnarySyntax() {
|
||||
init {
|
||||
operand.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a syntax node with superscript (*x<sup>2</sup>*).
|
||||
*
|
||||
* @property left The node.
|
||||
* @property right The superscript.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class SuperscriptSyntax(
|
||||
public override val operation: String,
|
||||
public override val left: MathSyntax,
|
||||
public override val right: MathSyntax,
|
||||
) : BinarySyntax() {
|
||||
init {
|
||||
left.parent = this
|
||||
right.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a syntax node with subscript (*x<sub>i</sup>*).
|
||||
*
|
||||
* @property left The node.
|
||||
* @property right The subscript.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class SubscriptSyntax(
|
||||
public override val operation: String,
|
||||
public override val left: MathSyntax,
|
||||
public override val right: MathSyntax,
|
||||
) : BinarySyntax() {
|
||||
init {
|
||||
left.parent = this
|
||||
right.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents binary, prefix operator syntax (like *f(a, b)*).
|
||||
*
|
||||
* @property prefix The prefix.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class BinaryOperatorSyntax(
|
||||
public override val operation: String,
|
||||
public var prefix: MathSyntax,
|
||||
public override val left: MathSyntax,
|
||||
public override val right: MathSyntax,
|
||||
) : BinarySyntax() {
|
||||
init {
|
||||
left.parent = this
|
||||
right.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents binary, infix addition (*42 + 42*).
|
||||
*
|
||||
* @param left The augend.
|
||||
* @param right The addend.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class BinaryPlusSyntax(
|
||||
public override val operation: String,
|
||||
public override val left: OperandSyntax,
|
||||
public override val right: OperandSyntax,
|
||||
) : BinarySyntax() {
|
||||
init {
|
||||
left.parent = this
|
||||
right.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents binary, infix subtraction (*42 - 42*).
|
||||
*
|
||||
* @param left The minuend.
|
||||
* @param right The subtrahend.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class BinaryMinusSyntax(
|
||||
public override val operation: String,
|
||||
public override val left: OperandSyntax,
|
||||
public override val right: OperandSyntax,
|
||||
) : BinarySyntax() {
|
||||
init {
|
||||
left.parent = this
|
||||
right.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents fraction with numerator and denominator.
|
||||
*
|
||||
* @property left The numerator.
|
||||
* @property right The denominator.
|
||||
* @property infix Whether infix (*1 / 2*) or normal (*½*) fraction should be made.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class FractionSyntax(
|
||||
public override val operation: String,
|
||||
public override val left: OperandSyntax,
|
||||
public override val right: OperandSyntax,
|
||||
public var infix: Boolean,
|
||||
) : BinarySyntax() {
|
||||
init {
|
||||
left.parent = this
|
||||
right.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents radical syntax with index (*<sup>3</sup>√x*).
|
||||
*
|
||||
* @property left The index.
|
||||
* @property right The radicand.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class RadicalWithIndexSyntax(
|
||||
public override val operation: String,
|
||||
public override val left: MathSyntax,
|
||||
public override val right: MathSyntax,
|
||||
) : BinarySyntax() {
|
||||
init {
|
||||
left.parent = this
|
||||
right.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents binary, infix multiplication in the form of coefficient (*2 x*) or with operator (*x × 2*).
|
||||
*
|
||||
* @property left The multiplicand.
|
||||
* @property right The multiplier.
|
||||
* @property times Whether the times (×) symbol should be used.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public data class MultiplicationSyntax(
|
||||
public override val operation: String,
|
||||
public override val left: OperandSyntax,
|
||||
public override val right: OperandSyntax,
|
||||
public var times: Boolean,
|
||||
) : BinarySyntax() {
|
||||
init {
|
||||
left.parent = this
|
||||
right.parent = this
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
|
||||
/**
|
||||
* Abstraction of writing [MathSyntax] as a string of an actual markup language. Typical implementation should
|
||||
* involve traversal of MathSyntax with handling each its subtype.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun interface SyntaxRenderer {
|
||||
/**
|
||||
* Renders the [MathSyntax] to [output].
|
||||
*/
|
||||
public fun render(node: MathSyntax, output: Appendable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls [SyntaxRenderer.render] with given [node] and a new [StringBuilder] instance, and returns its content.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun SyntaxRenderer.renderWithStringBuilder(node: MathSyntax): String {
|
||||
val sb = StringBuilder()
|
||||
render(node, sb)
|
||||
return sb.toString()
|
||||
}
|
@ -0,0 +1,483 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.ast.rendering.FeaturedMathRenderer.RenderFeature
|
||||
import space.kscience.kmath.expressions.MST
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Prints any [Symbol] as a [SymbolSyntax] containing the [Symbol.value] of it.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public val PrintSymbol: RenderFeature = RenderFeature { _, node ->
|
||||
if (node !is Symbol) null
|
||||
else SymbolSyntax(string = node.identity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints any [MST.Numeric] as a [NumberSyntax] containing the [Any.toString] result of it.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public val PrintNumeric: RenderFeature = RenderFeature { _, node ->
|
||||
if (node !is MST.Numeric)
|
||||
null
|
||||
else
|
||||
NumberSyntax(string = node.value.toString())
|
||||
}
|
||||
|
||||
@UnstableKMathAPI
|
||||
private fun printSignedNumberString(s: String): MathSyntax = if (s.startsWith('-'))
|
||||
UnaryMinusSyntax(
|
||||
operation = GroupOperations.MINUS_OPERATION,
|
||||
operand = OperandSyntax(
|
||||
operand = NumberSyntax(string = s.removePrefix("-")),
|
||||
parentheses = true,
|
||||
),
|
||||
)
|
||||
else
|
||||
NumberSyntax(string = s)
|
||||
|
||||
/**
|
||||
* Special printing for numeric types which are printed in form of
|
||||
* *('-'? (DIGIT+ ('.' DIGIT+)? ('E' '-'? DIGIT+)? | 'Infinity')) | 'NaN'*.
|
||||
*
|
||||
* @property types The suitable types.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : RenderFeature {
|
||||
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
||||
if (node !is MST.Numeric || node.value::class !in types) return null
|
||||
|
||||
val toString = when (val v = node.value) {
|
||||
is Float -> v.multiplatformToString()
|
||||
is Double -> v.multiplatformToString()
|
||||
else -> v.toString()
|
||||
}.removeSuffix(".0")
|
||||
|
||||
if (toString.contains('E', ignoreCase = true)) {
|
||||
val (beforeE, afterE) = toString.split('E', ignoreCase = true)
|
||||
val significand = beforeE.toDouble().toString().removeSuffix(".0")
|
||||
val exponent = afterE.toDouble().toString().removeSuffix(".0")
|
||||
|
||||
return MultiplicationSyntax(
|
||||
operation = RingOperations.TIMES_OPERATION,
|
||||
left = OperandSyntax(operand = NumberSyntax(significand), parentheses = true),
|
||||
right = OperandSyntax(
|
||||
operand = SuperscriptSyntax(
|
||||
operation = PowerOperations.POW_OPERATION,
|
||||
left = NumberSyntax(string = "10"),
|
||||
right = printSignedNumberString(exponent),
|
||||
),
|
||||
parentheses = true,
|
||||
),
|
||||
times = true,
|
||||
)
|
||||
}
|
||||
|
||||
if (toString.endsWith("Infinity")) {
|
||||
val infty = SpecialSymbolSyntax(SpecialSymbolSyntax.Kind.INFINITY)
|
||||
|
||||
if (toString.startsWith('-'))
|
||||
return UnaryMinusSyntax(
|
||||
operation = GroupOperations.MINUS_OPERATION,
|
||||
operand = OperandSyntax(operand = infty, parentheses = true),
|
||||
)
|
||||
|
||||
return infty
|
||||
}
|
||||
|
||||
return printSignedNumberString(toString)
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance containing [Float], and [Double].
|
||||
*/
|
||||
public val Default: PrettyPrintFloats = PrettyPrintFloats(setOf(Float::class, Double::class))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special printing for numeric types which are printed in form of *'-'? DIGIT+*.
|
||||
*
|
||||
* @property types The suitable types.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class PrettyPrintIntegers(public val types: Set<KClass<out Number>>) : RenderFeature {
|
||||
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? =
|
||||
if (node !is MST.Numeric || node.value::class !in types)
|
||||
null
|
||||
else
|
||||
printSignedNumberString(node.value.toString())
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance containing [Byte], [Short], [Int], and [Long].
|
||||
*/
|
||||
public val Default: PrettyPrintIntegers =
|
||||
PrettyPrintIntegers(setOf(Byte::class, Short::class, Int::class, Long::class))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special printing for symbols meaning Pi.
|
||||
*
|
||||
* @property symbols The allowed symbols.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class PrettyPrintPi(public val symbols: Set<String>) : RenderFeature {
|
||||
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? =
|
||||
if (node !is Symbol || node.identity !in symbols)
|
||||
null
|
||||
else
|
||||
SpecialSymbolSyntax(kind = SpecialSymbolSyntax.Kind.SMALL_PI)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance containing `pi`.
|
||||
*/
|
||||
public val Default: PrettyPrintPi = PrettyPrintPi(setOf("pi"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract printing of unary operations which discards [MST] if their operation is not in [operations] or its type is
|
||||
* not [MST.Unary].
|
||||
*
|
||||
* @param operations the allowed operations. If `null`, any operation is accepted.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public abstract class Unary(public val operations: Collection<String>?) : RenderFeature {
|
||||
/**
|
||||
* The actual render function specialized for [MST.Unary].
|
||||
*/
|
||||
protected abstract fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax?
|
||||
|
||||
public final override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? =
|
||||
if (node !is MST.Unary || operations != null && node.operation !in operations)
|
||||
null
|
||||
else
|
||||
renderUnary(renderer, node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract printing of unary operations which discards [MST] if their operation is not in [operations] or its type is
|
||||
* not [MST.Binary].
|
||||
*
|
||||
* @property operations the allowed operations. If `null`, any operation is accepted.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public abstract class Binary(public val operations: Collection<String>?) : RenderFeature {
|
||||
/**
|
||||
* The actual render function specialized for [MST.Binary].
|
||||
*/
|
||||
protected abstract fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax?
|
||||
|
||||
public final override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
||||
if (node !is MST.Binary || operations != null && node.operation !in operations) return null
|
||||
return renderBinary(renderer, node)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing [BinaryPlusSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class BinaryPlus(operations: Collection<String>?) : Binary(operations) {
|
||||
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
|
||||
BinaryPlusSyntax(
|
||||
operation = node.operation,
|
||||
left = OperandSyntax(parent.render(node.left), true),
|
||||
right = OperandSyntax(parent.render(node.right), true),
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [GroupOperations.PLUS_OPERATION].
|
||||
*/
|
||||
public val Default: BinaryPlus = BinaryPlus(setOf(GroupOperations.PLUS_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing [BinaryMinusSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class BinaryMinus(operations: Collection<String>?) : Binary(operations) {
|
||||
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
|
||||
BinaryMinusSyntax(
|
||||
operation = node.operation,
|
||||
left = OperandSyntax(operand = parent.render(node.left), parentheses = true),
|
||||
right = OperandSyntax(operand = parent.render(node.right), parentheses = true),
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [GroupOperations.MINUS_OPERATION].
|
||||
*/
|
||||
public val Default: BinaryMinus = BinaryMinus(setOf(GroupOperations.MINUS_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles unary nodes by producing [UnaryPlusSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class UnaryPlus(operations: Collection<String>?) : Unary(operations) {
|
||||
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryPlusSyntax(
|
||||
operation = node.operation,
|
||||
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [GroupOperations.PLUS_OPERATION].
|
||||
*/
|
||||
public val Default: UnaryPlus = UnaryPlus(setOf(GroupOperations.PLUS_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing [UnaryMinusSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class UnaryMinus(operations: Collection<String>?) : Unary(operations) {
|
||||
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryMinusSyntax(
|
||||
operation = node.operation,
|
||||
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [GroupOperations.MINUS_OPERATION].
|
||||
*/
|
||||
public val Default: UnaryMinus = UnaryMinus(setOf(GroupOperations.MINUS_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing [FractionSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class Fraction(operations: Collection<String>?) : Binary(operations) {
|
||||
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = FractionSyntax(
|
||||
operation = node.operation,
|
||||
left = OperandSyntax(operand = parent.render(node.left), parentheses = true),
|
||||
right = OperandSyntax(operand = parent.render(node.right), parentheses = true),
|
||||
infix = true,
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [FieldOperations.DIV_OPERATION].
|
||||
*/
|
||||
public val Default: Fraction = Fraction(setOf(FieldOperations.DIV_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing [BinaryOperatorSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class BinaryOperator(operations: Collection<String>?) : Binary(operations) {
|
||||
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
|
||||
BinaryOperatorSyntax(
|
||||
operation = node.operation,
|
||||
prefix = OperatorNameSyntax(name = node.operation),
|
||||
left = parent.render(node.left),
|
||||
right = parent.render(node.right),
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with `null`.
|
||||
*/
|
||||
public val Default: BinaryOperator = BinaryOperator(null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles unary nodes by producing [UnaryOperatorSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class UnaryOperator(operations: Collection<String>?) : Unary(operations) {
|
||||
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax =
|
||||
UnaryOperatorSyntax(
|
||||
operation = node.operation,
|
||||
prefix = OperatorNameSyntax(node.operation),
|
||||
operand = OperandSyntax(parent.render(node.value), true),
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with `null`.
|
||||
*/
|
||||
public val Default: UnaryOperator = UnaryOperator(null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing [SuperscriptSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class Power(operations: Collection<String>?) : Binary(operations) {
|
||||
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
|
||||
SuperscriptSyntax(
|
||||
operation = node.operation,
|
||||
left = OperandSyntax(parent.render(node.left), true),
|
||||
right = OperandSyntax(parent.render(node.right), true),
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [PowerOperations.POW_OPERATION].
|
||||
*/
|
||||
public val Default: Power = Power(setOf(PowerOperations.POW_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing [RadicalSyntax] with no index.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class SquareRoot(operations: Collection<String>?) : Unary(operations) {
|
||||
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax =
|
||||
RadicalSyntax(operation = node.operation, operand = parent.render(node.value))
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [PowerOperations.SQRT_OPERATION].
|
||||
*/
|
||||
public val Default: SquareRoot = SquareRoot(setOf(PowerOperations.SQRT_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles unary nodes by producing [ExponentSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class Exponent(operations: Collection<String>?) : Unary(operations) {
|
||||
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = ExponentSyntax(
|
||||
operation = node.operation,
|
||||
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||
useOperatorForm = true,
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [ExponentialOperations.EXP_OPERATION].
|
||||
*/
|
||||
public val Default: Exponent = Exponent(setOf(ExponentialOperations.EXP_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing [MultiplicationSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class Multiplication(operations: Collection<String>?) : Binary(operations) {
|
||||
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax =
|
||||
MultiplicationSyntax(
|
||||
operation = node.operation,
|
||||
left = OperandSyntax(operand = parent.render(node.left), parentheses = true),
|
||||
right = OperandSyntax(operand = parent.render(node.right), parentheses = true),
|
||||
times = true,
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [RingOperations.TIMES_OPERATION].
|
||||
*/
|
||||
public val Default: Multiplication = Multiplication(setOf(RingOperations.TIMES_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing inverse [UnaryOperatorSyntax] with *arc* prefix instead of *a*.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class InverseTrigonometricOperations(operations: Collection<String>?) : Unary(operations) {
|
||||
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax =
|
||||
UnaryOperatorSyntax(
|
||||
operation = node.operation,
|
||||
prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "arc")),
|
||||
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [TrigonometricOperations.ACOS_OPERATION],
|
||||
* [TrigonometricOperations.ASIN_OPERATION], [TrigonometricOperations.ATAN_OPERATION].
|
||||
*/
|
||||
public val Default: InverseTrigonometricOperations = InverseTrigonometricOperations(setOf(
|
||||
TrigonometricOperations.ACOS_OPERATION,
|
||||
TrigonometricOperations.ASIN_OPERATION,
|
||||
TrigonometricOperations.ATAN_OPERATION,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles binary nodes by producing inverse [UnaryOperatorSyntax] with *ar* prefix instead of *a*.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class InverseHyperbolicOperations(operations: Collection<String>?) : Unary(operations) {
|
||||
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax =
|
||||
UnaryOperatorSyntax(
|
||||
operation = node.operation,
|
||||
prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "ar")),
|
||||
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||
)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default instance configured with [ExponentialOperations.ACOSH_OPERATION],
|
||||
* [ExponentialOperations.ASINH_OPERATION], and [ExponentialOperations.ATANH_OPERATION].
|
||||
*/
|
||||
public val Default: InverseHyperbolicOperations = InverseHyperbolicOperations(setOf(
|
||||
ExponentialOperations.ACOSH_OPERATION,
|
||||
ExponentialOperations.ASINH_OPERATION,
|
||||
ExponentialOperations.ATANH_OPERATION,
|
||||
))
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
internal expect fun Double.multiplatformToString(): String
|
||||
internal expect fun Float.multiplatformToString(): String
|
@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.ast.rendering.FeaturedMathRendererWithPostProcess.PostProcessPhase
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.FieldOperations
|
||||
import space.kscience.kmath.operations.GroupOperations
|
||||
import space.kscience.kmath.operations.PowerOperations
|
||||
import space.kscience.kmath.operations.RingOperations
|
||||
|
||||
/**
|
||||
* Removes unnecessary times (×) symbols from [MultiplicationSyntax].
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public val BetterMultiplication: PostProcessPhase = PostProcessPhase { node ->
|
||||
fun perform(node: MathSyntax): Unit = when (node) {
|
||||
is NumberSyntax -> Unit
|
||||
is SymbolSyntax -> Unit
|
||||
is OperatorNameSyntax -> Unit
|
||||
is SpecialSymbolSyntax -> Unit
|
||||
is OperandSyntax -> perform(node.operand)
|
||||
|
||||
is UnaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.operand)
|
||||
}
|
||||
|
||||
is UnaryPlusSyntax -> perform(node.operand)
|
||||
is UnaryMinusSyntax -> perform(node.operand)
|
||||
is RadicalSyntax -> perform(node.operand)
|
||||
is ExponentSyntax -> perform(node.operand)
|
||||
|
||||
is SuperscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is SubscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryPlusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryMinusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is FractionSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is RadicalWithIndexSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is MultiplicationSyntax -> {
|
||||
node.times = node.right.operand is NumberSyntax && !node.right.parentheses
|
||||
|| node.left.operand is NumberSyntax && node.right.operand is FractionSyntax
|
||||
|| node.left.operand is NumberSyntax && node.right.operand is NumberSyntax
|
||||
|| node.left.operand is NumberSyntax && node.right.operand is SuperscriptSyntax && node.right.operand.left is NumberSyntax
|
||||
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
}
|
||||
|
||||
perform(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses [FractionSyntax.infix] depending on the context.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public val BetterFraction: PostProcessPhase = PostProcessPhase { node ->
|
||||
fun perform(node: MathSyntax, infix: Boolean = false): Unit = when (node) {
|
||||
is NumberSyntax -> Unit
|
||||
is SymbolSyntax -> Unit
|
||||
is OperatorNameSyntax -> Unit
|
||||
is SpecialSymbolSyntax -> Unit
|
||||
is OperandSyntax -> perform(node.operand, infix)
|
||||
|
||||
is UnaryOperatorSyntax -> {
|
||||
perform(node.prefix, infix)
|
||||
perform(node.operand, infix)
|
||||
}
|
||||
|
||||
is UnaryPlusSyntax -> perform(node.operand, infix)
|
||||
is UnaryMinusSyntax -> perform(node.operand, infix)
|
||||
is RadicalSyntax -> perform(node.operand, infix)
|
||||
is ExponentSyntax -> perform(node.operand, infix)
|
||||
|
||||
is SuperscriptSyntax -> {
|
||||
perform(node.left, true)
|
||||
perform(node.right, true)
|
||||
}
|
||||
|
||||
is SubscriptSyntax -> {
|
||||
perform(node.left, true)
|
||||
perform(node.right, true)
|
||||
}
|
||||
|
||||
is BinaryOperatorSyntax -> {
|
||||
perform(node.prefix, infix)
|
||||
perform(node.left, infix)
|
||||
perform(node.right, infix)
|
||||
}
|
||||
|
||||
is BinaryPlusSyntax -> {
|
||||
perform(node.left, infix)
|
||||
perform(node.right, infix)
|
||||
}
|
||||
|
||||
is BinaryMinusSyntax -> {
|
||||
perform(node.left, infix)
|
||||
perform(node.right, infix)
|
||||
}
|
||||
|
||||
is FractionSyntax -> {
|
||||
node.infix = infix
|
||||
perform(node.left, infix)
|
||||
perform(node.right, infix)
|
||||
}
|
||||
|
||||
is RadicalWithIndexSyntax -> {
|
||||
perform(node.left, true)
|
||||
perform(node.right, true)
|
||||
}
|
||||
|
||||
is MultiplicationSyntax -> {
|
||||
perform(node.left, infix)
|
||||
perform(node.right, infix)
|
||||
}
|
||||
}
|
||||
|
||||
perform(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies [ExponentSyntax.useOperatorForm] to [ExponentSyntax] when the operand contains a fraction, a
|
||||
* superscript or a subscript to improve readability.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public val BetterExponent: PostProcessPhase = PostProcessPhase { node ->
|
||||
fun perform(node: MathSyntax): Boolean {
|
||||
return when (node) {
|
||||
is NumberSyntax -> false
|
||||
is SymbolSyntax -> false
|
||||
is OperatorNameSyntax -> false
|
||||
is SpecialSymbolSyntax -> false
|
||||
is OperandSyntax -> perform(node.operand)
|
||||
is UnaryOperatorSyntax -> perform(node.prefix) || perform(node.operand)
|
||||
is UnaryPlusSyntax -> perform(node.operand)
|
||||
is UnaryMinusSyntax -> perform(node.operand)
|
||||
is RadicalSyntax -> true
|
||||
|
||||
is ExponentSyntax -> {
|
||||
val r = perform(node.operand)
|
||||
node.useOperatorForm = r
|
||||
r
|
||||
}
|
||||
|
||||
is SuperscriptSyntax -> true
|
||||
is SubscriptSyntax -> true
|
||||
is BinaryOperatorSyntax -> perform(node.prefix) || perform(node.left) || perform(node.right)
|
||||
is BinaryPlusSyntax -> perform(node.left) || perform(node.right)
|
||||
is BinaryMinusSyntax -> perform(node.left) || perform(node.right)
|
||||
is FractionSyntax -> true
|
||||
is RadicalWithIndexSyntax -> true
|
||||
is MultiplicationSyntax -> perform(node.left) || perform(node.right)
|
||||
}
|
||||
}
|
||||
|
||||
perform(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes unnecessary parentheses from [OperandSyntax].
|
||||
*
|
||||
* @property precedenceFunction Returns the precedence number for syntax node. Higher number is lower priority.
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) -> Int) :
|
||||
PostProcessPhase {
|
||||
public override fun perform(node: MathSyntax): Unit = when (node) {
|
||||
is NumberSyntax -> Unit
|
||||
is SymbolSyntax -> Unit
|
||||
is OperatorNameSyntax -> Unit
|
||||
is SpecialSymbolSyntax -> Unit
|
||||
|
||||
is OperandSyntax -> {
|
||||
val isRightOfSuperscript =
|
||||
(node.parent is SuperscriptSyntax) && (node.parent as SuperscriptSyntax).right === node
|
||||
|
||||
val precedence = precedenceFunction(node.operand)
|
||||
|
||||
val needParenthesesByPrecedence = when (val parent = node.parent) {
|
||||
null -> false
|
||||
|
||||
is BinarySyntax -> {
|
||||
val parentPrecedence = precedenceFunction(parent)
|
||||
|
||||
parentPrecedence < precedence ||
|
||||
parentPrecedence == precedence && parentPrecedence != 0 && node === parent.right
|
||||
}
|
||||
|
||||
else -> precedence > precedenceFunction(parent)
|
||||
}
|
||||
|
||||
val isInsideExpOperator =
|
||||
node.parent is ExponentSyntax && (node.parent as ExponentSyntax).useOperatorForm
|
||||
|
||||
val isOnOrUnderNormalFraction = node.parent is FractionSyntax && !((node.parent as FractionSyntax).infix)
|
||||
|
||||
node.parentheses = !isRightOfSuperscript
|
||||
&& (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax || isInsideExpOperator)
|
||||
&& !isOnOrUnderNormalFraction
|
||||
|
||||
perform(node.operand)
|
||||
}
|
||||
|
||||
is UnaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.operand)
|
||||
}
|
||||
|
||||
is UnaryPlusSyntax -> perform(node.operand)
|
||||
is UnaryMinusSyntax -> perform(node.operand)
|
||||
is RadicalSyntax -> perform(node.operand)
|
||||
is ExponentSyntax -> perform(node.operand)
|
||||
|
||||
is SuperscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is SubscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryPlusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryMinusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is FractionSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is MultiplicationSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is RadicalWithIndexSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The default configuration of [SimplifyParentheses] where power is 1, multiplicative operations are 2,
|
||||
* additive operations are 3.
|
||||
*/
|
||||
public val Default: SimplifyParentheses = SimplifyParentheses {
|
||||
when (it) {
|
||||
is TerminalSyntax -> 0
|
||||
is UnarySyntax -> 2
|
||||
|
||||
is BinarySyntax -> when (it.operation) {
|
||||
PowerOperations.POW_OPERATION -> 1
|
||||
RingOperations.TIMES_OPERATION -> 3
|
||||
FieldOperations.DIV_OPERATION -> 3
|
||||
GroupOperations.MINUS_OPERATION -> 4
|
||||
GroupOperations.PLUS_OPERATION -> 4
|
||||
else -> 0
|
||||
}
|
||||
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.expressions.MstField
|
||||
import space.kscience.kmath.expressions.MstRing
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.interpret
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.IntRing
|
||||
import space.kscience.kmath.operations.bindSymbol
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class TestCompilerConsistencyWithInterpreter {
|
||||
@Test
|
||||
fun intRing() = runCompilerTest {
|
||||
val mst = MstRing {
|
||||
binaryOperationFunction("+")(
|
||||
unaryOperationFunction("+")(
|
||||
(x - (2.toByte() + (scale(
|
||||
add(number(1), number(1)),
|
||||
2.0,
|
||||
) + 1.toByte()))) * 3.0 - 1.toByte()
|
||||
),
|
||||
|
||||
number(1),
|
||||
) * number(2)
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
mst.interpret(IntRing, x to 3),
|
||||
mst.compile(IntRing, x to 3),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun doubleField() = runCompilerTest {
|
||||
val mst = MstField {
|
||||
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")(
|
||||
(3.0 - (x + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
|
||||
+ number(1),
|
||||
number(1) / 2 + number(2.0) * one,
|
||||
) + zero
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
mst.interpret(DoubleField, x to 2.0),
|
||||
mst.compile(DoubleField, x to 2.0),
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.expressions.MstExtendedField
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.invoke
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class TestCompilerOperations {
|
||||
@Test
|
||||
fun testUnaryPlus() = runCompilerTest {
|
||||
val expr = MstExtendedField { +x }.compileToExpression(DoubleField)
|
||||
assertEquals(2.0, expr(x to 2.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnaryMinus() = runCompilerTest {
|
||||
val expr = MstExtendedField { -x }.compileToExpression(DoubleField)
|
||||
assertEquals(-2.0, expr(x to 2.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAdd() = runCompilerTest {
|
||||
val expr = MstExtendedField { x + x }.compileToExpression(DoubleField)
|
||||
assertEquals(4.0, expr(x to 2.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSine() = runCompilerTest {
|
||||
val expr = MstExtendedField { sin(x) }.compileToExpression(DoubleField)
|
||||
assertEquals(0.0, expr(x to 0.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCosine() = runCompilerTest {
|
||||
val expr = MstExtendedField { cos(x) }.compileToExpression(DoubleField)
|
||||
assertEquals(1.0, expr(x to 0.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubtract() = runCompilerTest {
|
||||
val expr = MstExtendedField { x - x }.compileToExpression(DoubleField)
|
||||
assertEquals(0.0, expr(x to 2.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDivide() = runCompilerTest {
|
||||
val expr = MstExtendedField { x / x }.compileToExpression(DoubleField)
|
||||
assertEquals(1.0, expr(x to 2.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPower() = runCompilerTest {
|
||||
val expr = MstExtendedField { x pow 2 }.compileToExpression(DoubleField)
|
||||
assertEquals(4.0, expr(x to 2.0))
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.expressions.MstRing
|
||||
import space.kscience.kmath.expressions.Symbol.Companion.x
|
||||
import space.kscience.kmath.expressions.invoke
|
||||
import space.kscience.kmath.operations.IntRing
|
||||
import space.kscience.kmath.operations.bindSymbol
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
internal class TestCompilerVariables {
|
||||
@Test
|
||||
fun testVariable() = runCompilerTest {
|
||||
val expr = MstRing { x }.compileToExpression(IntRing)
|
||||
assertEquals(1, expr(x to 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUndefinedVariableFails() = runCompilerTest {
|
||||
val expr = MstRing { x }.compileToExpression(IntRing)
|
||||
assertFailsWith<NoSuchElementException> { expr() }
|
||||
}
|
||||
}
|
@ -1,29 +1,28 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.complex.Complex
|
||||
import space.kscience.kmath.complex.ComplexField
|
||||
import space.kscience.kmath.expressions.invoke
|
||||
import space.kscience.kmath.expressions.evaluate
|
||||
import space.kscience.kmath.operations.Algebra
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class ParserTest {
|
||||
internal class TestParser {
|
||||
@Test
|
||||
fun `evaluate MST`() {
|
||||
fun evaluateParsedMst() {
|
||||
val mst = "2+2*(2+2)".parseMath()
|
||||
val res = ComplexField.evaluate(mst)
|
||||
assertEquals(Complex(10.0, 0.0), res)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate MSTExpression`() {
|
||||
val res = ComplexField.mstInField { number(2) + number(2) * (number(2) + number(2)) }()
|
||||
assertEquals(Complex(10.0, 0.0), res)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate MST with singular`() {
|
||||
fun evaluateMstSymbol() {
|
||||
val mst = "i".parseMath()
|
||||
val res = ComplexField.evaluate(mst)
|
||||
assertEquals(ComplexField.i, res)
|
||||
@ -31,16 +30,16 @@ internal class ParserTest {
|
||||
|
||||
|
||||
@Test
|
||||
fun `evaluate MST with unary function`() {
|
||||
fun evaluateMstUnary() {
|
||||
val mst = "sin(0)".parseMath()
|
||||
val res = RealField.evaluate(mst)
|
||||
val res = DoubleField.evaluate(mst)
|
||||
assertEquals(0.0, res)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate MST with binary function`() {
|
||||
fun evaluateMstBinary() {
|
||||
val magicalAlgebra = object : Algebra<String> {
|
||||
override fun bindSymbol(value: String): String = value
|
||||
override fun bindSymbolOrNull(value: String): String = value
|
||||
|
||||
override fun unaryOperationFunction(operation: String): (arg: String) -> String {
|
||||
throw NotImplementedError()
|
@ -1,13 +1,16 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.operations.Field
|
||||
import space.kscience.kmath.operations.RealField
|
||||
import space.kscience.kmath.expressions.evaluate
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class ParserPrecedenceTest {
|
||||
private val f: Field<Double> = RealField
|
||||
|
||||
internal class TestParserPrecedence {
|
||||
@Test
|
||||
fun test1(): Unit = assertEquals(6.0, f.evaluate("2*2+2".parseMath()))
|
||||
|
||||
@ -31,4 +34,8 @@ internal class ParserPrecedenceTest {
|
||||
|
||||
@Test
|
||||
fun test8(): Unit = assertEquals(18.0, f.evaluate("2*2^3+2".parseMath()))
|
||||
|
||||
private companion object {
|
||||
private val f = DoubleField
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.ast.rendering.TestUtils.testLatex
|
||||
import space.kscience.kmath.expressions.MST.Numeric
|
||||
import kotlin.test.Test
|
||||
|
||||
internal class TestFeatures {
|
||||
@Test
|
||||
fun printSymbolic() = testLatex("x", "x")
|
||||
|
||||
@Test
|
||||
fun printNumeric() {
|
||||
val num = object : Number() {
|
||||
override fun toByte(): Byte = throw UnsupportedOperationException()
|
||||
override fun toChar(): Char = throw UnsupportedOperationException()
|
||||
override fun toDouble(): Double = throw UnsupportedOperationException()
|
||||
override fun toFloat(): Float = throw UnsupportedOperationException()
|
||||
override fun toInt(): Int = throw UnsupportedOperationException()
|
||||
override fun toLong(): Long = throw UnsupportedOperationException()
|
||||
override fun toShort(): Short = throw UnsupportedOperationException()
|
||||
override fun toString(): String = "foo"
|
||||
}
|
||||
|
||||
testLatex(Numeric(num), "foo")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun prettyPrintFloats() {
|
||||
testLatex(Numeric(Double.NaN), "NaN")
|
||||
testLatex(Numeric(Double.POSITIVE_INFINITY), "\\infty")
|
||||
testLatex(Numeric(Double.NEGATIVE_INFINITY), "-\\infty")
|
||||
testLatex(Numeric(1.0), "1")
|
||||
testLatex(Numeric(-1.0), "-1")
|
||||
testLatex(Numeric(1.42), "1.42")
|
||||
testLatex(Numeric(-1.42), "-1.42")
|
||||
testLatex(Numeric(1.1e10), "1.1\\times10^{10}")
|
||||
testLatex(Numeric(1.1e-10), "1.1\\times10^{-10}")
|
||||
testLatex(Numeric(-1.1e-10), "-1.1\\times10^{-10}")
|
||||
testLatex(Numeric(-1.1e10), "-1.1\\times10^{10}")
|
||||
testLatex(Numeric(0.001), "0.001")
|
||||
testLatex(Numeric(0.0000001), "1\\times10^{-7}")
|
||||
|
||||
testLatex(Numeric(Float.NaN), "NaN")
|
||||
testLatex(Numeric(Float.POSITIVE_INFINITY), "\\infty")
|
||||
testLatex(Numeric(Float.NEGATIVE_INFINITY), "-\\infty")
|
||||
testLatex(Numeric(1.0f), "1")
|
||||
testLatex(Numeric(-1.0f), "-1")
|
||||
testLatex(Numeric(1.42f), "1.42")
|
||||
testLatex(Numeric(-1.42f), "-1.42")
|
||||
testLatex(Numeric(1e10f), "1\\times10^{10}")
|
||||
testLatex(Numeric(1e-10f), "1\\times10^{-10}")
|
||||
testLatex(Numeric(-1e-10f), "-1\\times10^{-10}")
|
||||
testLatex(Numeric(-1e10f), "-1\\times10^{10}")
|
||||
testLatex(Numeric(0.001f), "0.001")
|
||||
testLatex(Numeric(0.0000001f), "1\\times10^{-7}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun prettyPrintIntegers() {
|
||||
testLatex(Numeric(42), "42")
|
||||
testLatex(Numeric(-42), "-42")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun prettyPrintPi() {
|
||||
testLatex("pi", "\\pi")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun binaryPlus() = testLatex("2+2", "2+2")
|
||||
|
||||
@Test
|
||||
fun binaryMinus() = testLatex("2-2", "2-2")
|
||||
|
||||
@Test
|
||||
fun fraction() = testLatex("2/2", "\\frac{2}{2}")
|
||||
|
||||
@Test
|
||||
fun binaryOperator() = testLatex("f(x, y)", "\\operatorname{f}\\left(x,y\\right)")
|
||||
|
||||
@Test
|
||||
fun unaryOperator() = testLatex("f(x)", "\\operatorname{f}\\,\\left(x\\right)")
|
||||
|
||||
@Test
|
||||
fun power() = testLatex("x^y", "x^{y}")
|
||||
|
||||
@Test
|
||||
fun squareRoot() = testLatex("sqrt(x)", "\\sqrt{x}")
|
||||
|
||||
@Test
|
||||
fun exponential() = testLatex("exp(x)", "e^{x}")
|
||||
|
||||
@Test
|
||||
fun multiplication() = testLatex("x*1", "x\\times1")
|
||||
|
||||
@Test
|
||||
fun inverseTrigonometric() {
|
||||
testLatex("asin(x)", "\\operatorname{arcsin}\\,\\left(x\\right)")
|
||||
testLatex("acos(x)", "\\operatorname{arccos}\\,\\left(x\\right)")
|
||||
testLatex("atan(x)", "\\operatorname{arctan}\\,\\left(x\\right)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun inverseHyperbolic() {
|
||||
testLatex("asinh(x)", "\\operatorname{arsinh}\\,\\left(x\\right)")
|
||||
testLatex("acosh(x)", "\\operatorname{arcosh}\\,\\left(x\\right)")
|
||||
testLatex("atanh(x)", "\\operatorname{artanh}\\,\\left(x\\right)")
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun unaryPlus() {
|
||||
// testLatex("+1", "+1")
|
||||
// testLatex("+1", "++1")
|
||||
// }
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.ast.rendering.TestUtils.testLatex
|
||||
import space.kscience.kmath.expressions.MST
|
||||
import space.kscience.kmath.operations.GroupOperations
|
||||
import kotlin.test.Test
|
||||
|
||||
internal class TestLatex {
|
||||
@Test
|
||||
fun number() = testLatex("42", "42")
|
||||
|
||||
@Test
|
||||
fun symbol() = testLatex("x", "x")
|
||||
|
||||
@Test
|
||||
fun operatorName() = testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)")
|
||||
|
||||
@Test
|
||||
fun specialSymbol() {
|
||||
testLatex(MST.Numeric(Double.POSITIVE_INFINITY), "\\infty")
|
||||
testLatex("pi", "\\pi")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun operand() {
|
||||
testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)")
|
||||
testLatex("1+1", "1+1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unaryOperator() = testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)")
|
||||
|
||||
@Test
|
||||
fun unaryPlus() = testLatex(MST.Unary(GroupOperations.PLUS_OPERATION, MST.Numeric(1)), "+1")
|
||||
|
||||
@Test
|
||||
fun unaryMinus() = testLatex("-x", "-x")
|
||||
|
||||
@Test
|
||||
fun radical() = testLatex("sqrt(x)", "\\sqrt{x}")
|
||||
|
||||
@Test
|
||||
fun superscript() = testLatex("x^y", "x^{y}")
|
||||
|
||||
@Test
|
||||
fun subscript() = testLatex(SubscriptSyntax("", SymbolSyntax("x"), NumberSyntax("123")), "x_{123}")
|
||||
|
||||
@Test
|
||||
fun binaryOperator() = testLatex("f(x, y)", "\\operatorname{f}\\left(x,y\\right)")
|
||||
|
||||
@Test
|
||||
fun binaryPlus() = testLatex("x+x", "x+x")
|
||||
|
||||
@Test
|
||||
fun binaryMinus() = testLatex("x-x", "x-x")
|
||||
|
||||
@Test
|
||||
fun fraction() = testLatex("x/x", "\\frac{x}{x}")
|
||||
|
||||
@Test
|
||||
fun radicalWithIndex() = testLatex(RadicalWithIndexSyntax("", SymbolSyntax("x"), SymbolSyntax("y")), "\\sqrt[x]{y}")
|
||||
|
||||
@Test
|
||||
fun multiplication() {
|
||||
testLatex("x*1", "x\\times1")
|
||||
testLatex("1*x", "1\\,x")
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.ast.rendering.TestUtils.testMathML
|
||||
import space.kscience.kmath.expressions.MST
|
||||
import space.kscience.kmath.operations.GroupOperations
|
||||
import kotlin.test.Test
|
||||
|
||||
internal class TestMathML {
|
||||
@Test
|
||||
fun number() = testMathML("42", "<mn>42</mn>")
|
||||
|
||||
@Test
|
||||
fun symbol() = testMathML("x", "<mi>x</mi>")
|
||||
|
||||
@Test
|
||||
fun operatorName() = testMathML(
|
||||
"sin(1)",
|
||||
"<mo>sin</mo><mspace width=\"0.167em\"></mspace><mfenced open=\"(\" close=\")\" separators=\"\"><mn>1</mn></mfenced>",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun specialSymbol() {
|
||||
testMathML(MST.Numeric(Double.POSITIVE_INFINITY), "<mo>∞</mo>")
|
||||
testMathML("pi", "<mo>π</mo>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun operand() {
|
||||
testMathML(
|
||||
"sin(1)",
|
||||
"<mo>sin</mo><mspace width=\"0.167em\"></mspace><mfenced open=\"(\" close=\")\" separators=\"\"><mn>1</mn></mfenced>",
|
||||
)
|
||||
|
||||
testMathML("1+1", "<mn>1</mn><mo>+</mo><mn>1</mn>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unaryOperator() = testMathML(
|
||||
"sin(1)",
|
||||
"<mo>sin</mo><mspace width=\"0.167em\"></mspace><mfenced open=\"(\" close=\")\" separators=\"\"><mn>1</mn></mfenced>",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun unaryPlus() =
|
||||
testMathML(MST.Unary(GroupOperations.PLUS_OPERATION, MST.Numeric(1)), "<mo>+</mo><mn>1</mn>")
|
||||
|
||||
@Test
|
||||
fun unaryMinus() = testMathML("-x", "<mo>-</mo><mi>x</mi>")
|
||||
|
||||
@Test
|
||||
fun radical() = testMathML("sqrt(x)", "<msqrt><mi>x</mi></msqrt>")
|
||||
|
||||
@Test
|
||||
fun superscript() = testMathML("x^y", "<msup><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow></msup>")
|
||||
|
||||
@Test
|
||||
fun subscript() = testMathML(
|
||||
SubscriptSyntax("", SymbolSyntax("x"), NumberSyntax("123")),
|
||||
"<msub><mrow><mi>x</mi></mrow><mrow><mn>123</mn></mrow></msub>",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun binaryOperator() = testMathML(
|
||||
"f(x, y)",
|
||||
"<mo>f</mo><mfenced open=\"(\" close=\")\" separators=\"\"><mi>x</mi><mo>,</mo><mi>y</mi></mfenced>",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun binaryPlus() = testMathML("x+x", "<mi>x</mi><mo>+</mo><mi>x</mi>")
|
||||
|
||||
@Test
|
||||
fun binaryMinus() = testMathML("x-x", "<mi>x</mi><mo>-</mo><mi>x</mi>")
|
||||
|
||||
@Test
|
||||
fun fraction() = testMathML("x/x", "<mfrac><mrow><mi>x</mi></mrow><mrow><mi>x</mi></mrow></mfrac>")
|
||||
|
||||
@Test
|
||||
fun radicalWithIndex() =
|
||||
testMathML(RadicalWithIndexSyntax("", SymbolSyntax("x"), SymbolSyntax("y")),
|
||||
"<mroot><mrow><mi>y</mi></mrow><mrow><mi>x</mi></mrow></mroot>")
|
||||
|
||||
@Test
|
||||
fun multiplication() {
|
||||
testMathML("x*1", "<mi>x</mi><mo>×</mo><mn>1</mn>")
|
||||
testMathML("1*x", "<mn>1</mn><mspace width=\"0.167em\"></mspace><mi>x</mi>")
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.ast.rendering.TestUtils.testLatex
|
||||
import kotlin.test.Test
|
||||
|
||||
internal class TestStages {
|
||||
@Test
|
||||
fun betterMultiplication() {
|
||||
testLatex("a*1", "a\\times1")
|
||||
testLatex("1*(2/3)", "1\\times\\left(\\frac{2}{3}\\right)")
|
||||
testLatex("1*1", "1\\times1")
|
||||
testLatex("2e10", "2\\times10^{10}")
|
||||
testLatex("2*x", "2\\,x")
|
||||
testLatex("2*(x+1)", "2\\,\\left(x+1\\right)")
|
||||
testLatex("x*y", "x\\,y")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parentheses() {
|
||||
testLatex("(x+1)", "x+1")
|
||||
testLatex("x*x*x", "x\\,x\\,x")
|
||||
testLatex("(x+x)*x", "\\left(x+x\\right)\\,x")
|
||||
testLatex("x+x*x", "x+x\\,x")
|
||||
testLatex("x+x^x*x+x", "x+x^{x}\\,x+x")
|
||||
testLatex("(x+x)^x+x*x", "\\left(x+x\\right)^{x}+x\\,x")
|
||||
testLatex("x^(x+x)", "x^{x+x}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun exponent() {
|
||||
testLatex("exp(x)", "e^{x}")
|
||||
testLatex("exp(x/2)", "\\operatorname{exp}\\,\\left(\\frac{x}{2}\\right)")
|
||||
testLatex("exp(x^2)", "\\operatorname{exp}\\,\\left(x^{2}\\right)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fraction() {
|
||||
testLatex("x/y", "\\frac{x}{y}")
|
||||
testLatex("x^(x/y)", "x^{x/y}")
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
import space.kscience.kmath.ast.parseMath
|
||||
import space.kscience.kmath.expressions.MST
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal object TestUtils {
|
||||
private fun mathSyntax(mst: MST) = FeaturedMathRendererWithPostProcess.Default.render(mst)
|
||||
private fun latex(mst: MST) = LatexSyntaxRenderer.renderWithStringBuilder(mathSyntax(mst))
|
||||
private fun mathML(mst: MST) = MathMLSyntaxRenderer.renderWithStringBuilder(mathSyntax(mst))
|
||||
|
||||
internal fun testLatex(mst: MST, expectedLatex: String) = assertEquals(
|
||||
expected = expectedLatex,
|
||||
actual = latex(mst),
|
||||
)
|
||||
|
||||
internal fun testLatex(expression: String, expectedLatex: String) = assertEquals(
|
||||
expected = expectedLatex,
|
||||
actual = latex(expression.parseMath()),
|
||||
)
|
||||
|
||||
internal fun testLatex(expression: MathSyntax, expectedLatex: String) = assertEquals(
|
||||
expected = expectedLatex,
|
||||
actual = LatexSyntaxRenderer.renderWithStringBuilder(expression),
|
||||
)
|
||||
|
||||
internal fun testMathML(mst: MST, expectedMathML: String) = assertEquals(
|
||||
expected = "<math xmlns=\"https://www.w3.org/1998/Math/MathML\"><mrow>$expectedMathML</mrow></math>",
|
||||
actual = mathML(mst),
|
||||
)
|
||||
|
||||
internal fun testMathML(expression: String, expectedMathML: String) = assertEquals(
|
||||
expected = "<math xmlns=\"https://www.w3.org/1998/Math/MathML\"><mrow>$expectedMathML</mrow></math>",
|
||||
actual = mathML(expression.parseMath()),
|
||||
)
|
||||
|
||||
internal fun testMathML(expression: MathSyntax, expectedMathML: String) = assertEquals(
|
||||
expected = "<math xmlns=\"https://www.w3.org/1998/Math/MathML\"><mrow>$expectedMathML</mrow></math>",
|
||||
actual = MathMLSyntaxRenderer.renderWithStringBuilder(expression),
|
||||
)
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.expressions.Expression
|
||||
import space.kscience.kmath.expressions.MST
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.operations.DoubleField
|
||||
import space.kscience.kmath.operations.IntRing
|
||||
|
||||
internal interface CompilerTestContext {
|
||||
fun MST.compileToExpression(algebra: IntRing): Expression<Int>
|
||||
fun MST.compile(algebra: IntRing, arguments: Map<Symbol, Int>): Int
|
||||
fun MST.compile(algebra: IntRing, vararg arguments: Pair<Symbol, Int>): Int = compile(algebra, mapOf(*arguments))
|
||||
fun MST.compileToExpression(algebra: DoubleField): Expression<Double>
|
||||
fun MST.compile(algebra: DoubleField, arguments: Map<Symbol, Double>): Double
|
||||
|
||||
fun MST.compile(algebra: DoubleField, vararg arguments: Pair<Symbol, Double>): Double =
|
||||
compile(algebra, mapOf(*arguments))
|
||||
}
|
||||
|
||||
internal expect inline fun runCompilerTest(action: CompilerTestContext.() -> Unit)
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
internal actual fun Double.multiplatformToString(): String {
|
||||
val d = this
|
||||
if (d >= 1e7 || d <= -1e7) return js("d.toExponential()") as String
|
||||
return toString()
|
||||
}
|
||||
|
||||
internal actual fun Float.multiplatformToString(): String {
|
||||
val d = this
|
||||
if (d >= 1e7f || d <= -1e7f) return js("d.toExponential()") as String
|
||||
return toString()
|
||||
}
|
@ -1,44 +1,48 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.estree
|
||||
|
||||
import space.kscience.kmath.ast.MST
|
||||
import space.kscience.kmath.ast.MST.*
|
||||
import space.kscience.kmath.ast.MstExpression
|
||||
import space.kscience.kmath.estree.internal.ESTreeBuilder
|
||||
import space.kscience.kmath.estree.internal.estree.BaseExpression
|
||||
import space.kscience.kmath.expressions.Expression
|
||||
import space.kscience.kmath.expressions.MST
|
||||
import space.kscience.kmath.expressions.MST.*
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.expressions.invoke
|
||||
import space.kscience.kmath.internal.estree.BaseExpression
|
||||
import space.kscience.kmath.operations.Algebra
|
||||
import space.kscience.kmath.operations.NumericAlgebra
|
||||
import space.kscience.kmath.operations.bindSymbolOrNull
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T> MST.compileWith(algebra: Algebra<T>): Expression<T> {
|
||||
fun ESTreeBuilder<T>.visit(node: MST): BaseExpression = when (node) {
|
||||
is Symbolic -> {
|
||||
val symbol = try {
|
||||
algebra.bindSymbol(node.value)
|
||||
} catch (ignored: IllegalStateException) {
|
||||
null
|
||||
}
|
||||
is Symbol -> {
|
||||
val symbol = algebra.bindSymbolOrNull(node)
|
||||
|
||||
if (symbol != null)
|
||||
constant(symbol)
|
||||
else
|
||||
variable(node.value)
|
||||
variable(node.identity)
|
||||
}
|
||||
|
||||
is Numeric -> constant(node.value)
|
||||
|
||||
is Unary -> when {
|
||||
algebra is NumericAlgebra && node.value is Numeric -> constant(
|
||||
algebra.unaryOperationFunction(node.operation)(algebra.number(node.value.value)))
|
||||
algebra.unaryOperationFunction(node.operation)(algebra.number((node.value as Numeric).value)))
|
||||
|
||||
else -> call(algebra.unaryOperationFunction(node.operation), visit(node.value))
|
||||
}
|
||||
|
||||
is Binary -> when {
|
||||
algebra is NumericAlgebra && node.left is Numeric && node.right is Numeric -> constant(
|
||||
algebra
|
||||
.binaryOperationFunction(node.operation)
|
||||
.invoke(algebra.number(node.left.value), algebra.number(node.right.value))
|
||||
algebra.binaryOperationFunction(node.operation).invoke(
|
||||
algebra.number((node.left as Numeric).value),
|
||||
algebra.number((node.right as Numeric).value)
|
||||
)
|
||||
)
|
||||
|
||||
algebra is NumericAlgebra && node.left is Numeric -> call(
|
||||
@ -64,19 +68,21 @@ internal fun <T> MST.compileWith(algebra: Algebra<T>): Expression<T> {
|
||||
return ESTreeBuilder<T> { visit(this@compileWith) }.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a compiled expression with given [MST] and given [algebra].
|
||||
*/
|
||||
public fun <T : Any> MST.compileToExpression(algebra: Algebra<T>): Expression<T> = compileWith(algebra)
|
||||
|
||||
|
||||
/**
|
||||
* Compiles an [MST] to ESTree generated expression using given algebra.
|
||||
*
|
||||
* @author Alexander Nozik.
|
||||
* Compile given MST to expression and evaluate it against [arguments]
|
||||
*/
|
||||
public fun <T : Any> Algebra<T>.expression(mst: MST): Expression<T> =
|
||||
mst.compileWith(this)
|
||||
public inline fun <reified T : Any> MST.compile(algebra: Algebra<T>, arguments: Map<Symbol, T>): T =
|
||||
compileToExpression(algebra).invoke(arguments)
|
||||
|
||||
|
||||
/**
|
||||
* Optimizes performance of an [MstExpression] by compiling it into ESTree generated expression.
|
||||
*
|
||||
* @author Alexander Nozik.
|
||||
* Compile given MST to expression and evaluate it against [arguments]
|
||||
*/
|
||||
public fun <T : Any> MstExpression<T, Algebra<T>>.compile(): Expression<T> =
|
||||
mst.compileWith(algebra)
|
||||
public inline fun <reified T : Any> MST.compile(algebra: Algebra<T>, vararg arguments: Pair<Symbol, T>): T =
|
||||
compileToExpression(algebra).invoke(*arguments)
|
||||
|