Compare commits

..

43 Commits

Author SHA1 Message Date
Iaroslav Postovalov
7ee7daa1ab Torchscript 2021-07-14 00:14:09 +07:00
Iaroslav Postovalov
3d8390a130 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
2021-07-13 00:53:37 +07:00
Roland Grinis
bd736c6716 Added API 2021-03-23 16:09:12 +00:00
Roland Grinis
86528fc943 Cannot afford to derive from RingsWithNumbers 2021-03-01 23:08:23 +00:00
Roland Grinis
3e3d433a64 Merge branch 'feature/tensor-algebra' into feature/torch 2021-03-01 22:53:37 +00:00
Roland Grinis
0a4e7acb4c Inefficient implementations in RingWithNumbers 2021-03-01 22:44:46 +00:00
Roland Grinis
6594ffc965 Syncing with tensor-algebra 2021-03-01 22:23:54 +00:00
Roland Grinis
9b3258b06b Syncing with dev 2021-03-01 17:04:13 +00:00
Roland Grinis
120189fd89 Syncing with feature/tensor-algebra 2021-03-01 15:42:08 +00:00
Roland Grinis
40895e5936 Syncing with dev 2021-01-20 20:30:19 +00:00
Roland Grinis
c141c04e99 JVM implementation 2021-01-20 19:09:21 +00:00
Roland Grinis
c9dfb6a08c Add withChecks to most tests 2021-01-20 14:09:16 +00:00
Roland Grinis
391eb28cad Added build-essential to ubuntu-20.04 job 2021-01-20 13:14:09 +00:00
Roland Grinis
17e6ebbc14 JNI wrapper 2021-01-20 12:13:32 +00:00
Roland Grinis
d599d1132b Moving benchmarks implementations to common 2021-01-19 08:06:11 +00:00
Roland Grinis
274d1a3105 Moving tests implementation to common 2021-01-18 21:43:49 +00:00
Roland Grinis
b30ca920e1 Syncing with feature/tensor-algebra 2021-01-18 18:48:57 +00:00
Roland Grinis
6eb718f64a Refactoring project structure 2021-01-18 18:31:01 +00:00
Roland Grinis
889691a122 Integrating withDeferScope 2021-01-16 20:44:21 +00:00
Roland Grinis
e5205d5afd Corrected readme file 2021-01-16 20:29:47 +00:00
Roland Grinis
ed4ac2623d TensorAlgebra first syncing 2021-01-16 20:23:41 +00:00
Roland Grinis
97ef57697d Merge branch 'feature/tensor-algebra' into feature/torch 2021-01-16 19:30:41 +00:00
rgrit91
4f4fcba559 Adding systematic checks 2021-01-16 19:19:03 +00:00
rgrit91
ca2082405a Adding more systematic checks 2021-01-16 13:32:23 +00:00
Iaroslav Postovalov
0b784474b4
Add draft of multiplatform DeferScope 2021-01-16 20:00:00 +07:00
rgrit91
fbb414731b Hessian implementation 2021-01-14 22:28:47 +00:00
rgrit91
7d25aa2834 Benchmarking random generators 2021-01-14 16:56:04 +00:00
rgrit91
ef570254e6 Float, Long and Int support 2021-01-14 10:54:35 +00:00
rgrit91
80f28dbcd5 Batched operations in algebra 2021-01-13 20:07:59 +00:00
rgrit91
ca3cca65ef Tensor transformations first examples 2021-01-13 12:55:44 +00:00
rgrit91
39f3a87bbd More linear operations on tensors 2021-01-12 21:33:07 +00:00
rgrit91
524b1d80d1 Copyless data transfer 2021-01-12 19:49:16 +00:00
rgrit91
8967691b7d Improved stability for benchmarks and merge from dev 2021-01-11 13:19:32 +00:00
rgrit91
7105331149 Autograd support drafting 2021-01-10 20:02:00 +00:00
rgrit91
7894799e8e Benchmarks for tensor matrix multiplication over Double 2021-01-10 14:00:35 +00:00
rgrit91
0cb2c3f0da Dropping support for buffered NDStructures 2021-01-10 12:56:58 +00:00
rgrit91
9b1a958491 Initial drafts for TorchTensorAlgebra 2021-01-09 09:53:35 +00:00
rgrit91
cfe93886ac Merge branch 'dev' into feature/torch 2021-01-08 06:57:45 +00:00
rgrit91
fb9d612081 Memory management refactored 2021-01-08 06:57:10 +00:00
rgrit91
d97f8857a0 Merge branch 'dev' into feature/torch 2021-01-06 16:09:16 +00:00
rgrit91
0fc29b40c5 Support for tensors on GPU 2021-01-06 13:20:48 +00:00
rgrit91
32e4b68061 Fix build when CUDA not available 2021-01-05 13:05:16 +00:00
rgrit91
a229aaa6a4 Buffer protocol for torch tensors 2021-01-04 20:53:03 +00:00
748 changed files with 17808 additions and 45054 deletions

3
.github/CODEOWNERS vendored
View File

@ -1,3 +0,0 @@
@altavir
/kmath-trajectory @ESchouten

View File

@ -2,23 +2,41 @@ name: Gradle build
on: on:
push: push:
branches: [ dev, master ]
pull_request: pull_request:
types: [opened, edited]
jobs: jobs:
build: build:
runs-on: windows-latest strategy:
timeout-minutes: 20 matrix:
os: [ macOS-latest, windows-latest ]
runs-on: ${{matrix.os}}
timeout-minutes: 30
steps: steps:
- uses: actions/checkout@v3 - name: Checkout the repo
- uses: actions/setup-java@v3.5.1 uses: actions/checkout@v2
- name: Set up JDK 11
uses: DeLaGuardo/setup-graalvm@4.0
with: with:
java-version: '11' graalvm: 21.1.0
distribution: 'liberica' java: java11
cache: 'gradle' arch: amd64
- name: Gradle Wrapper Validation - name: Add msys to path
uses: gradle/wrapper-validation-action@v1.0.4 if: matrix.os == 'windows-latest'
- name: Gradle Build run: SETX PATH "%PATH%;C:\msys64\mingw64\bin"
uses: gradle/gradle-build-action@v2.4.2 - name: Cache gradle
uses: actions/cache@v2
with: with:
arguments: test jvmTest 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: Build
run: ./gradlew build --no-daemon --stacktrace

View File

@ -1,31 +1,24 @@
name: Dokka publication name: Dokka publication
on: on:
workflow_dispatch: push:
release: branches:
types: [ created ] - master
jobs: jobs:
build: build:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
timeout-minutes: 40
steps: steps:
- uses: actions/checkout@v3.0.0 - name: Checkout the repo
- uses: actions/setup-java@v3.0.0 uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v1
with: with:
java-version: 11 java-version: 11
distribution: liberica - name: Build
- name: Cache konan run: ./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace
uses: actions/cache@v3.0.1 - name: Deploy to GitHub Pages
with: uses: JamesIves/github-pages-deploy-action@4.1.0
path: ~/.konan
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- uses: gradle/gradle-build-action@v2.4.2
with:
arguments: dokkaHtmlMultiModule --no-parallel
- uses: JamesIves/github-pages-deploy-action@v4.3.0
with: with:
branch: gh-pages branch: gh-pages
folder: build/dokka/htmlMultiModule folder: build/dokka/htmlMultiModule

View File

@ -3,7 +3,8 @@ name: Gradle publish
on: on:
workflow_dispatch: workflow_dispatch:
release: release:
types: [ created ] types:
- created
jobs: jobs:
publish: publish:
@ -14,13 +15,26 @@ jobs:
os: [ macOS-latest, windows-latest ] os: [ macOS-latest, windows-latest ]
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v3.0.0 - name: Checkout the repo
- uses: actions/setup-java@v3.10.0 uses: actions/checkout@v2
- name: Set up JDK 11
uses: DeLaGuardo/setup-graalvm@4.0
with: with:
java-version: 11 graalvm: 21.1.0
distribution: liberica 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 - name: Cache konan
uses: actions/cache@v3.0.1 uses: actions/cache@v2
with: with:
path: ~/.konan path: ~/.konan
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }} key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
@ -28,23 +42,20 @@ jobs:
${{ runner.os }}-gradle- ${{ runner.os }}-gradle-
- name: Publish Windows Artifacts - name: Publish Windows Artifacts
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
uses: gradle/gradle-build-action@v2.4.2 run: >
with: ./gradlew release --no-daemon
arguments: | -Ppublishing.enabled=true
publishAllPublicationsToSpaceRepository -Ppublishing.github.user=${{ secrets.PUBLISHING_GITHUB_USER }}
-Ppublishing.targets=all -Ppublishing.github.token=${{ secrets.PUBLISHING_GITHUB_TOKEN }}
-Ppublishing.space.user=${{ secrets.SPACE_APP_ID }} -Ppublishing.space.user=${{ secrets.PUBLISHING_SPACE_USER }}
-Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }} -Ppublishing.space.token=${{ secrets.PUBLISHING_SPACE_TOKEN }}
- name: Publish Mac Artifacts - name: Publish Mac Artifacts
if: matrix.os == 'macOS-latest' if: matrix.os == 'macOS-latest'
uses: gradle/gradle-build-action@v2.4.2 run: >
with: ./gradlew release --no-daemon
arguments: | -Ppublishing.enabled=true
publishMacosX64PublicationToSpaceRepository -Ppublishing.platform=macosX64
publishMacosArm64PublicationToSpaceRepository -Ppublishing.github.user=${{ secrets.PUBLISHING_GITHUB_USER }}
publishIosX64PublicationToSpaceRepository -Ppublishing.github.token=${{ secrets.PUBLISHING_GITHUB_TOKEN }}
publishIosArm64PublicationToSpaceRepository -Ppublishing.space.user=${{ secrets.PUBLISHING_SPACE_USER }}
publishIosSimulatorArm64PublicationToSpaceRepository -Ppublishing.space.token=${{ secrets.PUBLISHING_SPACE_TOKEN }}
-Ppublishing.targets=all
-Ppublishing.space.user=${{ secrets.SPACE_APP_ID }}
-Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }}

12
.gitignore vendored
View File

@ -3,10 +3,11 @@ build/
out/ out/
.idea/ .idea/
.vscode/
.fleet/
.kotlin/
!.idea/copyright/
!.idea/scopes/
.vscode/
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar !gradle-wrapper.jar
@ -17,8 +18,3 @@ out/
# Generated by javac -h and runtime # Generated by javac -h and runtime
*.class *.class
*.log *.log
!/.idea/copyright/
!/.idea/scopes/
/gradle/yarn.lock

View File

@ -1,7 +1,6 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<copyright> <copyright>
<option name="allowReplaceRegexp" value="Copyright \d{4}-\d{4} KMath" /> <option name="notice" value="Copyright 2018-2021 KMath contributors.&#10;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="notice" value="Copyright 2018-&amp;#36;today.year KMath contributors.&#10;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" />
<option name="myName" value="kmath" /> </copyright>
</copyright>
</component> </component>

View File

@ -1,5 +1,5 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<settings> <settings default="kmath">
<module2copyright> <module2copyright>
<element module="Apply copyright" copyright="kmath" /> <element module="Apply copyright" copyright="kmath" />
</module2copyright> </module2copyright>

View File

@ -1,3 +1,4 @@
<component name="DependencyValidationManager"> <component name="DependencyValidationManager">
<scope name="Apply copyright" pattern="!file[*]:*//testData//*&amp;&amp;!file[*]:testData//*&amp;&amp;!file[*]:*.gradle.kts&amp;&amp;!file[*]:*.gradle&amp;&amp;!file[group:kotlin-ultimate]:*/&amp;&amp;!file[kotlin.libraries]:stdlib/api//*" /> <scope name="Apply copyright"
pattern="!file[*]:*//testData//*&amp;&amp;!file[*]:testData//*&amp;&amp;!file[*]:*.gradle.kts&amp;&amp;!file[*]:*.gradle&amp;&amp;!file[group:kotlin-ultimate]:*/&amp;&amp;!file[kotlin.libraries]:stdlib/api//*"/>
</component> </component>

View File

@ -1,48 +1,3 @@
import kotlin.io.path.readText
val projectName = "kmath"
job("Build") { job("Build") {
//Perform only jvm tests gradlew("openjdk:11", "build")
gradlew("spc.registry.jetbrains.space/p/sci/containers/kotlin-ci:1.0.3", "test", "jvmTest")
}
job("Publish") {
startOn {
gitPush { enabled = false }
}
container("spc.registry.jetbrains.space/p/sci/containers/kotlin-ci:1.0.3") {
env["SPACE_USER"] = "{{ project:space_user }}"
env["SPACE_TOKEN"] = "{{ project:space_token }}"
kotlinScript { api ->
val spaceUser = System.getenv("SPACE_USER")
val spaceToken = System.getenv("SPACE_TOKEN")
// write the version to the build directory
api.gradlew("version")
//read the version from build file
val version = java.nio.file.Path.of("build/project-version.txt").readText()
val revisionSuffix = if (version.endsWith("SNAPSHOT")) {
"-" + api.gitRevision().take(7)
} else {
""
}
api.space().projects.automation.deployments.start(
project = api.projectIdentifier(),
targetIdentifier = TargetIdentifier.Key(projectName),
version = version + revisionSuffix,
// automatically update deployment status based on the status of a job
syncWithAutomationJob = true
)
api.gradlew(
"publishAllPublicationsToSpaceRepository",
"-Ppublishing.space.user=\"$spaceUser\"",
"-Ppublishing.space.token=\"$spaceToken\"",
)
}
}
} }

View File

