Compare commits

...

47 Commits

Author SHA1 Message Date
72b0d9edc9 Workaround for dokka bug: https://github.com/Kotlin/dokka/issues/4001 2026-01-09 14:48:39 +03:00
f8b23261e1 Update Readme.
Move last projects from JVM to MPP.
Update changelog.
2026-01-09 14:00:17 +03:00
d64a117a9f Update changelog 2026-01-08 15:55:52 +03:00
845aea0cd7 Merge pull request 'Replace FlagAttributes with BooleanAttributes for matrices to solve KS-626' (!530) from feature/boolean-flag-attributes into dev
Reviewed-on: #530
Reviewed-by: Gleb Minaev <glebminaev02@yandex.ru>
2025-11-30 11:55:14 +03:00
7267880126 Remove unnecessary attributes type arguments for MatrixWrapper 2025-11-30 09:10:51 +03:00
f3e9e338bf Merge branch 'dev' into feature/boolean-flag-attributes 2025-11-30 08:37:03 +03:00
73a6ad28c3 Merge pull request 'Add contracts to operator functions that were missing' (!531) from add-contracts-to-operators into dev
Reviewed-on: #531
Reviewed-by: Alexander Nozik <altavir@gmail.com>
2025-11-30 08:36:47 +03:00
Gleb Minaev
7da0ace8f8 Add contracts. 2025-11-29 22:12:38 +03:00
9c22b51fa5 Minor fixes to builders 2025-11-28 18:17:05 +03:00
26dfdf39df Merge remote-tracking branch 'spc/feature/boolean-flag-attributes' into feature/boolean-flag-attributes 2025-11-28 18:03:12 +03:00
58a3f749a7 Merge branch 'dev' into feature/boolean-flag-attributes 2025-11-28 18:03:00 +03:00
5abc90a4ba Merge branch 'dev' into feature/boolean-flag-attributes 2025-11-28 09:39:59 +03:00
6e97e513fb Update github actions config 2025-11-28 09:13:55 +03:00
7d08aed5eb Remove unnecessary inheritance from BooleanAttributes for matrix attributes. 2025-11-28 09:06:29 +03:00
b9c1947b70 Replace FlagAttributes with BooleanAttributes for matrices to solve KS-626 2025-11-27 13:17:43 +03:00
b1446741db add test for failure on cholesky non positive-defined input 2025-11-27 10:14:08 +03:00
98dd43d404 adoptation fixes 2025-11-27 08:44:22 +03:00
577401bb08 Merge branch 'refs/heads/beta/kotlin-2.3.0' into dev 2025-11-25 21:30:40 +03:00
896822ec77 Fix for KS-627 2025-11-25 21:27:21 +03:00
ff5fbfa8d8 Kotlin 2.3.0 2025-10-10 18:03:08 +03:00
6fe77607d8 Stabilize build for 2.2.20-Beta2 2025-07-30 10:47:45 +03:00
b6892e2e5c Merge branch 'beta/2.2.0' into beta/kotlin-2.2.20 2025-07-11 13:12:59 +03:00
101eb612b1 add multik transpose direct comparison 2025-06-04 18:24:26 +03:00
SPC-code
785d3bd104 Merge pull request #543 from SciProgCentre/bug/multik-transposed
Fix transposed in mutlik algebra.
2025-06-04 18:07:12 +03:00
f362c3e31f fix geometric mean test 2025-06-04 17:54:09 +03:00
1fd5e3fba1 Fix transposed in mutlik algebra.
Add test for multik transpose
2025-06-04 17:33:50 +03:00
0cd9a329ce fix geometric mean test 2025-05-26 15:41:41 +03:00
d2a3fd4fa2 Merge pull request 'STUD-13 add Quantile' (!528) from qwazer/kmath:STUD-13-quantile into dev
Reviewed-on: #528
Reviewed-by: Alexander Nozik <altavir@gmail.com>
2025-05-26 12:19:24 +03:00
4afd350a4e STUD-13 polish docs 2025-05-26 10:40:08 +03:00
202585d956 STUD-13 add Quantile
adapted from Julia implementation
2025-05-22 18:03:31 +03:00
f6f9984c8e Merge pull request 'fix geometric mean ComposableStatistic' (!527) from qwazer/kmath:STUD-13-fix-composite-means into dev
Reviewed-on: #527
Reviewed-by: Alexander Nozik <altavir@gmail.com>
2025-05-12 17:32:09 +03:00
3649d013cd fix geometric mean ComposableStatistic 2025-05-12 17:23:49 +03:00
b10caebe2a fix mean statistics composition 2025-05-11 19:36:02 +03:00
752a849cd3 Merge pull request 'STUD-13 add Variance and StandardDeviation statistics' (!526) from qwazer/kmath:STUD-13 into dev
Reviewed-on: #526
Reviewed-by: Alexander Nozik <altavir@gmail.com>
2025-05-10 20:40:41 +03:00
2535d4536e STUD-13 add Variance and StandardDeviation statistics 2025-05-09 16:07:41 +03:00
4464b72e45 STUD-13. Improve numerical stability of mean statistic algorithm in method computeIntermediate 2025-05-06 16:11:19 +03:00
ce453129f0 Merge pull request 'STUD-13. Improve numerical stability of mean statistic algorithm' (!525) from qwazer/kmath:STUD-13 into dev
Reviewed-on: #525
Reviewed-by: Alexander Nozik <altavir@gmail.com>
2025-05-05 15:57:20 +03:00
d43ce15b99 STUD-13. Improve numerical stability of mean statistic algorithm
Replace naive summation that prone to floating-point errors (loss of precision) by Welford’s Online Algorithm which updates mean incrementally and more numerically stable.
 The cons is slightly higher computational overhead.
2025-05-05 15:34:49 +03:00
288ec467e6 Merge pull request 'add Geometric Mean statistic' (!524) from qwazer/kmath:STUD-13 into dev
Reviewed-on: #524
2025-04-28 10:38:08 +03:00
59fbe5165f add Geometric Mean statistic 2025-04-23 16:26:48 +03:00
2d37a5255f Merge remote-tracking branch 'github/dev' into dev 2025-04-23 09:13:46 +03:00
61209e81b5 kotlin 2.2.0 2025-04-23 09:12:36 +03:00
SPC-code
5bbff7e8ad Merge pull request #541 from qwazer/STUD-13
Add "min" and "max" statistics implemented as ExtremeValueStatistic
2025-04-23 09:11:20 +03:00
7ac9794c0c Add Min/Max statistics
- Remove ExtremeValueStatistic
- Add benchmark
2025-04-17 16:26:21 +03:00
e41bbe711c fix global scope pollution 2025-04-16 15:52:16 +03:00
57e4819430 Add "min" and "max" statistics implemented as ExtremeValueStatistic
- Add ExtremeValueStatistic
- Add statistics docs
2025-04-15 19:53:40 +03:00
e213db67da Add Float64Vector3D factory function 2025-04-07 12:27:23 +03:00
102 changed files with 2067 additions and 429 deletions

View File

@@ -10,15 +10,25 @@ jobs:
runs-on: windows-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3.5.1
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
java-version: '17'
java-version: '21'
distribution: 'liberica'
cache: 'gradle'
- name: Gradle Wrapper Validation
uses: gradle/wrapper-validation-action@v1.0.4
- name: Gradle Build
uses: gradle/gradle-build-action@v2.4.2
uses: gradle/gradle-build-action@v3
with:
arguments: test jvmTest
- name: Publish Test Report
uses: mikepenz/action-junit-report@v6
if: ${{ !cancelled() }} # always run even if the previous step fails
with:
report_paths: '**/test-results/**/TEST-*.xml'
annotate_only: true
detailed_summary: true
flaky_summary: true
include_empty_in_summary: false
skip_success_summary: true

View File

@@ -10,18 +10,13 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 40
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
java-version: 17
java-version: 21
distribution: liberica
- name: Cache konan
uses: actions/cache@v3
with:
path: ~/.konan
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Gradle Wrapper Validation
uses: gradle/wrapper-validation-action@v1.0.4
- uses: gradle/gradle-build-action@v3
with:
arguments: dokkaGenerate --no-parallel

View File

@@ -3,10 +3,8 @@
## Unreleased
### Added
- Fit accessors with Attribute
### Changed
- Upgrade tensorflow version to 1.0.0
### Deprecated
@@ -16,6 +14,22 @@
### Security
## 0.5.0 - 2026-01-09
### Added
- More statistics functions by @qwazer
- Fit accessors with Attribute
### Changed
- Flag attributes are replaced with boolean attributes to properly support missing values
- Upgrade tensorflow version to 1.0.0
### Removed
- Support for ND4J
## 0.4.2 - 2025-01-27
### Added

View File

