Compare commits
47 Commits
feature/fo
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 72b0d9edc9 | |||
| f8b23261e1 | |||
| d64a117a9f | |||
| 845aea0cd7 | |||
| 7267880126 | |||
| f3e9e338bf | |||
| 73a6ad28c3 | |||
|
|
7da0ace8f8 | ||
| 9c22b51fa5 | |||
| 26dfdf39df | |||
| 58a3f749a7 | |||
| 5abc90a4ba | |||
| 6e97e513fb | |||
| 7d08aed5eb | |||
| b9c1947b70 | |||
| b1446741db | |||
| 98dd43d404 | |||
| 577401bb08 | |||
| 896822ec77 | |||
| ff5fbfa8d8 | |||
| 6fe77607d8 | |||
| b6892e2e5c | |||
| 101eb612b1 | |||
|
|
785d3bd104 | ||
| f362c3e31f | |||
| 1fd5e3fba1 | |||
| 0cd9a329ce | |||
| d2a3fd4fa2 | |||
| 4afd350a4e | |||
| 202585d956 | |||
| f6f9984c8e | |||
| 3649d013cd | |||
| b10caebe2a | |||
| 752a849cd3 | |||
| 2535d4536e | |||
| 4464b72e45 | |||
| ce453129f0 | |||
| d43ce15b99 | |||
| 288ec467e6 | |||
| 59fbe5165f | |||
| 2d37a5255f | |||
| 61209e81b5 | |||
|
|
5bbff7e8ad | ||
| 7ac9794c0c | |||
| e41bbe711c | |||
| 57e4819430 | |||
| e213db67da |
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@@ -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
|
||||
|
||||
15
.github/workflows/pages.yml
vendored
15
.github/workflows/pages.yml
vendored
@@ -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
|
||||
|
||||
18
CHANGELOG.md
18
CHANGELOG.md
@@ -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
|
||||
|
||||
71
README.md
71
README.md
@@ -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.
|
||||
@@ -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 ± 3.4E+05 ops/s|
|
||||
|`benchmarkBufferRead`|4.0E+06 ± 3.2E+05 ops/s|
|
||||
|`nativeBufferRead`|3.9E+06 ± 2.0E+05 ops/s|
|
||||
|`benchmarkArrayRead`|3.9E+06 ± 1.1E+06 ops/s|
|
||||
|`benchmarkBufferRead`|4.0E+06 ± 2.2E+05 ops/s|
|
||||
|`nativeBufferRead`|4.0E+06 ± 1.7E+05 ops/s|
|
||||
### [BigIntBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`jvmAdd`|3.1E+07 ± 1.8E+07 ops/s|
|
||||
|`jvmAddLarge`|4.5E+04 ± 5.5E+03 ops/s|
|
||||
|`jvmMultiply`|3.6E+07 ± 1.7E+07 ops/s|
|
||||
|`jvmMultiplyLarge`|1.9E+02 ± 95 ops/s|
|
||||
|`jvmParsing10`|4.0E+06 ± 8.8E+05 ops/s|
|
||||
|`jvmParsing16`|3.6E+06 ± 6.5E+05 ops/s|
|
||||
|`jvmPower`|25 ± 1.4 ops/s|
|
||||
|`jvmSmallAdd`|5.7E+07 ± 9.7E+05 ops/s|
|
||||
|`kmAdd`|2.6E+07 ± 8.8E+05 ops/s|
|
||||
|`kmAddLarge`|2.3E+04 ± 1.2E+03 ops/s|
|
||||
|`kmMultiply`|3.8E+07 ± 5.5E+06 ops/s|
|
||||
|`kmMultiplyLarge`|36 ± 3.8 ops/s|
|
||||
|`kmParsing10`|2.5E+06 ± 1.4E+05 ops/s|
|
||||
|`kmParsing16`|3.7E+06 ± 4.7E+05 ops/s|
|
||||
|`kmPower`|6.6 ± 1.0 ops/s|
|
||||
|`kmSmallAdd`|2.0E+07 ± 1.7E+06 ops/s|
|
||||
|`jvmAdd`|2.9E+07 ± 2.8E+06 ops/s|
|
||||
|`jvmAddLarge`|3.8E+04 ± 6.4E+03 ops/s|
|
||||
|`jvmMultiply`|5.3E+07 ± 6.1E+06 ops/s|
|
||||
|`jvmMultiplyLarge`|2.2E+02 ± 1.9 ops/s|
|
||||
|`jvmParsing10`|3.9E+06 ± 4.7E+05 ops/s|
|
||||
|`jvmParsing16`|3.1E+06 ± 4.6E+05 ops/s|
|
||||
|`jvmPower`|24 ± 1.7 ops/s|
|
||||
|`jvmSmallAdd`|4.7E+07 ± 4.6E+06 ops/s|
|
||||
|`kmAdd`|2.3E+07 ± 5.1E+06 ops/s|
|
||||
|`kmAddLarge`|2.6E+04 ± 3.0E+02 ops/s|
|
||||
|`kmMultiply`|3.7E+07 ± 2.9E+06 ops/s|
|
||||
|`kmMultiplyLarge`|34 ± 2.8 ops/s|
|
||||
|`kmParsing10`|2.5E+06 ± 1.5E+05 ops/s|
|
||||
|`kmParsing16`|4.0E+06 ± 2.4E+05 ops/s|
|
||||
|`kmPower`|6.5 ± 0.69 ops/s|
|
||||
|`kmSmallAdd`|1.6E+07 ± 8.0E+05 ops/s|
|
||||
### [BufferBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`bufferViewReadWrite`|6.0E+06 ± 7.4E+05 ops/s|
|
||||
|`bufferViewReadWriteSpecialized`|7.6E+05 ± 1.1E+04 ops/s|
|
||||
|`complexBufferReadWrite`|2.4E+06 ± 2.7E+05 ops/s|
|
||||
|`doubleArrayReadWrite`|7.3E+06 ± 4.3E+05 ops/s|
|
||||
|`doubleBufferReadWrite`|7.3E+06 ± 3.4E+05 ops/s|
|
||||
|`bufferViewReadWrite`|5.4E+06 ± 3.8E+05 ops/s|
|
||||
|`bufferViewReadWriteSpecialized`|5.0E+06 ± 1.2E+06 ops/s|
|
||||
|`complexBufferReadWrite`|2.2E+06 ± 5.7E+04 ops/s|
|
||||
|`doubleArrayReadWrite`|6.9E+06 ± 1.2E+06 ops/s|
|
||||
|`doubleBufferReadWrite`|6.6E+06 ± 1.1E+06 ops/s|
|
||||
### [DotBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`bufferedDot`|1.3 ± 0.032 ops/s|
|
||||
|`cmDot`|0.42 ± 0.20 ops/s|
|
||||
|`cmDotWithConversion`|0.83 ± 0.12 ops/s|
|
||||
|`ejmlDot`|2.6 ± 0.049 ops/s|
|
||||
|`ejmlDotWithConversion`|2.5 ± 0.075 ops/s|
|
||||
|`multikDot`|25 ± 0.52 ops/s|
|
||||
|`ojalgoDot`|11 ± 1.3 ops/s|
|
||||
|`parallelDot`|11 ± 0.17 ops/s|
|
||||
|`tensorDot`|1.1 ± 0.028 ops/s|
|
||||
|`tfDot`|4.7 ± 0.14 ops/s|
|
||||
|`bufferedDot`|1.2 ± 0.20 ops/s|
|
||||
|`cmDot`|0.36 ± 0.14 ops/s|
|
||||
|`cmDotWithConversion`|0.80 ± 0.092 ops/s|
|
||||
|`ejmlDot`|2.9 ± 0.61 ops/s|
|
||||
|`ejmlDotWithConversion`|2.7 ± 0.15 ops/s|
|
||||
|`multikDot`|23 ± 2.4 ops/s|
|
||||
|`ojalgoDot`|11 ± 0.79 ops/s|
|
||||
|`parallelDot`|9.4 ± 1.3 ops/s|
|
||||
|`tensorDot`|1.0 ± 0.15 ops/s|
|
||||
|`tfDot`|3.9 ± 0.90 ops/s|
|
||||
### [ExpressionsInterpretersBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`asmGenericExpression`|12 ± 0.099 ops/s|
|
||||
|`asmPrimitiveExpression`|26 ± 0.57 ops/s|
|
||||
|`asmPrimitiveExpressionArray`|74 ± 1.7 ops/s|
|
||||
|`functionalExpression`|5.3 ± 0.015 ops/s|
|
||||
|`justCalculate`|74 ± 0.85 ops/s|
|
||||
|`mstExpression`|4.2 ± 0.10 ops/s|
|
||||
|`rawExpression`|25 ± 0.74 ops/s|
|
||||
|`asmGenericExpression`|15 ± 1.8 ops/s|
|
||||
|`asmPrimitiveExpression`|27 ± 0.98 ops/s|
|
||||
|`asmPrimitiveExpressionArray`|78 ± 14 ops/s|
|
||||
|`functionalExpression`|4.4 ± 0.25 ops/s|
|
||||
|`justCalculate`|79 ± 5.4 ops/s|
|
||||
|`mstExpression`|4.2 ± 0.93 ops/s|
|
||||
|`rawExpression`|25 ± 5.0 ops/s|
|
||||
### [IntegrationBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/IntegrationBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`complexIntegration`|2.6E+03 ± 46 ops/s|
|
||||
|`doubleIntegration`|2.8E+03 ± 1.1E+02 ops/s|
|
||||
|`complexIntegration`|2.2E+03 ± 3.0E+02 ops/s|
|
||||
|`doubleIntegration`|2.3E+03 ± 6.4E+02 ops/s|
|
||||
### [MatrixInverseBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`cmLUPInversion`|2.1E+03 ± 35 ops/s|
|
||||
|`ejmlInverse`|1.2E+03 ± 27 ops/s|
|
||||
|`kmathLupInversion`|4.0E+02 ± 52 ops/s|
|
||||
|`kmathParallelLupInversion`|4.0E+02 ± 9.6 ops/s|
|
||||
|`ojalgoInverse`|2.1E+03 ± 3.3E+02 ops/s|
|
||||
|`cmLUPInversion`|2.0E+03 ± 1.1E+02 ops/s|
|
||||
|`ejmlInverse`|1.2E+03 ± 29 ops/s|
|
||||
|`kmathLupInversion`|3.9E+02 ± 92 ops/s|
|
||||
|`kmathParallelLupInversion`|55 ± 5.0 ops/s|
|
||||
|`ojalgoInverse`|1.7E+03 ± 35 ops/s|
|
||||
### [MinStatisticBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MinStatisticBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`kotlinArrayMin`|1.6E+03 ± 3.0E+02 ops/s|
|
||||
|`minBlocking`|1.2E+03 ± 1.2E+02 ops/s|
|
||||
### [NDFieldBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`boxingFieldAdd`|1.7 ± 0.11 ops/s|
|
||||
|`multikAdd`|7.0 ± 0.41 ops/s|
|
||||
|`multikInPlaceAdd`|34 ± 1.7 ops/s|
|
||||
|`specializedFieldAdd`|7.2 ± 1.2 ops/s|
|
||||
|`tensorAdd`|7.2 ± 1.6 ops/s|
|
||||
|`tensorInPlaceAdd`|7.4 ± 4.9 ops/s|
|
||||
|`viktorAdd`|5.8 ± 0.65 ops/s|
|
||||
|`boxingFieldAdd`|1.9 ± 0.089 ops/s|
|
||||
|`multikAdd`|6.8 ± 1.0 ops/s|
|
||||
|`multikInPlaceAdd`|32 ± 4.7 ops/s|
|
||||
|`specializedFieldAdd`|6.7 ± 0.98 ops/s|
|
||||
|`tensorAdd`|7.9 ± 1.1 ops/s|
|
||||
|`tensorInPlaceAdd`|11 ± 3.4 ops/s|
|
||||
|`viktorAdd`|6.4 ± 0.41 ops/s|
|
||||
### [ViktorBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`doubleFieldAddition`|7.1 ± 2.0 ops/s|
|
||||
|`rawViktor`|6.2 ± 1.0 ops/s|
|
||||
|`viktorFieldAddition`|6.4 ± 0.29 ops/s|
|
||||
|`doubleFieldAddition`|7.3 ± 1.1 ops/s|
|
||||
|`rawViktor`|6.0 ± 0.88 ops/s|
|
||||
|`viktorFieldAddition`|6.7 ± 0.47 ops/s|
|
||||
### [ViktorLogBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt)
|
||||
|
||||
| Benchmark | Score |
|
||||
|:---------:|:-----:|
|
||||
|`rawViktorLog`|1.3 ± 0.016 ops/s|
|
||||
|`realFieldLog`|1.3 ± 0.019 ops/s|
|
||||
|`viktorFieldLog`|1.3 ± 0.020 ops/s|
|
||||
|`rawViktorLog`|1.3 ± 0.40 ops/s|
|
||||
|`realFieldLog`|1.2 ± 0.34 ops/s|
|
||||
|`viktorFieldLog`|1.3 ± 0.0073 ops/s|
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Module KMath-Benchmarks
|
||||
|
||||
# BenchmarksResult
|
||||
|
||||
${benchmarkMain}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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
34
docs/statistics.md
Normal 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`)* |
|
||||
53
docs/templates/README-TEMPLATE.md
vendored
53
docs/templates/README-TEMPLATE.md
vendored
@@ -2,14 +2,10 @@
|
||||
[](https://zenodo.org/badge/latestdoi/129486382)
|
||||

|
||||
[](https://search.maven.org/search?q=g:%22space.kscience%22)
|
||||
[](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.
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 },
|
||||
|
||||
@@ -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
|
||||
@@ -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"}
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ internal abstract class AsmBuilder {
|
||||
|
||||
protected val classLoader = ByteArrayClassLoader(javaClass.classLoader)
|
||||
|
||||
protected companion object {
|
||||
companion object {
|
||||
/**
|
||||
* ASM type for [Expression].
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
|
||||
dependencies {
|
||||
api(projects.kmathCore)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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] · [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 · 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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
|
||||
dependencies {
|
||||
api(projects.kmathCore)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
|
||||
dependencies {
|
||||
api(projects.kmathCore)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
|
||||
dependencies {
|
||||
api(projects.kmathCore)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
|
||||
dependencies {
|
||||
api(projects.kmathCore)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,9 +6,9 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
|
||||
useContextReceivers()
|
||||
useContextParameters()
|
||||
useSerialization()
|
||||
dependencies {
|
||||
api(projects.kmath.kmathComplex)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) }
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
useCoroutines()
|
||||
}
|
||||
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
|
||||
dependencies {
|
||||
api(projects.kmathCore)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
}
|
||||
|
||||
kotlin.sourceSets {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
wasm()
|
||||
wasmJs()
|
||||
|
||||
useCoroutines()
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user