@ -1,96 +1,7 @@
# KMath # KMath
## Unreleased ## [Unreleased]
### Added ### Added
- Metropolis-Hastings sampler
### Changed
### Deprecated
### Removed
### Fixed
### Security
## 0.4.0-dev-3 - 2024-02-18
### Added
- Reification. Explicit `SafeType` for algebras.
- Integer division algebras.
- Float32 geometries.
- New Attributes-kt module that could be used as stand-alone. It declares. type-safe attributes containers.
- Explicit `mutableStructureND` builders for mutable structures.
- `Buffer.asList()` zero-copy transformation.
- Wasm support.
- Parallel implementation of `LinearSpace` for Float64
- Parallel buffer factories
### Changed
- Buffer copy removed from API (added as an extension).
- Default naming for algebra and buffers now uses IntXX/FloatXX notation instead of Java types.
- Remove unnecessary inlines in basic algebras.
- QuaternionField -> QuaternionAlgebra and does not implement `Field` anymore since it is non-commutative
- kmath-geometry is split into `euclidean2d` and `euclidean3d`
- Features replaced with Attributes.
- Transposed refactored.
- Kmath-memory is moved on top of core.
### Deprecated
- ND4J engine
### Removed
- `asPolynomial` function due to scope pollution
- Codegend for ejml (450 lines of codegen for 1000 lines of code is too much)
### Fixed
- Median statistics
- Complex power of negative real numbers
- Add proper mutability for MutableBufferND rows and columns
- Generic Float32 and Float64 vectors are used in geometry algebras.
## 0.3.1 - 2023-04-09
### Added
- Wasm support for `memory`, `core`, `complex` and `functions` modules.
- Generic builders for `BufferND` and `MutableBufferND`
- `NamedMatrix` - matrix with symbol-based indexing
- `Expression` with default arguments
- Type-aliases for numbers like `Float64`
- Autodiff for generic algebra elements in core!
- Algebra now has an obligatory `bufferFactory` (#477).
### Changed
- Removed marker `Vector` type for geometry
- Geometry uses type-safe angles
- Tensor operations switched to prefix notation
- Row-wise and column-wise ND shapes in the core
- Shape is read-only
- Major refactor of tensors (only minor API changes)
- Kotlin 1.8.20
- `LazyStructure` `deffered` -> `async` to comply with coroutines code style
- Default `dot` operation in tensor algebra no longer support broadcasting. Instead `matmul` operation is added
to `DoubleTensorAlgebra`.
- Multik went MPP
### Removed
- Trajectory moved to https://github.com/SciProgCentre/maps-kt
- Polynomials moved to https://github.com/SciProgCentre/kmath-polynomial
## 0.3.0
### Added
- `ScaleOperations` interface - `ScaleOperations` interface
- `Field` extends `ScaleOperations` - `Field` extends `ScaleOperations`
- Basic integration API - Basic integration API
@ -102,21 +13,10 @@
- Extended operations for ND4J fields - Extended operations for ND4J fields
- Jupyter Notebook integration module (kmath-jupyter) - Jupyter Notebook integration module (kmath-jupyter)
- `@PerformancePitfall` annotation to mark possibly slow API - `@PerformancePitfall` annotation to mark possibly slow API
- Unified architecture for Integration and Optimization using features.
- `BigInt` operation performance improvement and fixes by @zhelenskiy (#328) - `BigInt` operation performance improvement and fixes by @zhelenskiy (#328)
- Integration between `MST` and Symja `IExpr` - Integration between `MST` and Symja `IExpr`
- Complex power
- Separate methods for UInt, Int and Number powers. NaN safety.
- Tensorflow prototype
- `ValueAndErrorField`
- MST compilation to WASM: #286
- Jafama integration: #176
- `contentEquals` with tolerance: #364
- Compilation to TeX for MST: #254
### Changed ### Changed
- Annotations moved to `space.kscience.kmath`
- Exponential operations merged with hyperbolic functions - Exponential operations merged with hyperbolic functions
- Space is replaced by Group. Space is reserved for vector spaces. - Space is replaced by Group. Space is reserved for vector spaces.
- VectorSpace is now a vector space - VectorSpace is now a vector space
@ -136,24 +36,10 @@
- Remove Any restriction on polynomials - Remove Any restriction on polynomials
- Add `out` variance to type parameters of `StructureND` and its implementations where possible - Add `out` variance to type parameters of `StructureND` and its implementations where possible
- Rename `DifferentiableMstExpression` to `KotlingradExpression` - Rename `DifferentiableMstExpression` to `KotlingradExpression`
- `FeatureSet` now accepts only `Feature`. It is possible to override keys and use interfaces.
- Use `Symbol` factory function instead of `StringSymbol`
- New discoverability pattern: `<Type>.algebra.<nd/etc>`
- Adjusted commons-math API for linear solvers to match conventions.
- Buffer algebra does not require size anymore
- Operations -> Ops
- Default Buffer and ND algebras are now Ops and lack neutral elements (0, 1) as well as algebra-level shapes.
- Tensor algebra takes read-only structures as input and inherits AlgebraND
- `UnivariateDistribution` renamed to `Distribution1D`
- Rework of histograms.
- `UnivariateFunction` -> `Function1D`, `MultivariateFunction` -> `FunctionND`
### Deprecated ### Deprecated
- Specialized `DoubleBufferAlgebra`
### Removed ### Removed
- Nearest in Domain. To be implemented in geometry package. - Nearest in Domain. To be implemented in geometry package.
- Number multiplication and division in main Algebra chain - Number multiplication and division in main Algebra chain
- `contentEquals` from Buffer. It moved to the companion. - `contentEquals` from Buffer. It moved to the companion.
@ -161,17 +47,15 @@
- Expression algebra builders - Expression algebra builders
- Complex and Quaternion no longer are elements. - Complex and Quaternion no longer are elements.
- Second generic from DifferentiableExpression - Second generic from DifferentiableExpression
- Algebra elements are completely removed. Use algebra contexts instead.
### Fixed ### Fixed
- Ring inherits RingOperations, not GroupOperations - Ring inherits RingOperations, not GroupOperations
- Univariate histogram filling - Univariate histogram filling
## 0.2.0 ### Security
## [0.2.0]
### Added ### Added
- `fun` annotation for SAM interfaces in library - `fun` annotation for SAM interfaces in library
- Explicit `public` visibility for all public APIs - Explicit `public` visibility for all public APIs
- Better trigonometric and hyperbolic functions for `AutoDiffField` (https://github.com/mipt-npm/kmath/pull/140) - Better trigonometric and hyperbolic functions for `AutoDiffField` (https://github.com/mipt-npm/kmath/pull/140)
@ -191,7 +75,6 @@
- Basic Quaternion vector support in `kmath-complex`. - Basic Quaternion vector support in `kmath-complex`.
### Changed ### Changed
- Package changed from `scientifik` to `space.kscience` - Package changed from `scientifik` to `space.kscience`
- Gradle version: 6.6 -> 6.8.2 - Gradle version: 6.6 -> 6.8.2
- Minor exceptions refactor (throwing `IllegalArgumentException` by argument checks instead of `IllegalStateException`) - Minor exceptions refactor (throwing `IllegalArgumentException` by argument checks instead of `IllegalStateException`)
@ -201,7 +84,7 @@
- Full autodiff refactoring based on `Symbol` - Full autodiff refactoring based on `Symbol`
- `kmath-prob` renamed to `kmath-stat` - `kmath-prob` renamed to `kmath-stat`
- Grid generators moved to `kmath-for-real` - Grid generators moved to `kmath-for-real`
- Use `Point<Float64>` instead of specialized type in `kmath-for-real` - Use `Point<Double>` instead of specialized type in `kmath-for-real`
- Optimized dot product for buffer matrices moved to `kmath-for-real` - Optimized dot product for buffer matrices moved to `kmath-for-real`
- EjmlMatrix context is an object - EjmlMatrix context is an object
- Matrix LUP `inverse` renamed to `inverseWithLup` - Matrix LUP `inverse` renamed to `inverseWithLup`
@ -215,8 +98,9 @@
- `symbol` method in `Algebra` renamed to `bindSymbol` to avoid ambiguity - `symbol` method in `Algebra` renamed to `bindSymbol` to avoid ambiguity
- Add `out` projection to `Buffer` generic - Add `out` projection to `Buffer` generic
### Removed ### Deprecated
### Removed
- `kmath-koma` module because it doesn't support Kotlin 1.4. - `kmath-koma` module because it doesn't support Kotlin 1.4.
- Support of `legacy` JS backend (we will support only IR) - Support of `legacy` JS backend (we will support only IR)
- `toGrid` method. - `toGrid` method.
@ -225,24 +109,22 @@
- StructureND identity and equals - StructureND identity and equals
### Fixed ### Fixed
- `symbol` method in `MstExtendedField` (https://github.com/mipt-npm/kmath/pull/140) - `symbol` method in `MstExtendedField` (https://github.com/mipt-npm/kmath/pull/140)
## 0.1.4 ### Security
## [0.1.4]
### Added ### Added
- Functional Expressions API - Functional Expressions API
- Mathematical Syntax Tree, its interpreter and API - Mathematical Syntax Tree, its interpreter and API
- String to MST parser (https://github.com/mipt-npm/kmath/pull/120) - String to MST parser (https://github.com/mipt-npm/kmath/pull/120)
- MST to JVM bytecode translator (https://github.com/mipt-npm/kmath/pull/94) - MST to JVM bytecode translator (https://github.com/mipt-npm/kmath/pull/94)
- FloatBuffer (specialized MutableBuffer over FloatArray) - FloatBuffer (specialized MutableBuffer over FloatArray)
- FlaggedBuffer to associate primitive numbers buffer with flags (to mark values infinite or missing, etc.) - FlaggedBuffer to associate primitive numbers buffer with flags (to mark values infinite or missing, etc.)
- Specialized builder functions for all primitive buffers - Specialized builder functions for all primitive buffers like `IntBuffer(25) { it + 1 }` (https://github.com/mipt-npm/kmath/pull/125)
like `IntBuffer(25) { it + 1 }` (https://github.com/mipt-npm/kmath/pull/125)
- Interface `NumericAlgebra` where `number` operation is available to convert numbers to algebraic elements - Interface `NumericAlgebra` where `number` operation is available to convert numbers to algebraic elements
- Inverse trigonometric functions support in - Inverse trigonometric functions support in ExtendedField (`asin`, `acos`, `atan`) (https://github.com/mipt-npm/kmath/pull/114)
ExtendedField (`asin`, `acos`, `atan`) (https://github.com/mipt-npm/kmath/pull/114)
- New space extensions: `average` and `averageWith` - New space extensions: `average` and `averageWith`
- Local coding conventions - Local coding conventions
- Geometric Domains API in `kmath-core` - Geometric Domains API in `kmath-core`
@ -251,12 +133,10 @@
- Norm support for `Complex` - Norm support for `Complex`
### Changed ### Changed
- `readAsMemory` now has `throws IOException` in JVM signature. - `readAsMemory` now has `throws IOException` in JVM signature.
- Several functions taking functional types were made `inline`. - Several functions taking functional types were made `inline`.
- Several functions taking functional types now have `callsInPlace` contracts. - Several functions taking functional types now have `callsInPlace` contracts.
- BigInteger and BigDecimal algebra: JBigDecimalField has companion object with default math context; minor - BigInteger and BigDecimal algebra: JBigDecimalField has companion object with default math context; minor optimizations
optimizations
- `power(T, Int)` extension function has preconditions and supports `Field<T>` - `power(T, Int)` extension function has preconditions and supports `Field<T>`
- Memory objects have more preconditions (overflow checking) - Memory objects have more preconditions (overflow checking)
- `tg` function is renamed to `tan` (https://github.com/mipt-npm/kmath/pull/114) - `tg` function is renamed to `tan` (https://github.com/mipt-npm/kmath/pull/114)
@ -264,7 +144,6 @@
- Moved probability distributions to commons-rng and to `kmath-prob` - Moved probability distributions to commons-rng and to `kmath-prob`
### Fixed ### Fixed
- Missing copy method in Memory implementation on JS (https://github.com/mipt-npm/kmath/pull/106) - Missing copy method in Memory implementation on JS (https://github.com/mipt-npm/kmath/pull/106)
- D3.dim value in `kmath-dimensions` - D3.dim value in `kmath-dimensions`
- Multiplication in integer rings in `kmath-core` (https://github.com/mipt-npm/kmath/pull/101) - Multiplication in integer rings in `kmath-core` (https://github.com/mipt-npm/kmath/pull/101)

226
README.md
View File

@ -1,76 +1,95 @@
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![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) [![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382)
![Gradle build](https://github.com/SciProgCentre/kmath/workflows/Gradle%20build/badge.svg) ![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg)
[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22) [![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22)
[![Space](https://img.shields.io/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/) [![Space](https://img.shields.io/badge/dynamic/xml?color=orange&label=Space&query=//metadata/versioning/latest&url=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fspace%2Fkscience%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/)
# KMath # KMath
Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based analog to
analog to Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior architecture
architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could
experience could be achieved with [kmath-for-real](/kmath-for-real) extension module. be achieved with [kmath-for-real](/kmath-for-real) extension module.
[Documentation site](https://SciProgCentre.github.io/kmath/) [Documentation site (**WIP**)](https://mipt-npm.github.io/kmath/)
## Publications and talks ## Publications and talks
* [A conceptual article about context-oriented design](https://proandroiddev.com/an-introduction-context-oriented-programming-in-kotlin-2e79d316b0a2) * [A conceptual article about context-oriented design](https://proandroiddev.com/an-introduction-context-oriented-programming-in-kotlin-2e79d316b0a2)
* [Another article about context-oriented design](https://proandroiddev.com/diving-deeper-into-context-oriented-programming-in-kotlin-3ecb4ec38814) * [Another article about context-oriented design](https://proandroiddev.com/diving-deeper-into-context-oriented-programming-in-kotlin-3ecb4ec38814)
* [ACAT 2019 conference paper](https://aip.scitation.org/doi/abs/10.1063/1.5130103) * [ACAT 2019 conference paper](https://aip.scitation.org/doi/abs/10.1063/1.5130103)
* [A talk at KotlinConf 2019 about using kotlin for science](https://youtu.be/LI_5TZ7tnOE?si=4LknX41gl_YeUbIe)
* [A talk on architecture at Joker-2021 (in Russian)](https://youtu.be/1bZ2doHiRRM?si=9w953ro9yu98X_KJ)
* [The same talk in English](https://youtu.be/yP5DIc2fVwQ?si=louZzQ1dcXV6gP10)
* [A seminar on tensor API](https://youtu.be/0H99wUs0xTM?si=6c__04jrByFQtVpo)
# Goal # Goal
* Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS, Native and * Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS and Native).
Wasm).
* Provide basic multiplatform implementations for those abstractions (without significant performance optimization). * Provide basic multiplatform implementations for those abstractions (without significant performance optimization).
* Provide bindings and wrappers with those abstractions for popular optimized platform libraries. * Provide bindings and wrappers with those abstractions for popular optimized platform libraries.
## Non-goals ## Non-goals
* Be like NumPy. It was the idea at the beginning, but we decided that we can do better in API. * Be like NumPy. It was the idea at the beginning, but we decided that we can do better in terms of API.
* Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them. * Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them.
* Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually. * Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually.
* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like * Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like
for `Double` in the core. For that we will have specialization modules like `kmath-for-real`, which will give better for `Double` in the core. For that we will have specialization modules like `kmath-for-real`, which will give better
experience for those, who want to work with specific types. experience for those, who want to work with specific types.
## Features and stability ## Features and stability
KMath is a modular library. Different modules provide different features with different API stability guarantees. All KMath is a modular library. Different modules provide different features with different API stability guarantees. All core modules are released with the same version, but with different API change policy. The features are described in module definitions below. The module stability could have following levels:
core modules are released with the same version, but with different API change policy. The features are described in
module definitions below. The module stability could have the following levels:
* **PROTOTYPE**. On this level there are no compatibility guarantees. All methods and classes form those modules could * **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.
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.
* **EXPERIMENTAL**. The general API is decided, but some changes could be made. Volatile API is marked * **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.
with `@UnstableKMathAPI` or other stability warning annotations.
* **DEVELOPMENT**. API breaking generally follows semantic versioning ideology. There could be changes in minor
versions, but not in patch versions. API is protected
with [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator) tool.
* **STABLE**. The API stabilized. Breaking changes are allowed only in major releases. * **STABLE**. The API stabilized. Breaking changes are allowed only in major releases.
<!--Current feature list is [here](/docs/features.md)-->
<!--* **Array-like structures** Full support of many-dimensional array-like structures -->
<!--including mixed arithmetic operations and function operations over arrays and numbers (with the added benefit of static type checking).-->
<!--* **Histograms** Fast multi-dimensional histograms.-->
<!--* **Streaming** Streaming operations on mathematical objects and objects buffers.-->
<!--* **Type-safe dimensions** Type-safe dimensions for matrix operations.-->
<!--* **Commons-math wrapper** It is planned to gradually wrap most parts of -->
<!--[Apache commons-math](http://commons.apache.org/proper/commons-math/) library in Kotlin code and maybe rewrite some -->
<!--parts to better suit the Kotlin programming paradigm, however there is no established roadmap for that. Feel free to -->
<!--submit a feature request if you want something to be implemented first.-->
<!-- -->
<!--## Planned features-->
<!--* **Messaging** A mathematical notation to support multi-language and multi-node communication for mathematical tasks.-->
<!--* **Array statistics** -->
<!--* **Integration** Univariate and multivariate integration framework.-->
<!--* **Probability and distributions**-->
<!--* **Fitting** Non-linear curve fitting facilities-->
## Modules ## Modules
<hr/>
### [attributes-kt](attributes-kt) * ### [benchmarks](benchmarks)
> An API and basic implementation for arranging objects in a continuous memory block.
> >
> **Maturity**: DEVELOPMENT
### [benchmarks](benchmarks)
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
<hr/>
### [examples](examples) * ### [examples](examples)
>
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
<hr/>
### [kmath-ast](kmath-ast) * ### [kmath-ast](kmath-ast)
>
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
> >
@ -80,23 +99,26 @@ module definitions below. The module stability could have the following levels:
> - [mst-js-codegen](kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler > - [mst-js-codegen](kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
> - [rendering](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt) : Extendable MST rendering > - [rendering](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt) : Extendable MST rendering
<hr/>
### [kmath-commons](kmath-commons) * ### [kmath-commons](kmath-commons)
> Commons math binding for kmath >
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
<hr/>
### [kmath-complex](kmath-complex) * ### [kmath-complex](kmath-complex)
> Complex numbers and quaternions. > Complex numbers and quaternions.
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
> >
> **Features:** > **Features:**
> - [complex](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt) : Complex numbers operations > - [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 and their composition > - [quaternion](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt) : Quaternions
<hr/>
### [kmath-core](kmath-core) * ### [kmath-core](kmath-core)
> Core classes, algebra definitions, basic linear algebra > Core classes, algebra definitions, basic linear algebra
> >
> **Maturity**: DEVELOPMENT > **Maturity**: DEVELOPMENT
@ -110,20 +132,24 @@ module definitions below. The module stability could have the following levels:
objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high
performance calculations to code generation. performance calculations to code generation.
> - [domains](kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains) : Domains > - [domains](kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains) : Domains
> - [autodiff](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation > - [autodif](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation
> - [Parallel linear algebra](kmath-core/#) : Parallel implementation for `LinearAlgebra`
<hr/>
### [kmath-coroutines](kmath-coroutines) * ### [kmath-coroutines](kmath-coroutines)
>
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
<hr/>
### [kmath-dimensions](kmath-dimensions) * ### [kmath-dimensions](kmath-dimensions)
> A proof of concept module for adding type-safe dimensions to structures >
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
<hr/>
### [kmath-ejml](kmath-ejml) * ### [kmath-ejml](kmath-ejml)
>
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
> >
@ -132,8 +158,9 @@ performance calculations to code generation.
> - [ejml-matrix](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : Matrix implementation. > - [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. > - [ejml-linear-space](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : LinearSpace implementations.
<hr/>
### [kmath-for-real](kmath-for-real) * ### [kmath-for-real](kmath-for-real)
> Extension module that should be used to achieve numpy-like behavior. > Extension module that should be used to achieve numpy-like behavior.
All operations are specialized to work with `Double` numbers without declaring algebraic contexts. All operations are specialized to work with `Double` numbers without declaring algebraic contexts.
One can still use generic algebras though. One can still use generic algebras though.
@ -145,9 +172,10 @@ One can still use generic algebras though.
> - [DoubleMatrix](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleMatrix.kt) : Numpy-like operations for 2d real structures > - [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 > - [grids](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/structures/grids.kt) : Uniform grid generators
<hr/>
### [kmath-functions](kmath-functions) * ### [kmath-functions](kmath-functions)
> Functions, integration and interpolation >
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
> >
@ -158,95 +186,94 @@ One can still use generic algebras though.
> - [spline interpolation](kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt) : Cubic spline 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 > - [integration](kmath-functions/#) : Univariate and multivariate quadratures
<hr/>
### [kmath-geometry](kmath-geometry) * ### [kmath-geometry](kmath-geometry)
>
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
<hr/>
### [kmath-histograms](kmath-histograms) * ### [kmath-histograms](kmath-histograms)
>
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
<hr/>
### [kmath-jafama](kmath-jafama) * ### [kmath-jafama](kmath-jafama)
> Jafama integration module
> >
> **Maturity**: DEPRECATED >
> **Maturity**: PROTOTYPE
> >
> **Features:** > **Features:**
> - [jafama-double](kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/) : Double ExtendedField implementations based on Jafama > - [jafama-double](kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/) : Double ExtendedField implementations based on Jafama
<hr/>
### [kmath-jupyter](kmath-jupyter) * ### [kmath-jupyter](kmath-jupyter)
>
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
<hr/>
### [kmath-kotlingrad](kmath-kotlingrad) * ### [kmath-kotlingrad](kmath-kotlingrad)
> Kotlin∇ integration module >
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
> >
> **Features:** > **Features:**
> - [differentiable-mst-expression](kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt) : MST based DifferentiableExpression. > - [differentiable-mst-expression](kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt) : MST based DifferentiableExpression.
> - [scalars-adapters](kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/scalarsAdapters.kt) : Conversions between Kotlin∇'s SFun and MST > - [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) * ### [kmath-memory](kmath-memory)
> An API and basic implementation for arranging objects in a continuous memory block. > An API and basic implementation for arranging objects in a continuous memory block.
> >
> **Maturity**: DEVELOPMENT > **Maturity**: DEVELOPMENT
<hr/>
### [kmath-multik](kmath-multik) * ### [kmath-nd4j](kmath-nd4j)
> JetBrains Multik connector
> >
> **Maturity**: PROTOTYPE
### [kmath-nd4j](kmath-nd4j)
> ND4J NDStructure implementation and according NDAlgebra classes
> >
> **Maturity**: DEPRECATED > **Maturity**: EXPERIMENTAL
> >
> **Features:** > **Features:**
> - [nd4jarraystructure](kmath-nd4j/#) : NDStructure wrapper for INDArray > - [nd4jarraystructure](kmath-nd4j/#) : NDStructure wrapper for INDArray
> - [nd4jarrayrings](kmath-nd4j/#) : Rings over Nd4jArrayStructure of Int and Long > - [nd4jarrayrings](kmath-nd4j/#) : Rings over Nd4jArrayStructure of Int and Long
> - [nd4jarrayfields](kmath-nd4j/#) : Fields over Nd4jArrayStructure of Float and Double > - [nd4jarrayfields](kmath-nd4j/#) : Fields over Nd4jArrayStructure of Float and Double
<hr/>
### [kmath-optimization](kmath-optimization) * ### [kmath-stat](kmath-stat)
>
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
<hr/>
### [kmath-stat](kmath-stat) * ### [kmath-symja](kmath-symja)
> >
> **Maturity**: EXPERIMENTAL
### [kmath-symja](kmath-symja)
> Symja integration module
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
<hr/>
### [kmath-tensorflow](kmath-tensorflow) * ### [kmath-tensors](kmath-tensors)
> Google tensorflow connector
> >
> **Maturity**: PROTOTYPE
### [kmath-tensors](kmath-tensors)
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
> >
> **Features:** > **Features:**
> - [tensor algebra](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt) : Basic linear algebra operations on tensors (plus, dot, etc.) > - [tensor algebra](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/TensorAlgebra.kt) : Basic linear algebra operations on tensors (plus, dot, etc.)
> - [tensor algebra with broadcasting](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt) : Basic linear algebra operations implemented with broadcasting. > - [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. > - [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) * ### [kmath-viktor](kmath-viktor)
> Binding for https://github.com/JetBrains-Research/viktor
> >
> **Maturity**: DEPRECATED
### [test-utils](test-utils)
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: DEVELOPMENT
<hr/>
## Multi-platform support ## Multi-platform support
@ -254,31 +281,27 @@ One can still use generic algebras though.
KMath is developed as a multi-platform library, which means that most of the interfaces are declared in the KMath is developed as a multi-platform library, which means that most of the interfaces are declared in the
[common source sets](/kmath-core/src/commonMain) and implemented there wherever it is possible. In some cases, features [common source sets](/kmath-core/src/commonMain) and implemented there wherever it is possible. In some cases, features
are delegated to platform-specific implementations even if they could be provided in the common module for performance are delegated to platform-specific implementations even if they could be provided in the common module for performance
reasons. Currently, Kotlin/JVM is the primary platform, however, Kotlin/Native and Kotlin/JS contributions and reasons. Currently, the Kotlin/JVM is the primary platform, however Kotlin/Native and Kotlin/JS contributions and
feedback are also welcome. feedback are also welcome.
## Performance ## Performance
Calculation of performance is one of the major goals of KMath in the future, but in some cases it is impossible to Calculation performance is one of major goals of KMath in the future, but in some cases it is impossible to achieve
achieve both both performance and flexibility.
performance and flexibility.
We expect to focus on creating a convenient universal API first and then work on increasing performance for specific We expect to focus on creating convenient universal API first and then work on increasing performance for specific
cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized
native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be
better than SciPy. better than SciPy.
## Requirements ## Requirements
KMath currently relies on JDK 11 for compilation and execution of Kotlin-JVM part. We recommend using GraalVM-CE or 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.
Oracle GraalVM for execution to get better performance.
### Repositories ### Repositories
Release and development artifacts are accessible from mipt-npm [Space](https://www.jetbrains.com/space/) 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
repository `https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven` (see documentation of [Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details). The repository could be reached through [repo.kotlin.link](https://repo.kotlin.link) proxy:
[Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details). The repository could
be reached through [repo.kotlin.link](https://repo.kotlin.link) proxy:
```kotlin ```kotlin
repositories { repositories {
@ -286,15 +309,16 @@ repositories {
} }
dependencies { dependencies {
api("space.kscience:kmath-core:$version") api("space.kscience:kmath-core:0.3.0-dev-14")
// api("space.kscience:kmath-core-jvm:$version") for jvm-specific version // api("space.kscience:kmath-core-jvm:0.3.0-dev-14") for jvm-specific version
} }
``` ```
Gradle `6.0+` is required for multiplatform artifacts.
## Contributing ## Contributing
The project requires a lot of additional work. The most important thing we need is feedback about what features are The project requires a lot of additional work. The most important thing we need is a feedback about what features are
required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues required the most. Feel free to create feature requests. We are also welcome to code contributions,
marked especially in issues marked with
with [good first issue](hhttps://github.com/SciProgCentre/kmath/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) [waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero) label.
label.

View File

@ -1,21 +0,0 @@
# Module attributes-kt
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:attributes-kt:0.1.0`.
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:attributes-kt:0.1.0")
}
```

View File

@ -1,104 +0,0 @@
public abstract interface class space/kscience/attributes/Attribute {
}
public abstract interface class space/kscience/attributes/AttributeContainer {
public abstract fun getAttributes ()Lspace/kscience/attributes/Attributes;
}
public abstract interface class space/kscience/attributes/AttributeScope {
}
public abstract interface class space/kscience/attributes/AttributeWithDefault : space/kscience/attributes/Attribute {
public abstract fun getDefault ()Ljava/lang/Object;
}
public abstract interface class space/kscience/attributes/Attributes {
public static final field Companion Lspace/kscience/attributes/Attributes$Companion;
public abstract fun equals (Ljava/lang/Object;)Z
public fun get (Lspace/kscience/attributes/Attribute;)Ljava/lang/Object;
public abstract fun getContent ()Ljava/util/Map;
public fun getKeys ()Ljava/util/Set;
public abstract fun hashCode ()I
public abstract fun toString ()Ljava/lang/String;
}
public final class space/kscience/attributes/Attributes$Companion {
public final fun equals (Lspace/kscience/attributes/Attributes;Lspace/kscience/attributes/Attributes;)Z
public final fun getEMPTY ()Lspace/kscience/attributes/Attributes;
}
public final class space/kscience/attributes/AttributesBuilder : space/kscience/attributes/Attributes {
public final fun add (Lspace/kscience/attributes/SetAttribute;Ljava/lang/Object;)V
public final fun build ()Lspace/kscience/attributes/Attributes;
public fun equals (Ljava/lang/Object;)Z
public fun getContent ()Ljava/util/Map;
public fun hashCode ()I
public final fun invoke (Lspace/kscience/attributes/Attribute;Ljava/lang/Object;)V
public final fun put (Lspace/kscience/attributes/Attribute;Ljava/lang/Object;)V
public final fun putAll (Lspace/kscience/attributes/Attributes;)V
public final fun remove (Lspace/kscience/attributes/SetAttribute;Ljava/lang/Object;)V
public final fun set (Lspace/kscience/attributes/Attribute;Ljava/lang/Object;)V
public fun toString ()Ljava/lang/String;
}
public final class space/kscience/attributes/AttributesBuilderKt {
public static final fun Attributes (Lkotlin/jvm/functions/Function1;)Lspace/kscience/attributes/Attributes;
}
public final class space/kscience/attributes/AttributesKt {
public static final fun Attributes (Lspace/kscience/attributes/Attribute;)Lspace/kscience/attributes/Attributes;
public static final fun Attributes (Lspace/kscience/attributes/Attribute;Ljava/lang/Object;)Lspace/kscience/attributes/Attributes;
public static final fun getOrDefault (Lspace/kscience/attributes/Attributes;Lspace/kscience/attributes/AttributeWithDefault;)Ljava/lang/Object;
public static final fun isEmpty (Lspace/kscience/attributes/Attributes;)Z
public static final fun modified (Lspace/kscience/attributes/Attributes;Lkotlin/jvm/functions/Function1;)Lspace/kscience/attributes/Attributes;
public static final fun plus (Lspace/kscience/attributes/Attributes;Lspace/kscience/attributes/Attributes;)Lspace/kscience/attributes/Attributes;
public static final fun withAttribute (Lspace/kscience/attributes/Attributes;Lspace/kscience/attributes/Attribute;)Lspace/kscience/attributes/Attributes;
public static final fun withAttribute (Lspace/kscience/attributes/Attributes;Lspace/kscience/attributes/Attribute;Ljava/lang/Object;)Lspace/kscience/attributes/Attributes;
public static final fun withAttributeElement (Lspace/kscience/attributes/Attributes;Lspace/kscience/attributes/SetAttribute;Ljava/lang/Object;)Lspace/kscience/attributes/Attributes;
public static final fun withoutAttribute (Lspace/kscience/attributes/Attributes;Lspace/kscience/attributes/Attribute;)Lspace/kscience/attributes/Attributes;
public static final fun withoutAttributeElement (Lspace/kscience/attributes/Attributes;Lspace/kscience/attributes/SetAttribute;Ljava/lang/Object;)Lspace/kscience/attributes/Attributes;
}
public abstract interface class space/kscience/attributes/FlagAttribute : space/kscience/attributes/Attribute {
}
public abstract class space/kscience/attributes/PolymorphicAttribute : space/kscience/attributes/Attribute {
public synthetic fun <init> (Lkotlin/reflect/KType;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getType-V0oMfBY ()Lkotlin/reflect/KType;
public fun hashCode ()I
}
public final class space/kscience/attributes/PolymorphicAttributeKt {
public static final fun get (Lspace/kscience/attributes/Attributes;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
public static final fun set (Lspace/kscience/attributes/AttributesBuilder;Lkotlin/jvm/functions/Function0;Ljava/lang/Object;)V
}
public final class space/kscience/attributes/SafeType {
public static final synthetic fun box-impl (Lkotlin/reflect/KType;)Lspace/kscience/attributes/SafeType;
public static fun constructor-impl (Lkotlin/reflect/KType;)Lkotlin/reflect/KType;
public fun equals (Ljava/lang/Object;)Z
public static fun equals-impl (Lkotlin/reflect/KType;Ljava/lang/Object;)Z
public static final fun equals-impl0 (Lkotlin/reflect/KType;Lkotlin/reflect/KType;)Z
public final fun getKType ()Lkotlin/reflect/KType;
public fun hashCode ()I
public static fun hashCode-impl (Lkotlin/reflect/KType;)I
public fun toString ()Ljava/lang/String;
public static fun toString-impl (Lkotlin/reflect/KType;)Ljava/lang/String;
public final synthetic fun unbox-impl ()Lkotlin/reflect/KType;
}
public final class space/kscience/attributes/SafeTypeKt {
public static final fun getKClass-X0YbwmU (Lkotlin/reflect/KType;)Lkotlin/reflect/KClass;
}
public abstract interface class space/kscience/attributes/SetAttribute : space/kscience/attributes/Attribute {
}
public abstract interface annotation class space/kscience/attributes/UnstableAttributesAPI : java/lang/annotation/Annotation {
}
public abstract interface class space/kscience/attributes/WithType {
public abstract fun getType-V0oMfBY ()Lkotlin/reflect/KType;
}

View File

@ -1,20 +0,0 @@
plugins {
id("space.kscience.gradle.mpp")
`maven-publish`
}
version = rootProject.extra.get("attributesVersion").toString()
kscience {
jvm()
js()
native()
wasm()
}
readme {
maturity = space.kscience.gradle.Maturity.DEVELOPMENT
description = """
An API and basic implementation for arranging objects in a continuous memory block.
""".trimIndent()
}

View File

@ -1,29 +0,0 @@
/*
* Copyright 2018-2023 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.attributes
/**
* A marker interface for an attribute. Attributes are used as keys to access contents of type [T] in the container.
*/
public interface Attribute<T>
/**
* An attribute that could be either present or absent
*/
public interface FlagAttribute : Attribute<Unit>
/**
* An attribute with a default value
*/
public interface AttributeWithDefault<T> : Attribute<T> {
public val default: T
}
/**
* Attribute containing a set of values
*/
public interface SetAttribute<V> : Attribute<Set<V>>

View File

@ -1,20 +0,0 @@
/*
* Copyright 2018-2023 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.attributes
/**
* A container for [Attributes]
*/
public interface AttributeContainer {
public val attributes: Attributes
}
/**
* A scope, where attribute keys could be resolved.
* [O] is used only to resolve types in compile-time.
*/
public interface AttributeScope<O>

View File

@ -1,157 +0,0 @@
/*
* Copyright 2018-2023 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.attributes
/**
* A set of attributes. The implementation must guarantee that [content] keys correspond to their value types.
*/
public interface Attributes {
/**
* Raw content for this [Attributes]
*/
public val content: Map<out Attribute<*>, Any?>
/**
* Attribute keys contained in this [Attributes]
*/
public val keys: Set<Attribute<*>> get() = content.keys
/**
* Provide an attribute value. Return null if attribute is not present or if its value is null.
*/
@Suppress("UNCHECKED_CAST")
public operator fun <T> get(attribute: Attribute<T>): T? = content[attribute] as? T
override fun toString(): String
override fun equals(other: Any?): Boolean
override fun hashCode(): Int
public companion object {
public val EMPTY: Attributes = object : Attributes {
override val content: Map<out Attribute<*>, Any?> get() = emptyMap()
override fun toString(): String = "Attributes.EMPTY"
override fun equals(other: Any?): Boolean = (other as? Attributes)?.isEmpty() ?: false
override fun hashCode(): Int = Unit.hashCode()
}
public fun equals(a1: Attributes, a2: Attributes): Boolean =
a1.keys == a2.keys && a1.keys.all { a1[it] == a2[it] }
}
}
internal class MapAttributes(override val content: Map<out Attribute<*>, Any?>) : Attributes {
override fun toString(): String = "Attributes(value=${content.entries})"
override fun equals(other: Any?): Boolean = other is Attributes && Attributes.equals(this, other)
override fun hashCode(): Int = content.hashCode()
}
public fun Attributes.isEmpty(): Boolean = keys.isEmpty()
/**
* Get attribute value or default
*/
public fun <T> Attributes.getOrDefault(attribute: AttributeWithDefault<T>): T = get(attribute) ?: attribute.default
/**
* Check if there is an attribute that matches given key by type and adheres to [predicate].
*/
@Suppress("UNCHECKED_CAST")
public inline fun <T, reified A : Attribute<T>> Attributes.hasAny(predicate: (value: T) -> Boolean): Boolean =
content.any { (mapKey, mapValue) -> mapKey is A && predicate(mapValue as T) }
/**
* Check if there is an attribute of given type (subtypes included)
*/
public inline fun <reified A : Attribute<*>> Attributes.hasAny(): Boolean =
content.any { (mapKey, _) -> mapKey is A }
/**
* Check if [Attributes] contains a flag. Multiple keys that are instances of a flag could be present
*/
public inline fun <reified A : FlagAttribute> Attributes.hasFlag(): Boolean =
content.keys.any { it is A }
/**
* Create [Attributes] with an added or replaced attribute key.
*/
public fun <T, A : Attribute<T>> Attributes.withAttribute(
attribute: A,
attrValue: T,
): Attributes = MapAttributes(content + (attribute to attrValue))
public fun <A : Attribute<Unit>> Attributes.withAttribute(attribute: A): Attributes =
withAttribute(attribute, Unit)
/**
* Create a new [Attributes] by modifying the current one
*/
public fun <O> Attributes.modified(block: AttributesBuilder<O>.() -> Unit): Attributes = Attributes<O> {
putAll(this@modified)
block()
}
/**
* Create new [Attributes] by removing [attribute] key
*/
public fun Attributes.withoutAttribute(attribute: Attribute<*>): Attributes = MapAttributes(content.minus(attribute))
/**
* Add an element to a [SetAttribute]
*/
public fun <T, A : SetAttribute<T>> Attributes.withAttributeElement(
attribute: A,
attrValue: T,
): Attributes {
val currentSet: Set<T> = get(attribute) ?: emptySet()
return MapAttributes(
content + (attribute to (currentSet + attrValue))
)
}
/**
* Remove an element from [SetAttribute]
*/
public fun <T, A : SetAttribute<T>> Attributes.withoutAttributeElement(
attribute: A,
attrValue: T,
): Attributes {
val currentSet: Set<T> = get(attribute) ?: emptySet()
return MapAttributes(content + (attribute to (currentSet - attrValue)))
}
/**
* Create [Attributes] with a single key
*/
public fun <T, A : Attribute<T>> Attributes(
attribute: A,
attrValue: T,
): Attributes = MapAttributes(mapOf(attribute to attrValue))
/**
* Create Attributes with a single [Unit] valued attribute
*/
public fun <A : Attribute<Unit>> Attributes(
attribute: A,
): Attributes = MapAttributes(mapOf(attribute to Unit))
/**
* Create a new [Attributes] that overlays [other] on top of this set of attributes. New attributes are added.
* Existing attribute keys are replaced.
*/
public operator fun Attributes.plus(other: Attributes): Attributes = when {
isEmpty() -> other
other.isEmpty() -> this
else -> MapAttributes(content + other.content)
}
/**
* Create a new [Attributes] with removed [key] (if it is present).
*/
public operator fun Attributes.minus(key: Attribute<*>): Attributes =
if (content.contains(key)) MapAttributes(content.minus(key)) else this

View File

@ -1,68 +0,0 @@
/*
* Copyright 2018-2023 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.attributes
/**
* A builder for [Attributes].
* The builder is not thread safe
*
* @param O type marker of an owner object, for which these attributes are made
*/
public class AttributesBuilder<out O> internal constructor() : Attributes {
private val map = mutableMapOf<Attribute<*>, Any?>()
override fun toString(): String = "Attributes(value=${map.entries})"
override fun equals(other: Any?): Boolean = other is Attributes && Attributes.equals(this, other)
override fun hashCode(): Int = map.hashCode()
override val content: Map<out Attribute<*>, Any?> get() = map
public operator fun <T> set(attribute: Attribute<T>, value: T?) {
if (value == null) {
map.remove(attribute)
} else {
map[attribute] = value
}
}
public operator fun <V> Attribute<V>.invoke(value: V?) {
set(this, value)
}
public infix fun <V> Attribute<V>.put(value: V?) {
set(this, value)
}
/**
* Put all attributes for given [attributes]
*/
public fun putAll(attributes: Attributes) {
map.putAll(attributes.content)
}
public infix fun <V> SetAttribute<V>.add(attrValue: V) {
val currentSet: Set<V> = get(this) ?: emptySet()
map[this] = currentSet + attrValue
}
/**
* Remove an element from [SetAttribute]
*/
public infix fun <V> SetAttribute<V>.remove(attrValue: V) {
val currentSet: Set<V> = get(this) ?: emptySet()
map[this] = currentSet - attrValue
}
public fun build(): Attributes = MapAttributes(map)
}
/**
* Create [Attributes] with a given [builder]
* @param O the type for which attributes are built. The type is used only during compilation phase for static extension dispatch
*/
public fun <O> Attributes(builder: AttributesBuilder<O>.() -> Unit): Attributes =
AttributesBuilder<O>().apply(builder).build()

View File

@ -1,34 +0,0 @@
/*
* Copyright 2018-2023 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.attributes
/**
* An attribute that has a type parameter for value
* @param type parameter-type
*/
public abstract class PolymorphicAttribute<T>(public val type: SafeType<T>) : Attribute<T> {
override fun equals(other: Any?): Boolean = other != null &&
(this::class == other::class) &&
(other as? PolymorphicAttribute<*>)?.type == this.type
override fun hashCode(): Int = this::class.hashCode() + type.hashCode()
}
/**
* Get a polymorphic attribute using attribute factory
*/
@UnstableAttributesAPI
public operator fun <T> Attributes.get(attributeKeyBuilder: () -> PolymorphicAttribute<T>): T? =
get(attributeKeyBuilder())
/**
* Set a polymorphic attribute using its factory
*/
@UnstableAttributesAPI
public operator fun <O, T> AttributesBuilder<O>.set(attributeKeyBuilder: () -> PolymorphicAttribute<T>, value: T) {
set(attributeKeyBuilder(), value)
}

View File

@ -1,35 +0,0 @@
/*
* Copyright 2018-2023 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.attributes
import kotlin.jvm.JvmInline
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.typeOf
/**
* Safe variant ok Kotlin [KType] that ensures that the type parameter is of the same type as [kType]
*
* @param kType raw [KType]
*/
@JvmInline
public value class SafeType<out T> @PublishedApi internal constructor(public val kType: KType)
public inline fun <reified T> safeTypeOf(): SafeType<T> = SafeType(typeOf<T>())
/**
* Derive Kotlin [KClass] from this type and fail if the type is not a class (should not happen)
*/
@Suppress("UNCHECKED_CAST")
@UnstableAttributesAPI
public val <T> SafeType<T>.kClass: KClass<T & Any> get() = kType.classifier as KClass<T & Any>
/**
* An interface containing [type] for dynamic type checking.
*/
public interface WithType<out T> {
public val type: SafeType<T>
}

View File

@ -1,17 +0,0 @@
/*
* Copyright 2018-2023 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.attributes
/**
* Marks declarations that are still experimental in the Attributes-kt APIs, which means that the design of the corresponding
* declarations has open issues that may (or may not) lead to their changes in the future. Roughly speaking, there is
* a chance of those declarations will be deprecated in the future or the semantics of their behavior may change
* in some way that may break some code.
*/
@MustBeDocumented
@Retention(value = AnnotationRetention.BINARY)
@RequiresOptIn("This API is unstable and could change in future", RequiresOptIn.Level.WARNING)
public annotation class UnstableAttributesAPI

View File

@ -1,119 +0,0 @@
# BenchmarksResult
## Report for benchmark configuration <code>main</code>
* Run on Java HotSpot(TM) 64-Bit Server VM (build 21.0.4+8-LTS-jvmci-23.1-b41) with Java process:
```
C:\Users\altavir\scoop\apps\graalvm-oracle-21jdk\current\bin\java.exe -XX:ThreadPriorityPolicy=1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCIProduct -XX:-UnlockExperimentalVMOptions -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant
```
* JMH 1.21 was used in `thrpt` mode with 5 warmup iterations by 10 s and 5 measurement iterations by 10 s.
### [ArrayBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ArrayBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`benchmarkArrayRead`|1.9E+07 &plusmn; 2.3E+05 ops/s|
|`benchmarkBufferRead`|1.4E+07 &plusmn; 8.7E+05 ops/s|
|`nativeBufferRead`|1.4E+07 &plusmn; 1.3E+06 ops/s|
### [BigIntBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`jvmAdd`|5.1E+07 &plusmn; 1.3E+06 ops/s|
|`jvmAddLarge`|5.1E+04 &plusmn; 8.2E+02 ops/s|
|`jvmMultiply`|8.5E+07 &plusmn; 9.7E+06 ops/s|
|`jvmMultiplyLarge`|2.5E+02 &plusmn; 15 ops/s|
|`jvmParsing10`|8.7E+06 &plusmn; 5.1E+05 ops/s|
|`jvmParsing16`|6.4E+06 &plusmn; 1.8E+05 ops/s|
|`jvmPower`|28 &plusmn; 0.79 ops/s|
|`jvmSmallAdd`|7.0E+07 &plusmn; 4.3E+06 ops/s|
|`kmAdd`|4.8E+07 &plusmn; 2.2E+06 ops/s|
|`kmAddLarge`|3.5E+04 &plusmn; 3.7E+03 ops/s|
|`kmMultiply`|6.7E+07 &plusmn; 1.5E+07 ops/s|
|`kmMultiplyLarge`|54 &plusmn; 4.2 ops/s|
|`kmParsing10`|4.5E+06 &plusmn; 8.3E+04 ops/s|
|`kmParsing16`|4.9E+06 &plusmn; 1.1E+05 ops/s|
|`kmPower`|10 &plusmn; 0.96 ops/s|
|`kmSmallAdd`|4.1E+07 &plusmn; 5.9E+05 ops/s|
### [BufferBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`bufferViewReadWrite`|5.8E+06 &plusmn; 1.6E+05 ops/s|
|`bufferViewReadWriteSpecialized`|5.6E+06 &plusmn; 2.6E+05 ops/s|
|`complexBufferReadWrite`|6.6E+06 &plusmn; 2.7E+05 ops/s|
|`doubleArrayReadWrite`|7.5E+06 &plusmn; 1.0E+06 ops/s|
|`doubleBufferReadWrite`|8.0E+06 &plusmn; 6.7E+05 ops/s|
### [DotBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`bufferedDot`|1.3 &plusmn; 0.020 ops/s|
|`cmDot`|0.47 &plusmn; 0.42 ops/s|
|`cmDotWithConversion`|0.76 &plusmn; 0.13 ops/s|
|`ejmlDot`|6.7 &plusmn; 0.091 ops/s|
|`ejmlDotWithConversion`|6.4 &plusmn; 0.82 ops/s|
|`multikDot`|40 &plusmn; 6.7 ops/s|
|`parallelDot`|12 &plusmn; 1.8 ops/s|
|`tensorDot`|1.2 &plusmn; 0.041 ops/s|
|`tfDot`|5.9 &plusmn; 0.49 ops/s|
### [ExpressionsInterpretersBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`asmGenericExpression`|29 &plusmn; 1.2 ops/s|
|`asmPrimitiveExpression`|43 &plusmn; 1.3 ops/s|
|`asmPrimitiveExpressionArray`|71 &plusmn; 0.38 ops/s|
|`functionalExpression`|5.6 &plusmn; 0.11 ops/s|
|`justCalculate`|69 &plusmn; 9.0 ops/s|
|`mstExpression`|7.1 &plusmn; 0.020 ops/s|
|`rawExpression`|41 &plusmn; 1.5 ops/s|
### [IntegrationBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/IntegrationBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`complexIntegration`|3.6E+03 &plusmn; 1.9E+02 ops/s|
|`doubleIntegration`|3.7E+03 &plusmn; 12 ops/s|
### [JafamaBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`core`|38 &plusmn; 0.64 ops/s|
|`jafama`|52 &plusmn; 0.36 ops/s|
|`strictJafama`|52 &plusmn; 4.0 ops/s|
### [MatrixInverseBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`cmLUPInversion`|2.2E+03 &plusmn; 76 ops/s|
|`ejmlInverse`|1.3E+03 &plusmn; 5.7 ops/s|
|`kmathLupInversion`|9.5E+02 &plusmn; 1.8E+02 ops/s|
|`kmathParallelLupInversion`|9.1E+02 &plusmn; 1.4E+02 ops/s|
### [NDFieldBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`boxingFieldAdd`|7.7 &plusmn; 0.79 ops/s|
|`multikAdd`|6.5 &plusmn; 0.33 ops/s|
|`multikInPlaceAdd`|64 &plusmn; 0.79 ops/s|
|`specializedFieldAdd`|8.0 &plusmn; 0.090 ops/s|
|`tensorAdd`|9.2 &plusmn; 0.053 ops/s|
|`tensorInPlaceAdd`|17 &plusmn; 10 ops/s|
|`viktorAdd`|7.6 &plusmn; 1.2 ops/s|
### [ViktorBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`doubleFieldAddition`|7.7 &plusmn; 0.34 ops/s|
|`rawViktor`|5.9 &plusmn; 1.1 ops/s|
|`viktorFieldAddition`|7.3 &plusmn; 1.1 ops/s|
### [ViktorLogBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`rawViktorLog`|1.4 &plusmn; 0.076 ops/s|
|`realFieldLog`|1.3 &plusmn; 0.069 ops/s|
|`viktorFieldLog`|1.3 &plusmn; 0.032 ops/s|

View File

@ -1,14 +1,11 @@
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper @file:Suppress("UNUSED_VARIABLE")
import com.fasterxml.jackson.module.kotlin.readValue
import kotlinx.benchmark.gradle.BenchmarksExtension import space.kscience.kmath.benchmarks.addBenchmarkProperties
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
alias(spclibs.plugins.kotlin.plugin.allopen) kotlin("plugin.allopen")
alias(spclibs.plugins.kotlinx.benchmark) id("org.jetbrains.kotlinx.benchmark")
} }
allOpen.annotation("org.openjdk.jmh.annotations.State") allOpen.annotation("org.openjdk.jmh.annotations.State")
@ -16,25 +13,19 @@ sourceSets.register("benchmarks")
repositories { repositories {
mavenCentral() 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 { kotlin {
jvm() jvm()
js(IR) {
nodejs()
}
sourceSets { sourceSets {
all {
languageSettings {
progressiveMode = true
optIn("kotlin.contracts.ExperimentalContracts")
optIn("kotlin.ExperimentalUnsignedTypes")
optIn("space.kscience.kmath.UnstableKMathAPI")
}
}
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
implementation(project(":kmath-ast")) implementation(project(":kmath-ast"))
@ -44,10 +35,8 @@ kotlin {
implementation(project(":kmath-stat")) implementation(project(":kmath-stat"))
implementation(project(":kmath-dimensions")) implementation(project(":kmath-dimensions"))
implementation(project(":kmath-for-real")) implementation(project(":kmath-for-real"))
implementation(project(":kmath-tensors")) implementation(project(":kmath-jafama"))
implementation(project(":kmath-multik")) implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.1")
implementation(libs.multik.default)
implementation(spclibs.kotlinx.benchmark.runtime)
} }
} }
@ -58,9 +47,6 @@ kotlin {
implementation(project(":kmath-nd4j")) implementation(project(":kmath-nd4j"))
implementation(project(":kmath-kotlingrad")) implementation(project(":kmath-kotlingrad"))
implementation(project(":kmath-viktor")) implementation(project(":kmath-viktor"))
implementation(project(":kmath-jafama"))
implementation(projects.kmath.kmathTensorflow)
implementation("org.tensorflow:tensorflow-core-platform:0.4.0")
implementation("org.nd4j:nd4j-native:1.0.0-M1") implementation("org.nd4j:nd4j-native:1.0.0-M1")
// uncomment if your system supports AVX2 // uncomment if your system supports AVX2
// val os = System.getProperty("os.name") // val os = System.getProperty("os.name")
@ -81,13 +67,12 @@ benchmark {
// Setup configurations // Setup configurations
targets { targets {
register("jvm") register("jvm")
register("js")
} }
fun kotlinx.benchmark.gradle.BenchmarkConfiguration.commonConfiguration() { fun kotlinx.benchmark.gradle.BenchmarkConfiguration.commonConfiguration() {
warmups = 2 warmups = 1
iterations = 5 iterations = 5
iterationTime = 2000 iterationTime = 1000
iterationTimeUnit = "ms" iterationTimeUnit = "ms"
} }
@ -96,23 +81,13 @@ benchmark {
include("BufferBenchmark") include("BufferBenchmark")
} }
configurations.register("nd") {
commonConfiguration()
include("NDFieldBenchmark")
}
configurations.register("dot") { configurations.register("dot") {
commonConfiguration() commonConfiguration()
include("DotBenchmark") include("DotBenchmark")
} }
configurations.register("expressions") { configurations.register("expressions") {
// Some extra precision commonConfiguration()
warmups = 2
iterations = 10
iterationTime = 10
iterationTimeUnit = "s"
outputTimeUnit = "s"
include("ExpressionsInterpretersBenchmark") include("ExpressionsInterpretersBenchmark")
} }
@ -130,152 +105,34 @@ benchmark {
commonConfiguration() commonConfiguration()
include("JafamaBenchmark") include("JafamaBenchmark")
} }
configurations.register("tensorAlgebra") {
commonConfiguration()
include("TensorAlgebraBenchmark")
}
configurations.register("viktor") {
commonConfiguration()
include("ViktorBenchmark")
}
configurations.register("viktorLog") {
commonConfiguration()
include("ViktorLogBenchmark")
}
configurations.register("integration") {
commonConfiguration()
include("IntegrationBenchmark")
}
} }
kotlin { // Fix kotlinx-benchmarks bug
jvmToolchain(11) afterEvaluate {
compilerOptions { val jvmBenchmarkJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) {
optIn.addAll( duplicatesStrategy = DuplicatesStrategy.EXCLUDE
"space.kscience.kmath.UnstableKMathAPI"
)
} }
} }
private data class JmhReport( kotlin.sourceSets.all {
val jmhVersion: String, with(languageSettings) {
val benchmark: String, useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
val mode: String, useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
val threads: Int, useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI")
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
} }
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
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
} }
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all"
}
}
readme { readme {
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
val jsonMapper = jacksonObjectMapper()
fun noun(number: Number, singular: String, plural: String) = if (number.toLong() == 1L) singular else plural
extensions.findByType(BenchmarksExtension::class.java)?.configurations?.forEach { cfg ->
val propertyName =
"benchmark${cfg.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}"
logger.info("Processing benchmark data from benchmark ${cfg.name} into readme property $propertyName")
val launches = layout.buildDirectory.dir("reports/benchmarks/${cfg.name}").get().asFile
if (!launches.exists()) return@forEach
property(propertyName) {
val resDirectory = launches.listFiles()?.maxByOrNull {
LocalDateTime.parse(it.name).atZone(ZoneId.systemDefault()).toInstant()
}
if (resDirectory == null || !(resDirectory.resolve("jvm.json")).exists()) {
"> **Can't find appropriate benchmark data. Try generating readme files after running benchmarks**."
} else {
val reports: List<JmhReport> =
jsonMapper.readValue<List<JmhReport>>(resDirectory.resolve("jvm.json"))
buildString {
appendLine("## Report for benchmark configuration <code>${cfg.name}</code>")
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}."
)
reports.groupBy { it.benchmark.substringBeforeLast(".") }.forEach { (cl, compare) ->
appendLine("### [${cl.substringAfterLast(".")}](src/jvmMain/kotlin/${cl.replace(".","/")}.kt)")
appendLine()
appendLine("| Benchmark | Score |")
appendLine("|:---------:|:-----:|")
compare.forEach { report ->
val benchmarkName = report.benchmark.substringAfterLast(".")
val score = String.format("%.2G", report.primaryMetric.score)
val error = String.format("%.2G", report.primaryMetric.scoreError)
appendLine("|`$benchmarkName`|$score &plusmn; $error ${report.primaryMetric.scoreUnit}|")
}
}
}
}
}
}
} }
addBenchmarkProperties()

View File

@ -1,5 +0,0 @@
# BenchmarksResult
${benchmarkMain}

View File

@ -1,108 +0,0 @@
/*
* Copyright 2018-2024 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.UnstableKMathAPI
import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Float64
import kotlin.math.sin
import kotlin.random.Random
import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression
import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression
@State(Scope.Benchmark)
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 wasmExpression(blackhole: Blackhole) = invokeAndSum(wasm, blackhole)
/**
* Benchmark case for [Expression] created with [compileToExpression].
*/
@Benchmark
fun estreeExpression(blackhole: Blackhole) = invokeAndSum(estree, 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<Float64>, blackhole: Blackhole) {
val random = Random(0)
var sum = 0.0
val m = HashMap<Symbol, Double>()
repeat(times) {
m[x] = random.nextDouble()
sum += expr(m)
}
blackhole.consume(sum)
}
private companion object {
private val x by symbol
private const val times = 1_000_000
private val functional = Float64Field.expression {
val x = bindSymbol(Symbol.x)
x * number(2.0) + 2.0 / x - 16.0 / sin(x)
}
private val node = MstExtendedField {
x * 2.0 + number(2.0) / x - number(16.0) / sin(x)
}
private val mst = node.toExpression(Float64Field)
@OptIn(UnstableKMathAPI::class)
private val wasm = node.wasmCompileToExpression(Float64Field)
private val estree = node.estreeCompileToExpression(Float64Field)
private val raw = Expression<Float64> { args ->
val x = args.getValue(x)
x * 2.0 + 2.0 / x - 16.0 / sin(x)
}
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -10,11 +10,8 @@ import kotlinx.benchmark.Blackhole
import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State import org.openjdk.jmh.annotations.State
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.BigIntField import space.kscience.kmath.operations.*
import space.kscience.kmath.operations.JBigIntegerField
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.operations.parseBigInteger
import java.math.BigInteger import java.math.BigInteger
@ -22,24 +19,12 @@ import java.math.BigInteger
@State(Scope.Benchmark) @State(Scope.Benchmark)
internal class BigIntBenchmark { internal class BigIntBenchmark {
val kmSmallNumber = BigIntField.number(100)
val jvmSmallNumber = JBigIntegerField.number(100)
val kmNumber = BigIntField.number(Int.MAX_VALUE) val kmNumber = BigIntField.number(Int.MAX_VALUE)
val jvmNumber = JBigIntegerField.number(Int.MAX_VALUE) val jvmNumber = JBigIntegerField.number(Int.MAX_VALUE)
val kmLargeNumber = BigIntField { number(11).pow(100_000U) } val largeKmNumber = BigIntField { number(11).pow(100_000U) }
val jvmLargeNumber: BigInteger = JBigIntegerField { number(11).pow(100_000) } val largeJvmNumber: BigInteger = JBigIntegerField { number(11).pow(100_000) }
val bigExponent = 50_000 val bigExponent = 50_000
@Benchmark
fun kmSmallAdd(blackhole: Blackhole) = BigIntField {
blackhole.consume(kmSmallNumber + kmSmallNumber + kmSmallNumber)
}
@Benchmark
fun jvmSmallAdd(blackhole: Blackhole) = JBigIntegerField {
blackhole.consume(jvmSmallNumber + jvmSmallNumber + jvmSmallNumber)
}
@Benchmark @Benchmark
fun kmAdd(blackhole: Blackhole) = BigIntField { fun kmAdd(blackhole: Blackhole) = BigIntField {
blackhole.consume(kmNumber + kmNumber + kmNumber) blackhole.consume(kmNumber + kmNumber + kmNumber)
@ -52,12 +37,12 @@ internal class BigIntBenchmark {
@Benchmark @Benchmark
fun kmAddLarge(blackhole: Blackhole) = BigIntField { fun kmAddLarge(blackhole: Blackhole) = BigIntField {
blackhole.consume(kmLargeNumber + kmLargeNumber + kmLargeNumber) blackhole.consume(largeKmNumber + largeKmNumber + largeKmNumber)
} }
@Benchmark @Benchmark
fun jvmAddLarge(blackhole: Blackhole) = JBigIntegerField { fun jvmAddLarge(blackhole: Blackhole) = JBigIntegerField {
blackhole.consume(jvmLargeNumber + jvmLargeNumber + jvmLargeNumber) blackhole.consume(largeJvmNumber + largeJvmNumber + largeJvmNumber)
} }
@Benchmark @Benchmark
@ -67,7 +52,7 @@ internal class BigIntBenchmark {
@Benchmark @Benchmark
fun kmMultiplyLarge(blackhole: Blackhole) = BigIntField { fun kmMultiplyLarge(blackhole: Blackhole) = BigIntField {
blackhole.consume(kmLargeNumber * kmLargeNumber) blackhole.consume(largeKmNumber*largeKmNumber)
} }
@Benchmark @Benchmark
@ -77,7 +62,7 @@ internal class BigIntBenchmark {
@Benchmark @Benchmark
fun jvmMultiplyLarge(blackhole: Blackhole) = JBigIntegerField { fun jvmMultiplyLarge(blackhole: Blackhole) = JBigIntegerField {
blackhole.consume(jvmLargeNumber * jvmLargeNumber) blackhole.consume(largeJvmNumber*largeJvmNumber)
} }
@Benchmark @Benchmark

View File

@ -1,80 +1,39 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.benchmarks package space.kscience.kmath.benchmarks
import kotlinx.benchmark.Benchmark import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import space.kscience.kmath.complex.Complex import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.complex import space.kscience.kmath.complex.complex
import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.Float64Buffer
import space.kscience.kmath.structures.getDouble
import space.kscience.kmath.structures.permute
@State(Scope.Benchmark) @State(Scope.Benchmark)
internal class BufferBenchmark { internal class BufferBenchmark {
@Benchmark @Benchmark
fun doubleArrayReadWrite(blackhole: Blackhole) { fun genericDoubleBufferReadWrite() {
val buffer = DoubleArray(size) { it.toDouble() } val buffer = DoubleBuffer(size) { it.toDouble() }
var res = 0.0
(0 until size).forEach { (0 until size).forEach {
res += buffer[it] buffer[it]
} }
blackhole.consume(res)
} }
@Benchmark @Benchmark
fun doubleBufferReadWrite(blackhole: Blackhole) { fun complexBufferReadWrite() {
val buffer = Float64Buffer(size) { it.toDouble() } val buffer = MutableBuffer.complex(size / 2) { Complex(it.toDouble(), -it.toDouble()) }
var res = 0.0
(0 until size).forEach {
res += buffer[it]
}
blackhole.consume(res)
}
@Benchmark
fun bufferViewReadWrite(blackhole: Blackhole) {
val buffer = Float64Buffer(size) { it.toDouble() }.permute(reversedIndices)
var res = 0.0
(0 until size).forEach {
res += buffer[it]
}
blackhole.consume(res)
}
@Benchmark
fun bufferViewReadWriteSpecialized(blackhole: Blackhole) {
val buffer = Float64Buffer(size) { it.toDouble() }.permute(reversedIndices)
var res = 0.0
(0 until size).forEach {
res += buffer.getDouble(it)
}
blackhole.consume(res)
}
@Benchmark
fun complexBufferReadWrite(blackhole: Blackhole) = ComplexField {
val buffer = Buffer.complex(size / 2) { Complex(it.toDouble(), -it.toDouble()) }
var res = zero
(0 until size / 2).forEach { (0 until size / 2).forEach {
res += buffer[it] buffer[it]
} }
blackhole.consume(res)
} }
private companion object { private companion object {
private const val size = 100 private const val size = 100
private val reversedIndices = IntArray(size) { it }.apply { reverse() }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -11,12 +11,9 @@ import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.linear.Float64ParallelLinearSpace import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.invoke import space.kscience.kmath.linear.invoke
import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.tensorflow.produceWithTF
import space.kscience.kmath.tensors.core.tensorAlgebra
import kotlin.random.Random import kotlin.random.Random
@State(Scope.Benchmark) @State(Scope.Benchmark)
@ -26,12 +23,8 @@ internal class DotBenchmark {
const val dim = 1000 const val dim = 1000
//creating invertible matrix //creating invertible matrix
val matrix1 = Float64Field.linearSpace.buildMatrix(dim, dim) { _, _ -> val matrix1 = LinearSpace.real.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
random.nextDouble() val matrix2 = LinearSpace.real.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
}
val matrix2 = Float64Field.linearSpace.buildMatrix(dim, dim) { _, _ ->
random.nextDouble()
}
val cmMatrix1 = CMLinearSpace { matrix1.toCM() } val cmMatrix1 = CMLinearSpace { matrix1.toCM() }
val cmMatrix2 = CMLinearSpace { matrix2.toCM() } val cmMatrix2 = CMLinearSpace { matrix2.toCM() }
@ -40,54 +33,38 @@ internal class DotBenchmark {
val ejmlMatrix2 = EjmlLinearSpaceDDRM { matrix2.toEjml() } val ejmlMatrix2 = EjmlLinearSpaceDDRM { matrix2.toEjml() }
} }
@Benchmark @Benchmark
fun tfDot(blackhole: Blackhole) { fun cmDot(blackhole: Blackhole) {
blackhole.consume( CMLinearSpace.run {
Float64Field.produceWithTF { blackhole.consume(cmMatrix1 dot cmMatrix2)
matrix1 dot matrix1 }
}
)
} }
@Benchmark @Benchmark
fun cmDotWithConversion(blackhole: Blackhole) = CMLinearSpace { fun ejmlDot(blackhole: Blackhole) {
blackhole.consume(matrix1 dot matrix2) EjmlLinearSpaceDDRM {
blackhole.consume(ejmlMatrix1 dot ejmlMatrix2)
}
} }
@Benchmark @Benchmark
fun cmDot(blackhole: Blackhole) = CMLinearSpace { fun ejmlDotWithConversion(blackhole: Blackhole) {
blackhole.consume(cmMatrix1 dot cmMatrix2) EjmlLinearSpaceDDRM {
blackhole.consume(matrix1 dot matrix2)
}
} }
@Benchmark @Benchmark
fun ejmlDot(blackhole: Blackhole) = EjmlLinearSpaceDDRM { fun bufferedDot(blackhole: Blackhole) {
blackhole.consume(ejmlMatrix1 dot ejmlMatrix2) LinearSpace.auto(DoubleField).invoke {
blackhole.consume(matrix1 dot matrix2)
}
} }
@Benchmark @Benchmark
fun ejmlDotWithConversion(blackhole: Blackhole) = EjmlLinearSpaceDDRM { fun realDot(blackhole: Blackhole) {
blackhole.consume(matrix1 dot matrix2) LinearSpace.real {
blackhole.consume(matrix1 dot matrix2)
}
} }
@Benchmark
fun multikDot(blackhole: Blackhole) = with(multikAlgebra) {
blackhole.consume(matrix1 dot matrix2)
}
@Benchmark
fun tensorDot(blackhole: Blackhole) = with(Float64Field.tensorAlgebra) {
blackhole.consume(matrix1 dot matrix2)
}
@Benchmark
fun bufferedDot(blackhole: Blackhole) = with(Float64Field.linearSpace) {
blackhole.consume(matrix1 dot matrix2)
}
@Benchmark
fun parallelDot(blackhole: Blackhole) = with(Float64ParallelLinearSpace) {
blackhole.consume(matrix1 dot matrix2)
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -11,11 +11,9 @@ import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import space.kscience.kmath.asm.compileToExpression import space.kscience.kmath.asm.compileToExpression
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Float64
import kotlin.math.sin import kotlin.math.sin
import kotlin.random.Random import kotlin.random.Random
@ -37,30 +35,7 @@ internal class ExpressionsInterpretersBenchmark {
* Benchmark case for [Expression] created with [compileToExpression]. * Benchmark case for [Expression] created with [compileToExpression].
*/ */
@Benchmark @Benchmark
fun asmGenericExpression(blackhole: Blackhole) = invokeAndSum(asmGeneric, blackhole) fun asmExpression(blackhole: Blackhole) = invokeAndSum(asm, blackhole)
/**
* Benchmark case for [Expression] created with [compileToExpression].
*/
@Benchmark
fun asmPrimitiveExpressionArray(blackhole: Blackhole) {
val random = Random(0)
var sum = 0.0
val m = DoubleArray(1)
repeat(times) {
m[xIdx] = random.nextDouble()
sum += asmPrimitive(m)
}
blackhole.consume(sum)
}
/**
* Benchmark case for [Expression] created with [compileToExpression].
*/
@Benchmark
fun asmPrimitiveExpression(blackhole: Blackhole) = invokeAndSum(asmPrimitive, blackhole)
/** /**
* Benchmark case for [Expression] implemented manually with `kotlin.math` functions. * Benchmark case for [Expression] implemented manually with `kotlin.math` functions.
@ -84,14 +59,12 @@ internal class ExpressionsInterpretersBenchmark {
blackhole.consume(sum) blackhole.consume(sum)
} }
private fun invokeAndSum(expr: Expression<Float64>, blackhole: Blackhole) { private fun invokeAndSum(expr: Expression<Double>, blackhole: Blackhole) {
val random = Random(0) val random = Random(0)
var sum = 0.0 var sum = 0.0
val m = HashMap<Symbol, Double>()
repeat(times) { repeat(times) {
m[x] = random.nextDouble() sum += expr(x to random.nextDouble())
sum += expr(m)
} }
blackhole.consume(sum) blackhole.consume(sum)
@ -99,25 +72,21 @@ internal class ExpressionsInterpretersBenchmark {
private companion object { private companion object {
private val x by symbol private val x by symbol
private val algebra = DoubleField
private const val times = 1_000_000 private const val times = 1_000_000
private val functional = Float64Field.expression { private val functional = DoubleField.expressionInExtendedField {
val x = bindSymbol(Symbol.x) bindSymbol(x) * number(2.0) + number(2.0) / bindSymbol(x) - number(16.0) / sin(bindSymbol(x))
x * number(2.0) + 2.0 / x - 16.0 / sin(x)
} }
private val node = MstExtendedField { private val node = MstExtendedField {
x * 2.0 + number(2.0) / x - number(16.0) / sin(x) x * 2.0 + number(2.0) / x - number(16.0) / sin(x)
} }
private val mst = node.toExpression(Float64Field) private val mst = node.toExpression(DoubleField)
private val asm = node.compileToExpression(DoubleField)
private val asmPrimitive = node.compileToExpression(Float64Field) private val raw = Expression<Double> { args ->
private val xIdx = asmPrimitive.indexer.indexOf(x)
private val asmGeneric = node.compileToExpression(Float64Field as Algebra<Float64>)
private val raw = Expression<Float64> { args ->
val x = args[x]!! val x = args[x]!!
x * 2.0 + 2.0 / x - 16.0 / sin(x) x * 2.0 + 2.0 / x - 16.0 / sin(x)
} }

View File

@ -1,40 +0,0 @@
/*
* Copyright 2018-2024 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 org.openjdk.jmh.infra.Blackhole
import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.algebra
import space.kscience.kmath.integration.gaussIntegrator
import space.kscience.kmath.integration.integrate
import space.kscience.kmath.integration.value
import space.kscience.kmath.operations.algebra
@State(Scope.Benchmark)
internal class IntegrationBenchmark {
@Benchmark
fun doubleIntegration(blackhole: Blackhole) {
val res = Double.algebra.gaussIntegrator.integrate(0.0..1.0, intervals = 1000) { x: Double ->
//sin(1 / x)
1 / x
}.value
blackhole.consume(res)
}
@Benchmark
fun complexIntegration(blackhole: Blackhole) = with(Complex.algebra) {
val res = gaussIntegrator.integrate(0.0..1.0, intervals = 1000) { x: Double ->
// sin(1 / x) + i * cos(1 / x)
1 / x - i / x
}.value
blackhole.consume(res)
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -11,7 +11,7 @@ import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State import org.openjdk.jmh.annotations.State
import space.kscience.kmath.jafama.JafamaDoubleField import space.kscience.kmath.jafama.JafamaDoubleField
import space.kscience.kmath.jafama.StrictJafamaDoubleField import space.kscience.kmath.jafama.StrictJafamaDoubleField
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.random.Random import kotlin.random.Random
@ -24,16 +24,16 @@ internal class JafamaBenchmark {
@Benchmark @Benchmark
fun core(blackhole: Blackhole) = invokeBenchmarks(blackhole) { x -> fun core(blackhole: Blackhole) = invokeBenchmarks(blackhole) { x ->
Float64Field { x * power(x, 4) * exp(x) / cos(x) + sin(x) } DoubleField { x * power(x, 4) * exp(x) / cos(x) + sin(x) }
} }
@Benchmark @Benchmark
fun strictJafama(blackhole: Blackhole) = invokeBenchmarks(blackhole) { x -> fun strictJafama(blackhole: Blackhole) = invokeBenchmarks(blackhole) { x ->
StrictJafamaDoubleField { x * power(x, 4) * exp(x) / cos(x) + sin(x) } StrictJafamaDoubleField { x * power(x, 4) * exp(x) / cos(x) + sin(x) }
} }
}
private inline fun invokeBenchmarks(blackhole: Blackhole, expr: (Double) -> Double) { private inline fun invokeBenchmarks(blackhole: Blackhole, expr: (Double) -> Double) {
val rng = Random(0) val rng = Random(0)
repeat(1000000) { blackhole.consume(expr(rng.nextDouble())) } repeat(1000000) { blackhole.consume(expr(rng.nextDouble())) }
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -10,13 +10,13 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.commons.linear.lupSolver import space.kscience.kmath.commons.linear.inverse
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.linear.InverseMatrixFeature
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.inverseWithLup
import space.kscience.kmath.linear.invoke import space.kscience.kmath.linear.invoke
import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.nd.getFeature
import space.kscience.kmath.linear.lupSolver
import space.kscience.kmath.linear.parallel
import space.kscience.kmath.operations.algebra
import kotlin.random.Random import kotlin.random.Random
@State(Scope.Benchmark) @State(Scope.Benchmark)
@ -25,7 +25,7 @@ internal class MatrixInverseBenchmark {
private val random = Random(1224) private val random = Random(1224)
private const val dim = 100 private const val dim = 100
private val space = Double.algebra.linearSpace private val space = LinearSpace.real
//creating invertible matrix //creating invertible matrix
private val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } private val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
@ -35,23 +35,20 @@ internal class MatrixInverseBenchmark {
@Benchmark @Benchmark
fun kmathLupInversion(blackhole: Blackhole) { fun kmathLupInversion(blackhole: Blackhole) {
blackhole.consume(Double.algebra.linearSpace.lupSolver().inverse(matrix)) blackhole.consume(LinearSpace.real.inverseWithLup(matrix))
} }
@Benchmark @Benchmark
fun kmathParallelLupInversion(blackhole: Blackhole) { fun cmLUPInversion(blackhole: Blackhole) {
blackhole.consume(Double.algebra.linearSpace.parallel.lupSolver().inverse(matrix)) with(CMLinearSpace) {
blackhole.consume(inverse(matrix))
}
} }
@Benchmark @Benchmark
fun cmLUPInversion(blackhole: Blackhole) = CMLinearSpace { fun ejmlInverse(blackhole: Blackhole) {
blackhole.consume(lupSolver().inverse(matrix)) with(EjmlLinearSpaceDDRM) {
blackhole.consume(matrix.getFeature<InverseMatrixFeature<Double>>()?.inverse)
}
} }
@Benchmark
fun ejmlInverse(blackhole: Blackhole) = EjmlLinearSpaceDDRM {
blackhole.consume(matrix.toEjml().inverted())
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -9,89 +9,45 @@ import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import org.jetbrains.kotlinx.multik.api.Multik
import org.jetbrains.kotlinx.multik.api.ones
import org.jetbrains.kotlinx.multik.ndarray.data.DN
import org.jetbrains.kotlinx.multik.ndarray.data.DataType
import space.kscience.kmath.UnsafeKMathAPI
import space.kscience.kmath.nd.* import space.kscience.kmath.nd.*
import space.kscience.kmath.nd4j.nd4j import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.Float64
import space.kscience.kmath.tensors.core.DoubleTensor
import space.kscience.kmath.tensors.core.one
import space.kscience.kmath.tensors.core.tensorAlgebra
import space.kscience.kmath.viktor.viktorAlgebra
@State(Scope.Benchmark) @State(Scope.Benchmark)
internal class NDFieldBenchmark { 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 companion object {
private const val dim = 1000 private const val dim = 1000
private const val n = 100 private const val n = 100
private val shape = ShapeND(dim, dim) private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val specializedField = Float64Field.ndAlgebra private val specializedField = AlgebraND.real(dim, dim)
private val genericField = BufferedFieldOpsND(Float64Field) private val genericField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim)
private val nd4jField = Float64Field.nd4j
private val viktorField = Float64Field.viktorAlgebra
} }
@Benchmark
fun specializedFieldAdd(blackhole: Blackhole) = with(specializedField) {
var res: StructureND<Float64> = one(shape)
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@Benchmark
fun boxingFieldAdd(blackhole: Blackhole) = with(genericField) {
var res: StructureND<Float64> = one(shape)
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@Benchmark
fun multikAdd(blackhole: Blackhole) = with(multikAlgebra) {
var res: StructureND<Float64> = one(shape)
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@Benchmark
fun viktorAdd(blackhole: Blackhole) = with(viktorField) {
var res: StructureND<Float64> = one(shape)
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@Benchmark
fun tensorAdd(blackhole: Blackhole) = with(Double.tensorAlgebra) {
var res: DoubleTensor = one(shape)
repeat(n) { res = res + 1.0 }
blackhole.consume(res)
}
@Benchmark
fun tensorInPlaceAdd(blackhole: Blackhole) = with(Double.tensorAlgebra) {
val res: DoubleTensor = one(shape)
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@OptIn(UnsafeKMathAPI::class)
@Benchmark
fun multikInPlaceAdd(blackhole: Blackhole) = with(multikAlgebra) {
val res = Multik.ones<Double, DN>(shape.asArray(), DataType.DoubleDataType).wrap()
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
// @Benchmark
// fun nd4jAdd(blackhole: Blackhole) = with(nd4jField) {
// var res: StructureND<Float64> = one(dim, dim)
// repeat(n) { res += 1.0 }
// blackhole.consume(res)
// }
} }

View File

@ -1,39 +0,0 @@
/*
* Copyright 2018-2024 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.linear.linearSpace
import space.kscience.kmath.linear.matrix
import space.kscience.kmath.linear.symmetric
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.tensors.core.symEigJacobi
import space.kscience.kmath.tensors.core.symEigSvd
import space.kscience.kmath.tensors.core.tensorAlgebra
import kotlin.random.Random
@State(Scope.Benchmark)
internal class TensorAlgebraBenchmark {
companion object {
private val random = Random(12224)
private const val dim = 30
private val matrix = Float64Field.linearSpace.matrix(dim, dim).symmetric { _, _ -> random.nextDouble() }
}
@Benchmark
fun tensorSymEigSvd(blackhole: Blackhole) = with(Double.tensorAlgebra) {
blackhole.consume(symEigSvd(matrix, 1e-10))
}
@Benchmark
fun tensorSymEigJacobi(blackhole: Blackhole) = with(Double.tensorAlgebra) {
blackhole.consume(symEigJacobi(matrix, 50, 1e-10))
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -10,21 +10,28 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.auto
import space.kscience.kmath.nd.one import space.kscience.kmath.nd.real
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Float64 import space.kscience.kmath.viktor.ViktorNDField
import space.kscience.kmath.viktor.ViktorFieldND
@State(Scope.Benchmark) @State(Scope.Benchmark)
internal class ViktorBenchmark { internal class ViktorBenchmark {
@Benchmark
fun automaticFieldAddition(blackhole: Blackhole) {
with(autoField) {
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
}
@Benchmark @Benchmark
fun doubleFieldAddition(blackhole: Blackhole) { fun realFieldAddition(blackhole: Blackhole) {
with(doubleField) { with(realField) {
var res: StructureND<Float64> = one(shape) var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
blackhole.consume(res) blackhole.consume(res)
} }
@ -33,7 +40,7 @@ internal class ViktorBenchmark {
@Benchmark @Benchmark
fun viktorFieldAddition(blackhole: Blackhole) { fun viktorFieldAddition(blackhole: Blackhole) {
with(viktorField) { with(viktorField) {
var res = one(shape) var res = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
blackhole.consume(res) blackhole.consume(res)
} }
@ -50,10 +57,10 @@ internal class ViktorBenchmark {
private companion object { private companion object {
private const val dim = 1000 private const val dim = 1000
private const val n = 100 private const val n = 100
private val shape = ShapeND(dim, dim)
// automatically build context most suited for given type. // automatically build context most suited for given type.
private val doubleField = Float64Field.ndAlgebra private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val viktorField = ViktorFieldND(dim, dim) private val realField = AlgebraND.real(dim, dim)
private val viktorField = ViktorNDField(dim, dim)
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -10,19 +10,19 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.auto
import space.kscience.kmath.nd.one import space.kscience.kmath.nd.real
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.viktor.ViktorFieldND import space.kscience.kmath.viktor.ViktorFieldND
@State(Scope.Benchmark) @State(Scope.Benchmark)
internal class ViktorLogBenchmark { internal class ViktorLogBenchmark {
@Benchmark @Benchmark
fun realFieldLog(blackhole: Blackhole) { fun realFieldLog(blackhole: Blackhole) {
with(doubleField) { with(realNdField) {
val fortyTwo = structureND(shape) { 42.0 } val fortyTwo = produce { 42.0 }
var res = one(shape) var res = one
repeat(n) { res = ln(fortyTwo) } repeat(n) { res = ln(fortyTwo) }
blackhole.consume(res) blackhole.consume(res)
} }
@ -31,7 +31,7 @@ internal class ViktorLogBenchmark {
@Benchmark @Benchmark
fun viktorFieldLog(blackhole: Blackhole) { fun viktorFieldLog(blackhole: Blackhole) {
with(viktorField) { with(viktorField) {
val fortyTwo = structureND(shape) { 42.0 } val fortyTwo = produce { 42.0 }
var res = one var res = one
repeat(n) { res = ln(fortyTwo) } repeat(n) { res = ln(fortyTwo) }
blackhole.consume(res) blackhole.consume(res)
@ -49,10 +49,10 @@ internal class ViktorLogBenchmark {
private companion object { private companion object {
private const val dim = 1000 private const val dim = 1000
private const val n = 100 private const val n = 100
private val shape = ShapeND(dim, dim)
// automatically build context most suited for given type. // automatically build context most suited for given type.
private val doubleField = Float64Field.ndAlgebra private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val viktorField = ViktorFieldND(dim, dim) private val realNdField = AlgebraND.real(dim, dim)
private val viktorField = ViktorFieldND(intArrayOf(dim, dim))
} }
} }

View File

@ -1,11 +0,0 @@
/*
* Copyright 2018-2024 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.jetbrains.kotlinx.multik.default.DefaultEngine
import space.kscience.kmath.multik.MultikDoubleAlgebra
val multikAlgebra = MultikDoubleAlgebra(DefaultEngine())

View File

@ -1,53 +1,38 @@
import space.kscience.gradle.useApache2Licence
import space.kscience.gradle.useSPCTeam
plugins { plugins {
alias(spclibs.plugins.kscience.project) id("ru.mipt.npm.gradle.project")
alias(spclibs.plugins.kotlinx.kover) kotlin("jupyter.api") apply false
} }
val attributesVersion by extra("0.2.0")
allprojects { allprojects {
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://clojars.org/repo")
maven("https://jitpack.io")
maven("http://logicrunch.research.it.uu.se/maven") {
isAllowInsecureProtocol = true
}
maven("https://oss.sonatype.org/content/repositories/snapshots") maven("https://oss.sonatype.org/content/repositories/snapshots")
mavenCentral() mavenCentral()
} }
group = "space.kscience" group = "space.kscience"
version = "0.4.1-dev" version = "0.3.0-dev-14"
} }
subprojects { subprojects {
if (name.startsWith("kmath")) apply<MavenPublishPlugin>() if (name.startsWith("kmath")) apply<MavenPublishPlugin>()
plugins.withId("org.jetbrains.dokka") { afterEvaluate {
tasks.withType<org.jetbrains.dokka.gradle.DokkaTaskPartial> { tasks.withType<org.jetbrains.dokka.gradle.DokkaTaskPartial> {
dependsOn(tasks["assemble"]) dependsOn(tasks.getByName("assemble"))
dokkaSourceSets.all { dokkaSourceSets.all {
val readmeFile = this@subprojects.projectDir.resolve("README.md") val readmeFile = File(this@subprojects.projectDir, "README.md")
if (readmeFile.exists()) includes.from(readmeFile) if (readmeFile.exists()) includes.from(readmeFile.absolutePath)
val kotlinDirPath = "src/$name/kotlin" externalDocumentationLink("https://ejml.org/javadoc/")
val kotlinDir = file(kotlinDirPath)
if (kotlinDir.exists()) sourceLink {
localDirectory.set(kotlinDir)
remoteUrl.set(
uri("https://github.com/SciProgCentre/kmath/tree/master/${this@subprojects.name}/$kotlinDirPath").toURL()
)
}
externalDocumentationLink("https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/") externalDocumentationLink("https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/")
externalDocumentationLink("https://deeplearning4j.org/api/latest/") externalDocumentationLink("https://deeplearning4j.org/api/latest/")
externalDocumentationLink("https://axelclk.bitbucket.io/symja/javadoc/") externalDocumentationLink("https://axelclk.bitbucket.io/symja/javadoc/")
externalDocumentationLink("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/")
externalDocumentationLink(
"https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/",
"https://kotlin.github.io/kotlinx.coroutines/package-list",
)
externalDocumentationLink( externalDocumentationLink(
"https://breandan.net/kotlingrad/kotlingrad/", "https://breandan.net/kotlingrad/kotlingrad/",
@ -58,15 +43,16 @@ subprojects {
} }
} }
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md") readme {
readmeTemplate = file("docs/templates/README-TEMPLATE.md")
ksciencePublish {
pom("https://github.com/SciProgCentre/kmath") {
useApache2Licence()
useSPCTeam()
}
repository("spc", "https://maven.sciprog.center/kscience")
sonatype("https://oss.sonatype.org")
} }
apiValidation.nonPublicMarkers.add("space.kscience.kmath.UnstableKMathAPI") ksciencePublish {
github("kmath")
space()
sonatype()
}
apiValidation {
nonPublicMarkers.add("space.kscience.kmath.misc.UnstableKMathAPI")
}

20
buildSrc/build.gradle.kts Normal file
View 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")
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.benchmarks
import kotlinx.serialization.Serializable
@Serializable
data class JmhReport(
val jmhVersion: String,
val benchmark: String,
val mode: String,
val threads: Int,
val forks: Int,
val jvm: String,
val jvmArgs: List<String>,
val jdkVersion: String,
val vmName: String,
val vmVersion: String,
val warmupIterations: Int,
val warmupTime: String,
val warmupBatchSize: Int,
val measurementIterations: Int,
val measurementTime: String,
val measurementBatchSize: Int,
val params: Map<String, String> = emptyMap(),
val primaryMetric: PrimaryMetric,
val secondaryMetrics: Map<String, SecondaryMetric>,
) {
interface Metric {
val score: Double
val scoreError: Double
val scoreConfidence: List<Double>
val scorePercentiles: Map<Double, Double>
val scoreUnit: String
}
@Serializable
data class PrimaryMetric(
override val score: Double,
override val scoreError: Double,
override val scoreConfidence: List<Double>,
override val scorePercentiles: Map<Double, Double>,
override val scoreUnit: String,
val rawDataHistogram: List<List<List<List<Double>>>>? = null,
val rawData: List<List<Double>>? = null,
) : Metric
@Serializable
data class SecondaryMetric(
override val score: Double,
override val scoreError: Double,
override val scoreConfidence: List<Double>,
override val scorePercentiles: Map<Double, Double>,
override val scoreUnit: String,
val rawData: List<List<Double>>,
) : Metric
}

View File

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

View File

@ -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> &middot; [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> &middot; [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,
)
}
}

View File

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

View File

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

View File

@ -1,13 +1,12 @@
# Coding Conventions # Coding Conventions
Generally, KMath code follows KMath code follows general [Kotlin conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but
general [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but with a number of with a number of small changes and clarifications.
small changes and clarifications.
## Utility Class Naming ## Utility Class Naming
Filename should coincide with a name of one of the classes contained in the file or start with small letter and describe Filename should coincide with a name of one of the classes contained in the file or start with small letter and
its contents. describe its contents.
The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that
file names should start with a capital letter even if file does not contain classes. Yet starting utility classes and file names should start with a capital letter even if file does not contain classes. Yet starting utility classes and

View File

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

File diff suppressed because it is too large Load Diff

View File

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

14
docs/features.md Normal file
View File

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

View File

@ -1,9 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
- Copyright 2018-2024 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.
-->
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 248 KiB

View File

@ -1,9 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
- Copyright 2018-2024 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.
-->
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,9 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
- Copyright 2018-2024 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.
-->
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 278 KiB

View File

@ -1,9 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
- Copyright 2018-2024 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.
-->
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View File

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

View File

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

View File

@ -1,223 +0,0 @@
# Polynomials and Rational Functions
KMath provides a way to work with uni- and multivariate polynomials and rational functions. It includes full support of
arithmetic operations of integers, **constants** (elements of ring polynomials are build over), variables (for certain
multivariate implementations), polynomials and rational functions encapsulated in so-called **polynomial space** and *
*rational function space** and some other utilities such as algebraic differentiation and substitution.
## Concrete realizations
There are 3 approaches to represent polynomials:
1. For univariate polynomials one can represent and store polynomial as a list of coefficients for each power of the
variable. I.e. polynomial $a_0 + \dots + a_n x^n $ can be represented as a finite sequence $(a_0; \dots; a_n)$. (
Compare to sequential definition of polynomials.)
2. For multivariate polynomials one can represent and store polynomial as a matching (in programming it is called "map"
or "dictionary", in math it is
called [functional relation](https://en.wikipedia.org/wiki/Binary_relation#Special_types_of_binary_relations)) of
each "**term signature**" (that describes what variables and in what powers appear in the term) with corresponding
coefficient of the term. But there are 2 possible approaches of term signature representation:
1. One can number all the variables, so term signature can be represented as a sequence describing powers of the
variables. I.e. signature of term $c \\; x_0^{d_0} \dots x_n^{d_n} $ (for natural or zero $d_i $) can be
represented as a finite sequence $(d_0; \dots; d_n)$.
2. One can represent variables as objects ("**labels**"), so term signature can be also represented as a matching of
each appeared variable with its power in the term. I.e. signature of term $c \\; x_0^{d_0} \dots x_n^{d_n} $ (for
natural non-zero $d_i $) can be represented as a finite matching $(x_0 \to d_1; \dots; x_n \to d_n)$.
All that three approaches are implemented by "list", "numbered", and "labeled" versions of polynomials and polynomial
spaces respectively. Whereas all rational functions are represented as fractions with corresponding polynomial numerator
and denominator, and rational functions' spaces are implemented in the same way as usual field of rational numbers (or
more precisely, as any field of fractions over integral domain) should be implemented.
So here are a bit of details. Let `C` by type of constants. Then:
1. `ListPolynomial`, `ListPolynomialSpace`, `ListRationalFunction` and `ListRationalFunctionSpace` implement the first
scenario. `ListPolynomial` stores polynomial $a_0 + \dots + a_n x^n $ as a coefficients
list `listOf(a_0, ..., a_n)` (of type `List<C>`).
They also have variation `ScalableListPolynomialSpace` that replaces former polynomials and
implements `ScaleOperations`.
2. `NumberedPolynomial`, `NumberedPolynomialSpace`, `NumberedRationalFunction` and `NumberedRationalFunctionSpace`
implement second scenario. `NumberedPolynomial` stores polynomials as structures of type `Map<List<UInt>, C>`.
Signatures are stored as `List<UInt>`. To prevent ambiguity signatures should not end with zeros.
3. `LabeledPolynomial`, `LabeledPolynomialSpace`, `LabeledRationalFunction` and `LabeledRationalFunctionSpace` implement
third scenario using common `Symbol` as variable type. `LabeledPolynomial` stores polynomials as structures of
type `Map<Map<Symbol, UInt>, C>`. Signatures are stored as `Map<Symbol, UInt>`. To prevent ambiguity each signature
should not map any variable to zero.
### Example: `ListPolynomial`
For example, polynomial $2 - 3x + x^2 $ (with `Int` coefficients) is represented
```kotlin
val polynomial: ListPolynomial<Int> = ListPolynomial(listOf(2, -3, 1))
// or
val polynomial: ListPolynomial<Int> = ListPolynomial(2, -3, 1)
```
All algebraic operations can be used in corresponding space:
```kotlin
val computationResult = Int.algebra.listPolynomialSpace {
ListPolynomial(2, -3, 1) + ListPolynomial(0, 6) == ListPolynomial(2, 3, 1)
}
println(computationResult) // true
```
For more see [examples](../examples/src/main/kotlin/space/kscience/kmath/functions/polynomials.kt).
### Example: `NumberedPolynomial`
For example, polynomial $3 + 5 x_1 - 7 x_0^2 x_2 $ (with `Int` coefficients) is represented
```kotlin
val polynomial: NumberedPolynomial<Int> = NumberedPolynomial(
mapOf(
listOf<UInt>() to 3,
listOf(0u, 1u) to 5,
listOf(2u, 0u, 1u) to -7,
)
)
// or
val polynomial: NumberedPolynomial<Int> = NumberedPolynomial(
listOf<UInt>() to 3,
listOf(0u, 1u) to 5,
listOf(2u, 0u, 1u) to -7,
)
```
All algebraic operations can be used in corresponding space:
```kotlin
val computationResult = Int.algebra.numberedPolynomialSpace {
NumberedPolynomial(
listOf<UInt>() to 3,
listOf(0u, 1u) to 5,
listOf(2u, 0u, 1u) to -7,
) + NumberedPolynomial(
listOf(0u, 1u) to -5,
listOf(0u, 0u, 0u, 4u) to 4,
) == NumberedPolynomial(
listOf<UInt>() to 3,
listOf(0u, 1u) to 0,
listOf(2u, 0u, 1u) to -7,
listOf(0u, 0u, 0u, 4u) to 4,
)
}
println(computationResult) // true
```
For more see [examples](../examples/src/main/kotlin/space/kscience/kmath/functions/polynomials.kt).
### Example: `LabeledPolynomial`
For example, polynomial $3 + 5 y - 7 x^2 z $ (with `Int` coefficients) is represented
```kotlin
val polynomial: LabeledPolynomial<Int> = LabeledPolynomial(
mapOf(
mapOf<Symbol, UInt>() to 3,
mapOf(y to 1u) to 5,
mapOf(x to 2u, z to 1u) to -7,
)
)
// or
val polynomial: LabeledPolynomial<Int> = LabeledPolynomial(
mapOf<Symbol, UInt>() to 3,
mapOf(y to 1u) to 5,
mapOf(x to 2u, z to 1u) to -7,
)
```
All algebraic operations can be used in corresponding space:
```kotlin
val computationResult = Int.algebra.labeledPolynomialSpace {
LabeledPolynomial(
listOf<UInt>() to 3,
listOf(0u, 1u) to 5,
listOf(2u, 0u, 1u) to -7,
) + LabeledPolynomial(
listOf(0u, 1u) to -5,
listOf(0u, 0u, 0u, 4u) to 4,
) == LabeledPolynomial(
listOf<UInt>() to 3,
listOf(0u, 1u) to 0,
listOf(2u, 0u, 1u) to -7,
listOf(0u, 0u, 0u, 4u) to 4,
)
}
println(computationResult) // true
```
For more see [examples](../examples/src/main/kotlin/space/kscience/kmath/functions/polynomials.kt).
## Abstract entities (interfaces and abstract classes)
```mermaid
classDiagram
Polynomial <|-- ListPolynomial
Polynomial <|-- NumberedPolynomial
Polynomial <|-- LabeledPolynomial
RationalFunction <|-- ListRationalFunction
RationalFunction <|-- NumberedRationalFunction
RationalFunction <|-- LabeledRationalFunction
Ring <|-- PolynomialSpace
PolynomialSpace <|-- MultivariatePolynomialSpace
PolynomialSpace <|-- PolynomialSpaceOverRing
Ring <|-- RationalFunctionSpace
RationalFunctionSpace <|-- MultivariateRationalFunctionSpace
RationalFunctionSpace <|-- RationalFunctionSpaceOverRing
RationalFunctionSpace <|-- RationalFunctionSpaceOverPolynomialSpace
RationalFunctionSpace <|-- PolynomialSpaceOfFractions
RationalFunctionSpaceOverPolynomialSpace <|-- MultivariateRationalFunctionSpaceOverMultivariatePolynomialSpace
MultivariateRationalFunctionSpace <|-- MultivariateRationalFunctionSpaceOverMultivariatePolynomialSpace
MultivariateRationalFunctionSpace <|-- MultivariatePolynomialSpaceOfFractions
PolynomialSpaceOfFractions <|-- MultivariatePolynomialSpaceOfFractions
```
There are implemented `Polynomial` and `RationalFunction` interfaces as abstractions of polynomials and rational
functions respectively (although, there is not a lot of logic in them) and `PolynomialSpace`
and `RationalFunctionSpace` (that implement `Ring` interface) as abstractions of polynomials' and rational functions'
spaces respectively. More precisely, that means they allow to declare common logic of interaction with such objects and
spaces:
- `Polynomial` does not provide any logic. It is marker interface.
- `RationalFunction` provides numerator and denominator of rational function and destructuring declaration for them.
- `PolynomialSpace` provides all possible arithmetic interactions of integers, constants (of type `C`), and
polynomials (of type `P`) like addition, subtraction, multiplication, and some others and common properties like
degree of polynomial.
- `RationalFunctionSpace` provides the same as `PolynomialSpace` but also for rational functions: all possible
arithmetic interactions of integers, constants (of type `C`), polynomials (of type `P`), and rational functions (of
type `R`) like addition, subtraction, multiplication, division (in some cases), and some others and common properties
like degree of polynomial.
Then to add abstraction of similar behaviour with variables (in multivariate case) there are
implemented `MultivariatePolynomialSpace` and `MultivariateRationalFunctionSpace`. They just include variables (of
type `V`) in the interactions of the entities.
Also, to remove boilerplates there were provided helping subinterfaces and abstract subclasses:
- `PolynomialSpaceOverRing` allows to replace implementation of interactions of integers and constants with
implementations from provided ring over constants (of type `A: Ring<C>`).
- `RationalFunctionSpaceOverRing` &mdash; the same but for `RationalFunctionSpace`.
- `RationalFunctionSpaceOverPolynomialSpace` &mdash; the same but "the inheritance" includes interactions with
polynomials from provided `PolynomialSpace`.
- `PolynomialSpaceOfFractions` is actually abstract subclass of `RationalFunctionSpace` that implements all fractions
boilerplates with provided (`protected`) constructor of rational functions by polynomial numerator and denominator.
- `MultivariateRationalFunctionSpaceOverMultivariatePolynomialSpace` and `MultivariatePolynomialSpaceOfFractions`
&mdash; the same stories of operators inheritance and fractions boilerplates respectively but in multivariate case.
## Utilities
For all kinds of polynomials there are provided (implementation details depend on kind of polynomials) such common
utilities as:
1. differentiation and anti-differentiation,
2. substitution, invocation and functional representation.

View File

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

View File

@ -3,7 +3,17 @@
The Maven coordinates of this project are `${group}:${name}:${version}`. The Maven coordinates of this project are `${group}:${name}:${version}`.
**Gradle:** **Gradle:**
```gradle
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation '${group}:${name}:${version}'
}
```
**Gradle Kotlin DSL:**
```kotlin ```kotlin
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")

View File

@ -1,93 +1,107 @@
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![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) [![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382)
![Gradle build](https://github.com/SciProgCentre/kmath/workflows/Gradle%20build/badge.svg) ![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg)
[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22) [![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22)
[![Space](https://img.shields.io/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/) [![Space](https://img.shields.io/badge/dynamic/xml?color=orange&label=Space&query=//metadata/versioning/latest&url=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fspace%2Fkscience%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/)
# KMath # KMath
Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based analog to
analog to Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior architecture
architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could
experience could be achieved with [kmath-for-real](/kmath-for-real) extension module. be achieved with [kmath-for-real](/kmath-for-real) extension module.
[Documentation site](https://SciProgCentre.github.io/kmath/) [Documentation site (**WIP**)](https://mipt-npm.github.io/kmath/)
## Publications and talks ## Publications and talks
* [A conceptual article about context-oriented design](https://proandroiddev.com/an-introduction-context-oriented-programming-in-kotlin-2e79d316b0a2) * [A conceptual article about context-oriented design](https://proandroiddev.com/an-introduction-context-oriented-programming-in-kotlin-2e79d316b0a2)
* [Another article about context-oriented design](https://proandroiddev.com/diving-deeper-into-context-oriented-programming-in-kotlin-3ecb4ec38814) * [Another article about context-oriented design](https://proandroiddev.com/diving-deeper-into-context-oriented-programming-in-kotlin-3ecb4ec38814)
* [ACAT 2019 conference paper](https://aip.scitation.org/doi/abs/10.1063/1.5130103) * [ACAT 2019 conference paper](https://aip.scitation.org/doi/abs/10.1063/1.5130103)
* [A talk at KotlinConf 2019 about using kotlin for science](https://youtu.be/LI_5TZ7tnOE?si=4LknX41gl_YeUbIe)
* [A talk on architecture at Joker-2021 (in Russian)](https://youtu.be/1bZ2doHiRRM?si=9w953ro9yu98X_KJ)
* [The same talk in English](https://youtu.be/yP5DIc2fVwQ?si=louZzQ1dcXV6gP10)
* [A seminar on tensor API](https://youtu.be/0H99wUs0xTM?si=6c__04jrByFQtVpo)
# Goal # Goal
* Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS, Native and * Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM, JS and Native).
Wasm).
* Provide basic multiplatform implementations for those abstractions (without significant performance optimization). * Provide basic multiplatform implementations for those abstractions (without significant performance optimization).
* Provide bindings and wrappers with those abstractions for popular optimized platform libraries. * Provide bindings and wrappers with those abstractions for popular optimized platform libraries.
## Non-goals ## Non-goals
* Be like NumPy. It was the idea at the beginning, but we decided that we can do better in API. * Be like NumPy. It was the idea at the beginning, but we decided that we can do better in terms of API.
* Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them. * Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them.
* Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually. * Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually.
* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like * Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like
for `Double` in the core. For that we will have specialization modules like `kmath-for-real`, which will give better for `Double` in the core. For that we will have specialization modules like `kmath-for-real`, which will give better
experience for those, who want to work with specific types. experience for those, who want to work with specific types.
## Features and stability ## Features and stability
KMath is a modular library. Different modules provide different features with different API stability guarantees. All KMath is a modular library. Different modules provide different features with different API stability guarantees. All core modules are released with the same version, but with different API change policy. The features are described in module definitions below. The module stability could have following levels:
core modules are released with the same version, but with different API change policy. The features are described in
module definitions below. The module stability could have the following levels:
* **PROTOTYPE**. On this level there are no compatibility guarantees. All methods and classes form those modules could * **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.
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.
* **EXPERIMENTAL**. The general API is decided, but some changes could be made. Volatile API is marked * **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.
with `@UnstableKMathAPI` or other stability warning annotations.
* **DEVELOPMENT**. API breaking generally follows semantic versioning ideology. There could be changes in minor
versions, but not in patch versions. API is protected
with [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator) tool.
* **STABLE**. The API stabilized. Breaking changes are allowed only in major releases. * **STABLE**. The API stabilized. Breaking changes are allowed only in major releases.
<!--Current feature list is [here](/docs/features.md)-->
<!--* **Array-like structures** Full support of many-dimensional array-like structures -->
<!--including mixed arithmetic operations and function operations over arrays and numbers (with the added benefit of static type checking).-->
<!--* **Histograms** Fast multi-dimensional histograms.-->
<!--* **Streaming** Streaming operations on mathematical objects and objects buffers.-->
<!--* **Type-safe dimensions** Type-safe dimensions for matrix operations.-->
<!--* **Commons-math wrapper** It is planned to gradually wrap most parts of -->
<!--[Apache commons-math](http://commons.apache.org/proper/commons-math/) library in Kotlin code and maybe rewrite some -->
<!--parts to better suit the Kotlin programming paradigm, however there is no established roadmap for that. Feel free to -->
<!--submit a feature request if you want something to be implemented first.-->
<!-- -->
<!--## Planned features-->
<!--* **Messaging** A mathematical notation to support multi-language and multi-node communication for mathematical tasks.-->
<!--* **Array statistics** -->
<!--* **Integration** Univariate and multivariate integration framework.-->
<!--* **Probability and distributions**-->
<!--* **Fitting** Non-linear curve fitting facilities-->
## Modules ## Modules
${modules} $modules
## Multi-platform support ## Multi-platform support
KMath is developed as a multi-platform library, which means that most of the interfaces are declared in the KMath is developed as a multi-platform library, which means that most of the interfaces are declared in the
[common source sets](/kmath-core/src/commonMain) and implemented there wherever it is possible. In some cases, features [common source sets](/kmath-core/src/commonMain) and implemented there wherever it is possible. In some cases, features
are delegated to platform-specific implementations even if they could be provided in the common module for performance are delegated to platform-specific implementations even if they could be provided in the common module for performance
reasons. Currently, Kotlin/JVM is the primary platform, however, Kotlin/Native and Kotlin/JS contributions and reasons. Currently, the Kotlin/JVM is the primary platform, however Kotlin/Native and Kotlin/JS contributions and
feedback are also welcome. feedback are also welcome.
## Performance ## Performance
Calculation of performance is one of the major goals of KMath in the future, but in some cases it is impossible to Calculation performance is one of major goals of KMath in the future, but in some cases it is impossible to achieve
achieve both both performance and flexibility.
performance and flexibility.
We expect to focus on creating a convenient universal API first and then work on increasing performance for specific We expect to focus on creating convenient universal API first and then work on increasing performance for specific
cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized
native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be
better than SciPy. better than SciPy.
## Requirements ## Requirements
KMath currently relies on JDK 11 for compilation and execution of Kotlin-JVM part. We recommend using GraalVM-CE or 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.
Oracle GraalVM for execution to get better performance.
### Repositories ### Repositories
Release and development artifacts are accessible from mipt-npm [Space](https://www.jetbrains.com/space/) 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
repository `https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven` (see documentation of [Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details). The repository could be reached through [repo.kotlin.link](https://repo.kotlin.link) proxy:
[Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) for more details). The repository could
be reached through [repo.kotlin.link](https://repo.kotlin.link) proxy:
```kotlin ```kotlin
repositories { repositories {
@ -100,10 +114,11 @@ dependencies {
} }
``` ```
Gradle `6.0+` is required for multiplatform artifacts.
## Contributing ## Contributing
The project requires a lot of additional work. The most important thing we need is feedback about what features are The project requires a lot of additional work. The most important thing we need is a feedback about what features are
required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues required the most. Feel free to create feature requests. We are also welcome to code contributions,
marked especially in issues marked with
with [good first issue](hhttps://github.com/SciProgCentre/kmath/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) [waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero) label.
label.

View File

@ -1,4 +0,0 @@
# Module examples

View File

@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
plugins { plugins {
kotlin("jvm") kotlin("jvm")
} }
@ -7,7 +5,12 @@ plugins {
repositories { repositories {
mavenCentral() mavenCentral()
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
maven("https://clojars.org/repo")
maven("https://jitpack.io")
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers") maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers")
maven("http://logicrunch.research.it.uu.se/maven") {
isAllowInsecureProtocol = true
}
} }
dependencies { dependencies {
@ -17,8 +20,6 @@ dependencies {
implementation(project(":kmath-coroutines")) implementation(project(":kmath-coroutines"))
implementation(project(":kmath-commons")) implementation(project(":kmath-commons"))
implementation(project(":kmath-complex")) implementation(project(":kmath-complex"))
implementation(project(":kmath-functions"))
implementation(project(":kmath-optimization"))
implementation(project(":kmath-stat")) implementation(project(":kmath-stat"))
implementation(project(":kmath-viktor")) implementation(project(":kmath-viktor"))
implementation(project(":kmath-dimensions")) implementation(project(":kmath-dimensions"))
@ -27,14 +28,6 @@ dependencies {
implementation(project(":kmath-tensors")) implementation(project(":kmath-tensors"))
implementation(project(":kmath-symja")) implementation(project(":kmath-symja"))
implementation(project(":kmath-for-real")) implementation(project(":kmath-for-real"))
//jafama
implementation(project(":kmath-jafama"))
//multik
implementation(project(":kmath-multik"))
implementation(libs.multik.default)
//datetime
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
implementation("org.nd4j:nd4j-native:1.0.0-beta7") implementation("org.nd4j:nd4j-native:1.0.0-beta7")
@ -48,28 +41,28 @@ dependencies {
// } else // } else
implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7") implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7")
implementation("org.slf4j:slf4j-simple:1.7.32") implementation("org.slf4j:slf4j-simple:1.7.30")
// plotting // plotting
implementation("space.kscience:plotlykt-server:0.7.0") implementation("space.kscience:plotlykt-server:0.4.0")
//jafama
implementation(project(":kmath-jafama"))
} }
kotlin { kotlin.sourceSets.all {
jvmToolchain(11) with(languageSettings) {
sourceSets.all { useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
languageSettings { useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
optIn("kotlin.contracts.ExperimentalContracts") useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI")
optIn("kotlin.ExperimentalUnsignedTypes")
optIn("space.kscience.kmath.UnstableKMathAPI")
}
} }
} }
tasks.withType<KotlinJvmCompile> { tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
compilerOptions { kotlinOptions{
freeCompilerArgs.addAll("-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn", "-Xlambdas=indy") jvmTarget = "11"
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" + "-Xopt-in=kotlin.RequiresOptIn"
} }
} }
readme { readme {
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
} }

View File

@ -1,418 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"source": [
"%use kmath(0.3.1-dev-5)\n",
"%use plotly(0.5.0)\n",
"@file:DependsOn(\"space.kscience:kmath-commons:0.3.1-dev-5\")"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "lQbSB87rNAn9lV6poArVWW",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "code",
"source": [
"//Uncomment to work in Jupyter classic or DataLore\n",
"//Plotly.jupyter.notebook()"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "0UP158hfccGgjQtHz0wAi6",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "markdown",
"source": [
"# The model\n",
"\n",
"Defining the input data format, the statistic abstraction and the statistic implementation based on a weighted sum of elements."
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"source": [
"class XYValues(val xValues: DoubleArray, val yValues: DoubleArray) {\n",
" init {\n",
" require(xValues.size == yValues.size)\n",
" }\n",
"}\n",
"\n",
"fun interface XYStatistic {\n",
" operator fun invoke(values: XYValues): Double\n",
"}\n",
"\n",
"class ConvolutionalXYStatistic(val weights: DoubleArray) : XYStatistic {\n",
" override fun invoke(values: XYValues): Double {\n",
" require(weights.size == values.yValues.size)\n",
" val norm = values.yValues.sum()\n",
" return values.yValues.zip(weights) { value, weight -> value * weight }.sum()/norm\n",
" }\n",
"}"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "Zhgz1Ui91PWz0meJiQpHol",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "markdown",
"source": [
"# Generator\n",
"Generate sample data for parabolas and hyperbolas"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"fun generateParabolas(xValues: DoubleArray, a: Double, b: Double, c: Double): XYValues {\n",
" val yValues = xValues.map { x -> a * x * x + b * x + c }.toDoubleArray()\n",
" return XYValues(xValues, yValues)\n",
"}\n",
"\n",
"fun generateHyperbols(xValues: DoubleArray, gamma: Double, x0: Double, y0: Double): XYValues {\n",
" val yValues = xValues.map { x -> y0 + gamma / (x - x0) }.toDoubleArray()\n",
" return XYValues(xValues, yValues)\n",
"}"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"source": [
"val xValues = (1.0..10.0).step(1.0).toDoubleArray()\n",
"\n",
"val xy = generateHyperbols(xValues, 1.0, 0.0, 0.0)\n",
"\n",
"Plotly.plot {\n",
" scatter {\n",
" this.x.doubles = xValues\n",
" this.y.doubles = xy.yValues\n",
" }\n",
"}"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "ZE2atNvFzQsCvpAF8KK4ch",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "markdown",
"source": [
"Create a default statistic with uniform weights"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"source": [
"val statistic = ConvolutionalXYStatistic(DoubleArray(xValues.size){1.0})\n",
"statistic(xy)"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "EA5HaydTddRKYrtAUwd29h",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "code",
"source": [
"import kotlin.random.Random\n",
"\n",
"val random = Random(1288)\n",
"\n",
"val parabolas = buildList{\n",
" repeat(500){\n",
" add(\n",
" generateParabolas(\n",
" xValues, \n",
" random.nextDouble(), \n",
" random.nextDouble(), \n",
" random.nextDouble()\n",
" )\n",
" )\n",
" }\n",
"}\n",
"\n",
"val hyperbolas: List<XYValues> = buildList{\n",
" repeat(500){\n",
" add(\n",
" generateHyperbols(\n",
" xValues, \n",
" random.nextDouble()*10, \n",
" random.nextDouble(), \n",
" random.nextDouble()\n",
" )\n",
" )\n",
" }\n",
"}"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "t5t6IYmD7Q1ykeo9uijFfQ",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "code",
"source": [
"Plotly.plot { \n",
" scatter { \n",
" x.doubles = xValues\n",
" y.doubles = parabolas[257].yValues\n",
" }\n",
" scatter { \n",
" x.doubles = xValues\n",
" y.doubles = hyperbolas[252].yValues\n",
" }\n",
" }"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "oXB8lmju7YVYjMRXITKnhO",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "code",
"source": [
"Plotly.plot { \n",
" histogram { \n",
" name = \"parabolae\"\n",
" x.numbers = parabolas.map { statistic(it) }\n",
" }\n",
" histogram { \n",
" name = \"hyperbolae\"\n",
" x.numbers = hyperbolas.map { statistic(it) }\n",
" }\n",
"}"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "8EIIecUZrt2NNrOkhxG5P0",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "code",
"source": [
"val lossFunction: (XYStatistic) -> Double = { statistic ->\n",
" - abs(parabolas.sumOf { statistic(it) } - hyperbolas.sumOf { statistic(it) })\n",
"}"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "h7UmglJW5zXkAfKHK40oIL",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "markdown",
"source": [
"Using commons-math optimizer to optimize weights"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"source": [
"import org.apache.commons.math3.optim.*\n",
"import org.apache.commons.math3.optim.nonlinear.scalar.*\n",
"import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.*\n",
"\n",
"val optimizer = SimplexOptimizer(1e-1, Double.MAX_VALUE)\n",
"\n",
"val result = optimizer.optimize(\n",
" ObjectiveFunction { point ->\n",
" lossFunction(ConvolutionalXYStatistic(point))\n",
" },\n",
" NelderMeadSimplex(xValues.size),\n",
" InitialGuess(DoubleArray(xValues.size){ 1.0 }),\n",
" GoalType.MINIMIZE,\n",
" MaxEval(100000)\n",
")"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "0EG3K4aCUciMlgGQKPvJ57",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "markdown",
"source": [
"Print resulting weights of optimization"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"source": [
"result.point"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "LelUlY0ZSlJEO9yC6SLk5B",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "code",
"source": [
"Plotly.plot { \n",
" scatter { \n",
" y.doubles = result.point\n",
" }\n",
"}"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "AuFOq5t9KpOIkGrOLsVXNf",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "markdown",
"source": [
"# The resulting statistic distribution"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"source": [
"val resultStatistic = ConvolutionalXYStatistic(result.point)\n",
"Plotly.plot { \n",
" histogram { \n",
" name = \"parabolae\"\n",
" x.numbers = parabolas.map { resultStatistic(it) }\n",
" }\n",
" histogram { \n",
" name = \"hyperbolae\"\n",
" x.numbers = hyperbolas.map { resultStatistic(it) }\n",
" }\n",
"}"
],
"execution_count": null,
"outputs": [],
"metadata": {
"datalore": {
"node_id": "zvmq42DRdM5mZ3SpzviHwI",
"type": "CODE",
"hide_input_from_viewers": false,
"hide_output_from_viewers": false
}
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false
}
}
],
"metadata": {
"kernelspec": {
"display_name": "Kotlin",
"language": "kotlin",
"name": "kotlin"
},
"datalore": {
"version": 1,
"computation_mode": "JUPYTER",
"package_manager": "pip",
"base_environment": "default",
"packages": []
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -10,7 +10,7 @@ import space.kscience.kmath.ast.rendering.LatexSyntaxRenderer
import space.kscience.kmath.ast.rendering.MathMLSyntaxRenderer import space.kscience.kmath.ast.rendering.MathMLSyntaxRenderer
import space.kscience.kmath.ast.rendering.renderWithStringBuilder import space.kscience.kmath.ast.rendering.renderWithStringBuilder
fun main() { public fun main() {
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath() val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath()
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst) val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
println("MathSyntax:") println("MathSyntax:")

View File

@ -1,26 +1,22 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.ast package space.kscience.kmath.ast
import space.kscience.kmath.asm.compileToExpression import space.kscience.kmath.expressions.MstField
import space.kscience.kmath.expressions.MstExtendedField
import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.Symbol.Companion.x
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.expressions.interpret
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
fun main() { fun main() {
val expr = MstExtendedField { val expr = MstField {
x * 2.0 + number(2.0) / x - number(16.0) + asinh(x) / sin(x) x * 2.0 + number(2.0) / x - 16.0
}.compileToExpression(Float64Field) }
val m = DoubleArray(expr.indexer.symbols.size)
val xIdx = expr.indexer.indexOf(x)
repeat(10000000) { repeat(10000000) {
m[xIdx] = 1.0 expr.interpret(DoubleField, x to 1.0)
expr(m)
} }
} }

View File

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

View File

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

View File

@ -1,31 +1,32 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.fit package space.kscience.kmath.commons.fit
import kotlinx.html.br import kotlinx.html.br
import kotlinx.html.h3 import kotlinx.html.h3
import space.kscience.kmath.commons.optimization.CMOptimizer import space.kscience.kmath.commons.optimization.chiSquared
import space.kscience.kmath.commons.optimization.minimize
import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.expressions.autodiff
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.operations.asIterable import space.kscience.kmath.optimization.FunctionOptimization
import space.kscience.kmath.operations.toList import space.kscience.kmath.optimization.OptimizationResult
import space.kscience.kmath.optimization.*
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.real.DoubleVector import space.kscience.kmath.real.DoubleVector
import space.kscience.kmath.real.map import space.kscience.kmath.real.map
import space.kscience.kmath.real.step import space.kscience.kmath.real.step
import space.kscience.kmath.stat.chiSquaredExpression 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.*
import space.kscience.plotly.models.ScatterMode import space.kscience.plotly.models.ScatterMode
import space.kscience.plotly.models.TraceValues import space.kscience.plotly.models.TraceValues
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.sqrt import kotlin.math.sqrt
// Forward declaration of symbols that will be used in expressions. //Forward declaration of symbols that will be used in expressions.
// This declaration is required for
private val a by symbol private val a by symbol
private val b by symbol private val b by symbol
private val c by symbol private val c by symbol
@ -42,7 +43,7 @@ operator fun TraceValues.invoke(vector: DoubleVector) {
*/ */
suspend fun main() { suspend fun main() {
//A generator for a normally distributed values //A generator for a normally distributed values
val generator = NormalDistribution(0.0, 1.0) val generator = NormalDistribution(2.0, 7.0)
//A chain/flow of random values with the given seed //A chain/flow of random values with the given seed
val chain = generator.sample(RandomGenerator.default(112667)) val chain = generator.sample(RandomGenerator.default(112667))
@ -53,7 +54,7 @@ suspend fun main() {
//Perform an operation on each x value (much more effective, than numpy) //Perform an operation on each x value (much more effective, than numpy)
val y = x.map { it -> val y = x.map {
val value = it.pow(2) + it + 1 val value = it.pow(2) + it + 1
value + chain.next() * sqrt(value) value + chain.next() * sqrt(value)
} }
@ -64,22 +65,17 @@ suspend fun main() {
val yErr = y.map { sqrt(it) }//RealVector.same(x.size, sigma) val yErr = y.map { sqrt(it) }//RealVector.same(x.size, sigma)
// compute differentiable chi^2 sum for given model ax^2 + bx + c // compute differentiable chi^2 sum for given model ax^2 + bx + c
val chi2 = Double.autodiff.chiSquaredExpression(x, y, yErr) { arg -> val chi2 = FunctionOptimization.chiSquared(x, y, yErr) { x1 ->
//bind variables to autodiff context //bind variables to autodiff context
val a = bindSymbol(a) val a = bindSymbol(a)
val b = bindSymbol(b) val b = bindSymbol(b)
//Include default value for c if it is not provided as a parameter //Include default value for c if it is not provided as a parameter
val c = bindSymbolOrNull(c) ?: one val c = bindSymbolOrNull(c) ?: one
a * arg.pow(2) + b * arg + c a * x1.pow(2) + b * x1 + c
} }
//minimize the chi^2 in given starting point. Derivatives are not required, they are already included. //minimize the chi^2 in given starting point. Derivatives are not required, they are already included.
val result = chi2.optimizeWith( val result: OptimizationResult<Double> = chi2.minimize(a to 1.5, b to 0.9, c to 1.0)
CMOptimizer,
mapOf(a to 1.5, b to 0.9, c to 1.0),
) {
FunctionOptimizationTarget(OptimizationDirection.MINIMIZE)
}
//display a page with plot and numerical results //display a page with plot and numerical results
val page = Plotly.page { val page = Plotly.page {
@ -96,7 +92,7 @@ suspend fun main() {
scatter { scatter {
mode = ScatterMode.lines mode = ScatterMode.lines
x(x) x(x)
y(x.map { result.result[a]!! * it.pow(2) + result.result[b]!! * it + 1 }) y(x.map { result.point[a]!! * it.pow(2) + result.point[b]!! * it + 1 })
name = "fit" name = "fit"
} }
} }
@ -105,7 +101,7 @@ suspend fun main() {
+"Fit result: $result" +"Fit result: $result"
} }
h3 { h3 {
+"Chi2/dof = ${result.resultValue / (x.size - 3)}" +"Chi2/dof = ${result.value / (x.size - 3)}"
} }
} }

View File

@ -1,92 +0,0 @@
/*
* Copyright 2018-2024 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.expressions
import space.kscience.kmath.UnstableKMathAPI
// Only kmath-core is needed.
// Let's declare some variables
val x by symbol
val y by symbol
val z by symbol
@OptIn(UnstableKMathAPI::class)
fun main() {
// Let's define some random expression.
val someExpression = Double.autodiff.differentiate {
// We bind variables `x` and `y` to the builder scope,
val x = bindSymbol(x)
val y = bindSymbol(y)
// Then we use the bindings to define expression `xy + x + y - 1`
x * y + x + y - 1
}
// Then we can evaluate it at any point ((-1, -1) in the case):
println(someExpression(x to -1.0, y to -1.0))
// >>> -2.0
// We can also construct its partial derivatives:
val dxExpression = someExpression.derivative(x) // ∂/∂x. Must be `y+1`
val dyExpression = someExpression.derivative(y) // ∂/∂y. Must be `x+1`
val dxdxExpression = someExpression.derivative(x, x) // ∂^2/∂x^2. Must be `0`
// We can evaluate them as well
println(dxExpression(x to 57.0, y to 6.0))
// >>> 7.0
println(dyExpression(x to -1.0, y to 179.0))
// >>> 0.0
println(dxdxExpression(x to 239.0, y to 30.0))
// >>> 0.0
// You can also provide extra arguments that obviously won't affect the result:
println(dxExpression(x to 57.0, y to 6.0, z to 42.0))
// >>> 7.0
println(dyExpression(x to -1.0, y to 179.0, z to 0.0))
// >>> 0.0
println(dxdxExpression(x to 239.0, y to 30.0, z to 100_000.0))
// >>> 0.0
// But in case you forgot to specify bound symbol's value, exception is thrown:
println(runCatching { someExpression(z to 4.0) })
// >>> Failure(java.lang.IllegalStateException: Symbol 'x' is not supported in ...)
// The reason is that the expression is evaluated lazily,
// and each `bindSymbol` operation actually substitutes the provided symbol with the corresponding value.
// For example, let there be an expression
val simpleExpression = Double.autodiff.differentiate {
val x = bindSymbol(x)
x pow 2
}
// When you evaluate it via
simpleExpression(x to 1.0, y to 57.0, z to 179.0)
// lambda above has the context of map `{x: 1.0, y: 57.0, z: 179.0}`.
// When x is bound, you can think of it as substitution `x -> 1.0`.
// Other values are unused which does not make any problem to us.
// But in the case the corresponding value is not provided,
// we cannot bind the variable. Thus, exception is thrown.
// There is also a function `bindSymbolOrNull` that fixes the problem:
val fixedExpression = Double.autodiff.differentiate {
val x = bindSymbolOrNull(x) ?: const(8.0)
x pow -2
}
println(fixedExpression())
// >>> 0.015625
// It works!
// The expression provides a bunch of operations:
// 1. Constant bindings (via `const` and `number`).
// 2. Variable bindings (via `bindVariable`, `bindVariableOrNull`).
// 3. Arithmetic operations (via `+`, `-`, `*`, and `-`).
// 4. Exponentiation (via `pow` or `power`).
// 5. `exp` and `ln`.
// 6. Trigonometrical functions (`sin`, `cos`, `tan`, `cot`).
// 7. Inverse trigonometrical functions (`asin`, `acos`, `atan`, `acot`).
// 8. Hyperbolic functions and inverse hyperbolic functions.
}

View File

@ -1,112 +0,0 @@
/*
* Copyright 2018-2024 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.fit
import kotlinx.html.br
import kotlinx.html.h3
import space.kscience.attributes.Attributes
import space.kscience.kmath.data.XYErrorColumnarData
import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.expressions.autodiff
import space.kscience.kmath.expressions.binding
import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.operations.asIterable
import space.kscience.kmath.operations.toList
import space.kscience.kmath.optimization.*
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.real.map
import space.kscience.kmath.real.step
import space.kscience.plotly.*
import space.kscience.plotly.models.ScatterMode
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sqrt
// Forward declaration of symbols that will be used in expressions.
private val a by symbol
private val b by symbol
private val c by symbol
private val d by symbol
private val e by symbol
/**
* Least squares fie with auto-differentiation. Uses `kmath-commons` and `kmath-for-real` modules.
*/
suspend fun main() {
//A generator for a normally distributed values
val generator = NormalDistribution(0.0, 1.0)
//A chain/flow of random values with the given seed
val chain = generator.sample(RandomGenerator.default(112667))
//Create a uniformly distributed x values like numpy.arrange
val x = 1.0..100.0 step 1.0
//Perform an operation on each x value (much more effective, than numpy)
val y = x.map { it ->
val value = it.pow(2) + it + 1
value + chain.next() * sqrt(value)
}
// this will also work, but less effective:
// val y = x.pow(2)+ x + 1 + chain.nextDouble()
// create same errors for all xs
val yErr = y.map { sqrt(abs(it)) }
require(yErr.asIterable().all { it > 0 }) { "All errors must be strictly positive" }
val result = XYErrorColumnarData.of(x, y, yErr).fitWith(
QowOptimizer,
Double.autodiff,
mapOf(a to 0.9, b to 1.2, c to 2.0, e to 1.0, d to 1.0, e to 0.0),
attributes = Attributes(OptimizationParameters, listOf(a, b, c, d))
) { arg ->
//bind variables to autodiff context
val a by binding
val b by binding
//Include default value for c if it is not provided as a parameter
val c = bindSymbolOrNull(c) ?: one
val d by binding
val e by binding
a * arg.pow(2) + b * arg + c + d * arg.pow(3) + e / arg
}
println("Resulting chi2/dof: ${result.chiSquaredOrNull}/${result.dof}")
//display a page with plot and numerical results
val page = Plotly.page {
plot {
scatter {
mode = ScatterMode.markers
x(x)
y(y)
error_y {
array = yErr.toList()
}
name = "data"
}
scatter {
mode = ScatterMode.lines
x(x)
y(x.map { result.model(result.startPoint + result.result + (Symbol.x to it)) })
name = "fit"
}
}
br()
h3 {
+"Fit result: ${result.result}"
}
h3 {
+"Chi2/dof = ${result.chiSquaredOrNull!! / result.dof}"
}
}
page.makeFile()
}

View File

@ -1,37 +1,23 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.functions package space.kscience.kmath.functions
import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.ComplexField.div
import space.kscience.kmath.complex.ComplexField.minus
import space.kscience.kmath.complex.algebra
import space.kscience.kmath.integration.gaussIntegrator import space.kscience.kmath.integration.gaussIntegrator
import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.integrate
import space.kscience.kmath.integration.value import space.kscience.kmath.integration.value
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Float64
import kotlin.math.pow import kotlin.math.pow
fun main() { fun main() {
//Define a function //Define a function
val function: Function1D<Float64> = { x -> 3 * x.pow(2) + 2 * x + 1 } val function: UnivariateFunction<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 }
//get the result of the integration //get the result of the integration
val result = Float64Field.gaussIntegrator.integrate(0.0..10.0, function = function) val result = DoubleField.gaussIntegrator.integrate(0.0..10.0, function = function)
//the value is nullable because in some cases the integration could not succeed //the value is nullable because in some cases the integration could not succeed
println(result.value) println(result.value)
repeat(100000) {
Complex.algebra.gaussIntegrator.integrate(0.0..1.0, intervals = 1000) { x: Double ->
// sin(1 / x) + i * cos(1 / x)
1 / x - ComplexField.i / x
}.value
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -7,8 +7,8 @@ package space.kscience.kmath.functions
import space.kscience.kmath.interpolation.SplineInterpolator import space.kscience.kmath.interpolation.SplineInterpolator
import space.kscience.kmath.interpolation.interpolatePolynomials import space.kscience.kmath.interpolation.interpolatePolynomials
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.plotly.Plotly import space.kscience.plotly.Plotly
import space.kscience.plotly.UnstablePlotlyAPI import space.kscience.plotly.UnstablePlotlyAPI
import space.kscience.plotly.makeFile import space.kscience.plotly.makeFile
@ -24,9 +24,11 @@ fun main() {
x to sin(x) x to sin(x)
} }
val polynomial: PiecewisePolynomial<Float64> = SplineInterpolator(Float64Field).interpolatePolynomials(data) val polynomial: PiecewisePolynomial<Double> = SplineInterpolator(
DoubleField, ::DoubleBuffer
).interpolatePolynomials(data)
val function = polynomial.asFunction(Float64Field, 0.0) val function = polynomial.asFunction(DoubleField, 0.0)
val cmInterpolate = org.apache.commons.math3.analysis.interpolation.SplineInterpolator().interpolate( val cmInterpolate = org.apache.commons.math3.analysis.interpolation.SplineInterpolator().interpolate(
data.map { it.first }.toDoubleArray(), data.map { it.first }.toDoubleArray(),

View File

@ -1,16 +1,15 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.functions package space.kscience.kmath.functions
import space.kscience.kmath.interpolation.SplineInterpolator
import space.kscience.kmath.interpolation.interpolatePolynomials import space.kscience.kmath.interpolation.interpolatePolynomials
import space.kscience.kmath.interpolation.splineInterpolator import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.real.map
import space.kscience.kmath.real.step import space.kscience.kmath.real.step
import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.map
import space.kscience.plotly.Plotly import space.kscience.plotly.Plotly
import space.kscience.plotly.UnstablePlotlyAPI import space.kscience.plotly.UnstablePlotlyAPI
import space.kscience.plotly.makeFile import space.kscience.plotly.makeFile
@ -19,7 +18,7 @@ import space.kscience.plotly.scatter
@OptIn(UnstablePlotlyAPI::class) @OptIn(UnstablePlotlyAPI::class)
fun main() { fun main() {
val function: Function1D<Float64> = { x -> val function: UnivariateFunction<Double> = { x ->
if (x in 30.0..50.0) { if (x in 30.0..50.0) {
1.0 1.0
} else { } else {
@ -29,9 +28,9 @@ fun main() {
val xs = 0.0..100.0 step 0.5 val xs = 0.0..100.0 step 0.5
val ys = xs.map(function) val ys = xs.map(function)
val polynomial: PiecewisePolynomial<Float64> = Float64Field.splineInterpolator.interpolatePolynomials(xs, ys) val polynomial: PiecewisePolynomial<Double> = SplineInterpolator.double.interpolatePolynomials(xs, ys)
val polyFunction = polynomial.asFunction(Float64Field, 0.0) val polyFunction = polynomial.asFunction(DoubleField, 0.0)
Plotly.plot { Plotly.plot {
scatter { scatter {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -9,25 +9,25 @@ import space.kscience.kmath.integration.gaussIntegrator
import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.integrate
import space.kscience.kmath.integration.value import space.kscience.kmath.integration.value
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.structureND import space.kscience.kmath.nd.nd
import space.kscience.kmath.nd.withNdAlgebra import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Float64
import kotlin.math.pow
fun main(): Unit = Double.algebra.withNdAlgebra(2, 2) { fun main(): Unit = DoubleField {
nd(2, 2) {
//Produce a diagonal StructureND //Produce a diagonal StructureND
fun diagonal(v: Double) = structureND { (i, j) -> fun diagonal(v: Double) = produce { (i, j) ->
if (i == j) v else 0.0 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)
} }
//Define a function in a nd space
val function: (Double) -> StructureND<Float64> = { x: Double -> 3 * 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)
} }

View File

@ -1,15 +0,0 @@
/*
* Copyright 2018-2024 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.jafama
import space.kscience.kmath.operations.invoke
fun main() {
val a = 2.0
val b = StrictJafamaDoubleField { exp(a) }
println(JafamaDoubleField { b + a })
println(StrictJafamaDoubleField { ln(b) })
}

View File

@ -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))
}

View File

@ -1,31 +0,0 @@
/*
* Copyright 2018-2024 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 kotlin.random.Random
import kotlin.time.measureTime
fun main() = with(Float64ParallelLinearSpace) {
val random = Random(12224)
val dim = 1000
//creating invertible matrix
val matrix1 = buildMatrix(dim, dim) { i, j ->
if (i <= j) random.nextDouble() else 0.0
}
val matrix2 = buildMatrix(dim, dim) { i, j ->
if (i <= j) random.nextDouble() else 0.0
}
val time = measureTime {
repeat(30) {
matrix1 dot matrix2
}
}
println(time)
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2018-2024 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.commons.linear.CMLinearSpace
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.structures.Float64
import kotlin.random.Random
fun main() {
val dim = 46
val random = Random(123)
val u = Float64.algebra.linearSpace.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
listOf(CMLinearSpace, EjmlLinearSpaceDDRM).forEach { algebra ->
with(algebra) {
//create a simmetric matrix
val matrix = buildMatrix(dim, dim) { row, col ->
if (row >= col) u[row, col] else u[col, row]
}
val eigen = matrix.getOrComputeAttribute(EIG) ?: error("Failed to compute eigenvalue decomposition")
check(
StructureND.contentEquals(
matrix,
eigen.v dot eigen.d dot eigen.v.transposed(),
1e-4
)
) { "$algebra decomposition failed" }
println("$algebra eigenvalue decomposition complete and checked" )
}
}
}

View File

@ -1,28 +1,27 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.kmath.real.* import space.kscience.kmath.real.*
import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.Float64Buffer
fun main() { fun main() {
val x0 = DoubleVector(0.0, 0.0, 0.0) val x0 = DoubleVector(0.0, 0.0, 0.0)
val sigma = DoubleVector(1.0, 1.0, 1.0) val sigma = DoubleVector(1.0, 1.0, 1.0)
val gaussian: (Point<Float64>) -> Double = { x -> val gaussian: (Point<Double>) -> Double = { x ->
require(x.size == x0.size) require(x.size == x0.size)
kotlin.math.exp(-((x - x0) / sigma).square().sum()) kotlin.math.exp(-((x - x0) / sigma).square().sum())
} }
fun ((Point<Float64>) -> Double).grad(x: Point<Float64>): Point<Float64> { fun ((Point<Double>) -> Double).grad(x: Point<Double>): Point<Double> {
require(x.size == x0.size) require(x.size == x0.size)
return Float64Buffer(x.size) { i -> return DoubleBuffer(x.size) { i ->
val h = sigma[i] / 5 val h = sigma[i] / 5
val dVector = Float64Buffer(x.size) { if (it == i) h else 0.0 } val dVector = DoubleBuffer(x.size) { if (it == i) h else 0.0 }
val f1 = this(x + dVector / 2) val f1 = this(x + dVector / 2)
val f0 = this(x - dVector / 2) val f0 = this(x - dVector / 2)
(f1 - f0) / h (f1 - f0) / h

View File

@ -1,27 +0,0 @@
/*
* Copyright 2018-2024 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 kotlin.random.Random
import kotlin.time.measureTime
fun main(): Unit = with(Float64LinearSpace) {
val random = Random(1224)
val dim = 500
//creating invertible matrix
val u = buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val l = buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 }
val matrix = l dot u
val time = measureTime {
repeat(20) {
lupSolver().inverse(matrix)
}
}
println(time)
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */

View File

@ -0,0 +1,29 @@
/*
* 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.AlgebraND
fun main() {
// 2d element
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(AlgebraND.complex(8)) {
val a = produce { (it) -> i * it - it.toDouble() }
val b = 3
val c = Complex(1.0, 1.0)
(a pow b) + c
}
println(result)
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2018-2024 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.algebra
import space.kscience.kmath.complex.ndAlgebra
import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.structureND
fun main() = Complex.algebra {
val complex = 2 + 2 * i
println(complex * 8 - 5 * i)
//flat buffer
val buffer = with(bufferAlgebra) {
buffer(8) { Complex(it, -it) }.map { Complex(it.im, it.re) }
}
println(buffer)
// 2d element
val element: BufferND<Complex> = ndAlgebra.structureND(2, 2) { (i, j) ->
Complex(i - j, i + j)
}
println(element)
// 1d element operation
val result: StructureND<Complex> = ndAlgebra {
val a = structureND(8) { (it) -> i * it - it.toDouble() }
val b = 3
val c = Complex(1.0, 1.0)
(a pow b) + c
}
println(result)
}

View File

@ -1,31 +0,0 @@
/*
* Copyright 2018-2024 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.commons.linear.CMLinearSpace
import space.kscience.kmath.linear.matrix
import space.kscience.kmath.nd.Float64BufferND
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.mutableStructureND
import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.structures.Float64
import space.kscience.kmath.viktor.viktorAlgebra
import kotlin.collections.component1
import kotlin.collections.component2
fun main() {
val viktorStructure = Float64Field.viktorAlgebra.mutableStructureND(2, 2) { (i, j) ->
if (i == j) 2.0 else 0.0
}
val cmMatrix: Structure2D<Float64> = CMLinearSpace.matrix(2, 2)(0.0, 1.0, 0.0, 3.0)
val res: Float64BufferND = Float64Field.ndAlgebra {
exp(viktorStructure) + 2.0 * cmMatrix
}
println(res)
}

View File

@ -1,17 +0,0 @@
/*
* Copyright 2018-2024 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.series
import kotlinx.datetime.Instant
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.operations.bufferAlgebra
import kotlin.time.Duration
fun SeriesAlgebra.Companion.time(zero: Instant, step: Duration) = MonotonicSeriesAlgebra(
bufferAlgebra = Double.algebra.bufferAlgebra,
offsetToLabel = { zero + step * it },
labelToOffset = { (it - zero) / step }
)

View File

@ -1,65 +0,0 @@
/*
* Copyright 2018-2024 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.series
import kotlinx.html.h1
import kotlinx.html.p
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.operations.bufferAlgebra
import space.kscience.kmath.operations.toList
import space.kscience.kmath.stat.KMComparisonResult
import space.kscience.kmath.stat.ksComparisonStatistic
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.Float64
import space.kscience.kmath.structures.slice
import space.kscience.plotly.*
import kotlin.math.PI
fun Double.Companion.seriesAlgebra() = Double.algebra.bufferAlgebra.seriesAlgebra()
fun main() = with(Double.seriesAlgebra()) {
fun Plot.plotSeries(name: String, buffer: Buffer<Float64>) {
scatter {
this.name = name
x.numbers = buffer.labels
y.numbers = buffer.toList()
}
}
val s1 = series(100) { sin(2 * PI * it / 100) + 1.0 }
val s2 = s1.slice(20..50).moveTo(40)
val s3: Buffer<Float64> = s1.zip(s2) { l, r -> l + r } //s1 + s2
val s4 = s3.map { ln(it) }
val kmTest: KMComparisonResult<Float64> = ksComparisonStatistic(s1, s2)
Plotly.page {
h1 { +"This is my plot" }
p {
+"Kolmogorov-smirnov test for s1 and s2: ${kmTest.value}"
}
plot {
plotSeries("s1", s1)
plotSeries("s2", s2)
plotSeries("s3", s3)
plotSeries("s4", s4)
layout {
xaxis {
range(0.0..100.0)
}
}
}
}.makeFile()
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2018-2023 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.series
import space.kscience.kmath.structures.*
import space.kscience.plotly.*
import space.kscience.plotly.models.Scatter
import space.kscience.plotly.models.ScatterMode
import kotlin.random.Random
fun main(): Unit = with(Double.seriesAlgebra()) {
val random = Random(1234)
val arrayOfRandoms = DoubleArray(20) { random.nextDouble() }
val series1: Float64Buffer = arrayOfRandoms.asBuffer()
val series2: Series<Float64> = series1.moveBy(3)
val res = series2 - series1
println(res.size)
println(res)
fun Plot.series(name: String, buffer: Buffer<Float64>, block: Scatter.() -> Unit = {}) {
scatter {
this.name = name
x.numbers = buffer.offsetIndices
y.doubles = buffer.toDoubleArray()
block()
}
}
Plotly.plot {
series("series1", series1)
series("series2", series2)
series("dif", res) {
mode = ScatterMode.lines
line.color("magenta")
}
}.makeFile(resourceLocation = ResourceLocation.REMOTE)
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -10,7 +10,6 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.apache.commons.rng.sampling.distribution.BoxMullerNormalizedGaussianSampler import org.apache.commons.rng.sampling.distribution.BoxMullerNormalizedGaussianSampler
import org.apache.commons.rng.simple.RandomSource import org.apache.commons.rng.simple.RandomSource
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.samplers.GaussianSampler import space.kscience.kmath.samplers.GaussianSampler
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
@ -36,28 +35,8 @@ private suspend fun runKMathChained(): Duration {
return Duration.between(startTime, Instant.now()) return Duration.between(startTime, Instant.now())
} }
private fun runKMathBlocking(): Duration { private fun runApacheDirect(): Duration {
val generator = RandomGenerator.fromSource(RandomSource.MT, 123L) val rng = RandomSource.create(RandomSource.MT, 123L)
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.nextBlocking()
if (counter % 100000 == 0) {
val duration = Duration.between(startTime, Instant.now())
val meanValue = sum / counter
println("Chain sampler completed $counter elements in $duration: $meanValue")
}
}
return Duration.between(startTime, Instant.now())
}
private fun runCMDirect(): Duration {
val rng = RandomSource.MT.create(123L)
val sampler = CMGaussianSampler.of( val sampler = CMGaussianSampler.of(
BoxMullerNormalizedGaussianSampler.of(rng), BoxMullerNormalizedGaussianSampler.of(rng),
@ -85,10 +64,8 @@ private fun runCMDirect(): Duration {
* Comparing chain sampling performance with direct sampling performance * Comparing chain sampling performance with direct sampling performance
*/ */
fun main(): Unit = runBlocking(Dispatchers.Default) { fun main(): Unit = runBlocking(Dispatchers.Default) {
val directJob = async { runCMDirect() } val directJob = async { runApacheDirect() }
val chainJob = async { runKMathChained() } val chainJob = async { runKMathChained() }
val blockingJob = async { runKMathBlocking() }
println("KMath Chained: ${chainJob.await()}") println("KMath Chained: ${chainJob.await()}")
println("KMath Blocking: ${blockingJob.await()}")
println("Apache Direct: ${directJob.await()}") println("Apache Direct: ${directJob.await()}")
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -7,21 +7,22 @@ package space.kscience.kmath.stat
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.chains.combineWithState import space.kscience.kmath.chains.collectWithState
import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.structures.Float64
/**
* The state of distribution averager.
*/
private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0)
/** /**
* Averaging. * Averaging.
*/ */
private fun Chain<Float64>.mean(): Chain<Float64> = combineWithState(AveragingChainState(), { it.copy() }) { chain -> private fun Chain<Double>.mean(): Chain<Double> = collectWithState(AveragingChainState(), { it.copy() }) { chain ->
val next = chain.next() val next = chain.next()
num++ num++
value += next value += next
return@combineWithState value / num return@collectWithState value / num
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -8,12 +8,11 @@
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.kmath.complex.* import space.kscience.kmath.complex.*
import space.kscience.kmath.linear.transposed import space.kscience.kmath.linear.transpose
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.real
import space.kscience.kmath.nd.structureND
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -21,12 +20,12 @@ fun main() {
val dim = 1000 val dim = 1000
val n = 1000 val n = 1000
val realField = Float64Field.ndAlgebra(dim, dim) val realField = AlgebraND.real(dim, dim)
val complexField: ComplexFieldND = ComplexField.ndAlgebra(dim, dim) val complexField: ComplexFieldND = AlgebraND.complex(dim, dim)
val realTime = measureTimeMillis { val realTime = measureTimeMillis {
realField { realField {
var res: StructureND<Float64> = one var res: StructureND<Double> = one
repeat(n) { repeat(n) {
res += 1.0 res += 1.0
} }
@ -50,17 +49,17 @@ fun main() {
fun complexExample() { fun complexExample() {
//Create a context for 2-d structure with complex values //Create a context for 2-d structure with complex values
ComplexField { ComplexField {
withNdAlgebra(4, 8) { nd(4, 8) {
//a constant real-valued structure //a constant real-valued structure
val x = one * 2.5 val x = one * 2.5
operator fun Number.plus(other: Complex) = Complex(this.toDouble() + other.re, other.im) operator fun Number.plus(other: Complex) = Complex(this.toDouble() + other.re, other.im)
//a structure generator specific to this context //a structure generator specific to this context
val matrix = structureND { (k, l) -> k + l * i } val matrix = produce { (k, l) -> k + l * i }
//Perform sum //Perform sum
val sum = matrix + x + 1.0 val sum = matrix + x + 1.0
//Represent the sum as 2d-structure and transpose //Represent the sum as 2d-structure and transpose
sum.as2D().transposed() sum.as2D().transpose()
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -9,10 +9,10 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import org.nd4j.linalg.factory.Nd4j import org.nd4j.linalg.factory.Nd4j
import space.kscience.kmath.nd.* import space.kscience.kmath.nd.*
import space.kscience.kmath.nd4j.nd4j import space.kscience.kmath.nd4j.Nd4jArrayField
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.viktor.ViktorFieldND import space.kscience.kmath.viktor.ViktorNDField
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -29,57 +29,64 @@ fun main() {
Nd4j.zeros(0) Nd4j.zeros(0)
val dim = 1000 val dim = 1000
val n = 1000 val n = 1000
val shape = ShapeND(dim, dim)
// automatically build context most suited for given type.
// specialized nd-field for Double. It works as generic Double field as well. val autoField = AlgebraND.auto(DoubleField, dim, dim)
val doubleField = Float64Field.ndAlgebra // specialized nd-field for Double. It works as generic Double field as well
//A generic field. It should be used for objects, not primitives. val realField = AlgebraND.real(dim, dim)
val genericField = BufferedFieldOpsND(Float64Field) //A generic boxing field. It should be used for objects, not primitives.
val boxingField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim)
// Nd4j specialized field. // Nd4j specialized field.
val nd4jField = Float64Field.nd4j val nd4jField = Nd4jArrayField.real(dim, dim)
//viktor field //viktor field
val viktorField = ViktorFieldND(dim, dim) val viktorField = ViktorNDField(dim, dim)
//parallel processing based on Java Streams //parallel processing based on Java Streams
val parallelField = Float64Field.ndStreaming(dim, dim) val parallelField = AlgebraND.realWithStream(dim, dim)
measureAndPrint("Boxing addition") { measureAndPrint("Boxing addition") {
genericField { boxingField {
var res: StructureND<Float64> = one(shape) var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Specialized addition") { measureAndPrint("Specialized addition") {
doubleField { realField {
var res: StructureND<Float64> = one(shape) var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Nd4j specialized addition") { measureAndPrint("Nd4j specialized addition") {
nd4jField { nd4jField {
var res: StructureND<Float64> = one(shape) var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Viktor addition") { measureAndPrint("Viktor addition") {
viktorField { viktorField {
var res: StructureND<Float64> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Parallel stream addition") { measureAndPrint("Parallel stream addition") {
parallelField { parallelField {
var res: StructureND<Float64> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
}
}
measureAndPrint("Automatic field addition") {
autoField {
var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Lazy addition") { measureAndPrint("Lazy addition") {
val res = doubleField.one(shape).mapAsync(GlobalScope) { val res = realField.one.mapAsync(GlobalScope) {
var c = 0.0 var c = 0.0
repeat(n) { repeat(n) {
c += 1.0 c += 1.0

View File

@ -1,16 +1,14 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.nd.* import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.NumbersAddOperations
import space.kscience.kmath.operations.NumbersAddOps
import java.util.* import java.util.*
import java.util.stream.IntStream import java.util.stream.IntStream
@ -18,67 +16,51 @@ import java.util.stream.IntStream
* A demonstration implementation of NDField over Real using Java [java.util.stream.DoubleStream] for parallel * A demonstration implementation of NDField over Real using Java [java.util.stream.DoubleStream] for parallel
* execution. * execution.
*/ */
class StreamDoubleFieldND(override val shape: ShapeND) : FieldND<Double, Float64Field>, class StreamDoubleFieldND(override val shape: IntArray) : FieldND<Double, DoubleField>,
NumbersAddOps<StructureND<Float64>>, NumbersAddOperations<StructureND<Double>>,
ExtendedField<StructureND<Float64>> { ExtendedField<StructureND<Double>> {
private val strides = ColumnStrides(shape) private val strides = DefaultStrides(shape)
override val elementAlgebra: Float64Field get() = Float64Field override val elementContext: DoubleField get() = DoubleField
override val zero: BufferND<Float64> by lazy { structureND(shape) { zero } } override val zero: BufferND<Double> by lazy { produce { zero } }
override val one: BufferND<Float64> by lazy { structureND(shape) { one } } override val one: BufferND<Double> by lazy { produce { one } }
override fun number(value: Number): BufferND<Float64> { override fun number(value: Number): BufferND<Double> {
val d = value.toDouble() // minimize conversions val d = value.toDouble() // minimize conversions
return structureND(shape) { d } return produce { d }
} }
@OptIn(PerformancePitfall::class) private val StructureND<Double>.buffer: DoubleBuffer
private val StructureND<Float64>.buffer: Float64Buffer
get() = when { get() = when {
shape != this@StreamDoubleFieldND.shape -> throw ShapeMismatchException( !shape.contentEquals(this@StreamDoubleFieldND.shape) -> throw ShapeMismatchException(
this@StreamDoubleFieldND.shape, this@StreamDoubleFieldND.shape,
shape shape
) )
this is BufferND && this.strides == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer
this is BufferND && indices == this@StreamDoubleFieldND.strides -> this.buffer as Float64Buffer else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
else -> Float64Buffer(strides.linearSize) { offset -> get(strides.index(offset)) }
} }
override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): BufferND<Float64> { override fun produce(initializer: DoubleField.(IntArray) -> Double): BufferND<Double> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
val index = strides.index(offset)
Float64Field.initializer(index)
}.toArray()
return BufferND(strides, array.asBuffer())
}
override fun mutableStructureND(
shape: ShapeND,
initializer: DoubleField.(IntArray) -> Double,
): MutableBufferND<Float64> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
val index = strides.index(offset) val index = strides.index(offset)
DoubleField.initializer(index) DoubleField.initializer(index)
}.toArray() }.toArray()
return MutableBufferND(strides, array.asBuffer())
}
@OptIn(PerformancePitfall::class)
override fun StructureND<Float64>.map(
transform: Float64Field.(Double) -> Double,
): BufferND<Float64> {
val array = Arrays.stream(buffer.array).parallel().map { Float64Field.transform(it) }.toArray()
return BufferND(strides, array.asBuffer()) return BufferND(strides, array.asBuffer())
} }
@OptIn(PerformancePitfall::class) override fun StructureND<Double>.map(
override fun StructureND<Float64>.mapIndexed( transform: DoubleField.(Double) -> Double,
transform: Float64Field.(index: IntArray, Double) -> Double, ): BufferND<Double> {
): BufferND<Float64> { 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 -> val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
Float64Field.transform( DoubleField.transform(
strides.index(offset), strides.index(offset),
buffer.array[offset] buffer.array[offset]
) )
@ -87,41 +69,40 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND<Double, Float64
return BufferND(strides, array.asBuffer()) return BufferND(strides, array.asBuffer())
} }
@OptIn(PerformancePitfall::class) override fun combine(
override fun zip( a: StructureND<Double>,
left: StructureND<Float64>, b: StructureND<Double>,
right: StructureND<Float64>, transform: DoubleField.(Double, Double) -> Double,
transform: Float64Field.(Double, Double) -> Double, ): BufferND<Double> {
): BufferND<Float64> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
Float64Field.transform(left.buffer.array[offset], right.buffer.array[offset]) DoubleField.transform(a.buffer.array[offset], b.buffer.array[offset])
}.toArray() }.toArray()
return BufferND(strides, array.asBuffer()) return BufferND(strides, array.asBuffer())
} }
override fun StructureND<Float64>.unaryMinus(): StructureND<Float64> = map { -it } override fun StructureND<Double>.unaryMinus(): StructureND<Double> = map { -it }
override fun scale(a: StructureND<Float64>, value: Double): StructureND<Float64> = a.map { it * value } override fun scale(a: StructureND<Double>, value: Double): StructureND<Double> = a.map { it * value }
override fun power(arg: StructureND<Float64>, pow: Number): BufferND<Float64> = arg.map { power(it, pow) } override fun power(arg: StructureND<Double>, pow: Number): BufferND<Double> = arg.map { power(it, pow) }
override fun exp(arg: StructureND<Float64>): BufferND<Float64> = arg.map { exp(it) } override fun exp(arg: StructureND<Double>): BufferND<Double> = arg.map { exp(it) }
override fun ln(arg: StructureND<Float64>): BufferND<Float64> = arg.map { ln(it) } override fun ln(arg: StructureND<Double>): BufferND<Double> = arg.map { ln(it) }
override fun sin(arg: StructureND<Float64>): BufferND<Float64> = arg.map { sin(it) } override fun sin(arg: StructureND<Double>): BufferND<Double> = arg.map { sin(it) }
override fun cos(arg: StructureND<Float64>): BufferND<Float64> = arg.map { cos(it) } override fun cos(arg: StructureND<Double>): BufferND<Double> = arg.map { cos(it) }
override fun tan(arg: StructureND<Float64>): BufferND<Float64> = arg.map { tan(it) } override fun tan(arg: StructureND<Double>): BufferND<Double> = arg.map { tan(it) }
override fun asin(arg: StructureND<Float64>): BufferND<Float64> = arg.map { asin(it) } override fun asin(arg: StructureND<Double>): BufferND<Double> = arg.map { asin(it) }
override fun acos(arg: StructureND<Float64>): BufferND<Float64> = arg.map { acos(it) } override fun acos(arg: StructureND<Double>): BufferND<Double> = arg.map { acos(it) }
override fun atan(arg: StructureND<Float64>): BufferND<Float64> = arg.map { atan(it) } override fun atan(arg: StructureND<Double>): BufferND<Double> = arg.map { atan(it) }
override fun sinh(arg: StructureND<Float64>): BufferND<Float64> = arg.map { sinh(it) } override fun sinh(arg: StructureND<Double>): BufferND<Double> = arg.map { sinh(it) }
override fun cosh(arg: StructureND<Float64>): BufferND<Float64> = arg.map { cosh(it) } override fun cosh(arg: StructureND<Double>): BufferND<Double> = arg.map { cosh(it) }
override fun tanh(arg: StructureND<Float64>): BufferND<Float64> = arg.map { tanh(it) } override fun tanh(arg: StructureND<Double>): BufferND<Double> = arg.map { tanh(it) }
override fun asinh(arg: StructureND<Float64>): BufferND<Float64> = arg.map { asinh(it) } override fun asinh(arg: StructureND<Double>): BufferND<Double> = arg.map { asinh(it) }
override fun acosh(arg: StructureND<Float64>): BufferND<Float64> = arg.map { acosh(it) } override fun acosh(arg: StructureND<Double>): BufferND<Double> = arg.map { acosh(it) }
override fun atanh(arg: StructureND<Float64>): BufferND<Float64> = arg.map { atanh(it) } override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
} }
fun Float64Field.ndStreaming(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(ShapeND(shape)) fun AlgebraND.Companion.realWithStream(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(shape)

View File

@ -1,45 +1,42 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.nd.BufferND import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.DefaultStrides
import space.kscience.kmath.nd.ShapeND
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
@OptIn(PerformancePitfall::class)
fun main() { fun main() {
val n = 6000 val n = 6000
val array = DoubleArray(n * n) { 1.0 } val array = DoubleArray(n * n) { 1.0 }
val buffer = Float64Buffer(array) val buffer = DoubleBuffer(array)
val strides = ColumnStrides(ShapeND(n, n)) val strides = DefaultStrides(intArrayOf(n, n))
val structure = BufferND(strides, buffer) val structure = BufferND(strides, buffer)
measureTimeMillis { measureTimeMillis {
var res = 0.0 var res = 0.0
strides.asSequence().forEach { res = structure[it] } strides.indices().forEach { res = structure[it] }
} // warmup } // warmup
val time1 = measureTimeMillis { val time1 = measureTimeMillis {
var res = 0.0 var res = 0.0
strides.asSequence().forEach { res = structure[it] } strides.indices().forEach { res = structure[it] }
} }
println("Structure reading finished in $time1 millis") println("Structure reading finished in $time1 millis")
val time2 = measureTimeMillis { val time2 = measureTimeMillis {
var res = 0.0 var res = 0.0
strides.asSequence().forEach { res = buffer[strides.offset(it)] } strides.indices().forEach { res = buffer[strides.offset(it)] }
} }
println("Buffer reading finished in $time2 millis") println("Buffer reading finished in $time2 millis")
val time3 = measureTimeMillis { val time3 = measureTimeMillis {
var res = 0.0 var res = 0.0
strides.asSequence().forEach { res = array[strides.offset(it)] } strides.indices().forEach { res = array[strides.offset(it)] }
} }
println("Array reading finished in $time3 millis") println("Array reading finished in $time3 millis")
} }

View File

@ -1,25 +1,20 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.kmath.nd.BufferND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.mapToBuffer import space.kscience.kmath.nd.mapToBuffer
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
private inline fun <T, reified R : Any> BufferND<T>.mapToBufferND(
bufferFactory: BufferFactory<R> = BufferFactory(),
crossinline block: (T) -> R,
): BufferND<R> = BufferND(indices, buffer.mapToBuffer(bufferFactory, block))
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
fun main() { fun main() {
val n = 6000 val n = 6000
val structure = BufferND(n, n) { 1.0 } val structure = StructureND.buffered(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 }
structure.mapToBufferND { it + 1 } // warm-up structure.mapToBuffer { it + 1 } // warm-up
val time1 = measureTimeMillis { val res = structure.mapToBufferND { it + 1 } } val time1 = measureTimeMillis { val res = structure.mapToBuffer { it + 1 } }
println("Structure mapping finished in $time1 millis") println("Structure mapping finished in $time1 millis")
val array = DoubleArray(n * n) { 1.0 } val array = DoubleArray(n * n) { 1.0 }
@ -30,10 +25,10 @@ fun main() {
println("Array mapping finished in $time2 millis") println("Array mapping finished in $time2 millis")
val buffer = Float64Buffer(DoubleArray(n * n) { 1.0 }) val buffer = DoubleBuffer(DoubleArray(n * n) { 1.0 })
val time3 = measureTimeMillis { val time3 = measureTimeMillis {
val target = Float64Buffer(DoubleArray(n * n)) val target = DoubleBuffer(DoubleArray(n * n))
val res = array.forEachIndexed { index, value -> val res = array.forEachIndexed { index, value ->
target[index] = value + 1 target[index] = value + 1
} }

View File

@ -1,23 +0,0 @@
/*
* Copyright 2018-2024 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.operations.Float64Field
import space.kscience.kmath.operations.buffer
import space.kscience.kmath.operations.bufferAlgebra
import space.kscience.kmath.operations.withSize
inline fun <reified R : Any> MutableBuffer.Companion.same(
n: Int,
value: R,
): MutableBuffer<R> = MutableBuffer(n) { value }
fun main() {
with(Float64Field.bufferAlgebra.withSize(5)) {
println(number(2.0) + buffer(1, 2, 3, 4, 5))
}
}

View File

@ -1,26 +0,0 @@
/*
* Copyright 2018-2023 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.PerformancePitfall
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.algebra
@OptIn(PerformancePitfall::class)
fun main(): Unit = with(Double.algebra.ndAlgebra) {
val structure: MutableStructure2D<Float64> = mutableStructureND(ShapeND(2, 2)) { (i, j) ->
i.toDouble() + j.toDouble()
}.as2D()
structure[0, 1] = -2.0
val structure2 = mutableStructureND(2, 2) { (i, j) -> i.toDouble() + j.toDouble() }.as2D()
structure2[0, 1] = 2.0
println(structure + structure2)
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@ -15,11 +15,11 @@ private fun DMatrixContext<Double, *>.simple() {
val m2 = produce<D3, D2> { i, j -> (i + j).toDouble() } val m2 = produce<D3, D2> { i, j -> (i + j).toDouble() }
//Dimension-safe addition //Dimension-safe addition
m1.transposed() + m2 m1.transpose() + m2
} }
private object D5 : Dimension { private object D5 : Dimension {
override val dim: Int = 5 override val dim: UInt = 5u
} }
private fun DMatrixContext<Double, *>.custom() { private fun DMatrixContext<Double, *>.custom() {

View File

@ -0,0 +1,42 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.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)}")
}

View File

@ -1,92 +0,0 @@
/*
* Copyright 2018-2024 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.LevenbergMarquardt.StaticLm
import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.component1
import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.LMInput
import space.kscience.kmath.tensors.core.levenbergMarquardt
import kotlin.math.roundToInt
fun main() {
val NData = 200
var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D()
for (i in 0 until NData) {
t_example[i, 0] = t_example[i, 0] * (i + 1) - 104
}
val Nparams = 15
var p_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D()
for (i in 0 until Nparams) {
p_example[i, 0] = p_example[i, 0] + i - 25
}
val exampleNumber = 1
var y_hat = funcDifficultForLm(t_example, p_example, exampleNumber)
var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D()
for (i in 0 until Nparams) {
p_init[i, 0] = (p_example[i, 0] + 0.9)
}
var t = t_example
val y_dat = y_hat
val weight = 1.0 / Nparams * 1.0 - 0.085
val dp = BroadcastDoubleTensorAlgebra.fromArray(
ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 }
).as2D()
var p_min = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1)))
p_min = p_min.div(1.0 / -50.0)
val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1)))
p_min = p_min.div(1.0 / 50.0)
val opts = doubleArrayOf(3.0, 10000.0, 1e-6, 1e-6, 1e-6, 1e-6, 1e-2, 11.0, 9.0, 1.0)
// val opts = doubleArrayOf(3.0, 10000.0, 1e-6, 1e-6, 1e-6, 1e-6, 1e-3, 11.0, 9.0, 1.0)
val inputData = LMInput(
::funcDifficultForLm,
p_init.as2D(),
t,
y_dat,
weight,
dp,
p_min.as2D(),
p_max.as2D(),
opts[1].toInt(),
doubleArrayOf(opts[2], opts[3], opts[4], opts[5]),
doubleArrayOf(opts[6], opts[7], opts[8]),
opts[9].toInt(),
10,
1
)
val result = DoubleTensorAlgebra.levenbergMarquardt(inputData)
println("Parameters:")
for (i in 0 until result.resultParameters.shape.component1()) {
val x = (result.resultParameters[i, 0] * 10000).roundToInt() / 10000.0
print("$x ")
}
println()
println("Y true and y received:")
var y_hat_after = funcDifficultForLm(t_example, result.resultParameters, exampleNumber)
for (i in 0 until y_hat.shape.component1()) {
val x = (y_hat[i, 0] * 10000).roundToInt() / 10000.0
val y = (y_hat_after[i, 0] * 10000).roundToInt() / 10000.0
println("$x $y")
}
println("Сhi_sq:")
println(result.resultChiSq)
println("Number of iterations:")
println(result.iterations)
}

View File

@ -1,59 +0,0 @@
/*
* Copyright 2018-2024 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.LevenbergMarquardt.StaticLm
import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.component1
import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm
import space.kscience.kmath.tensors.LevenbergMarquardt.funcEasyForLm
import space.kscience.kmath.tensors.LevenbergMarquardt.getStartDataForFuncEasy
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.LMInput
import space.kscience.kmath.tensors.core.levenbergMarquardt
import kotlin.math.roundToInt
fun main() {
val startedData = getStartDataForFuncEasy()
val inputData = LMInput(
::funcEasyForLm,
DoubleTensorAlgebra.ones(ShapeND(intArrayOf(4, 1))).as2D(),
startedData.t,
startedData.y_dat,
startedData.weight,
startedData.dp,
startedData.p_min,
startedData.p_max,
startedData.opts[1].toInt(),
doubleArrayOf(startedData.opts[2], startedData.opts[3], startedData.opts[4], startedData.opts[5]),
doubleArrayOf(startedData.opts[6], startedData.opts[7], startedData.opts[8]),
startedData.opts[9].toInt(),
10,
startedData.example_number
)
val result = DoubleTensorAlgebra.levenbergMarquardt(inputData)
println("Parameters:")
for (i in 0 until result.resultParameters.shape.component1()) {
val x = (result.resultParameters[i, 0] * 10000).roundToInt() / 10000.0
print("$x ")
}
println()
println("Y true and y received:")
var y_hat_after = funcDifficultForLm(startedData.t, result.resultParameters, startedData.example_number)
for (i in 0 until startedData.y_dat.shape.component1()) {
val x = (startedData.y_dat[i, 0] * 10000).roundToInt() / 10000.0
val y = (y_hat_after[i, 0] * 10000).roundToInt() / 10000.0
println("$x $y")
}
println("Сhi_sq:")
println(result.resultChiSq)
println("Number of iterations:")
println(result.iterations)
}

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