@@ -5,10 +5,7 @@
# KMath
Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based
analog to Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior
architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like
experience could be achieved with [kmath-for-real](/kmath-for-real) extension module.
Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based analog to Python's NumPy library. Later we found that kotlin is a much more flexible language and allows superior architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could be achieved with [kmath-for-real](kmath-for-real) extension module.
[Documentation site](https://SciProgCentre.github.io/kmath/)
@@ -34,15 +31,17 @@ experience could be achieved with [kmath-for-real](/kmath-for-real) extension mo
* Be like NumPy. It was the idea at the beginning, but we decided that we can do better in API.
* Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them.
* 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
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.
* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like for `Float64` in the core. For that we will have specialization modules like [kmath-for-real](kmath-for-real), which will give a better experience for those who want to work with specific types.
## Contributing
The project requires a lot of additional work. The most important thing we need is feedback about what features are required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues marked with [good first issue](https://github.com/SciProgCentre/kmath/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label.
Project roadmap will be available at [GitHub Projects](https://github.com/orgs/SciProgCentre/projects/3).
## Features and stability
KMath is a modular library. Different modules provide different features with different API stability guarantees. All
core modules are released with the same version, but with different API change policy. The features are described in
module definitions below. The module stability could have the following levels:
KMath is a modular library. Different modules provide different features with different API stability guarantees. All core modules are released with the same version, but with different API change policy. The features are described in module definitions below. The module stability could have the following levels:
* **PROTOTYPE**. On this level there are no compatibility guarantees. All methods and classes form those modules could
break any moment. You can still use it, but be sure to fix the specific version.
@@ -50,7 +49,7 @@ module definitions below. The module stability could have the following levels:
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.
with [binary-compatibility-validator](https://kotlinlang.org/docs/gradle-binary-compatibility-validation.html) tool.
* **STABLE**. The API stabilized. Breaking changes are allowed only in major releases.
## Modules
@@ -60,10 +59,6 @@ module definitions below. The module stability could have the following levels:
>
> **Maturity**: EXPERIMENTAL
### [examples](examples)
>
> **Maturity**: EXPERIMENTAL
### [kmath-ast](kmath-ast)
>
> **Maturity**: EXPERIMENTAL
@@ -98,9 +93,9 @@ module definitions below. The module stability could have the following levels:
> **Features:**
> - [algebras](kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Algebraic structures like rings, spaces and fields.
> - [nd](kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/StructureND.kt) : Many-dimensional structures and operations on them.
> - [linear](kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition.
> - [linear](kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Basic linear algebra operations (sums, products, etc.), backed by the `Space` API.
> - [buffers](kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffers.kt) : One-dimensional structure
> - [expressions](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions) : By writing a single mathematical expression once, users will be able to apply different types of
> - [expressions](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions) : By writing a single mathematical expression once, users will be able to apply different types of
> - [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
> - [Parallel linear algebra](kmath-core/#) : Parallel implementation for `LinearAlgebra`
@@ -183,17 +178,6 @@ One can still use generic algebras though.
>
> **Maturity**: PROTOTYPE
### [kmath-nd4j](kmath-nd4j)
> ND4J NDStructure implementation and according NDAlgebra classes
>
> **Maturity**: DEPRECATED
>
> **Features:**
> - [nd4jarraystructure](kmath-nd4j/#) : NDStructure wrapper for INDArray
> - [nd4jarrayrings](kmath-nd4j/#) : Rings over Nd4jArrayStructure of Int and Long
> - [nd4jarrayfields](kmath-nd4j/#) : Fields over Nd4jArrayStructure of Float and Double
### [kmath-ojalgo](kmath-ojalgo)
> Ojalgo bindings for kmath
>
@@ -239,34 +223,21 @@ One can still use generic algebras though.
## Multi-platform support
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
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
feedback are also welcome.
KMath is developed as a multi-platform library, which means that most of the interfaces are declared in common source sets like [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 reasons. Currently, Kotlin/JVM is the primary platform, however, Kotlin/Native, Kotlin/JS and Kotlin/Wasm contributions and feedback are also welcome.
## Performance
Calculation of performance is one of the major goals of KMath in the future, but in some cases it is impossible to
achieve both
performance and flexibility.
Performance of mathematical operations is hard to achieve without a lot of effort. KMath focus is to provide a reasonable performance for common cases, out of the box and good interoperability with optimized libraries for edge cases. For example, one could prototype an algorithm using KMath core implementations and then use Multik or Ojalgo for performance-critical parts just by adding a dependency and algebra context switch.
We expect to focus on creating a 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
native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be
better than SciPy.
As for core implementations, we expect to focus on creating a 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 native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be better than SciPy.
## Requirements
KMath currently relies on JDK 11 for compilation and execution of Kotlin-JVM part. We recommend using GraalVM-CE or
Oracle GraalVM for execution to get better performance.
KMath currently relies on JDK 21 for compilation and execution of Kotlin-JVM part.
### Repositories
Release and development artifacts are accessible from mipt-npm [Space](https://www.jetbrains.com/space/)
repository `https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven` (see documentation of
[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:
Intermediate releases are published to [Kotlin.Link](https://repo.kotlin.link) repository.
```kotlin
repositories {
@@ -278,11 +249,3 @@ dependencies {
// api("space.kscience:kmath-core-jvm:$version") for jvm-specific version
}
```
## Contributing
The project requires a lot of additional work. The most important thing we need is feedback about what features are
required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues
marked
with [good first issue](hhttps://github.com/SciProgCentre/kmath/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
label.

View File

@@ -1,114 +1,122 @@
# Module KMath-Benchmarks
# BenchmarksResult
## Report for benchmark configuration <code>main</code>
* Run on OpenJDK 64-Bit Server VM (build 17.0.11+9) with Java process:
* Run on OpenJDK 64-Bit Server VM (build 21.0.9+10-LTS) with Java process:
```
C:\Users\altavir\scoop\apps\gradle\current\.gradle\jdks\eclipse_adoptium-17-amd64-windows.2\bin\java.exe -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant
C:\Users\altavir\.gradle\jdks\eclipse_adoptium-21-amd64-windows.2\bin\java.exe -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.
* JMH 1.37 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`|3.9E+06 &plusmn; 3.4E+05 ops/s|
|`benchmarkBufferRead`|4.0E+06 &plusmn; 3.2E+05 ops/s|
|`nativeBufferRead`|3.9E+06 &plusmn; 2.0E+05 ops/s|
|`benchmarkArrayRead`|3.9E+06 &plusmn; 1.1E+06 ops/s|
|`benchmarkBufferRead`|4.0E+06 &plusmn; 2.2E+05 ops/s|
|`nativeBufferRead`|4.0E+06 &plusmn; 1.7E+05 ops/s|
### [BigIntBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`jvmAdd`|3.1E+07 &plusmn; 1.8E+07 ops/s|
|`jvmAddLarge`|4.5E+04 &plusmn; 5.5E+03 ops/s|
|`jvmMultiply`|3.6E+07 &plusmn; 1.7E+07 ops/s|
|`jvmMultiplyLarge`|1.9E+02 &plusmn; 95 ops/s|
|`jvmParsing10`|4.0E+06 &plusmn; 8.8E+05 ops/s|
|`jvmParsing16`|3.6E+06 &plusmn; 6.5E+05 ops/s|
|`jvmPower`|25 &plusmn; 1.4 ops/s|
|`jvmSmallAdd`|5.7E+07 &plusmn; 9.7E+05 ops/s|
|`kmAdd`|2.6E+07 &plusmn; 8.8E+05 ops/s|
|`kmAddLarge`|2.3E+04 &plusmn; 1.2E+03 ops/s|
|`kmMultiply`|3.8E+07 &plusmn; 5.5E+06 ops/s|
|`kmMultiplyLarge`|36 &plusmn; 3.8 ops/s|
|`kmParsing10`|2.5E+06 &plusmn; 1.4E+05 ops/s|
|`kmParsing16`|3.7E+06 &plusmn; 4.7E+05 ops/s|
|`kmPower`|6.6 &plusmn; 1.0 ops/s|
|`kmSmallAdd`|2.0E+07 &plusmn; 1.7E+06 ops/s|
|`jvmAdd`|2.9E+07 &plusmn; 2.8E+06 ops/s|
|`jvmAddLarge`|3.8E+04 &plusmn; 6.4E+03 ops/s|
|`jvmMultiply`|5.3E+07 &plusmn; 6.1E+06 ops/s|
|`jvmMultiplyLarge`|2.2E+02 &plusmn; 1.9 ops/s|
|`jvmParsing10`|3.9E+06 &plusmn; 4.7E+05 ops/s|
|`jvmParsing16`|3.1E+06 &plusmn; 4.6E+05 ops/s|
|`jvmPower`|24 &plusmn; 1.7 ops/s|
|`jvmSmallAdd`|4.7E+07 &plusmn; 4.6E+06 ops/s|
|`kmAdd`|2.3E+07 &plusmn; 5.1E+06 ops/s|
|`kmAddLarge`|2.6E+04 &plusmn; 3.0E+02 ops/s|
|`kmMultiply`|3.7E+07 &plusmn; 2.9E+06 ops/s|
|`kmMultiplyLarge`|34 &plusmn; 2.8 ops/s|
|`kmParsing10`|2.5E+06 &plusmn; 1.5E+05 ops/s|
|`kmParsing16`|4.0E+06 &plusmn; 2.4E+05 ops/s|
|`kmPower`|6.5 &plusmn; 0.69 ops/s|
|`kmSmallAdd`|1.6E+07 &plusmn; 8.0E+05 ops/s|
### [BufferBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`bufferViewReadWrite`|6.0E+06 &plusmn; 7.4E+05 ops/s|
|`bufferViewReadWriteSpecialized`|7.6E+05 &plusmn; 1.1E+04 ops/s|
|`complexBufferReadWrite`|2.4E+06 &plusmn; 2.7E+05 ops/s|
|`doubleArrayReadWrite`|7.3E+06 &plusmn; 4.3E+05 ops/s|
|`doubleBufferReadWrite`|7.3E+06 &plusmn; 3.4E+05 ops/s|
|`bufferViewReadWrite`|5.4E+06 &plusmn; 3.8E+05 ops/s|
|`bufferViewReadWriteSpecialized`|5.0E+06 &plusmn; 1.2E+06 ops/s|
|`complexBufferReadWrite`|2.2E+06 &plusmn; 5.7E+04 ops/s|
|`doubleArrayReadWrite`|6.9E+06 &plusmn; 1.2E+06 ops/s|
|`doubleBufferReadWrite`|6.6E+06 &plusmn; 1.1E+06 ops/s|
### [DotBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`bufferedDot`|1.3 &plusmn; 0.032 ops/s|
|`cmDot`|0.42 &plusmn; 0.20 ops/s|
|`cmDotWithConversion`|0.83 &plusmn; 0.12 ops/s|
|`ejmlDot`|2.6 &plusmn; 0.049 ops/s|
|`ejmlDotWithConversion`|2.5 &plusmn; 0.075 ops/s|
|`multikDot`|25 &plusmn; 0.52 ops/s|
|`ojalgoDot`|11 &plusmn; 1.3 ops/s|
|`parallelDot`|11 &plusmn; 0.17 ops/s|
|`tensorDot`|1.1 &plusmn; 0.028 ops/s|
|`tfDot`|4.7 &plusmn; 0.14 ops/s|
|`bufferedDot`|1.2 &plusmn; 0.20 ops/s|
|`cmDot`|0.36 &plusmn; 0.14 ops/s|
|`cmDotWithConversion`|0.80 &plusmn; 0.092 ops/s|
|`ejmlDot`|2.9 &plusmn; 0.61 ops/s|
|`ejmlDotWithConversion`|2.7 &plusmn; 0.15 ops/s|
|`multikDot`|23 &plusmn; 2.4 ops/s|
|`ojalgoDot`|11 &plusmn; 0.79 ops/s|
|`parallelDot`|9.4 &plusmn; 1.3 ops/s|
|`tensorDot`|1.0 &plusmn; 0.15 ops/s|
|`tfDot`|3.9 &plusmn; 0.90 ops/s|
### [ExpressionsInterpretersBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`asmGenericExpression`|12 &plusmn; 0.099 ops/s|
|`asmPrimitiveExpression`|26 &plusmn; 0.57 ops/s|
|`asmPrimitiveExpressionArray`|74 &plusmn; 1.7 ops/s|
|`functionalExpression`|5.3 &plusmn; 0.015 ops/s|
|`justCalculate`|74 &plusmn; 0.85 ops/s|
|`mstExpression`|4.2 &plusmn; 0.10 ops/s|
|`rawExpression`|25 &plusmn; 0.74 ops/s|
|`asmGenericExpression`|15 &plusmn; 1.8 ops/s|
|`asmPrimitiveExpression`|27 &plusmn; 0.98 ops/s|
|`asmPrimitiveExpressionArray`|78 &plusmn; 14 ops/s|
|`functionalExpression`|4.4 &plusmn; 0.25 ops/s|
|`justCalculate`|79 &plusmn; 5.4 ops/s|
|`mstExpression`|4.2 &plusmn; 0.93 ops/s|
|`rawExpression`|25 &plusmn; 5.0 ops/s|
### [IntegrationBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/IntegrationBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`complexIntegration`|2.6E+03 &plusmn; 46 ops/s|
|`doubleIntegration`|2.8E+03 &plusmn; 1.1E+02 ops/s|
|`complexIntegration`|2.2E+03 &plusmn; 3.0E+02 ops/s|
|`doubleIntegration`|2.3E+03 &plusmn; 6.4E+02 ops/s|
### [MatrixInverseBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`cmLUPInversion`|2.1E+03 &plusmn; 35 ops/s|
|`ejmlInverse`|1.2E+03 &plusmn; 27 ops/s|
|`kmathLupInversion`|4.0E+02 &plusmn; 52 ops/s|
|`kmathParallelLupInversion`|4.0E+02 &plusmn; 9.6 ops/s|
|`ojalgoInverse`|2.1E+03 &plusmn; 3.3E+02 ops/s|
|`cmLUPInversion`|2.0E+03 &plusmn; 1.1E+02 ops/s|
|`ejmlInverse`|1.2E+03 &plusmn; 29 ops/s|
|`kmathLupInversion`|3.9E+02 &plusmn; 92 ops/s|
|`kmathParallelLupInversion`|55 &plusmn; 5.0 ops/s|
|`ojalgoInverse`|1.7E+03 &plusmn; 35 ops/s|
### [MinStatisticBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MinStatisticBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`kotlinArrayMin`|1.6E+03 &plusmn; 3.0E+02 ops/s|
|`minBlocking`|1.2E+03 &plusmn; 1.2E+02 ops/s|
### [NDFieldBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`boxingFieldAdd`|1.7 &plusmn; 0.11 ops/s|
|`multikAdd`|7.0 &plusmn; 0.41 ops/s|
|`multikInPlaceAdd`|34 &plusmn; 1.7 ops/s|
|`specializedFieldAdd`|7.2 &plusmn; 1.2 ops/s|
|`tensorAdd`|7.2 &plusmn; 1.6 ops/s|
|`tensorInPlaceAdd`|7.4 &plusmn; 4.9 ops/s|
|`viktorAdd`|5.8 &plusmn; 0.65 ops/s|
|`boxingFieldAdd`|1.9 &plusmn; 0.089 ops/s|
|`multikAdd`|6.8 &plusmn; 1.0 ops/s|
|`multikInPlaceAdd`|32 &plusmn; 4.7 ops/s|
|`specializedFieldAdd`|6.7 &plusmn; 0.98 ops/s|
|`tensorAdd`|7.9 &plusmn; 1.1 ops/s|
|`tensorInPlaceAdd`|11 &plusmn; 3.4 ops/s|
|`viktorAdd`|6.4 &plusmn; 0.41 ops/s|
### [ViktorBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`doubleFieldAddition`|7.1 &plusmn; 2.0 ops/s|
|`rawViktor`|6.2 &plusmn; 1.0 ops/s|
|`viktorFieldAddition`|6.4 &plusmn; 0.29 ops/s|
|`doubleFieldAddition`|7.3 &plusmn; 1.1 ops/s|
|`rawViktor`|6.0 &plusmn; 0.88 ops/s|
|`viktorFieldAddition`|6.7 &plusmn; 0.47 ops/s|
### [ViktorLogBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt)
| Benchmark | Score |
|:---------:|:-----:|
|`rawViktorLog`|1.3 &plusmn; 0.016 ops/s|
|`realFieldLog`|1.3 &plusmn; 0.019 ops/s|
|`viktorFieldLog`|1.3 &plusmn; 0.020 ops/s|
|`rawViktorLog`|1.3 &plusmn; 0.40 ops/s|
|`realFieldLog`|1.2 &plusmn; 0.34 ops/s|
|`viktorFieldLog`|1.3 &plusmn; 0.0073 ops/s|

View File

@@ -1,10 +1,12 @@
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import kotlinx.benchmark.gradle.BenchmarksExtension
import java.util.*
plugins {
kotlin("multiplatform")
id("space.kscience.gradle.mpp")
alias(spclibs.plugins.kotlin.plugin.allopen)
alias(spclibs.plugins.kotlinx.benchmark)
}
@@ -16,21 +18,59 @@ repositories {
mavenCentral()
}
kotlin {
jvmToolchain(17)
kscience {
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL
jvm()
js {
nodejs()
}
commonMain{
implementation(project(":kmath-ast"))
implementation(project(":kmath-core"))
implementation(project(":kmath-coroutines"))
implementation(project(":kmath-complex"))
implementation(project(":kmath-stat"))
implementation(project(":kmath-dimensions"))
implementation(project(":kmath-for-real"))
implementation(project(":kmath-tensors"))
implementation(libs.multik.default)
implementation(spclibs.kotlinx.benchmark.runtime)
}
jvmMain {
implementation(projects.kmathCommons)
implementation(projects.kmathEjml)
implementation(projects.kmathKotlingrad)
implementation(projects.kmathViktor)
implementation(projects.kmathOjalgo)
implementation(projects.kmath.kmathTensorflow)
implementation(projects.kmathMultik)
implementation(libs.tensorflow.core.platform)
// implementation(projects.kmathNd4j)
// implementation(libs.nd4j.native.platform)
// uncomment if your system supports AVX2
// val os = System.getProperty("os.name")
//
// if (System.getProperty("os.arch") in arrayOf("x86_64", "amd64")) when {
// os.startsWith("Windows") -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:windows-x86_64-avx2")
// os == "Linux" -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:linux-x86_64-avx2")
// os == "Mac OS X" -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:macosx-x86_64-avx2")
// } else
// implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7")
}
}
kotlin {
compilerOptions {
optIn.addAll(
"space.kscience.kmath.UnstableKMathAPI"
)
}
jvm()
js(IR) {
nodejs()
}
sourceSets {
all {
languageSettings {
@@ -40,46 +80,6 @@ kotlin {
optIn("space.kscience.kmath.UnstableKMathAPI")
}
}
val commonMain by getting {
dependencies {
implementation(project(":kmath-ast"))
implementation(project(":kmath-core"))
implementation(project(":kmath-coroutines"))
implementation(project(":kmath-complex"))
implementation(project(":kmath-stat"))
implementation(project(":kmath-dimensions"))
implementation(project(":kmath-for-real"))
implementation(project(":kmath-tensors"))
implementation(libs.multik.default)
implementation(spclibs.kotlinx.benchmark.runtime)
}
}
val jvmMain by getting {
dependencies {
implementation(projects.kmathCommons)
implementation(projects.kmathEjml)
implementation(projects.kmathKotlingrad)
implementation(projects.kmathViktor)
implementation(projects.kmathOjalgo)
implementation(projects.kmath.kmathTensorflow)
implementation(projects.kmathMultik)
implementation(libs.tensorflow.core.platform)
// implementation(projects.kmathNd4j)
// implementation(libs.nd4j.native.platform)
// uncomment if your system supports AVX2
// val os = System.getProperty("os.name")
//
// if (System.getProperty("os.arch") in arrayOf("x86_64", "amd64")) when {
// os.startsWith("Windows") -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:windows-x86_64-avx2")
// os == "Linux" -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:linux-x86_64-avx2")
// os == "Mac OS X" -> implementation("org.nd4j:nd4j-native:1.0.0-beta7:macosx-x86_64-avx2")
// } else
// implementation("org.nd4j:nd4j-native-platform:1.0.0-beta7")
}
}
}
}
@@ -103,6 +103,11 @@ benchmark {
include("BufferBenchmark")
}
configurations.register("minStatistic") {
commonConfiguration()
include("MinStatisticBenchmark")
}
configurations.register("nd") {
commonConfiguration()
include("NDFieldBenchmark")
@@ -210,7 +215,6 @@ private data class JmhReport(
}
readme {
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL
val jsonMapper = jacksonObjectMapper()
@@ -260,7 +264,7 @@ readme {
)
reports.groupBy { it.benchmark.substringBeforeLast(".") }.forEach { (cl, compare) ->
appendLine("### [${cl.substringAfterLast(".")}](src/jvmMain/kotlin/${cl.replace(".","/")}.kt)")
appendLine("### [${cl.substringAfterLast(".")}](src/jvmMain/kotlin/${cl.replace(".", "/")}.kt)")
appendLine()
appendLine("| Benchmark | Score |")
appendLine("|:---------:|:-----:|")
@@ -276,4 +280,6 @@ readme {
}
}
}
}
}
kotlin.explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled

View File

@@ -1,3 +1,5 @@
# Module KMath-Benchmarks
# BenchmarksResult
${benchmarkMain}

View File

@@ -35,13 +35,13 @@ class ExpressionsInterpretersBenchmark {
fun mstExpression(blackhole: Blackhole) = invokeAndSum(mst, blackhole)
/**
* Benchmark case for [Expression] created with [compileToExpression].
* Benchmark case for [Expression] created with [space.kscience.kmath.estree.compileToExpression].
*/
@Benchmark
fun wasmExpression(blackhole: Blackhole) = invokeAndSum(wasm, blackhole)
/**
* Benchmark case for [Expression] created with [compileToExpression].
* Benchmark case for [Expression] created with [space.kscience.kmath.estree.compileToExpression].
*/
@Benchmark
fun estreeExpression(blackhole: Blackhole) = invokeAndSum(estree, blackhole)

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2018-2025 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 kotlinx.coroutines.runBlocking
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.stat.min
import space.kscience.kmath.structures.*
@State(Scope.Benchmark)
internal class MinStatisticBenchmark {
@Benchmark
fun kotlinArrayMin(blackhole: Blackhole) {
val array = DoubleArray(size) { it.toDouble() }
var res = 0.0
(0 until size).forEach {
res += array.min()
}
blackhole.consume(res)
}
@Benchmark
fun minBlocking(blackhole: Blackhole) {
val buffer = Float64Buffer(size) { it.toDouble() }
var res = 0.0
(0 until size).forEach {
res += Float64Field.min.evaluateBlocking(buffer)
}
blackhole.consume(res)
}
private companion object {
private const val size = 1000
}
}

View File

@@ -1,3 +1,4 @@
import org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation
import space.kscience.gradle.useApache2Licence
import space.kscience.gradle.useSPCTeam
@@ -14,7 +15,7 @@ allprojects {
}
group = "space.kscience"
version = "0.4.3-dev-1"
version = "0.5.0"
}
dependencies {
@@ -23,7 +24,7 @@ dependencies {
}
}
dokka{
dokka {
dokkaSourceSets.configureEach {
val readmeFile = projectDir.resolve("README.md")
if (readmeFile.exists()) includes.from(readmeFile)
@@ -48,7 +49,7 @@ subprojects {
)
}
fun externalDocumentationLink(url: String, packageListUrl: String? = null){
fun externalDocumentationLink(url: String, packageListUrl: String? = null) {
externalDocumentationLinks.register(url) {
url(url)
packageListUrl?.let {
@@ -76,15 +77,26 @@ subprojects {
}
}
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
kscienceProject {
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
ksciencePublish {
pom("https://github.com/SciProgCentre/kmath") {
useApache2Licence()
useSPCTeam()
}
repository("spc", "https://maven.sciprog.center/kscience")
central()
publishTo("spc", "https://maven.sciprog.center/kscience")
publishToCentral()
@OptIn(ExperimentalAbiValidation::class)
abiValidation {
filters {
excluded {
annotatedWith.add("space.kscience.kmath.UnstableKMathAPI")
}
}
}
}
apiValidation.nonPublicMarkers.add("space.kscience.kmath.UnstableKMathAPI")

View File

@@ -11,4 +11,6 @@
* [Expressions](expressions.md)
* [Statistics](statistics.md): statistical functions on data [Buffers](buffers.md)
* Commons math integration

34
docs/statistics.md Normal file
View File

@@ -0,0 +1,34 @@
# Statistics
Mathematically speaking, a statistic is a measurable numerical function of sample data.
In KMath, a statistic is a function that operates on a [Buffer](buffers.md) and is implemented as the `evaluate` method
of the `Statistic` interface.
There are two subinterfaces of the `Statistic` interface:
* `BlockingStatistic` A statistic that is computed in a synchronous blocking mode
* `ComposableStatistic` A statistic tha could be computed separately on different blocks of data and then composed
## Common statistics and Implementation Status
| Category | Statistic | Description | Implementation Status |
|------------------|-------------------|------------------------------------|--------------------------------|
| **Basic** | Min | Minimum value | ✅ `ComposableStatistic` |
| | Max | Maximum value | ✅ `ComposableStatistic` |
| | Mean | Arithmetic mean | ✅ `ComposableStatistic` |
| | Sum | Sum of all values | 🚧 Not yet implemented |
| | Product | Product of all values | 🚧 Not yet implemented |
| **Distribution** | Median | Median (50th percentile) | ✅ `BlockingStatistic` |
| | Quantile | Quantile and percentile | ✅ `BlockingStatistic` |
| | Variance | Unbiased sample variance | ✅ `BlockingStatistic` |
| | StandardDeviation | Population standard deviation (σ) | ✅ `BlockingStatistic` |
| | Skewness | Measure of distribution asymmetry | 🚧 *(Requires `ThirdMoment`)* |
| | Kurtosis | Measure of distribution tailedness | 🚧 *(Requires `FourthMoment`)* |
| **Advanced** | GeometricMean | Nth root of product of values | ✅ `ComposableStatistic` |
| | SumOfLogs | Sum of natural logarithms | Does not planned |
| | SumOfSquares | Sum of squared values | 🚧 *(Blocks `Variance`)* |
| **Moments** | FirstMoment | Mean (same as `Mean`) | ✅ *(Alias for `Mean`)* |
| | SecondMoment | Variance (same as `Variance`) | ✅ *(Alias for `Variance`)* |
| | ThirdMoment | Used in skewness calculation | 🚧 Not yet implemented |
| | FourthMoment | Used in kurtosis calculation | 🚧 Not yet implemented |
| **Risk Metrics** | SemiVariance | Downside variance | 🚧 *(Depends on `Variance`)* |

View File

@@ -2,14 +2,10 @@
[![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)
[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22)
[![Space](https://img.shields.io/badge/dynamic/xml?color=orange&label=Space&query=//metadata/versioning/latest&url=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fspace%2Fkscience%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/)
# KMath
Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based
analog to Python's NumPy library. Later we found that kotlin is much more flexible language and allows superior
architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like
experience could be achieved with [kmath-for-real](/kmath-for-real) extension module.
Could be pronounced as `key-math`. The **K**otlin **Math**ematics library was initially intended as a Kotlin-based analog to Python's NumPy library. Later we found that kotlin is a much more flexible language and allows superior architecture designs. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The `numpy`-like experience could be achieved with [kmath-for-real](kmath-for-real) extension module.
[Documentation site](https://SciProgCentre.github.io/kmath/)
@@ -35,15 +31,17 @@ experience could be achieved with [kmath-for-real](/kmath-for-real) extension mo
* Be like NumPy. It was the idea at the beginning, but we decided that we can do better in API.
* Provide the best performance out of the box. We have specialized libraries for that. Need only API wrappers for them.
* 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
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.
* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like for `Float64` in the core. For that we will have specialization modules like [kmath-for-real](kmath-for-real), which will give a better experience for those who want to work with specific types.
## Contributing
The project requires a lot of additional work. The most important thing we need is feedback about what features are required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues marked with [good first issue](https://github.com/SciProgCentre/kmath/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label.
Project roadmap will be available at [GitHub Projects](https://github.com/orgs/SciProgCentre/projects/3).
## Features and stability
KMath is a modular library. Different modules provide different features with different API stability guarantees. All
core modules are released with the same version, but with different API change policy. The features are described in
module definitions below. The module stability could have the following levels:
KMath is a modular library. Different modules provide different features with different API stability guarantees. All core modules are released with the same version, but with different API change policy. The features are described in module definitions below. The module stability could have the following levels:
* **PROTOTYPE**. On this level there are no compatibility guarantees. All methods and classes form those modules could
break any moment. You can still use it, but be sure to fix the specific version.
@@ -51,7 +49,7 @@ module definitions below. The module stability could have the following levels:
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.
with [binary-compatibility-validator](https://kotlinlang.org/docs/gradle-binary-compatibility-validation.html) tool.
* **STABLE**. The API stabilized. Breaking changes are allowed only in major releases.
## Modules
@@ -60,34 +58,21 @@ ${modules}
## Multi-platform support
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
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
feedback are also welcome.
KMath is developed as a multi-platform library, which means that most of the interfaces are declared in common source sets like [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 reasons. Currently, Kotlin/JVM is the primary platform, however, Kotlin/Native, Kotlin/JS and Kotlin/Wasm contributions and feedback are also welcome.
## Performance
Calculation of performance is one of the major goals of KMath in the future, but in some cases it is impossible to
achieve both
performance and flexibility.
Performance of mathematical operations is hard to achieve without a lot of effort. KMath focus is to provide a reasonable performance for common cases, out of the box and good interoperability with optimized libraries for edge cases. For example, one could prototype an algorithm using KMath core implementations and then use Multik or Ojalgo for performance-critical parts just by adding a dependency and algebra context switch.
We expect to focus on creating a 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
native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be
better than SciPy.
As for core implementations, we expect to focus on creating a 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 native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be better than SciPy.
## Requirements
KMath currently relies on JDK 11 for compilation and execution of Kotlin-JVM part. We recommend using GraalVM-CE or
Oracle GraalVM for execution to get better performance.
KMath currently relies on JDK 21 for compilation and execution of Kotlin-JVM part.
### Repositories
Release and development artifacts are accessible from mipt-npm [Space](https://www.jetbrains.com/space/)
repository `https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven` (see documentation of
[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:
Intermediate releases are published to [Kotlin.Link](https://repo.kotlin.link) repository.
```kotlin
repositories {
@@ -99,11 +84,3 @@ dependencies {
// api("${group}:kmath-core-jvm:$version") for jvm-specific version
}
```
## Contributing
The project requires a lot of additional work. The most important thing we need is feedback about what features are
required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues
marked
with [good first issue](hhttps://github.com/SciProgCentre/kmath/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
label.

View File

@@ -52,7 +52,7 @@ dependencies {
}
kotlin {
jvmToolchain(17)
jvmToolchain(21)
sourceSets.all {
languageSettings {
optIn("kotlin.contracts.ExperimentalContracts")
@@ -64,10 +64,6 @@ kotlin {
tasks.withType<KotlinJvmCompile> {
compilerOptions {
freeCompilerArgs.addAll("-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn", "-Xlambdas=indy")
freeCompilerArgs.addAll("-Xopt-in=kotlin.RequiresOptIn")
}
}
readme {
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL
}
}

View File

@@ -5,11 +5,13 @@
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
import kotlin.time.ExperimentalTime
import kotlin.time.Instant
@OptIn(ExperimentalTime::class)
fun SeriesAlgebra.Companion.time(zero: Instant, step: Duration) = MonotonicSeriesAlgebra(
bufferAlgebra = Double.algebra.bufferAlgebra,
offsetToLabel = { zero + step * it },

View File

@@ -2,7 +2,6 @@
# 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.
#
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4096m
org.gradle.parallel=true
org.gradle.workers.max=4
@@ -13,4 +12,4 @@ kotlin.native.ignoreDisabledTargets=true
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
kotlin.native.enableKlibsCrossCompilation=true
toolsVersion=0.16.1-kotlin-2.1.0
toolsVersion=0.20.2-kotlin-2.3.0

View File

@@ -3,10 +3,10 @@
commons-rng = "1.6"
multik = "0.2.3"
nd4j = "1.0.0-M2.1"
tensorflow = "1.0.0"
tensorflow = "1.1.0"
[libraries]
attributes = "space.kscience:attributes-kt:0.3.0"
attributes = "space.kscience:attributes-kt:0.4.0"
commons-math = "org.apache.commons:commons-math3:3.6.1"
@@ -19,7 +19,7 @@ multik-default = { module = "org.jetbrains.kotlinx:multik-default", version.ref
nd4j-api = { module = "org.nd4j:nd4j-api", version.ref = "nd4j" }
nd4j-native-platform = { module = "org.nd4j:nd4j-native-platform", version.ref = "nd4j" }
ojalgo = "org.ojalgo:ojalgo:55.1.0"
ojalgo = "org.ojalgo:ojalgo:56.2.0"
tensorflow-core-api = {module = "org.tensorflow:tensorflow-core-api", version.ref="tensorflow"}

View File

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

View File

@@ -10,7 +10,7 @@ Extensions to MST API: transformations, dynamic compilation and visualization.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-ast:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-ast:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -20,7 +20,7 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-ast:0.4.2")
implementation("space.kscience:kmath-ast:0.5.0")
}
```

View File

@@ -19,7 +19,7 @@ internal abstract class AsmBuilder {
protected val classLoader = ByteArrayClassLoader(javaClass.classLoader)
protected companion object {
companion object {
/**
* ASM type for [Expression].
*/

View File

@@ -5,11 +5,16 @@
package space.kscience.kmath.asm.internal
import org.objectweb.asm.*
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.Type
import org.objectweb.asm.Type.*
import org.objectweb.asm.commons.InstructionAdapter
import space.kscience.kmath.expressions.*
import space.kscience.kmath.expressions.Expression
import space.kscience.kmath.expressions.MST
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.expressions.invoke
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.nio.file.Paths
@@ -24,6 +29,7 @@ import kotlin.io.path.writeBytes
* @property expressionResultCallback the function to apply to this object when generating expression value.
* @author Iaroslav Postovalov
*/
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
internal class GenericAsmBuilder<T>(
classOfT: Class<*>,
private val className: String,

View File

@@ -16,7 +16,7 @@ import space.kscience.kmath.structures.Float64
import space.kscience.kmath.asm.compile as asmCompile
import space.kscience.kmath.asm.compileToExpression as asmCompileToExpression
private object GenericAsmCompilerTestContext : CompilerTestContext {
internal object GenericAsmCompilerTestContext : CompilerTestContext {
override fun MST.compileToExpression(algebra: Int32Ring): Expression<Int> =
asmCompileToExpression(algebra as Algebra<Int>)
@@ -31,7 +31,7 @@ private object GenericAsmCompilerTestContext : CompilerTestContext {
}
@OptIn(UnstableKMathAPI::class)
private object PrimitiveAsmCompilerTestContext : CompilerTestContext {
internal object PrimitiveAsmCompilerTestContext : CompilerTestContext {
override fun MST.compileToExpression(algebra: Int32Ring): Expression<Int> = asmCompileToExpression(algebra)
override fun MST.compile(algebra: Int32Ring, arguments: Map<Symbol, Int>): Int = asmCompile(algebra, arguments)
override fun MST.compileToExpression(algebra: Float64Field): Expression<Float64> = asmCompileToExpression(algebra)

View File

@@ -6,7 +6,7 @@ Commons math binding for kmath
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-commons:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-commons:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-commons:0.4.2")
implementation("space.kscience:kmath-commons:0.5.0")
}
```

View File

@@ -8,7 +8,7 @@ Complex and hypercomplex number systems in KMath.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-complex:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-complex:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -18,6 +18,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-complex:0.4.2")
implementation("space.kscience:kmath-complex:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
dependencies {
api(projects.kmathCore)

View File

@@ -4,9 +4,9 @@ The core interfaces of KMath.
- [algebras](src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Algebraic structures like rings, spaces and fields.
- [nd](src/commonMain/kotlin/space/kscience/kmath/structures/StructureND.kt) : Many-dimensional structures and operations on them.
- [linear](src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition.
- [linear](src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Basic linear algebra operations (sums, products, etc.), backed by the `Space` API.
- [buffers](src/commonMain/kotlin/space/kscience/kmath/structures/Buffers.kt) : One-dimensional structure
- [expressions](src/commonMain/kotlin/space/kscience/kmath/expressions) : By writing a single mathematical expression once, users will be able to apply different types of
- [expressions](src/commonMain/kotlin/space/kscience/kmath/expressions) : By writing a single mathematical expression once, users will be able to apply different types of
- [domains](src/commonMain/kotlin/space/kscience/kmath/domains) : Domains
- [autodiff](src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation
- [Parallel linear algebra](#) : Parallel implementation for `LinearAlgebra`
@@ -14,7 +14,7 @@ The core interfaces of KMath.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-core:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-core:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -24,6 +24,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-core:0.4.2")
implementation("space.kscience:kmath-core:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
dependencies {
api(libs.attributes)
@@ -44,7 +44,12 @@ readme {
feature(
id = "linear",
ref = "src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt",
) { "Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition." }
) {
"""
Basic linear algebra operations (sums, products, etc.), backed by the `Space` API.
Advanced linear algebra operations like matrix inversion and LU decomposition.
""".trimIndent()
}
feature(
id = "buffers",
@@ -56,7 +61,7 @@ readme {
ref = "src/commonMain/kotlin/space/kscience/kmath/expressions"
) {
"""
By writing a single mathematical expression once, users will be able to apply different types of
By writing a single mathematical expression once, users will be able to apply different types of
objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high
performance calculations to code generation.
""".trimIndent()

View File

@@ -209,12 +209,12 @@ internal fun <T, A> DSCompiler<T, A>.pow(
n: Int,
result: MutableBuffer<T>,
resultOffset: Int,
) where A : Field<T>, A : PowerOperations<T> = algebra {
): Unit where A : Field<T>, A : PowerOperations<T> = algebra {
if (n == 0) {
// special case, x^0 = 1 for all x
result[resultOffset] = one
result.fill(zero, resultOffset + 1, resultOffset + size)
return
return Unit
}
// create the power function value and derivatives

View File

@@ -5,13 +5,20 @@
package space.kscience.kmath.linear
import space.kscience.attributes.*
import space.kscience.attributes.Attributes
import space.kscience.attributes.SafeType
import space.kscience.attributes.withAttribute
import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.nd.MutableStructure2D
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.StructureAttribute
import space.kscience.kmath.nd.as1D
import space.kscience.kmath.operations.BufferRingOps
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Alias for [Structure2D] with more familiar name.
@@ -179,7 +186,7 @@ public interface LinearSpace<T, out A : Ring<T>> : MatrixScope<T> {
* If the structure holds given [attribute] return itself. Otherwise, return a new [Matrix] that contains a computed attribute.
*
* This method is used to compute and cache attribute inside the structure. If one needs an attribute only once,
* better use [StructureND.getOrComputeAttribute].
* better use [Structure2D.getOrComputeAttribute].
*/
@UnstableKMathAPI
public fun <V : Any, A : StructureAttribute<V>> Matrix<T>.withComputedAttribute(
@@ -210,7 +217,12 @@ public interface LinearSpace<T, out A : Ring<T>> : MatrixScope<T> {
}
public inline operator fun <LS : LinearSpace<*, *>, R> LS.invoke(block: LS.() -> R): R = run(block)
public inline operator fun <LS : LinearSpace<*, *>, R> LS.invoke(block: LS.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return this.block()
}
/**

View File

@@ -15,7 +15,9 @@ import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.*
import space.kscience.kmath.structures.Float64
import space.kscience.kmath.structures.IntBuffer
import space.kscience.kmath.structures.asBuffer
public interface LupDecomposition<T> {
public val pivot: IntBuffer
@@ -47,7 +49,7 @@ public class GenericLupDecomposition<T>(
override val l: Matrix<T>
get() = VirtualMatrix(lu.rowNum, lu.colNum, attributes = Attributes(LowerTriangular)) { i, j ->
get() = VirtualMatrix(lu.rowNum, lu.colNum, attributes = Attributes(LowerTriangular, true)) { i, j ->
when {
j < i -> lu[i, j]
j == i -> elementAlgebra.one
@@ -56,7 +58,7 @@ public class GenericLupDecomposition<T>(
}
override val u: Matrix<T>
get() = VirtualMatrix(lu.rowNum, lu.colNum, attributes = Attributes(UpperTriangular)) { i, j ->
get() = VirtualMatrix(lu.rowNum, lu.colNum, attributes = Attributes(UpperTriangular,true)) { i, j ->
if (j >= i) lu[i, j] else elementAlgebra.zero
}

View File

@@ -80,7 +80,7 @@ public fun <T, A : Ring<T>> MatrixBuilder<T, A>.diagonal(
builder: A.(Int) -> T
): Matrix<T> = with(linearSpace.elementAlgebra) {
require(colNum == rowNum) { "In order to build symmetric matrix, number of rows $rowNum should be equal to number of columns $colNum" }
return VirtualMatrix(rowNum, colNum, attributes = Attributes(IsDiagonal)) { i, j ->
return VirtualMatrix(rowNum, colNum, attributes = Attributes(IsDiagonal, true)) { i, j ->
check(i in 0 until rowNum) { "$i out of bounds: 0..<$rowNum" }
check(j in 0 until colNum) { "$j out of bounds: 0..<$colNum" }
if (i == j) {
@@ -97,7 +97,8 @@ public fun <T, A : Ring<T>> MatrixBuilder<T, A>.diagonal(
@UnstableKMathAPI
public fun <T> MatrixBuilder<T, Ring<T>>.diagonal(vararg elements: T): Matrix<T> {
require(colNum == rowNum) { "In order to build symmetric matrix, number of rows $rowNum should be equal to number of columns $colNum" }
return return VirtualMatrix(rowNum, colNum, attributes = Attributes(IsDiagonal)) { i, j ->
return VirtualMatrix(rowNum, colNum, attributes = Attributes(IsDiagonal,true)) { i, j ->
check(i in 0 until rowNum) { "$i out of bounds: 0..<$rowNum" }
check(j in 0 until colNum) { "$j out of bounds: 0..<$colNum" }
if (i == j) {

View File

@@ -36,8 +36,8 @@ public val <T : Any> Matrix<T>.origin: Matrix<T>
/**
* Add a single feature to a [Matrix]
*/
public fun <T, A : Attribute<T>> Matrix<T>.withAttribute(
attribute: A,
public fun <T> Matrix<T>.withAttribute(
attribute: Attribute<T>,
attrValue: T,
): MatrixWrapper<T> = if (this is MatrixWrapper) {
MatrixWrapper(origin, attributes.withAttribute(attribute, attrValue))
@@ -45,14 +45,26 @@ public fun <T, A : Attribute<T>> Matrix<T>.withAttribute(
MatrixWrapper(this, Attributes(attribute, attrValue))
}
public fun <T, A : Attribute<Unit>> Matrix<T>.withAttribute(
attribute: A,
public fun <T> Matrix<T>.withAttribute(
attribute: Attribute<Unit>,
): MatrixWrapper<T> = if (this is MatrixWrapper) {
MatrixWrapper(origin, attributes.withFlag(attribute))
} else {
MatrixWrapper(this, Attributes(attribute, Unit))
}
/**
* Add boolean attribute with default value `true`
*/
public fun <T> Matrix<T>.withAttribute(
attribute: Attribute<Boolean>,
value: Boolean = true
): MatrixWrapper<T> = if (this is MatrixWrapper) {
MatrixWrapper(origin, attributes.withAttribute(attribute, value))
} else {
MatrixWrapper(this, Attributes(attribute, value))
}
/**
* Modify matrix attributes
*/

View File

@@ -23,11 +23,14 @@ public interface MatrixScope<T> : WithType<T>
*/
public interface MatrixAttribute<T> : StructureAttribute<T>
public typealias BooleanAttribute = Attribute<Boolean>
/**
* Matrices with this feature are symmetric, meaning `matrix[i,j] == matrix[j,i]`
*/
public interface Symmetric : MatrixAttribute<Unit>, FlagAttribute{
public companion object: Symmetric
public interface Symmetric : MatrixAttribute<Boolean> {
public companion object : Symmetric
}
/**
@@ -72,12 +75,12 @@ public val <T> MatrixScope<T>.Determinant: Determinant<T> get() = Determinant(ty
/**
* Matrices with this feature are lower triangular ones.
*/
public object LowerTriangular : MatrixAttribute<Unit>, FlagAttribute
public object LowerTriangular : MatrixAttribute<Boolean>
/**
* Matrices with this feature are upper triangular ones.
*/
public object UpperTriangular : MatrixAttribute<Unit>, FlagAttribute
public object UpperTriangular : MatrixAttribute<Boolean>
/**
* Matrices with this feature support LU factorization: *a = [l] &middot; [u]* where *a* is the owning matrix.
@@ -102,7 +105,7 @@ public val <T> MatrixScope<T>.LU: LuDecompositionAttribute<T> get() = LuDecompos
* Matrices with this feature are orthogonal ones: *a &middot; a<sup>T</sup> = u* where *a* is the owning matrix, *u*
* is the unit matrix ([IsUnit]).
*/
public object OrthogonalAttribute : MatrixAttribute<Unit>, FlagAttribute
public object OrthogonalAttribute : MatrixAttribute<Boolean>
public interface QRDecomposition<out T> {

View File

@@ -11,6 +11,8 @@ import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.operations.Ring.Companion.optimizedPower
import space.kscience.kmath.structures.MutableBufferFactory
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Represents an algebraic structure.
@@ -122,8 +124,12 @@ public fun <T> Algebra<T>.bindSymbol(symbol: Symbol): T = bindSymbol(symbol.iden
/**
* Call a block with an [Algebra] as receiver.
*/
// TODO add contract when KT-32313 is fixed
public inline operator fun <A : Algebra<*>, R> A.invoke(block: A.() -> R): R = run(block)
public inline operator fun <A : Algebra<*>, R> A.invoke(block: A.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return this.block()
}
/**
* Represents group without neutral element (also known as inverse semigroup) i.e., algebraic structure with

View File

@@ -6,7 +6,7 @@
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-coroutines:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-coroutines:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-coroutines:0.4.2")
implementation("space.kscience:kmath-coroutines:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
dependencies {
api(projects.kmathCore)

View File

@@ -5,27 +5,30 @@
package space.kscience.kmath.chains
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.runningReduce
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.transform
import space.kscience.kmath.operations.GroupOps
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke
public fun <T> Flow<T>.cumulativeSum(group: GroupOps<T>): Flow<T> =
group { runningReduce { sum, element -> sum + element } }
@ExperimentalCoroutinesApi
public fun <T, S> Flow<T>.mean(space: S): Flow<T> where S : Ring<T>, S : ScaleOperations<T> = space {
data class Accumulator(var sum: T, var num: Int)
scan(Accumulator(zero, 0)) { sum, element ->
sum.apply {
this.sum += element
this.num += 1
}
}.map { it.sum / it.num }
/**
* Return a [Flow] of a cumulative sum of elements in the flow. The operation is _intermediate_ and _stateful_.
*/
public fun <T> Flow<T>.cumulativeSum(group: GroupOps<T>): Flow<T> = with(group) {
runningReduce { sum, element -> sum + element }
}
/**
* Return a [Flow] of mean values of elements in the flow. The operation is _intermediate_ and _stateful_.
*/
public fun <T, S> Flow<T>.mean(space: S): Flow<T> where S : Ring<T>, S : ScaleOperations<T> = with(space) {
var sum = zero
var num = 0
transform {
sum += it
num++
emit(sum / num)
}
}

View File

@@ -6,7 +6,7 @@ A proof of concept module for adding type-safe dimensions to structures
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-dimensions:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-dimensions:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-dimensions:0.4.2")
implementation("space.kscience:kmath-dimensions:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
dependencies {
api(projects.kmathCore)

View File

@@ -9,7 +9,7 @@ EJML based linear algebra implementation.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-ejml:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-ejml:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -19,6 +19,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-ejml:0.4.2")
implementation("space.kscience:kmath-ejml:0.5.0")
}
```

View File

@@ -107,8 +107,8 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace<Double, Float64Field, DMatri
}
}
public fun <T: DMatrix> T.asMatrix() = EjmlDoubleMatrix(this)
public fun <T: DMatrix> T.asVector() = EjmlDoubleVector(this)
public fun <T: DMatrix> T.asMatrix(): EjmlDoubleMatrix<T> = EjmlDoubleMatrix(this)
public fun <T: DMatrix> T.asVector(): EjmlDoubleVector<T> = EjmlDoubleVector(this)
override fun buildMatrix(
rows: Int,
@@ -367,8 +367,8 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace<Float, Float32Field, FMatrix
}
}
public fun <T: FMatrix> T.asMatrix() = EjmlFloatMatrix(this)
public fun <T: FMatrix> T.asVector() = EjmlFloatVector(this)
public fun <T: FMatrix> T.asMatrix(): EjmlFloatMatrix<T> = EjmlFloatMatrix(this)
public fun <T: FMatrix> T.asVector(): EjmlFloatVector<T> = EjmlFloatVector(this)
override fun buildMatrix(
rows: Int,
@@ -626,8 +626,8 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace<Double, Float64Field, DMatri
}
}
public fun <T: DMatrix> T.asMatrix() = EjmlDoubleMatrix(this)
public fun <T: DMatrix> T.asVector() = EjmlDoubleVector(this)
public fun <T: DMatrix> T.asMatrix(): EjmlDoubleMatrix<T> = EjmlDoubleMatrix(this)
public fun <T: DMatrix> T.asVector(): EjmlDoubleVector<T> = EjmlDoubleVector(this)
override fun buildMatrix(
@@ -853,8 +853,8 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace<Float, Float32Field, FMatrix
}
}
public fun <T : FMatrix> T.asMatrix() = EjmlFloatMatrix(this)
public fun <T : FMatrix> T.asVector() = EjmlFloatVector(this)
public fun <T : FMatrix> T.asMatrix(): EjmlFloatMatrix<T> = EjmlFloatMatrix(this)
public fun <T : FMatrix> T.asVector(): EjmlFloatVector<T> = EjmlFloatVector(this)
override fun buildMatrix(
rows: Int,

View File

@@ -9,7 +9,7 @@ Specialization of KMath APIs for Double numbers.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-for-real:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-for-real:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -19,6 +19,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-for-real:0.4.2")
implementation("space.kscience:kmath-for-real:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
dependencies {
api(projects.kmathCore)

View File

@@ -11,7 +11,7 @@ Functions and interpolations.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-functions:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-functions:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -21,6 +21,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-functions:0.4.2")
implementation("space.kscience:kmath-functions:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
dependencies {
api(projects.kmathCore)

View File

@@ -6,7 +6,7 @@
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-geometry:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-geometry:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-geometry:0.4.2")
implementation("space.kscience:kmath-geometry:0.5.0")
}
```

View File

@@ -6,9 +6,9 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
useContextReceivers()
useContextParameters()
useSerialization()
dependencies {
api(projects.kmath.kmathComplex)

View File

@@ -27,8 +27,8 @@ public data class Float64Circle2D(
override val radius: Float64,
) : Circle2D<Float64>
public fun Circle2D(center: Vector2D<Float64>, radius: Double): Float64Circle2D = Float64Circle2D(
center as? Float64Vector2D ?: Float64Vector2D(center.x, center.y),
public fun Circle2D(center: Vector2D<Float64>, radius: Float64): Circle2D<Float64> = Float64Circle2D(
center,
radius
)

View File

@@ -124,6 +124,8 @@ public object Float64Space3D : GeometrySpace<Vector3D<Float64>, Double> {
override val bufferFactory: MutableBufferFactory<Vector3D<Float64>> = MutableBufferFactory()
}
public fun Float64Vector3D(x: Number, y: Number, z: Number): Float64Vector3D = Float64Space3D.vector(x, y, z)
@Suppress("UnusedReceiverParameter")
public val Float64Field.euclidean3D: Float64Space3D get() = Float64Space3D

View File

@@ -8,17 +8,18 @@
package space.kscience.kmath.geometry
import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.invoke
/**
* Get a line, containing this [LineSegment]
*/
context(Group<V>)
context(group: Group<V>)
public val <V : Any> LineSegment<V>.line: Line<V>
get() = Line(begin, end - begin)
get() = Line(begin, group { end - begin })
/**
* Get a length of a line segment
*/
context(GeometrySpace<V, D>)
context(space: GeometrySpace<V, D>)
public val <V : Any, D : Comparable<D>> LineSegment<V>.length: D
get() = norm(end - begin)
get() = space { norm(end - begin) }

View File

@@ -6,7 +6,7 @@
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-histograms:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-histograms:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-histograms:0.4.2")
implementation("space.kscience:kmath-histograms:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
useCoroutines()
}

View File

@@ -82,7 +82,7 @@ internal class MultivariateHistogramTest {
assertTrue {
res.bins.count() >= histogram1.bins.count()
}
assertEquals(0.0, res.bins.sumOf { it.binValue.toDouble() })
assertEquals(0.0, res.bins.sumOf { it.binValue })
}
}
}

View File

@@ -6,7 +6,7 @@
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-jupyter:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-jupyter:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-jupyter:0.4.2")
implementation("space.kscience:kmath-jupyter:0.5.0")
}
```

View File

@@ -1,19 +1,25 @@
plugins {
id("space.kscience.gradle.jvm")
kotlin("jupyter.api")
id("space.kscience.gradle.mpp")
alias(spclibs.plugins.kotlin.jupyter.api)
}
dependencies {
api(spclibs.kotlinx.html)
api(project(":kmath-ast"))
api(project(":kmath-complex"))
api(project(":kmath-for-real"))
kscience {
jvm()
jvmMain {
api(spclibs.kotlinx.html)
api(project(":kmath-ast"))
api(project(":kmath-complex"))
api(project(":kmath-for-real"))
}
}
readme {
maturity = space.kscience.gradle.Maturity.PROTOTYPE
}
tasks.processJupyterApiResources {
libraryProducers = listOf("space.kscience.kmath.jupyter.KMathJupyter")
kotlinJupyter {
integrations {
producer("space.kscience.kmath.jupyter.KMathJupyter")
}
}

View File

@@ -8,7 +8,7 @@
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-kotlingrad:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-kotlingrad:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -18,6 +18,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-kotlingrad:0.4.2")
implementation("space.kscience:kmath-kotlingrad:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-memory:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-memory:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-memory:0.4.2")
implementation("space.kscience:kmath-memory:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
dependencies {
api(projects.kmathCore)

View File

@@ -6,7 +6,7 @@ JetBrains Multik connector
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-multik:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-multik:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-multik:0.4.2")
implementation("space.kscience:kmath-multik:0.5.0")
}
```

View File

@@ -210,7 +210,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
override fun Tensor<T>.getTensor(i: Int): MultikTensor<T> = asMultik().array.mutableView(i).wrap()
override fun StructureND<T>.transposed(i: Int, j: Int): MultikTensor<T> = asMultik().array.transpose(i, j).wrap()
override fun StructureND<T>.transposed(i: Int, j: Int): MultikTensor<T> = asMultik().array.transpose(j, i).wrap()
override fun Tensor<T>.view(shape: ShapeND): MultikTensor<T> {
require(shape.asList().all { it > 0 })

View File

@@ -5,6 +5,8 @@
package space.kscience.kmath.multik
import org.jetbrains.kotlinx.multik.api.d2arrayIndices
import org.jetbrains.kotlinx.multik.api.mk
import org.jetbrains.kotlinx.multik.default.DefaultEngine
import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.nd.ShapeND
@@ -15,6 +17,7 @@ import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.randomNormal
import space.kscience.kmath.tensors.core.tensorAlgebra
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@OptIn(PerformancePitfall::class)
@@ -46,4 +49,16 @@ internal class MultikNDTest {
}
}
@Test
fun transposed() = with(multikAlgebra) {
val matrix = mk.d2arrayIndices(5, 3) { i, j -> (i + j).toDouble() }.wrap()
val transposed = matrix.transposed()
assertEquals(matrix.shape[0], transposed.shape[1])
assertEquals(matrix.shape[1], transposed.shape[0])
matrix.indices.forEach { index ->
assertEquals(matrix[index], transposed[index.reversed().toIntArray()], 1e-6)
}
}
}

View File

@@ -6,7 +6,7 @@ Ojalgo bindings for kmath
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-ojalgo:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-ojalgo:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-ojalgo:0.4.2")
implementation("space.kscience:kmath-ojalgo:0.5.0")
}
```

View File

@@ -5,6 +5,7 @@
package space.kscience.kmath.ojalgo
import org.ojalgo.matrix.decomposition.MatrixDecomposition
import org.ojalgo.matrix.store.MatrixStore
import org.ojalgo.matrix.store.PhysicalStore
import space.kscience.kmath.UnstableKMathAPI
@@ -34,6 +35,13 @@ public value class OjalgoMatrix<T : Comparable<T>>(public val ojalgoVector: Matr
override fun get(i: Int, j: Int): T = ojalgoVector.get(i.toLong(), j.toLong())
}
/**
* Decompose matrix and throw exception if decomposition failed
*/
private fun <T: Comparable<T>, D: MatrixDecomposition<T>> D.decomposeOrFail(matrix: MatrixStore<T>): D = apply {
if(!decompose(matrix)) error("Matrix decomposition failed")
}
public class OjalgoLinearSpace<T : Comparable<T>, A : Ring<T>>(
public val ojalgo: Ojalgo<T, A>
@@ -97,13 +105,13 @@ public class OjalgoLinearSpace<T : Comparable<T>, A : Ring<T>>(
val origin = structure.toOjalgo()
val raw: Any? = when (attribute) {
Determinant -> ojalgo.lu.make(origin).apply { decompose(origin) }.determinant
Determinant -> ojalgo.lu.make(origin).decomposeOrFail(origin).determinant
Inverted -> ojalgo.lu.make().apply { decompose(origin) }.inverse.asMatrix()
Inverted -> ojalgo.lu.make().decomposeOrFail(origin).inverse.asMatrix()
LUP -> object : LupDecomposition<T> {
val lup by lazy {
ojalgo.lu.make(origin).apply { decompose(origin) }
ojalgo.lu.make(origin).decomposeOrFail(origin)
}
override val pivot: IntBuffer get() = lup.pivotOrder.asBuffer()
override val l: Matrix<T> get() = lup.l.asMatrix().withAttribute(LowerTriangular)
@@ -112,14 +120,14 @@ public class OjalgoLinearSpace<T : Comparable<T>, A : Ring<T>>(
Cholesky -> object : CholeskyDecomposition<T> {
val cholesky by lazy {
ojalgo.cholesky.make(origin).apply { decompose(origin) }
ojalgo.cholesky.make(origin).decomposeOrFail(origin)
}
override val l: Matrix<T> get() = cholesky.l.asMatrix()
}
QR -> object : QRDecomposition<T> {
val qr by lazy {
ojalgo.qr.make(origin).apply { decompose(origin) }
ojalgo.qr.make(origin).decomposeOrFail(origin)
}
override val q: Matrix<T> get() = qr.q.asMatrix().withAttribute(OrthogonalAttribute)
override val r: Matrix<T> get() = qr.r.asMatrix().withAttribute(UpperTriangular)
@@ -127,7 +135,7 @@ public class OjalgoLinearSpace<T : Comparable<T>, A : Ring<T>>(
SVD -> object : SingularValueDecomposition<T> {
val svd by lazy {
ojalgo.svd.make(origin).apply { decompose(origin) }
ojalgo.svd.make(origin).decomposeOrFail(origin)
}
override val u: Matrix<T> get() = svd.u.asMatrix()
@@ -141,7 +149,7 @@ public class OjalgoLinearSpace<T : Comparable<T>, A : Ring<T>>(
EIG -> object : EigenDecomposition<T> {
val eigen by lazy {
ojalgo.eigen.make(origin).apply { decompose(origin) }
ojalgo.eigen.make(origin).decomposeOrFail(origin)
}
override val v: Matrix<T> get() = eigen.v.asMatrix()

View File

@@ -13,6 +13,7 @@ import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.structures.Float64
import space.kscience.kmath.testutils.assertStructureEquals
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
@@ -22,14 +23,14 @@ import kotlin.test.assertTrue
class OjalgoMatrixTest {
@Test
fun testTranspose() = with(Ojalgo.Companion.R064.linearSpace) {
fun testTranspose() = with(Ojalgo.R064.linearSpace) {
val matrix = one(3, 3)
val transposed = matrix.transposed()
assertTrue { StructureND.Companion.contentEquals(matrix, transposed) }
}
@Test
fun testBuilder() = Ojalgo.Companion.R064.linearSpace {
fun testBuilder() = Ojalgo.R064.linearSpace {
val matrix = MatrixBuilder(2, 3).fill(
1.0, 0.0, 0.0,
0.0, 1.0, 2.0
@@ -39,7 +40,7 @@ class OjalgoMatrixTest {
}
@Test
fun testMatrixExtension() = with(Ojalgo.Companion.R064.linearSpace) {
fun testMatrixExtension() = with(Ojalgo.R064.linearSpace) {
val transitionMatrix: Matrix<Float64> = VirtualMatrix(6, 6) { row, col ->
when {
col == 0 -> .50
@@ -61,7 +62,7 @@ class OjalgoMatrixTest {
}
@Test
fun test2DDot() = with(Ojalgo.Companion.R064.linearSpace) {
fun test2DDot() = with(Ojalgo.R064.linearSpace) {
val firstMatrix = buildMatrix(2, 3) { i, j -> (i + j).toDouble() }
val secondMatrix = buildMatrix(3, 2) { i, j -> (i + j).toDouble() }
@@ -76,7 +77,7 @@ class OjalgoMatrixTest {
}
@Test
fun testCholesky() = with(Ojalgo.Companion.R064.linearSpace) {
fun testCholesky() = with(Ojalgo.R064.linearSpace) {
val l = MatrixBuilder(4, 4).fill(
1.0, 0.0, 0.0, 0.0,
1.0, 1.0, 0.0, 0.0,
@@ -90,4 +91,18 @@ class OjalgoMatrixTest {
assertStructureEquals(l, chol!!.l, 1e-4)
}
@Test
fun testCholeskyNonPositive(): Unit = with(Ojalgo.R064.linearSpace) {
val matrix = MatrixBuilder(2, 2).fill(
1.0, 0.0,
0.0, -1.0,
)
assertFailsWith<IllegalStateException> {
computeAttribute(matrix, Cholesky)!!.l
}
}
}

View File

@@ -6,7 +6,7 @@
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-optimization:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-optimization:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-optimization:0.4.2")
implementation("space.kscience:kmath-optimization:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
}
kotlin.sourceSets {

View File

@@ -6,7 +6,7 @@
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-stat:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-stat:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-stat:0.4.2")
implementation("space.kscience:kmath-stat:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ kscience {
jvm()
js()
native()
wasm()
wasmJs()
useCoroutines()

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.Float32Field
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.Float32
import space.kscience.kmath.structures.Float64
import space.kscience.kmath.structures.indices
/**
* Geometric mean. Nth root of product of values.
*/
public class GeometricMean<T : Comparable<T>>(
private val field: ExtendedField<T>
) : BlockingStatistic<T, T>, ComposableStatistic<T, Pair<T, Int>, T> {
private fun logsum(data: Buffer<T>): T = with(field) {
require(data.size > 0) { "Data must not be empty" }
var res = zero
for (i in data.indices) {
if (data[i] < zero) throw ArithmeticException("Geometric mean is not defined for negative numbers. Found: " + data[i])
res += ln(data[i])
}
res
}
override fun evaluateBlocking(data: Buffer<T>): T = with(field) {
exp(logsum(data) / data.size)
}
override suspend fun computeIntermediate(data: Buffer<T>): Pair<T, Int> = logsum(data) to data.size
override suspend fun composeIntermediate(first: Pair<T, Int>, second: Pair<T, Int>): Pair<T, Int> = with(field) {
first.first + second.first to first.second + second.second
}
override suspend fun toResult(intermediate: Pair<T, Int>): T =
with(field) { exp(intermediate.first / intermediate.second) }
override suspend fun evaluate(data: Buffer<T>): T = super<ComposableStatistic>.evaluate(data)
}
public val Float32Field.geometricMean: GeometricMean<Float32> get() = GeometricMean(Float32Field)
public val Float64Field.geometricMean: GeometricMean<Float64> get() = GeometricMean(Float64Field)

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.*
/**
* Maximum element of non-empty input data
*/
public class MaxStatistic<T : Comparable<T>> : BlockingStatistic<T, T>, ComposableStatistic<T, T, T> {
override fun evaluateBlocking(data: Buffer<T>): T {
require(data.size > 0) { "Data must not be empty" }
var res = data[0]
for (i in 1..data.indices.last) {
val e = data[i]
if (e > res) res = e
}
return res
}
override suspend fun computeIntermediate(data: Buffer<T>): T = evaluateBlocking(data)
override suspend fun composeIntermediate(first: T, second: T): T = if (first > second) first else second
override suspend fun toResult(intermediate: T): T = intermediate
override suspend fun evaluate(data: Buffer<T>): T = super<ComposableStatistic>.evaluate(data)
}
// max
public val Float64Field.max: MaxStatistic<Float64> get() = MaxStatistic()
public val Int32Ring.max: MaxStatistic<Int32> get() = MaxStatistic()
public val Int64Ring.max: MaxStatistic<Int64> get() = MaxStatistic()

View File

@@ -9,32 +9,42 @@ import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.*
/**
* Arithmetic mean
* Arithmetic mean.
*
* To limit numeric errors, the value of the statistic is computed using the recursive updating algorithm:
*
* Initialize `m` = the first value. For each additional value, update using
* ```
* m = m + (new value - m) / (number of observations)
* ```
*
*/
public class Mean<T>(
private val field: Field<T>,
) : ComposableStatistic<T, Pair<T, Int>, T>, BlockingStatistic<T, T> {
override fun evaluateBlocking(data: Buffer<T>): T = with(field) {
var res = zero
var mean = zero
var delta: T
for (i in data.indices) {
res += data[i]
delta = data[i] - mean
mean += delta / (i + 1)
}
res / data.size
return mean
}
override suspend fun evaluate(data: Buffer<T>): T = super<ComposableStatistic>.evaluate(data)
override suspend fun computeIntermediate(data: Buffer<T>): Pair<T, Int> = with(field) {
var res = zero
for (i in data.indices) {
res += data[i]
}
res to data.size
var sum = zero
data.indices.forEach { sum += data[it] }
sum to data.size
}
override suspend fun composeIntermediate(first: Pair<T, Int>, second: Pair<T, Int>): Pair<T, Int> =
with(field) { first.first + second.first } to (first.second + second.second)
override suspend fun composeIntermediate(
first: Pair<T, Int>,
second: Pair<T, Int>
): Pair<T, Int> = with(field) { first.first + second.first } to (first.second + second.second)
override suspend fun toResult(intermediate: Pair<T, Int>): T = with(field) {
intermediate.first / intermediate.second
@@ -47,8 +57,6 @@ public class Mean<T>(
}
}
//TODO replace with optimized version which respects overflow
public val Float64Field.mean: Mean<Float64> get() = Mean(Float64Field)
public val Int32Ring.mean: Mean<Int> get() = Mean(Int32Field)
public val Int64Ring.mean: Mean<Long> get() = Mean(Int64Field)

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.*
/**
* Minimum element of non-empty input data
*/
public class MinStatistic<T : Comparable<T>> : BlockingStatistic<T, T>, ComposableStatistic<T, T, T> {
override fun evaluateBlocking(data: Buffer<T>): T {
require(data.size > 0) { "Data must not be empty" }
var res = data[0]
for (i in 1..data.indices.last) {
val e = data[i]
if (e < res) res = e
}
return res
}
override suspend fun computeIntermediate(data: Buffer<T>): T = evaluateBlocking(data)
override suspend fun composeIntermediate(first: T, second: T): T = if (first < second) first else second
override suspend fun toResult(intermediate: T): T = intermediate
override suspend fun evaluate(data: Buffer<T>): T = super<ComposableStatistic>.evaluate(data)
}
// min
public val Float64Field.min: MinStatistic<Float64> get() = MinStatistic()
public val Int32Ring.min: MinStatistic<Int32> get() = MinStatistic()
public val Int64Ring.min: MinStatistic<Int64> get() = MinStatistic()

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.misc.sortedWith
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.Float64
import kotlin.math.floor
/**
* Compute the quantile of a data [Buffer] at a specified probability [p] on the interval [0,1].
* The class argument [sorted] indicates whether data [Buffer] can be assumed to be sorted;
* if false (the default), then the elements of data [Buffer] will be partially sorted in-place using [comparator].
*
* Samples quantile are defined by `Q(p) = (1-γ)*x[j] + γ*x[j+1]`,
* where `x[j]` is the j-th order statistic of `v`, `j = floor(n*p + m)`,
* `m = alpha + p*(1 - alpha - beta)` and `γ = n*p + m - j`.
*
* By default (`alpha = beta = 1`), quantiles are computed via linear interpolation between the points
* `((k-1)/(n-1), x[k])`, for `k = 1:n` where `n = length(v)`. This corresponds to Definition 7
* of Hyndman and Fan (1996), and is the same as the R and NumPy default.
*
* The keyword arguments `alpha` and `beta` correspond to the same parameters in Hyndman and Fan,
* setting them to different values allows to calculate quantiles with any of the methods 4-9
* defined in this paper:
* - Def. 4: `alpha=0`, `beta=1`
* - Def. 5: `alpha=0.5`, `beta=0.5` (MATLAB default)
* - Def. 6: `alpha=0`, `beta=0` (Excel `PERCENTILE.EXC`, Python default, Stata `altdef`)
* - Def. 7: `alpha=1`, `beta=1` (Julia, R and NumPy default, Excel `PERCENTILE` and `PERCENTILE.INC`, Python `'inclusive'`)
* - Def. 8: `alpha=1/3`, `beta=1/3`
* - Def. 9: `alpha=3/8`, `beta=3/8`
*
* # References
* - Hyndman, R.J and Fan, Y. (1996) "Sample Quantiles in Statistical Packages",
* *The American Statistician*, Vol. 50, No. 4, pp. 361-365
*
* - [Quantile on Wikipedia](https://en.wikipedia.org/wiki/Quantile) details the different quantile definitions
*
* # Notes
* - Performance can be improved by using a selection algorithm instead of a complete sort.
* - As further improvement, API can be redesigned support "a batch mode" - scenarios when several different quantiles are desired.
*
*/
public class Quantile<T>(
private val field: Field<T>,
private val p: Float64,
private val sorted: Boolean = false,
private val comparator: Comparator<T>, //todo provide default?
private val alpha: Float64 = 1.0,
private val beta: Float64 = alpha,
) : BlockingStatistic<T, T> {
init {
require(p in 0.0..1.0) { "Quantile value must be in [0.0, 1.0] range. Got $p." }
require(alpha in 0.0..1.0) { "alpha parameter must be in [0.0, 1.0] range. Got $alpha." }
require(beta in 0.0..1.0) { "beta parameter must be in [0.0, 1.0] range. Got $beta." }
}
override fun evaluateBlocking(data: Buffer<T>): T {
// adapted from https://github.com/JuliaStats/Statistics.jl/blob/master/src/Statistics.jl#L1045
require(data.size > 0) { "Can't compute percentile of an empty buffer" }
if (data.size == 1) {
return data[0]
}
val n = data.size;
val m = alpha + p * (1 - alpha - beta)
val j = (floor(n*p + m).toInt()).coerceIn(1, n-1)
val aleph = n*p + m;// todo use Math.fma in case of double or float for better accuracy
val gamma = (aleph -j).coerceIn(0.0, 1.0)
var sortedData = data
if (!sorted) {
sortedData = data.sortedWith(comparator)
}
with(field) {
return sortedData[j-1] + gamma*(sortedData[j]-sortedData[j-1])
}
}
public companion object {
public fun evaluate(p: Float64, buffer: Buffer<Float64>): Double = Float64Field.quantile(p).evaluateBlocking(buffer)
}
}
public fun Float64Field.quantile(p: Float64): Quantile<Float64> = Quantile(Float64Field, p, false, naturalOrder())

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.Float32Field
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.Float32
import space.kscience.kmath.structures.Float64
/**
* The standard deviation.
*
* Standard deviation is the positive square root of the variance. This implementation use a [Variance] class.
* The [isBiasCorrected] property determines whether to compute:
* - The "sample standard deviation" (square root of bias-corrected sample variance) when true
* - The "population standard deviation" (square root of non-bias-corrected population variance) when false
*
* @param T the type of elements in the dataset
* @property field The [ExtendedField] used for mathematical operations
* @property isBiasCorrected If true, applies Bessel's correction (n-1 denominator) for sample variance
*
* @see Variance for more information about variance calculation
*/
public class StandardDeviation<T>(
private val field: ExtendedField<T>,
private val isBiasCorrected: Boolean = true
) : BlockingStatistic<T, T> {
override fun evaluateBlocking(data: Buffer<T>): T {
return field.sqrt(Variance(field, isBiasCorrected).evaluateBlocking(data))
}
public companion object {
public fun evaluate(buffer: Buffer<Float64>): Float64 = Float64Field.std.evaluateBlocking(buffer)
public fun evaluate(buffer: Buffer<Float32>): Float32 = Float32Field.std.evaluateBlocking(buffer)
}
}
public val Float64Field.std: StandardDeviation<Float64> get() = StandardDeviation(Float64Field)
public val Float32Field.std: StandardDeviation<Float32> get() = StandardDeviation(Float32Field)

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.Field
import space.kscience.kmath.operations.Float32Field
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.Int32Field
import space.kscience.kmath.operations.Int32Ring
import space.kscience.kmath.operations.Int64Field
import space.kscience.kmath.operations.Int64Ring
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.Float32
import space.kscience.kmath.structures.Float64
import space.kscience.kmath.structures.Int32
import space.kscience.kmath.structures.Int64
/**
* Variance of the available values.
*
* By default, the unbiased "sample variance" definitional formula is used:
*
* The variance is computed as:
* - Unbiased: σ² = Σ (xᵢ - μ)² / (n - 1)
* - Biased: σ² = Σ (xᵢ - μ)² / n
* where μ is the sample mean and `n` is the number of sample observations.
*
*
* The `isBiasCorrected` property determines whether the "population" (biased) or "sample" (unbiased)
* value is returned.
*/
public class Variance<T>(
private val field: Field<T>,
private val isBiasCorrected: Boolean = true
) : BlockingStatistic<T, T> {
/**
* Computes the variance of the elements in the given buffer using Welford's online algorithm.
* This method operates in a single pass and supports both biased and unbiased (Bessel's correction) variance.
*
* @param data The input buffer containing the elements to compute the variance for.
* @return The computed variance as a value of type [T].
* @throws IllegalArgumentException If the buffer size is less than 2 (variance requires at least 2 elements).
*
* Algorithm Details:
* - Uses Welford's method for numerical stability in a single pass.
* - For unbiased variance (Bessel's correction), divides by (n-1).
* - For biased variance, divides by n.
*
* Math Background:
* The variance is computed as:
* - Unbiased: σ² = Σ (xᵢ - μ)² / (n - 1)
* - Biased: σ² = Σ (xᵢ - μ)² / n
* where μ is the sample mean.
*
* The implementation avoids catastrophic cancellation by using the recurrence relation:
* M₂ = M₂ + (xᵢ - meanₙ₋₁) * (xᵢ - meanₙ)
* where meanₙ is the updated mean after including xᵢ.
*/
override fun evaluateBlocking(data: Buffer<T>): T = with(field) {
if (data.size < 2) return zero // Variance requires at least 2 elements
// Recursively updates mean and variance in a single pass.
var mean = data[0]
var m2 = zero
var delta: T
for (i in 1 until data.size) {
delta = data[i] - mean
mean += delta / (i + 1)
m2 += delta * (data[i] - mean)
}
if (isBiasCorrected) {
return m2 / (data.size - 1)
} else {
return m2 / (data.size)
}
}
/**
* Calculates the variance of sample data given a precomputed mean.
* The method use "corrected two-pass algorithm" from Chan, Golub, Levesque, "Algorithms for Computing the Sample Variance",
* American Statistician, vol. 37, no. 3 (1983) pp. 242-247 for numerical stability.
*
* @param data The input data buffer.
* @param mean Precomputed mean of the data.
* @return Variance of the data (biased or unbiased based on [isBiasCorrected]).
*/
public fun evaluate(data: Buffer<T>, mean: T): T = with(field) {
if (data.size < 2) return zero // Variance requires at least 2 elements
var sumOfSquaredDeviations = zero
var sumOfDeviations = zero
for (i in 0 until data.size) {
val deviation = data[i] - mean
sumOfDeviations += deviation
sumOfSquaredDeviations += deviation * deviation
}
return if (isBiasCorrected) {
(sumOfSquaredDeviations - (sumOfDeviations * sumOfDeviations) / data.size) / (data.size - 1)
} else {
(sumOfSquaredDeviations - (sumOfDeviations * sumOfDeviations)) / data.size
}
}
public companion object {
public fun evaluate(buffer: Buffer<Float64>): Float64 = Float64Field.variance.evaluateBlocking(buffer)
public fun evaluate(buffer: Buffer<Float32>): Float32 = Float32Field.variance.evaluateBlocking(buffer)
}
}
public val Float64Field.variance: Variance<Float64> get() = Variance(Float64Field)
public val Float32Field.variance: Variance<Float32> get() = Variance(Float32Field)

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.test.runTest
import space.kscience.kmath.operations.Float32Field
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.structures.Float32Buffer
import space.kscience.kmath.structures.Float64Buffer
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
internal class GeometricMeanBasicTest {
// Float64 tests
@Test
fun singleBlockingFloat64GeometricMean() = runTest {
val res = Float64Field.geometricMean.evaluateBlocking(Float64Buffer(1.0, 8.0, 27.0))
assertEquals(6.0, res)
}
@Test
fun float64GeometricMeanWithSingleElement() = runTest {
val res = Float64Field.geometricMean.evaluateBlocking(Float64Buffer(5.0))
assertEquals(5.0, res, 1e-1)
}
@Test
fun float64GeometricMeanWithNegativeNumbers() = runTest {
assertFailsWith<ArithmeticException> {
Float64Field.geometricMean.evaluateBlocking(Float64Buffer(1.0, -2.0, 3.0))
}
}
@Test
fun float64GeometricMeanWithZero() = runTest {
val res = Float64Field.geometricMean.evaluateBlocking(Float64Buffer(1.0, 0.0, 3.0))
assertEquals(0.0, res)
}
@Test
fun float64GeometricMeanWithEmptyBuffer() = runTest {
assertFailsWith<IllegalArgumentException> {
Float64Field.geometricMean.evaluateBlocking(Float64Buffer())
}
}
@Test
fun float64GeometricMeanWithLargeNumbers() = runTest {
val res = Float64Field.geometricMean.evaluateBlocking(Float64Buffer(1e6, 1e6, 1e6))
assertEquals(1e6, res,1e-1)
}
@Test
fun float64GeometricMeanComposite() = runTest {
val flow: Flow<Float64Buffer> = flow {
emit(Float64Buffer(doubleArrayOf(1.0)))
emit(Float64Buffer(doubleArrayOf(8.0, 27.0)))
}
val average = Float64Field.geometricMean
.flow(flow)
.take(10)
.last()
// Product = 1 * 8 * 27 = 216
// 3d root of 216 = 6
assertEquals(6.0, average)
}
// Float32 tests
@Test
fun singleBlockingFloat32GeometricMean() = runTest {
val res = Float32Field.geometricMean.evaluateBlocking(Float32Buffer(1f, 8f, 27f))
assertEquals(6f, res)
}
@Test
fun float32GeometricMeanWithSingleElement() = runTest {
val res = Float32Field.geometricMean.evaluateBlocking(Float32Buffer(5f))
assertEquals(5f, res)
}
@Test
fun float32GeometricMeanWithNegativeNumbers() = runTest {
assertFailsWith<ArithmeticException> {
Float32Field.geometricMean.evaluateBlocking(Float32Buffer(1f, -2f, 3f))
}
}
@Test
fun float32GeometricMeanWithZero() = runTest {
val res = Float32Field.geometricMean.evaluateBlocking(Float32Buffer(1f, 0f, 3f))
assertEquals(0f, res)
}
@Test
fun float32GeometricMeanWithEmptyBuffer() = runTest {
assertFailsWith<IllegalArgumentException> {
Float32Field.geometricMean.evaluateBlocking(Float32Buffer())
}
}
@Test
fun float32GeometricMeanWithPrecision() = runTest {
val res = Float32Field.geometricMean.evaluateBlocking(Float32Buffer(1f, 2f, 4f))
assertEquals(2f, res, 1e-8f) // 2^3 = 8, 8^(1/3) = 2
}
@Test
fun float32GeometricMeanComposite() = runTest {
val flow: Flow<Float32Buffer> = flow {
emit(Float32Buffer(1f))
emit(Float32Buffer(8f, 27f))
}
val average = Float32Field.geometricMean
.flow(flow)
.take(10)
.last()
// Product = 1 * 8 * 27 = 216
// 3d root of 216 = 6
assertEquals(6f, average)
}
// Extension property tests
@Test
fun float64ExtensionPropertyWorks() = runTest {
val res = Float64Field.geometricMean(Float64Buffer(1.0, 4.0, 16.0))
assertEquals(4.0, res,1e-1)
}
@Test
fun float32ExtensionPropertyWorks() = runTest {
val res = Float32Field.geometricMean(Float32Buffer(1f, 4f, 16f))
assertEquals(4f, res, 1e-1f)
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.structures.Float64Buffer
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlinx.coroutines.test.runTest
import space.kscience.kmath.operations.Int32Ring
import space.kscience.kmath.operations.Int64Ring
import space.kscience.kmath.structures.Int32Buffer
import space.kscience.kmath.structures.Int64Buffer
internal class MaxStatisticBasicTest {
// Int32 Tests
@Test
fun singleBlockingInt32Max() = runTest {
val res = Int32Ring.max(Int32Buffer(1, 2, 3))
assertEquals(3, res)
}
@Test
fun int32MaxWithNegativeValues() = runTest {
val res = Int32Ring.max(Int32Buffer(-1, -5, -3))
assertEquals(-1, res)
}
@Test
fun int32SingleElement() = runTest {
val res = Int32Ring.max(Int32Buffer(42))
assertEquals(42, res)
}
// Int64 Tests
@Test
fun singleBlockingInt64Max() = runTest {
val res = Int64Ring.max(Int64Buffer(1L, 2L, 3L))
assertEquals(3L, res)
}
@Test
fun int64MaxWithLargeValues() = runTest {
val res = Int64Ring.max(Int64Buffer(Long.MAX_VALUE, Long.MIN_VALUE, 0L))
assertEquals(Long.MAX_VALUE, res)
}
// Float64 Tests
@Test
fun singleBlockingFloat64Max() = runTest {
val res = Float64Field.max(Float64Buffer(1.0, 2.5, 3.1))
assertEquals(3.1, res)
}
@Test
fun float64MaxWithSpecialValues() = runTest {
val res = Float64Field.max(Float64Buffer(Double.POSITIVE_INFINITY, Double.MAX_VALUE, 0.0))
assertEquals(Double.POSITIVE_INFINITY, res)
}
@Test
fun float64MaxWithNaN() = runTest {
val res = Float64Field.max(Float64Buffer(Double.POSITIVE_INFINITY, Double.MAX_VALUE, Double.NaN))
assertEquals(Double.NaN, res)
}
// Edge Cases
@Test
fun emptyBufferThrowsException() = runTest {
assertFailsWith<IllegalArgumentException> {
Float64Field.max(Float64Buffer())
}
}
@Test
fun allEqualValuesReturnsSame() = runTest {
val res = Int32Ring.max(Int32Buffer(5, 5, 5))
assertEquals(5, res)
}
@Test
fun extremeValueAtBufferStart() = runTest {
val res = Float64Field.max(Float64Buffer(Double.MAX_VALUE, 1.0, 2.0))
assertEquals(Double.MAX_VALUE, res)
}
@Test
fun extremeValueAtBufferEnd() = runTest {
val res = Int64Ring.max(Int64Buffer(1L, 2L, Long.MAX_VALUE))
assertEquals(Long.MAX_VALUE, res)
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.stat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.test.runTest
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.structures.Float64Buffer
import kotlin.test.Test
import kotlin.test.assertEquals
class MeanStatisticBasicTest {
companion object {
val float64Sample = RandomGenerator.default(123).nextDoubleBuffer(100)
}
@Test
fun meanFloat64() {
assertEquals(0.488, Float64Field.mean(float64Sample), 0.0002)
}
@Test
fun numericalStability(){
val data = doubleArrayOf(1.0e4, 1.0, 1.0, 1.0, 1.0);
assertEquals(2000.8, Float64Field.mean.evaluateBlocking(Float64Buffer(data)));
}
@Test
fun compositeWithDifferentlySizedChunks() = runTest {
val flow: Flow<Float64Buffer> = flow {
emit(Float64Buffer(doubleArrayOf(1.0, 2.0)))
emit(Float64Buffer(doubleArrayOf(1.0, 2.0, 4.0)))
}
val average = Float64Field.mean
.flow(flow)
.take(100)
.last()
// (1+2+1+2+4)/5 = 10/5 = 2
assertEquals(2.0, average, 1e-6)
}
}

View File

@@ -13,7 +13,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals
@OptIn(UnstableKMathAPI::class)
class TestBasicStatistics {
class MedianStatisticBasicTest {
companion object {
val float64Sample = RandomGenerator.default(123).nextDoubleBuffer(100)
}
@@ -24,8 +24,4 @@ class TestBasicStatistics {
assertEquals(0.5055, Float64Field.median(float64Sample.slice { 0..<last }), 0.0005)
}
@Test
fun meanFloat64() {
assertEquals(0.488, Float64Field.mean(float64Sample), 0.0002)
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.structures.Float64Buffer
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlinx.coroutines.test.runTest
import space.kscience.kmath.operations.Int32Ring
import space.kscience.kmath.operations.Int64Ring
import space.kscience.kmath.structures.Int32Buffer
import space.kscience.kmath.structures.Int64Buffer
internal class MinStatisticBasicTest {
// Int32 Tests
@Test
fun singleBlockingInt32Min() = runTest {
val res = Int32Ring.min(Int32Buffer(1, 2, 3))
assertEquals(1, res)
}
@Test
fun int32MinWithNegativeValues() = runTest {
val res = Int32Ring.min(Int32Buffer(-1, -5, -3))
assertEquals(-5, res)
}
@Test
fun int32SingleElement() = runTest {
val res = Int32Ring.min(Int32Buffer(42))
assertEquals(42, res)
}
// Int64 Tests
@Test
fun singleBlockingInt64Min() = runTest {
val res = Int64Ring.min(Int64Buffer(1L, 2L, 3L))
assertEquals(1L, res)
}
// Float64 Tests
@Test
fun singleBlockingFloat64Min() = runTest {
val res = Float64Field.min(Float64Buffer(1.0, 2.5, 3.1))
assertEquals(1.0, res)
}
@Test
fun float64MinWithNaN() = runTest {
val res = Float64Field.min(Float64Buffer(Double.NEGATIVE_INFINITY, Double.MIN_VALUE, Double.NaN))
assertEquals(Double.NEGATIVE_INFINITY, res)
}
@Test
fun float64MinWithSpecialValues() = runTest {
val res = Float64Field.min(Float64Buffer(Double.NEGATIVE_INFINITY, Double.MIN_VALUE, 0.0))
assertEquals(Double.NEGATIVE_INFINITY, res)
}
// Edge Cases
@Test
fun emptyBufferThrowsException() = runTest {
assertFailsWith<IllegalArgumentException> {
Float64Field.min(Float64Buffer())
}
}
@Test
fun allEqualValuesReturnsSame() = runTest {
val res = Int32Ring.min(Int32Buffer(5, 5, 5))
assertEquals(5, res)
}
@Test
fun extremeValueAtBufferStart() = runTest {
val res = Float64Field.min(Float64Buffer(Double.MIN_VALUE, 1.0, 2.0))
assertEquals(Double.MIN_VALUE, res)
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.structures.Float64Buffer
import kotlin.math.abs
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
internal class QuantileBasicTest {
@Test
fun testBasicQuantile() {
val data = Float64Buffer(1.0, 2.0, 3.0, 4.0)
assertEquals(2.5, Quantile.evaluate(0.5, data))
}
@Test
fun testSingleElement() {
val data = Float64Buffer(1.0)
assertEquals(1.0, Quantile.evaluate(0.5, data))
}
@Test
fun testTwoElements() {
val data = Float64Buffer(1.0, 3.0)
assertEquals(2.0, Quantile.evaluate(0.5, data))
}
@Test
fun testSortedInput() {
val data = Float64Buffer(101) { it.toDouble() } // 0.0 to 100.0
val quantile = Quantile(DoubleField, 0.1, sorted = true, comparator = naturalOrder())
assertEquals(10.0, quantile.evaluateBlocking(data), 1e-6)
}
@Test
fun testReverseSortedInput() {
val data = Float64Buffer(101) { (100 - it).toDouble() } // 100.0 to 0.0
assertEquals(10.0, Quantile.evaluate(0.1, data), 1e-6)
}
// @Test //todo try to fix
// fun testExtremeValues() {
// val data = Float64Buffer(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)
// assertTrue(Quantile.evaluate(0.5, data).isInfinite())
//
// val data2 = Float64Buffer(Double.NEGATIVE_INFINITY, 1.0)
// assertTrue(Quantile.evaluate(0.5, data2).isInfinite())
// }
@Test
fun testVerySmallQuantile() {
val data = Float64Buffer(0.0, 1.0)
val result = Quantile.evaluate(1e-18, data)
assertTrue(abs(result) <= 1e-18)
}
@Test
fun testEmptyInput() {
val data = Float64Buffer(0) { 0.0 }
assertFailsWith<IllegalArgumentException> {
Quantile.evaluate(0.5, data)
}
}
@Test
fun testInvalidP() {
assertFailsWith<IllegalArgumentException> {
Quantile(Float64Field, -0.1, comparator = naturalOrder())
}
assertFailsWith<IllegalArgumentException> {
Quantile(Float64Field, 1.1, comparator = naturalOrder())
}
}
@Test
fun testAlphaBetaParameters() {
val v = Float64Buffer(2.0, 3.0, 4.0, 6.0, 9.0, 2.0, 6.0, 2.0, 21.0, 17.0)
// Tests against scipy.stats.mstats.mquantiles method
assertEquals(2.0, Quantile(Float64Field, 0.0, alpha = 0.0, beta = 0.0, comparator = naturalOrder()).evaluateBlocking(v))
assertEquals(2.0, Quantile(Float64Field, 0.2, alpha = 1.0, beta = 1.0, comparator = naturalOrder()).evaluateBlocking(v))
assertEquals(3.4, Quantile(Float64Field, 0.4, alpha = 0.0, beta = 0.0, comparator = naturalOrder()).evaluateBlocking(v),1e-6)
assertEquals(6.0, Quantile(Float64Field, 0.6, alpha = 0.0, beta = 0.0, comparator = naturalOrder()).evaluateBlocking(v),1e-6)
assertEquals(15.4, Quantile(Float64Field, 0.8, alpha = 0.0, beta = 0.0, comparator = naturalOrder()).evaluateBlocking(v),1e-6)
assertEquals(21.0, Quantile(Float64Field, 1.0, alpha = 0.0, beta = 0.0, comparator = naturalOrder()).evaluateBlocking(v),1e-6)
}
@Test
fun testRounding() {
val data = Float64Buffer(10) { (it + 1).toDouble() } // 1.0 to 10.0
for (i in 0..9) {
val p = i / 9.0
val expected = (i + 1).toDouble()
assertEquals(expected, Quantile.evaluate(p, data), 1e-6) //todo try to avoid rounding
}
}
// @Test
// fun testNoOverflow() { //todo try to fix
// val data1 = Float64Buffer(-9000.0, 100.0)
// assertEquals(100.0, Quantile.evaluate(1.0, data1))
//
// val data2 = Float64Buffer(-1e20, 100.0)
// assertEquals(100.0, Quantile.evaluate(1.0, data2))
// }
@Test
fun testIncreasingQuantiles() {
val data = Float64Buffer(1.0, 1.0, 1.0 + Double.MIN_VALUE, 1.0 + Double.MIN_VALUE)
val quantiles = (0..99).map { i ->
val p = i / 99.0
Quantile.evaluate(p, data)
}
assertTrue(quantiles.zipWithNext().all { (a, b) -> a <= b })
}
@Test
fun testUnsortedData() {
val data = Float64Buffer(4.0, 9.0, 1.0, 5.0, 7.0, 8.0, 2.0, 3.0, 5.0, 17.0, 11.0)
assertEquals(2.0, Quantile.evaluate(0.1, data))
assertEquals(3.0, Quantile.evaluate(0.2, data))
assertEquals(5.0, Quantile.evaluate(0.4, data))
assertEquals(11.0, Quantile.evaluate(0.9, data))
}
@Test
fun testMedianOdd() {
val res = Quantile.evaluate(0.5, Float64Buffer(1.0, 2.0, 3.0))
assertEquals(2.0, res)
}
@Test
fun testMedianEven() {
val res = Quantile.evaluate(0.5, Float64Buffer(1.0, 2.0, 3.0, 4.0))
assertEquals(2.5, res)
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.asBuffer
import kotlin.math.pow
import kotlin.math.sqrt
import kotlin.test.Test
import kotlin.test.assertEquals
internal class StandardDeviationBlockingTest{
private val doubleField = DoubleField
private val tolerance = 1e-10
@Test
fun testEmptyData() {
val stdDev = StandardDeviation(doubleField)
val data = doubleArrayOf().asBuffer()
assertEquals(0.0, stdDev.evaluateBlocking(data))
}
@Test
fun testSingleElement() {
val stdDev = StandardDeviation(doubleField)
val data = doubleArrayOf(5.0).asBuffer()
assertEquals(0.0, stdDev.evaluateBlocking(data))
}
@Test
fun testTwoElementsUnbiased() {
val stdDev = StandardDeviation(doubleField, isBiasCorrected = true)
val data = doubleArrayOf(1.0, 3.0).asBuffer()
// (1-2)² + (3-2)² = 2 / (2-1) = 2
assertEquals(sqrt(2.0), stdDev.evaluateBlocking(data))
}
@Test
fun testTwoElementsBiased() {
val stdDev = StandardDeviation(doubleField, isBiasCorrected = false)
val data = doubleArrayOf(1.0, 3.0).asBuffer()
// (1-2)² + (3-2)² = 2 / 2 = 1
assertEquals(1.0, stdDev.evaluateBlocking(data))
}
@Test
fun testMultipleElementsUnbiased() {
val stdDev = StandardDeviation(doubleField)
val data = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0).asBuffer()
// Mean = 3.0
// Variance = (4+1+0+1+4)/4 = 2.5
assertEquals(sqrt(2.5), stdDev.evaluateBlocking(data))
}
@Test
fun testMultipleElementsBiased() {
val stdDev = StandardDeviation(doubleField, isBiasCorrected = false)
val data = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0).asBuffer()
// Mean = 3.0
// Variance = (4+1+0+1+4)/5 = 2.0
assertEquals(sqrt(2.0), stdDev.evaluateBlocking(data))
}
@Test
fun testLargeNumbersUnbiased() {
val stdDev = StandardDeviation(doubleField)
val data = doubleArrayOf(1e9, 1e9 + 2, 1e9 + 4).asBuffer()
// Mean = 1e9 + 2
// Variance = (4 + 0 + 4)/2 = 4
assertEquals(2.0, stdDev.evaluateBlocking(data))
}
@Test
fun testLargeNumbersBiased() {
val stdDev = StandardDeviation(doubleField, isBiasCorrected = false)
val data = doubleArrayOf(1e9, 1e9 + 2, 1e9 + 4).asBuffer()
// Mean = 1e9 + 2
// Variance = (4 + 0 + 4)/3 ≈ 2.666...
assertEquals(sqrt(8.0 / 3.0), stdDev.evaluateBlocking(data), tolerance)
}
@Test
fun testConstantValues() {
val stdDev = StandardDeviation(doubleField)
val data = doubleArrayOf(5.0, 5.0, 5.0, 5.0).asBuffer()
assertEquals(0.0, stdDev.evaluateBlocking(data))
}
@Test
fun testPrecisionWithSmallVariations() {
val stdDev = StandardDeviation(doubleField)
val data = doubleArrayOf(
1.0000000001,
1.0000000002,
1.0000000003,
1.0000000004,
1.0000000005
).asBuffer()
// Expected standardDeviation calculation
val mean = 1.0000000003
val expected = (
(1.0000000001 - mean).pow(2) +
(1.0000000002 - mean).pow(2) +
(1.0000000003 - mean).pow(2) +
(1.0000000004 - mean).pow(2) +
(1.0000000005 - mean).pow(2)
) / 4
assertEquals(sqrt(expected), stdDev.evaluateBlocking(data), 1e-20)
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.asBuffer
import kotlin.math.pow
import kotlin.test.Test
import kotlin.test.assertEquals
internal class VarianceStatisticBlockingTest{
private val doubleField = DoubleField
private val tolerance = 1e-10
@Test
fun testEmptyData() {
val variance = Variance(doubleField)
val data = doubleArrayOf().asBuffer()
assertEquals(0.0, variance.evaluateBlocking(data))
}
@Test
fun testSingleElement() {
val variance = Variance(doubleField)
val data = doubleArrayOf(5.0).asBuffer()
assertEquals(0.0, variance.evaluateBlocking(data))
}
@Test
fun testTwoElementsUnbiased() {
val variance = Variance(doubleField, isBiasCorrected = true)
val data = doubleArrayOf(1.0, 3.0).asBuffer()
// (1-2)² + (3-2)² = 2 / (2-1) = 2
assertEquals(2.0, variance.evaluateBlocking(data))
}
@Test
fun testTwoElementsBiased() {
val variance = Variance(doubleField, isBiasCorrected = false)
val data = doubleArrayOf(1.0, 3.0).asBuffer()
// (1-2)² + (3-2)² = 2 / 2 = 1
assertEquals(1.0, variance.evaluateBlocking(data))
}
@Test
fun testMultipleElementsUnbiased() {
val variance = Variance(doubleField)
val data = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0).asBuffer()
// Mean = 3.0
// Variance = (4+1+0+1+4)/4 = 2.5
assertEquals(2.5, variance.evaluateBlocking(data))
}
@Test
fun testMultipleElementsBiased() {
val variance = Variance(doubleField, isBiasCorrected = false)
val data = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0).asBuffer()
// Mean = 3.0
// Variance = (4+1+0+1+4)/5 = 2.0
assertEquals(2.0, variance.evaluateBlocking(data))
}
@Test
fun testLargeNumbersUnbiased() {
val variance = Variance(doubleField)
val data = doubleArrayOf(1e9, 1e9 + 2, 1e9 + 4).asBuffer()
// Mean = 1e9 + 2
// Variance = (4 + 0 + 4)/2 = 4
assertEquals(4.0, variance.evaluateBlocking(data))
}
@Test
fun testLargeNumbersBiased() {
val variance = Variance(doubleField, isBiasCorrected = false)
val data = doubleArrayOf(1e9, 1e9 + 2, 1e9 + 4).asBuffer()
// Mean = 1e9 + 2
// Variance = (4 + 0 + 4)/3 ≈ 2.666...
assertEquals(8.0 / 3.0, variance.evaluateBlocking(data), tolerance)
}
@Test
fun testConstantValues() {
val variance = Variance(doubleField)
val data = doubleArrayOf(5.0, 5.0, 5.0, 5.0).asBuffer()
assertEquals(0.0, variance.evaluateBlocking(data))
}
@Test
fun testPrecisionWithSmallVariations() {
val variance = Variance(doubleField)
val data = doubleArrayOf(
1.0000000001,
1.0000000002,
1.0000000003,
1.0000000004,
1.0000000005
).asBuffer()
// Expected variance calculation
val mean = 1.0000000003
val expected = (
(1.0000000001 - mean).pow(2) +
(1.0000000002 - mean).pow(2) +
(1.0000000003 - mean).pow(2) +
(1.0000000004 - mean).pow(2) +
(1.0000000005 - mean).pow(2)
) / 4
assertEquals(expected, variance.evaluateBlocking(data), 1e-20)
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.asBuffer
import kotlin.math.pow
import kotlin.test.Test
import kotlin.test.assertEquals
internal class VarianceWithMeanTest {
private val doubleField = DoubleField
private val tolerance = 1e-10
@Test
fun testEmptyData() {
val variance = Variance(doubleField)
val data = doubleArrayOf().asBuffer()
assertEquals(0.0, variance.evaluate(data, 0.0))
}
@Test
fun testSingleElement() {
val variance = Variance(doubleField)
val data = doubleArrayOf(5.0).asBuffer()
assertEquals(0.0, variance.evaluate(data, 5.0))
}
@Test
fun testTwoElementsUnbiased() {
val variance = Variance(doubleField, isBiasCorrected = true)
val data = doubleArrayOf(1.0, 3.0).asBuffer()
val mean = 2.0
// (1-2)² + (3-2)² = 2 / (2-1) = 2
assertEquals(2.0, variance.evaluate(data, mean))
}
@Test
fun testTwoElementsBiased() {
val variance = Variance(doubleField, isBiasCorrected = false)
val data = doubleArrayOf(1.0, 3.0).asBuffer()
val mean = 2.0
// (1-2)² + (3-2)² = 2 / 2 = 1
assertEquals(1.0, variance.evaluate(data, mean))
}
@Test
fun testMultipleElementsUnbiased() {
val variance = Variance(doubleField)
val data = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0).asBuffer()
val mean = 3.0
// Variance = (4+1+0+1+4)/4 = 2.5
assertEquals(2.5, variance.evaluate(data, mean))
}
@Test
fun testMultipleElementsBiased() {
val variance = Variance(doubleField, isBiasCorrected = false)
val data = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0).asBuffer()
val mean = 3.0
// Variance = (4+1+0+1+4)/5 = 2.0
assertEquals(2.0, variance.evaluate(data, mean))
}
@Test
fun testLargeNumbersUnbiased() {
val variance = Variance(doubleField)
val data = doubleArrayOf(1e9, 1e9 + 2, 1e9 + 4).asBuffer()
val mean = 1e9 + 2
// Variance = (4 + 0 + 4)/2 = 4
assertEquals(4.0, variance.evaluate(data, mean))
}
@Test
fun testLargeNumbersBiased() {
val variance = Variance(doubleField, isBiasCorrected = false)
val data = doubleArrayOf(1e9, 1e9 + 2, 1e9 + 4).asBuffer()
val mean = 1e9 + 2
// Variance = (4 + 0 + 4)/3 ≈ 2.666...
assertEquals(8.0 / 3.0, variance.evaluate(data, mean), tolerance)
}
@Test
fun testConstantValues() {
val variance = Variance(doubleField)
val data = doubleArrayOf(5.0, 5.0, 5.0, 5.0).asBuffer()
assertEquals(0.0, variance.evaluate(data, 5.0))
}
@Test
fun testPrecisionWithSmallVariations() {
val variance = Variance(doubleField)
val data = doubleArrayOf(
1.0000000001,
1.0000000002,
1.0000000003,
1.0000000004,
1.0000000005
).asBuffer()
val mean = 1.0000000003
val expected = (
(1.0000000001 - mean).pow(2) +
(1.0000000002 - mean).pow(2) +
(1.0000000003 - mean).pow(2) +
(1.0000000004 - mean).pow(2) +
(1.0000000005 - mean).pow(2)
) / 4
assertEquals(expected, variance.evaluate(data, mean), 1e-20)
}
@Test
fun testNumericalStability() {
val variance = Variance(doubleField)
// This test verifies the corrected two-pass algorithm handles numerical stability better
val data = doubleArrayOf(
1.0 + 1e10,
1.0 + 1e10 + 1.0,
1.0 + 1e10 + 2.0,
1.0 + 1e10 + 3.0,
1.0 + 1e10 + 4.0
).asBuffer()
val mean = 1.0 + 1e10 + 2.0
// Expected variance should be same as (0,1,2,3,4) variance
assertEquals(2.5, variance.evaluate(data, mean))
}
}

View File

@@ -14,17 +14,31 @@ import space.kscience.kmath.samplers.GaussianSampler
internal class CommonsDistributionsTest {
@Test
fun testNormalDistributionSuspend() = runBlocking {
val distribution = GaussianSampler(7.0, 2.0)
val mu = 7.0; val sigma = 2.0
val distribution = GaussianSampler(mu, sigma)
val generator = RandomGenerator.default(1)
val sample = distribution.sample(generator).nextBuffer(1000)
Assertions.assertEquals(7.0, Mean.evaluate(sample), 0.2)
Assertions.assertEquals(mu, Mean.evaluate(sample), 0.2)
Assertions.assertEquals(sigma, StandardDeviation.evaluate(sample), 0.2)
//the first quartile (Q1)
// For a normal distribution with mean \(\mu \) and standard deviation \(\sigma \), the Q1 is approximately \(\mu -0.675\sigma \)
Assertions.assertEquals(mu-0.675*sigma, Quantile.evaluate(p=0.25, sample), 1e-1)
//the third quartile (Q3)
Assertions.assertEquals(mu+0.675*sigma, Quantile.evaluate(p=0.75, sample), 1e-1)
}
@Test
fun testNormalDistributionBlocking() {
val distribution = GaussianSampler(7.0, 2.0)
val mu = 7.0; val sigma = 2.0
val distribution = GaussianSampler(mu, sigma)
val generator = RandomGenerator.default(1)
val sample = distribution.sample(generator).nextBufferBlocking(1000)
Assertions.assertEquals(7.0, Mean.evaluate(sample), 0.2)
Assertions.assertEquals(mu, Mean.evaluate(sample), 0.2)
Assertions.assertEquals(sigma, StandardDeviation.evaluate(sample), 0.2)
//the first quartile (Q1)
// For a normal distribution with mean \(\mu \) and standard deviation \(\sigma \), the Q1 is approximately \(\mu -0.675\sigma \)
Assertions.assertEquals(mu-0.675*sigma, Quantile.evaluate(p=0.25, sample), 1e-1)
//the third quartile (Q3)
Assertions.assertEquals(mu+0.675*sigma, Quantile.evaluate(p=0.75, sample), 1e-1)
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.random.chain
import space.kscience.kmath.streaming.chunked
import space.kscience.kmath.structures.Float64Buffer
import kotlin.test.Test
import kotlin.test.assertEquals
internal class GeometricMeanStatisticTest {
//create a random number generator.
val generator = RandomGenerator.default(122)
private fun setupData(): Flow<Float64Buffer> {
//Create a stateless chain from generator.
val data = generator.chain { if (nextBoolean()) 1.0 else 4.0 }
//Convert a chain to Flow and break it into chunks.
return data.chunked(1000)
}
@Test
fun singleBlockingMean() = runTest {
val chunked = setupData()
val first = chunked.first()
val res = Float64Field.geometricMean(first)
assertEquals(2.0, res, 1e-1)
}
@Test
fun singleSuspendMean() = runTest {
val chunked = setupData()
val first = runBlocking { chunked.first() }
val res = Float64Field.geometricMean(first)
assertEquals(2.0, res, 1e-1)
}
@Test
fun parallelMean() = runTest {
val chunked = setupData()
val average = Float64Field.geometricMean
.flow(chunked) //create a flow from evaluated results
.take(100) // Take 100 data chunks from the source and accumulate them
.last() //get 1e5 data samples average
assertEquals(2.0, average, 1e-2)
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.random.chain
import space.kscience.kmath.streaming.chunked
import kotlin.test.Test
import kotlin.test.assertEquals
internal class MaxStatisticTest {
//create a random number generator.
val generator = RandomGenerator.default(1)
//Create a stateless chain from generator.
val data = generator.chain { nextDouble() }
//Convert a chain to Flow and break it into chunks.
val chunked = data.chunked(1000)
@Test
fun singleBlockingMax() = runTest {
val first = chunked.first()
val res = Float64Field.max(first)
assertEquals(1.0, res, 1e-1)
}
@Test
fun singleSuspendMax() = runTest {
val first = runBlocking { chunked.first() }
val res = Float64Field.max(first)
assertEquals(1.0, res, 1e-1)
}
@Test
fun parallelMax() = runTest {
val maxValue = Float64Field.max
.flow(chunked) //create a flow from evaluated results
.take(100) // Take 100 data chunks from the source and accumulate them
.last() //get the maximum from 1e5 data samples
assertEquals(1.0, maxValue, 1e-2)
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2018-2025 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.random.chain
import space.kscience.kmath.streaming.chunked
import space.kscience.kmath.structures.Float64Buffer
import kotlin.test.Test
import kotlin.test.assertEquals
internal class MeanStatisticTest {
//create a random number generator.
val generator = RandomGenerator.default(1)
private fun setupData(): Flow<Float64Buffer> {
//Create a stateless chain from generator.
val data = generator.chain { nextDouble() }
//Convert a chain to Flow and break it into chunks.
return data.chunked(1000)
}
@Test
fun singleBlockingMean() = runTest {
val chunked = setupData()
val first = chunked.first()
val res = Float64Field.mean(first)
assertEquals(0.5, res, 1e-1)
}
@Test
fun singleSuspendMean() = runTest {
val chunked = setupData()
val first = runBlocking { chunked.first() }
val res = Float64Field.mean(first)
assertEquals(0.5, res, 1e-1)
}
@Test
fun parallelMean() = runTest {
val chunked = setupData()
val average = Float64Field.mean
.flow(chunked) //create a flow from evaluated results
.take(100) // Take 100 data chunks from the source and accumulate them
.last() //get 1e5 data samples average
assertEquals(0.5, average, 1e-2)
}
@Test
fun quantileMatchMean() = runTest {
val chunked = setupData()
val first = runBlocking { chunked.first() }
val mean = Float64Field.mean(first)
val quantile = Quantile.evaluate(p=0.5,first)
assertEquals(mean, quantile, 1e-2)
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2024 KMath contributors.
* Copyright 2018-2025 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.
*/
@@ -17,7 +17,7 @@ import space.kscience.kmath.streaming.chunked
import kotlin.test.Test
import kotlin.test.assertEquals
internal class StatisticTest {
internal class MinStatisticTest {
//create a random number generator.
val generator = RandomGenerator.default(1)
@@ -28,27 +28,27 @@ internal class StatisticTest {
val chunked = data.chunked(1000)
@Test
fun singleBlockingMean() = runTest {
fun singleBlockingMin() = runTest {
val first = chunked.first()
val res = Float64Field.mean(first)
assertEquals(0.5, res, 1e-1)
val res = Float64Field.min(first)
assertEquals(0.0, res, 1e-1)
}
@Test
fun singleSuspendMean() = runTest {
fun singleSuspendMin() = runTest {
val first = runBlocking { chunked.first() }
val res = Float64Field.mean(first)
assertEquals(0.5, res, 1e-1)
val res = Float64Field.min(first)
assertEquals(0.0, res, 1e-1)
}
@Test
fun parallelMean() = runTest {
val average = Float64Field.mean
fun parallelMin() = runTest {
val average = Float64Field.min
.flow(chunked) //create a flow from evaluated results
.take(100) // Take 100 data chunks from the source and accumulate them
.last() //get 1e5 data samples average
assertEquals(0.5, average, 1e-2)
assertEquals(0.0, average, 1e-2)
}
}

View File

@@ -6,7 +6,7 @@ Symja integration module
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-symja:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-symja:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-symja:0.4.2")
implementation("space.kscience:kmath-symja:0.5.0")
}
```

View File

@@ -6,7 +6,7 @@ Google tensorflow connector
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-tensorflow:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-tensorflow:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -16,6 +16,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-tensorflow:0.4.2")
implementation("space.kscience:kmath-tensorflow:0.5.0")
}
```

View File

@@ -1,13 +1,20 @@
plugins {
id("space.kscience.gradle.jvm")
id("space.kscience.gradle.mpp")
}
description = "Google tensorflow connector"
dependencies {
api(projects.kmathTensors)
api(libs.tensorflow.core.api)
testImplementation(libs.tensorflow.core.platform)
kscience {
jvm()
jvmMain {
api(projects.kmathTensors)
api(libs.tensorflow.core.api)
}
jvmTest {
implementation(libs.tensorflow.core.platform)
}
}
readme {

View File

@@ -9,7 +9,7 @@ Common linear algebra operations on tensors.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-tensors:0.4.2`.
The Maven coordinates of this project are `space.kscience:kmath-tensors:0.5.0`.
**Gradle Kotlin DSL:**
```kotlin
@@ -19,6 +19,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-tensors:0.4.2")
implementation("space.kscience:kmath-tensors:0.5.0")
}
```

View File

@@ -14,7 +14,7 @@ kscience {
}
}
native()
wasm()
wasmJs()
dependencies {
api(projects.kmathCore)

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