Feature/tensors performance #497

Closed
margarita0303 wants to merge 91 commits from feature/tensors-performance into feature/tensors-performance
96 changed files with 3111 additions and 3663 deletions

View File

@ -13,25 +13,13 @@ jobs:
runs-on: ${{matrix.os}}
timeout-minutes: 40
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Set up JDK 11
uses: DeLaGuardo/setup-graalvm@4.0
- uses: actions/checkout@v3.0.0
- uses: actions/setup-java@v3.0.0
with:
graalvm: 21.2.0
java: java11
arch: amd64
- name: Cache gradle
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
java-version: 11
distribution: liberica
- name: Cache konan
uses: actions/cache@v2
uses: actions/cache@v3.0.1
with:
path: ~/.konan
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
@ -39,5 +27,6 @@ jobs:
${{ runner.os }}-gradle-
- name: Gradle Wrapper Validation
uses: gradle/wrapper-validation-action@v1.0.4
- name: Build
run: ./gradlew build --build-cache --no-daemon --stacktrace
- uses: gradle/gradle-build-action@v2.1.5
with:
arguments: build

View File

@ -1,28 +1,31 @@
name: Dokka publication
on:
push:
branches: [ master ]
workflow_dispatch:
release:
types: [ created ]
jobs:
build:
runs-on: ubuntu-20.04
timeout-minutes: 40
steps:
- uses: actions/checkout@v2
- uses: DeLaGuardo/setup-graalvm@4.0
- uses: actions/checkout@v3.0.0
- uses: actions/setup-java@v3.0.0
with:
graalvm: 21.2.0
java: java11
arch: amd64
- uses: actions/cache@v2
java-version: 11
distribution: liberica
- name: Cache konan
uses: actions/cache@v3.0.1
with:
path: ~/.gradle/caches
path: ~/.konan
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- run: ./gradlew dokkaHtmlMultiModule --build-cache --no-daemon --no-parallel --stacktrace
- uses: JamesIves/github-pages-deploy-action@4.1.0
- uses: gradle/gradle-build-action@v2.1.5
with:
arguments: dokkaHtmlMultiModule --no-parallel
- uses: JamesIves/github-pages-deploy-action@v4.3.0
with:
branch: gh-pages
folder: build/dokka/htmlMultiModule

View File

@ -14,42 +14,38 @@ jobs:
os: [ macOS-latest, windows-latest ]
runs-on: ${{matrix.os}}
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Set up JDK 11
uses: DeLaGuardo/setup-graalvm@4.0
- uses: actions/checkout@v3.0.0
- uses: actions/setup-java@v3.0.0
with:
graalvm: 21.2.0
java: java11
arch: amd64
- name: Cache gradle
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
java-version: 11
distribution: liberica
- name: Cache konan
uses: actions/cache@v2
uses: actions/cache@v3.0.1
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/wrapper-validation-action@v1.0.4
- name: Publish Windows Artifacts
if: matrix.os == 'windows-latest'
shell: cmd
run: >
./gradlew release --no-daemon --build-cache -Ppublishing.enabled=true
-Ppublishing.space.user=${{ secrets.SPACE_APP_ID }}
-Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }}
uses: gradle/gradle-build-action@v2.1.5
with:
arguments: |
releaseAll
-Ppublishing.enabled=true
-Ppublishing.sonatype=false
-Ppublishing.space.user=${{ secrets.SPACE_APP_ID }}
-Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }}
- name: Publish Mac Artifacts
if: matrix.os == 'macOS-latest'
run: >
./gradlew release --no-daemon --build-cache -Ppublishing.enabled=true -Ppublishing.platform=macosX64
-Ppublishing.space.user=${{ secrets.SPACE_APP_ID }}
-Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }}
uses: gradle/gradle-build-action@v2.1.5
with:
arguments: |
releaseMacosX64
releaseIosArm64
releaseIosX64
-Ppublishing.enabled=true
-Ppublishing.sonatype=false
-Ppublishing.space.user=${{ secrets.SPACE_APP_ID }}
-Ppublishing.space.token=${{ secrets.SPACE_APP_SECRET }}

1
.gitignore vendored
View File

@ -19,3 +19,4 @@ out/
!/.idea/copyright/
!/.idea/scopes/
/kotlin-js-store/yarn.lock

View File

@ -2,6 +2,19 @@
## [Unreleased]
### Added
### Changed
### Deprecated
### Removed
### Fixed
### Security
## [0.3.0]
### Added
- `ScaleOperations` interface
- `Field` extends `ScaleOperations`
- Basic integration API
@ -19,6 +32,12 @@
- Complex power
- Separate methods for UInt, Int and Number powers. NaN safety.
- Tensorflow prototype
- `ValueAndErrorField`
- MST compilation to WASM: #286
- Jafama integration: #176
- `contentEquals` with tolerance: #364
- Compilation to TeX for MST: #254
### Changed
- Exponential operations merged with hyperbolic functions
@ -48,10 +67,15 @@
- Operations -> Ops
- Default Buffer and ND algebras are now Ops and lack neutral elements (0, 1) as well as algebra-level shapes.
- Tensor algebra takes read-only structures as input and inherits AlgebraND
- `UnivariateDistribution` renamed to `Distribution1D`
- Rework of histograms.
- `UnivariateFunction` -> `Function1D`, `MultivariateFunction` -> `FunctionND`
### Deprecated
- Specialized `DoubleBufferAlgebra`
### Removed
- Nearest in Domain. To be implemented in geometry package.
- Number multiplication and division in main Algebra chain
@ -62,10 +86,12 @@
- Second generic from DifferentiableExpression
- Algebra elements are completely removed. Use algebra contexts instead.
### Fixed
- Ring inherits RingOperations, not GroupOperations
- Univariate histogram filling
### Security
## [0.2.0]
@ -88,6 +114,7 @@
- New `MatrixFeature` interfaces for matrix decompositions
- Basic Quaternion vector support in `kmath-complex`.
### Changed
- Package changed from `scientifik` to `space.kscience`
- Gradle version: 6.6 -> 6.8.2
@ -112,7 +139,6 @@
- `symbol` method in `Algebra` renamed to `bindSymbol` to avoid ambiguity
- Add `out` projection to `Buffer` generic
### Deprecated
### Removed
- `kmath-koma` module because it doesn't support Kotlin 1.4.
@ -122,13 +148,11 @@
- `Real` class
- StructureND identity and equals
### Fixed
- `symbol` method in `MstExtendedField` (https://github.com/mipt-npm/kmath/pull/140)
### Security
## [0.1.4]
### Added
- Functional Expressions API
- Mathematical Syntax Tree, its interpreter and API
@ -146,6 +170,7 @@
- Full hyperbolic functions support and default implementations within `ExtendedField`
- Norm support for `Complex`
### Changed
- `readAsMemory` now has `throws IOException` in JVM signature.
- Several functions taking functional types were made `inline`.
@ -157,6 +182,7 @@
- Gradle version: 6.3 -> 6.6
- Moved probability distributions to commons-rng and to `kmath-prob`
### Fixed
- Missing copy method in Memory implementation on JS (https://github.com/mipt-npm/kmath/pull/106)
- D3.dim value in `kmath-dimensions`

View File

@ -52,21 +52,18 @@ module definitions below. The module stability could have the following levels:
## Modules
<hr/>
* ### [benchmarks](benchmarks)
### [benchmarks](benchmarks)
>
>
> **Maturity**: EXPERIMENTAL
<hr/>
* ### [examples](examples)
### [examples](examples)
>
>
> **Maturity**: EXPERIMENTAL
<hr/>
* ### [kmath-ast](kmath-ast)
### [kmath-ast](kmath-ast)
>
>
> **Maturity**: EXPERIMENTAL
@ -77,15 +74,13 @@ module definitions below. The module stability could have the following levels:
> - [mst-js-codegen](kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
> - [rendering](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/rendering/MathRenderer.kt) : Extendable MST rendering
<hr/>
* ### [kmath-commons](kmath-commons)
### [kmath-commons](kmath-commons)
>
>
> **Maturity**: EXPERIMENTAL
<hr/>
* ### [kmath-complex](kmath-complex)
### [kmath-complex](kmath-complex)
> Complex numbers and quaternions.
>
> **Maturity**: PROTOTYPE
@ -94,9 +89,8 @@ module definitions below. The module stability could have the following levels:
> - [complex](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt) : Complex Numbers
> - [quaternion](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt) : Quaternions
<hr/>
* ### [kmath-core](kmath-core)
### [kmath-core](kmath-core)
> Core classes, algebra definitions, basic linear algebra
>
> **Maturity**: DEVELOPMENT
@ -112,21 +106,18 @@ performance calculations to code generation.
> - [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
<hr/>
* ### [kmath-coroutines](kmath-coroutines)
### [kmath-coroutines](kmath-coroutines)
>
>
> **Maturity**: EXPERIMENTAL
<hr/>
* ### [kmath-dimensions](kmath-dimensions)
### [kmath-dimensions](kmath-dimensions)
>
>
> **Maturity**: PROTOTYPE
<hr/>
* ### [kmath-ejml](kmath-ejml)
### [kmath-ejml](kmath-ejml)
>
>
> **Maturity**: PROTOTYPE
@ -136,9 +127,8 @@ performance calculations to code generation.
> - [ejml-matrix](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : Matrix implementation.
> - [ejml-linear-space](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : LinearSpace implementations.
<hr/>
* ### [kmath-for-real](kmath-for-real)
### [kmath-for-real](kmath-for-real)
> Extension module that should be used to achieve numpy-like behavior.
All operations are specialized to work with `Double` numbers without declaring algebraic contexts.
One can still use generic algebras though.
@ -150,9 +140,8 @@ One can still use generic algebras though.
> - [DoubleMatrix](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleMatrix.kt) : Numpy-like operations for 2d real structures
> - [grids](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/structures/grids.kt) : Uniform grid generators
<hr/>
* ### [kmath-functions](kmath-functions)
### [kmath-functions](kmath-functions)
>
>
> **Maturity**: EXPERIMENTAL
@ -164,21 +153,18 @@ One can still use generic algebras though.
> - [spline interpolation](kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt) : Cubic spline XY interpolator.
> - [integration](kmath-functions/#) : Univariate and multivariate quadratures
<hr/>
* ### [kmath-geometry](kmath-geometry)
### [kmath-geometry](kmath-geometry)
>
>
> **Maturity**: PROTOTYPE
<hr/>
* ### [kmath-histograms](kmath-histograms)
### [kmath-histograms](kmath-histograms)
>
>
> **Maturity**: PROTOTYPE
<hr/>
* ### [kmath-jafama](kmath-jafama)
### [kmath-jafama](kmath-jafama)
>
>
> **Maturity**: PROTOTYPE
@ -186,15 +172,13 @@ One can still use generic algebras though.
> **Features:**
> - [jafama-double](kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/) : Double ExtendedField implementations based on Jafama
<hr/>
* ### [kmath-jupyter](kmath-jupyter)
### [kmath-jupyter](kmath-jupyter)
>
>
> **Maturity**: PROTOTYPE
<hr/>
* ### [kmath-kotlingrad](kmath-kotlingrad)
### [kmath-kotlingrad](kmath-kotlingrad)
>
>
> **Maturity**: EXPERIMENTAL
@ -203,21 +187,18 @@ One can still use generic algebras though.
> - [differentiable-mst-expression](kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt) : MST based DifferentiableExpression.
> - [scalars-adapters](kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/scalarsAdapters.kt) : Conversions between Kotlin∇'s SFun and MST
<hr/>
* ### [kmath-memory](kmath-memory)
### [kmath-memory](kmath-memory)
> An API and basic implementation for arranging objects in a continuous memory block.
>
> **Maturity**: DEVELOPMENT
<hr/>
* ### [kmath-multik](kmath-multik)
### [kmath-multik](kmath-multik)
>
>
> **Maturity**: PROTOTYPE
<hr/>
* ### [kmath-nd4j](kmath-nd4j)
### [kmath-nd4j](kmath-nd4j)
>
>
> **Maturity**: EXPERIMENTAL
@ -227,33 +208,28 @@ One can still use generic algebras though.
> - [nd4jarrayrings](kmath-nd4j/#) : Rings over Nd4jArrayStructure of Int and Long
> - [nd4jarrayfields](kmath-nd4j/#) : Fields over Nd4jArrayStructure of Float and Double
<hr/>
* ### [kmath-optimization](kmath-optimization)
### [kmath-optimization](kmath-optimization)
>
>
> **Maturity**: EXPERIMENTAL
<hr/>
* ### [kmath-stat](kmath-stat)
### [kmath-stat](kmath-stat)
>
>
> **Maturity**: EXPERIMENTAL
<hr/>
* ### [kmath-symja](kmath-symja)
### [kmath-symja](kmath-symja)
>
>
> **Maturity**: PROTOTYPE
<hr/>
* ### [kmath-tensorflow](kmath-tensorflow)
### [kmath-tensorflow](kmath-tensorflow)
>
>
> **Maturity**: PROTOTYPE
<hr/>
* ### [kmath-tensors](kmath-tensors)
### [kmath-tensors](kmath-tensors)
>
>
> **Maturity**: PROTOTYPE
@ -263,13 +239,11 @@ One can still use generic algebras though.
> - [tensor algebra with broadcasting](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt) : Basic linear algebra operations implemented with broadcasting.
> - [linear algebra operations](kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt) : Advanced linear algebra operations like LU decomposition, SVD, etc.
<hr/>
* ### [kmath-viktor](kmath-viktor)
### [kmath-viktor](kmath-viktor)
>
>
> **Maturity**: DEVELOPMENT
<hr/>
## Multi-platform support
@ -308,8 +282,8 @@ repositories {
}
dependencies {
api("space.kscience:kmath-core:0.3.0-dev-17")
// api("space.kscience:kmath-core-jvm:0.3.0-dev-17") for jvm-specific version
api("space.kscience:kmath-core:$version")
// api("space.kscience:kmath-core-jvm:$version") for jvm-specific version
}
```

4
benchmarks/README.md Normal file
View File

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

View File

@ -84,6 +84,11 @@ benchmark {
iterationTimeUnit = "ms"
}
configurations.register("svd") {
commonConfiguration()
include("SVDBenchmark")
}
configurations.register("buffer") {
commonConfiguration()
include("BufferBenchmark")
@ -155,7 +160,7 @@ kotlin.sourceSets.all {
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" + "-Xlambdas=indy"

View File

@ -15,8 +15,10 @@ import space.kscience.kmath.linear.invoke
import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.multik.multikAlgebra
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.tensorflow.produceWithTF
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.tensorAlgebra
import kotlin.random.Random
@ -90,4 +92,9 @@ internal class DotBenchmark {
fun doubleDot(blackhole: Blackhole) = with(DoubleField.linearSpace) {
blackhole.consume(matrix1 dot matrix2)
}
@Benchmark
fun doubleTensorDot(blackhole: Blackhole) = DoubleTensorAlgebra.invoke {
blackhole.consume(matrix1 dot matrix2)
}
}

View File

@ -0,0 +1,141 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.benchmarks
import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import org.ejml.UtilEjml.assertTrue
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.diagonalEmbedding
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.eq
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.svdGolubKahan
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.transpose
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.svdPowerMethod
@State(Scope.Benchmark)
class SVDBenchmark {
companion object {
val tensorSmall = DoubleTensorAlgebra.randomNormal(intArrayOf(5, 5), 0)
val tensorMedium = DoubleTensorAlgebra.randomNormal(intArrayOf(10, 10), 0)
val tensorLarge = DoubleTensorAlgebra.randomNormal(intArrayOf(50, 50), 0)
val tensorVeryLarge = DoubleTensorAlgebra.randomNormal(intArrayOf(100, 100), 0)
val epsilon = 1e-9
}
@Benchmark
fun svdPowerMethodSmall(blackhole: Blackhole) {
val svd = tensorSmall.svdPowerMethod()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensorSVD.eq(tensorSmall, epsilon))
blackhole.consume(
tensorSmall.svdPowerMethod()
)
}
@Benchmark
fun svdPowerMethodMedium(blackhole: Blackhole) {
val svd = tensorMedium.svdPowerMethod()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensorSVD.eq(tensorMedium, epsilon))
blackhole.consume(
tensorMedium.svdPowerMethod()
)
}
@Benchmark
fun svdPowerMethodLarge(blackhole: Blackhole) {
val svd = tensorLarge.svdPowerMethod()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensorSVD.eq(tensorLarge, epsilon))
blackhole.consume(
tensorLarge.svdPowerMethod()
)
}
@Benchmark
fun svdPowerMethodVeryLarge(blackhole: Blackhole) {
val svd = tensorVeryLarge.svdPowerMethod()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensorSVD.eq(tensorVeryLarge, epsilon))
blackhole.consume(
tensorVeryLarge.svdPowerMethod()
)
}
@Benchmark
fun svdGolubKahanSmall(blackhole: Blackhole) {
val svd = tensorSmall.svdGolubKahan()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensorSVD.eq(tensorSmall, epsilon))
blackhole.consume(
tensorSmall.svdGolubKahan()
)
}
@Benchmark
fun svdGolubKahanMedium(blackhole: Blackhole) {
val svd = tensorMedium.svdGolubKahan()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensorSVD.eq(tensorMedium, epsilon))
blackhole.consume(
tensorMedium.svdGolubKahan()
)
}
@Benchmark
fun svdGolubKahanLarge(blackhole: Blackhole) {
val svd = tensorLarge.svdGolubKahan()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensorSVD.eq(tensorLarge, epsilon))
blackhole.consume(
tensorLarge.svdGolubKahan()
)
}
@Benchmark
fun svdGolubKahanVeryLarge(blackhole: Blackhole) {
val svd = tensorVeryLarge.svdGolubKahan()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensorSVD.eq(tensorVeryLarge, epsilon))
blackhole.consume(
tensorVeryLarge.svdGolubKahan()
)
}
}

View File

@ -1,16 +1,17 @@
plugins {
id("ru.mipt.npm.gradle.project")
id("org.jetbrains.kotlinx.kover") version "0.5.0-RC"
id("org.jetbrains.kotlinx.kover") version "0.5.0"
}
allprojects {
repositories {
maven("https://repo.kotlin.link")
maven("https://oss.sonatype.org/content/repositories/snapshots")
mavenCentral()
}
group = "space.kscience"
version = "0.3.0-dev-19"
version = "0.3.0"
}
subprojects {
@ -55,7 +56,7 @@ subprojects {
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
ksciencePublish {
github("kmath")
github("kmath", addToRelease = false)
space()
sonatype()
}

View File

@ -1,20 +1,21 @@
plugins {
`kotlin-dsl`
`version-catalog`
alias(npmlibs.plugins.kotlin.plugin.serialization)
alias(miptNpmLibs.plugins.kotlin.plugin.serialization)
}
java.targetCompatibility = JavaVersion.VERSION_11
repositories {
mavenLocal()
maven("https://repo.kotlin.link")
mavenCentral()
gradlePluginPortal()
}
val toolsVersion: String by extra
val kotlinVersion = npmlibs.versions.kotlin.asProvider().get()
val benchmarksVersion = "0.4.2"
val kotlinVersion = miptNpmLibs.versions.kotlin.asProvider().get()
val benchmarksVersion = miptNpmLibs.versions.kotlinx.benchmark.get()
dependencies {
api("ru.mipt.npm:gradle-tools:$toolsVersion")
@ -22,7 +23,7 @@ dependencies {
api("org.jetbrains.kotlinx:kotlinx-benchmark-plugin:$benchmarksVersion")
api("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion")
//to be used inside build-script only
implementation(npmlibs.kotlinx.serialization.json)
implementation(miptNpmLibs.kotlinx.serialization.json)
}
kotlin.sourceSets.all {

View File

@ -4,11 +4,4 @@
#
kotlin.code.style=official
kotlin.mpp.stability.nowarn=true
kotlin.jupyter.add.scanner=false
org.gradle.configureondemand=true
org.gradle.parallel=true
toolsVersion=0.10.9-kotlin-1.6.10
toolsVersion=0.11.2-kotlin-1.6.10

View File

@ -3,21 +3,20 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
enableFeaturePreview("VERSION_CATALOGS")
dependencyResolutionManagement {
val toolsVersion: String by extra
repositories {
mavenLocal()
maven("https://repo.kotlin.link")
mavenCentral()
gradlePluginPortal()
}
versionCatalogs {
create("npmlibs") {
create("miptNpmLibs") {
from("ru.mipt.npm:version-catalog:$toolsVersion")
}
}

View File

@ -52,7 +52,7 @@ module definitions below. The module stability could have the following levels:
## Modules
$modules
${modules}
## Multi-platform support

4
examples/README.md Normal file
View File

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

View File

@ -58,7 +58,7 @@ kotlin.sourceSets.all {
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" + "-Xopt-in=kotlin.RequiresOptIn" + "-Xlambdas=indy"

View File

@ -13,7 +13,7 @@ import kotlin.math.pow
fun main() {
//Define a function
val function: UnivariateFunction<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 }
val function: Function1D<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 }
//get the result of the integration
val result = DoubleField.gaussIntegrator.integrate(0.0..10.0, function = function)

View File

@ -5,8 +5,8 @@
package space.kscience.kmath.functions
import space.kscience.kmath.interpolation.SplineInterpolator
import space.kscience.kmath.interpolation.interpolatePolynomials
import space.kscience.kmath.interpolation.splineInterpolator
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.real.map
import space.kscience.kmath.real.step
@ -18,7 +18,7 @@ import space.kscience.plotly.scatter
@OptIn(UnstablePlotlyAPI::class)
fun main() {
val function: UnivariateFunction<Double> = { x ->
val function: Function1D<Double> = { x ->
if (x in 30.0..50.0) {
1.0
} else {
@ -28,7 +28,7 @@ fun main() {
val xs = 0.0..100.0 step 0.5
val ys = xs.map(function)
val polynomial: PiecewisePolynomial<Double> = SplineInterpolator.double.interpolatePolynomials(xs, ys)
val polynomial: PiecewisePolynomial<Double> = DoubleField.splineInterpolator.interpolatePolynomials(xs, ys)
val polyFunction = polynomial.asFunction(DoubleField, 0.0)

View File

@ -1,65 +0,0 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.tensors
import space.kscience.kmath.linear.transpose
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.MutableStructure2D
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.tensors.core.*
import space.kscience.kmath.tensors.core.tensorAlgebra
import kotlin.math.*
fun MutableStructure2D<Double>.print() {
val n = this.shape.component1()
val m = this.shape.component2()
for (i in 0 until n) {
for (j in 0 until m) {
val x = (this[i, j] * 100).roundToInt() / 100.0
print("$x ")
}
println()
}
println("______________")
}
@OptIn(PerformancePitfall::class)
fun main(): Unit = Double.tensorAlgebra.withBroadcast {
val shape = intArrayOf(5, 3)
val buffer = doubleArrayOf(
1.000000, 2.000000, 3.000000,
2.000000, 3.000000, 4.000000,
3.000000, 4.000000, 5.000000,
4.000000, 5.000000, 6.000000,
5.000000, 6.000000, 7.000000
)
val buffer2 = doubleArrayOf(
0.000000, 0.000000, 0.000000,
0.000000, 0.000000, 0.000000,
0.000000, 0.000000, 0.000000
)
val tensor = fromArray(shape, buffer).as2D()
val v = fromArray(intArrayOf(3, 3), buffer2).as2D()
val w_shape = intArrayOf(3, 1)
var w_buffer = doubleArrayOf(0.000000)
for (i in 0 until 3 - 1) {
w_buffer += doubleArrayOf(0.000000)
}
val w = BroadcastDoubleTensorAlgebra.fromArray(w_shape, w_buffer).as2D()
tensor.print()
var ans = Pair(w, v)
tensor.svdGolabKahan(v, w)
println("u")
tensor.print()
println("w")
w.print()
println("v")
v.print()
}

View File

@ -1,325 +0,0 @@
package space.kscience.kmath.tensors
import space.kscience.kmath.nd.*
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt
/*
* 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.
*/
fun pythag(a: Double, b: Double): Double {
val at: Double = abs(a)
val bt: Double = abs(b)
val ct: Double
val result: Double
if (at > bt) {
ct = bt / at
result = at * sqrt(1.0 + ct * ct)
} else if (bt > 0.0) {
ct = at / bt
result = bt * sqrt(1.0 + ct * ct)
} else result = 0.0
return result
}
fun SIGN(a: Double, b: Double): Double {
if (b >= 0.0)
return abs(a)
else
return -abs(a)
}
// matrix v is not transposed at the output
internal fun MutableStructure2D<Double>.svdGolabKahan(v: MutableStructure2D<Double>, w: MutableStructure2D<Double>) {
val shape = this.shape
val m = shape.component1()
val n = shape.component2()
var f = 0.0
val rv1 = DoubleArray(n)
var s = 0.0
var scale = 0.0
var anorm = 0.0
var g = 0.0
var l = 0
for (i in 0 until n) {
/* left-hand reduction */
l = i + 1
rv1[i] = scale * g
g = 0.0
s = 0.0
scale = 0.0
if (i < m) {
for (k in i until m) {
scale += abs(this[k, i]);
}
if (scale != 0.0) {
for (k in i until m) {
this[k, i] = (this[k, i] / scale)
s += this[k, i] * this[k, i]
}
f = this[i, i]
if (f >= 0) {
g = (-1) * abs(sqrt(s))
} else {
g = abs(sqrt(s))
}
val h = f * g - s
this[i, i] = f - g
if (i != n - 1) {
for (j in l until n) {
s = 0.0
for (k in i until m) {
s += this[k, i] * this[k, j]
}
f = s / h
for (k in i until m) {
this[k, j] += f * this[k, i]
}
}
}
for (k in i until m) {
this[k, i] = this[k, i] * scale
}
}
}
w[i, 0] = scale * g
/* right-hand reduction */
g = 0.0
s = 0.0
scale = 0.0
if (i < m && i != n - 1) {
for (k in l until n) {
scale += abs(this[i, k])
}
if (scale != 0.0) {
for (k in l until n) {
this[i, k] = this[i, k] / scale
s += this[i, k] * this[i, k]
}
f = this[i, l]
if (f >= 0) {
g = (-1) * abs(sqrt(s))
} else {
g = abs(sqrt(s))
}
val h = f * g - s
this[i, l] = f - g
for (k in l until n) {
rv1[k] = this[i, k] / h
}
if (i != m - 1) {
for (j in l until m) {
s = 0.0
for (k in l until n) {
s += this[j, k] * this[i, k]
}
for (k in l until n) {
this[j, k] += s * rv1[k]
}
}
}
for (k in l until n) {
this[i, k] = this[i, k] * scale
}
}
}
anorm = max(anorm, (abs(w[i, 0]) + abs(rv1[i])));
}
for (i in n - 1 downTo 0) {
if (i < n - 1) {
if (g != 0.0) {
for (j in l until n) {
v[j, i] = (this[i, j] / this[i, l]) / g
}
for (j in l until n) {
s = 0.0
for (k in l until n)
s += this[i, k] * v[k, j]
for (k in l until n)
v[k, j] += s * v[k, i]
}
}
for (j in l until n) {
v[i, j] = 0.0
v[j, i] = 0.0
}
}
v[i, i] = 1.0
g = rv1[i]
l = i
}
// до этого момента все правильно считается
// дальше - нет
for (i in min(n, m) - 1 downTo 0) {
l = i + 1
g = w[i, 0]
for (j in l until n) {
this[i, j] = 0.0
}
if (g != 0.0) {
// !!!!! вот тут деление на почти ноль
g = 1.0 / g
for (j in l until n) {
s = 0.0
for (k in l until m) {
s += this[k, i] * this[k, j]
}
f = (s / this[i, i]) * g
for (k in i until m) {
this[k, j] += f * this[k, i]
}
}
for (j in i until m) {
this[j, i] *= g
}
} else {
for (j in i until m) {
this[j, i] = 0.0
}
}
this[i, i] += 1.0
}
// println("matrix")
// this.print()
// тут матрица должна выглядеть так:
// 0.134840 -0.762770 0.522117
// -0.269680 -0.476731 -0.245388
// -0.404520 -0.190693 -0.527383
// -0.539360 0.095346 -0.297540
// -0.674200 0.381385 0.548193
this[0, 2] = 0.522117
this[1, 2] = -0.245388
this[2, 2] = -0.527383
this[3, 2] = -0.297540
this[4, 2] = 0.548193
// задала правильные значения, чтобы проверить правильность кода дальше
// дальше - все корректно
var flag = 0
var nm = 0
var c = 0.0
var h = 0.0
var y = 0.0
var z = 0.0
var x = 0.0
for (k in n - 1 downTo 0) {
for (its in 1 until 30) {
flag = 1
for (newl in k downTo 0) {
nm = newl - 1
if (abs(rv1[newl]) + anorm == anorm) {
flag = 0
l = newl
break
}
if (abs(w[nm, 0]) + anorm == anorm) {
l = newl
break
}
}
if (flag != 0) {
c = 0.0
s = 1.0
for (i in l until k) {
f = s * rv1[i]
rv1[i] = c * rv1[i]
if (abs(f) + anorm == anorm) {
break
}
h = pythag(f, g)
w[i, 0] = h
h = 1.0 / h
c = g * h
s = (-f) * h
for (j in 0 until m) {
y = this[j, nm]
z = this[j, i]
this[j, nm] = y * c + z * s
this[j, i] = z * c - y * s
}
}
}
z = w[k, 0]
if (l == k) {
if (z < 0.0) {
w[k, 0] = -z
for (j in 0 until n)
v[j, k] = -v[j, k]
}
break
}
// надо придумать, что сделать - выкинуть ошибку?
// if (its == 30) {
// return
// }
x = w[l, 0]
nm = k - 1
y = w[nm, 0]
g = rv1[nm]
h = rv1[k]
f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y)
g = pythag(f, 1.0)
f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x
c = 1.0
s = 1.0
var i = 0
for (j in l until nm + 1) {
i = j + 1
g = rv1[i]
y = w[i, 0]
h = s * g
g = c * g
z = pythag(f, h)
rv1[j] = z
c = f / z
s = h / z
f = x * c + g * s
g = g * c - x * s
h = y * s
y *= c
for (jj in 0 until n) {
x = v[jj, j];
z = v[jj, i];
v[jj, j] = x * c + z * s;
v[jj, i] = z * c - x * s;
}
z = pythag(f, h)
w[j, 0] = z
if (z != 0.0) {
z = 1.0 / z
c = f * z
s = h * z
}
f = c * g + s * y
x = c * y - s * g
for (jj in 0 until m) {
y = this[jj, j]
z = this[jj, i]
this[jj, j] = y * c + z * s
this[jj, i] = z * c - y * s
}
}
rv1[l] = 0.0
rv1[k] = f
w[k, 0] = x
}
}
}

View File

@ -2,14 +2,12 @@
# Copyright 2018-2021 KMath contributors.
# Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
#
kotlin.code.style=official
kotlin.mpp.stability.nowarn=true
kotlin.jupyter.add.scanner=false
kotlin.mpp.stability.nowarn=true
kotlin.native.ignoreDisabledTargets=true
#kotlin.incremental.js.ir=true
org.gradle.configureondemand=true
org.gradle.parallel=true
org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G
toolsVersion=0.11.1-kotlin-1.6.10
org.gradle.parallel=true

Binary file not shown.

View File

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

View File

@ -10,17 +10,17 @@ Extensions to MST API: transformations, dynamic compilation and visualization.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-ast:0.3.0-dev-17'
implementation 'space.kscience:kmath-ast:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-ast:0.3.0-dev-17")
implementation("space.kscience:kmath-ast:0.3.0")
}
```
@ -66,20 +66,19 @@ For example, the following code:
```kotlin
import space.kscience.kmath.asm.compileToExpression
import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.operations.DoubleField
"x+2".parseMath().compileToExpression(ComplexField)
"x^3-x+3".parseMath().compileToExpression(DoubleField)
```
&mldr; leads to generation of bytecode, which can be decompiled to the following Java class:
```java
import java.util.Map;
import kotlin.jvm.functions.Function2;
import space.kscience.kmath.asm.internal.MapIntrinsics;
import space.kscience.kmath.complex.Complex;
import space.kscience.kmath.expressions.Expression;
import space.kscience.kmath.expressions.Symbol;
import java.util.*;
import kotlin.jvm.functions.*;
import space.kscience.kmath.asm.internal.*;
import space.kscience.kmath.complex.*;
import space.kscience.kmath.expressions.*;
public final class CompiledExpression_45045_0 implements Expression<Complex> {
private final Object[] constants;
@ -91,6 +90,32 @@ public final class CompiledExpression_45045_0 implements Expression<Complex> {
}
```
For `LongRing`, `IntRing`, and `DoubleField` specialization is supported for better performance:
```java
import java.util.*;
import space.kscience.kmath.asm.internal.*;
import space.kscience.kmath.expressions.*;
public final class CompiledExpression_-386104628_0 implements DoubleExpression {
private final SymbolIndexer indexer;
public SymbolIndexer getIndexer() {
return this.indexer;
}
public double invoke(double[] arguments) {
double var2 = arguments[0];
return Math.pow(var2, 3.0D) - var2 + 3.0D;
}
public final Double invoke(Map<Symbol, ? extends Double> arguments) {
double var2 = ((Double)MapIntrinsics.getOrFail(arguments, "x")).doubleValue();
return Math.pow(var2, 3.0D) - var2 + 3.0D;
}
}
```
Setting JVM system property `space.kscience.kmath.ast.dump.generated.classes` to `1` makes the translator dump class files to program's working directory, so they can be reviewed manually.
#### Limitations
@ -134,9 +159,9 @@ MstField { x + 2 }.compileToExpression(DoubleField)
An example of emitted Wasm IR in the form of WAT:
```lisp
(func $executable (param $0 f64) (result f64)
(func \$executable (param \$0 f64) (result f64)
(f64.add
(local.get $0)
(local.get \$0)
(f64.const 2)
)
)

32
kmath-commons/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module kmath-commons
Commons math binding for kmath
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-commons:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-commons:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-commons:0.3.0")
}
```

View File

@ -8,17 +8,17 @@ Complex and hypercomplex number systems in KMath.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-complex:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-complex:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-complex:0.3.0-dev-17'
implementation 'space.kscience:kmath-complex:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -29,6 +29,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-complex:0.3.0-dev-17")
implementation("space.kscience:kmath-complex:0.3.0")
}
```

View File

@ -15,17 +15,17 @@ performance calculations to code generation.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-core:0.3.0-dev-17'
implementation 'space.kscience:kmath-core:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -36,6 +36,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-core:0.3.0-dev-17")
implementation("space.kscience:kmath-core:0.3.0")
}
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.domains
import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
@UnstableKMathAPI
public abstract class Domain1D<T : Comparable<T>>(public val range: ClosedRange<T>) : Domain<T> {
override val dimension: Int get() = 1
public operator fun contains(value: T): Boolean = range.contains(value)
override operator fun contains(point: Point<T>): Boolean {
require(point.size == 0)
return contains(point[0])
}
}
@UnstableKMathAPI
public class DoubleDomain1D(
@Suppress("CanBeParameter") public val doubleRange: ClosedFloatingPointRange<Double>,
) : Domain1D<Double>(doubleRange), DoubleDomain {
override fun getLowerBound(num: Int): Double {
require(num == 0)
return range.start
}
override fun getUpperBound(num: Int): Double {
require(num == 0)
return range.endInclusive
}
override fun volume(): Double = range.endInclusive - range.start
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as DoubleDomain1D
if (doubleRange != other.doubleRange) return false
return true
}
override fun hashCode(): Int = doubleRange.hashCode()
override fun toString(): String = doubleRange.toString()
}
@UnstableKMathAPI
public val Domain1D<Double>.center: Double
get() = (range.endInclusive + range.start) / 2

View File

@ -7,18 +7,28 @@ package space.kscience.kmath.domains
import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.indices
/**
*
* HyperSquareDomain class.
*
* @author Alexander Nozik
* A hyper-square (or hyper-cube) real-space domain. It is formed by a [Buffer] of [lower] boundaries
* and a [Buffer] of upper boundaries. Upper should be greater or equals than lower.
*/
@UnstableKMathAPI
public class HyperSquareDomain(private val lower: Buffer<Double>, private val upper: Buffer<Double>) : DoubleDomain {
public class HyperSquareDomain(public val lower: Buffer<Double>, public val upper: Buffer<Double>) : DoubleDomain {
init {
require(lower.size == upper.size) {
"Domain borders size mismatch. Lower borders size is ${lower.size}, but upper borders size is ${upper.size}."
}
require(lower.indices.all { lower[it] <= upper[it] }) {
"Domain borders order mismatch. Lower borders must be less or equals than upper borders."
}
}
override val dimension: Int get() = lower.size
public val center: DoubleBuffer get() = DoubleBuffer(dimension) { (lower[it] + upper[it]) / 2.0 }
override operator fun contains(point: Point<Double>): Boolean = point.indices.all { i ->
point[i] in lower[i]..upper[i]
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.domains
import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
@UnstableKMathAPI
public class UnivariateDomain(public val range: ClosedFloatingPointRange<Double>) : DoubleDomain {
override val dimension: Int get() = 1
public operator fun contains(d: Double): Boolean = range.contains(d)
override operator fun contains(point: Point<Double>): Boolean {
require(point.size == 0)
return contains(point[0])
}
override fun getLowerBound(num: Int): Double {
require(num == 0)
return range.start
}
override fun getUpperBound(num: Int): Double {
require(num == 0)
return range.endInclusive
}
override fun volume(): Double = range.endInclusive - range.start
}

View File

@ -3,43 +3,71 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package space.kscience.kmath.misc
import kotlin.comparisons.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.VirtualBuffer
/**
* Return a new list filled with buffer indices. Indice order is defined by sorting associated buffer value.
* This feature allows to sort buffer values without reordering its content.
* Return a new array filled with buffer indices. Indices order is defined by sorting associated buffer value.
* This feature allows sorting buffer values without reordering its content.
*
* @return List of buffer indices, sorted by associated value.
* @return Buffer indices, sorted by associated value.
*/
@PerformancePitfall
@UnstableKMathAPI
public fun <V: Comparable<V>> Buffer<V>.permSort() : IntArray = _permSortWith(compareBy<Int> { get(it) })
public fun <V : Comparable<V>> Buffer<V>.indicesSorted(): IntArray = permSortIndicesWith(compareBy { get(it) })
/**
* Create a zero-copy virtual buffer that contains the same elements but in ascending order
*/
@OptIn(UnstableKMathAPI::class)
public fun <V : Comparable<V>> Buffer<V>.sorted(): Buffer<V> {
val permutations = indicesSorted()
return VirtualBuffer(size) { this[permutations[it]] }
}
@PerformancePitfall
@UnstableKMathAPI
public fun <V: Comparable<V>> Buffer<V>.permSortDescending() : IntArray = _permSortWith(compareByDescending<Int> { get(it) })
public fun <V : Comparable<V>> Buffer<V>.indicesSortedDescending(): IntArray =
permSortIndicesWith(compareByDescending { get(it) })
/**
* Create a zero-copy virtual buffer that contains the same elements but in descending order
*/
@OptIn(UnstableKMathAPI::class)
public fun <V : Comparable<V>> Buffer<V>.sortedDescending(): Buffer<V> {
val permutations = indicesSortedDescending()
return VirtualBuffer(size) { this[permutations[it]] }
}
@PerformancePitfall
@UnstableKMathAPI
public fun <V, C: Comparable<C>> Buffer<V>.permSortBy(selector: (V) -> C) : IntArray = _permSortWith(compareBy<Int> { selector(get(it)) })
public fun <V, C : Comparable<C>> Buffer<V>.indicesSortedBy(selector: (V) -> C): IntArray =
permSortIndicesWith(compareBy { selector(get(it)) })
@OptIn(UnstableKMathAPI::class)
public fun <V, C : Comparable<C>> Buffer<V>.sortedBy(selector: (V) -> C): Buffer<V> {
val permutations = indicesSortedBy(selector)
return VirtualBuffer(size) { this[permutations[it]] }
}
@PerformancePitfall
@UnstableKMathAPI
public fun <V, C: Comparable<C>> Buffer<V>.permSortByDescending(selector: (V) -> C) : IntArray = _permSortWith(compareByDescending<Int> { selector(get(it)) })
public fun <V, C : Comparable<C>> Buffer<V>.indicesSortedByDescending(selector: (V) -> C): IntArray =
permSortIndicesWith(compareByDescending { selector(get(it)) })
@OptIn(UnstableKMathAPI::class)
public fun <V, C : Comparable<C>> Buffer<V>.sortedByDescending(selector: (V) -> C): Buffer<V> {
val permutations = indicesSortedByDescending(selector)
return VirtualBuffer(size) { this[permutations[it]] }
}
@PerformancePitfall
@UnstableKMathAPI
public fun <V> Buffer<V>.permSortWith(comparator : Comparator<V>) : IntArray = _permSortWith { i1, i2 -> comparator.compare(get(i1), get(i2)) }
public fun <V> Buffer<V>.indicesSortedWith(comparator: Comparator<V>): IntArray =
permSortIndicesWith { i1, i2 -> comparator.compare(get(i1), get(i2)) }
@PerformancePitfall
@UnstableKMathAPI
private fun <V> Buffer<V>._permSortWith(comparator : Comparator<Int>) : IntArray {
if (size < 2) return IntArray(size)
private fun <V> Buffer<V>.permSortIndicesWith(comparator: Comparator<Int>): IntArray {
if (size < 2) return IntArray(size) { 0 }
/* TODO: optimisation : keep a constant big array of indices (Ex: from 0 to 4096), then create indice
/* TODO: optimisation : keep a constant big array of indices (Ex: from 0 to 4096), then create indices
* arrays more efficiently by copying subpart of cached one. For bigger needs, we could copy entire
* cached array, then fill remaining indices manually. Not done for now, because:
* 1. doing it right would require some statistics about common used buffer sizes.
@ -53,3 +81,12 @@ private fun <V> Buffer<V>._permSortWith(comparator : Comparator<Int>) : IntArray
*/
return packedIndices.sortedWith(comparator).toIntArray()
}
/**
* Checks that the [Buffer] is sorted (ascending) and throws [IllegalArgumentException] if it is not.
*/
public fun <T : Comparable<T>> Buffer<T>.requireSorted() {
for (i in 0..(size - 2)) {
require(get(i + 1) >= get(i)) { "The buffer is not sorted at index $i" }
}
}

View File

@ -194,7 +194,7 @@ public interface RingOpsND<T, out A : RingOps<T>> : RingOps<StructureND<T>>, Gro
override fun multiply(left: StructureND<T>, right: StructureND<T>): StructureND<T> =
zip(left, right) { aValue, bValue -> multiply(aValue, bValue) }
//TODO move to extensions after KEEP-176
//TODO move to extensions with context receivers
/**
* Multiplies an ND structure by an element of it.

View File

@ -32,18 +32,23 @@ public open class BufferND<out T>(
/**
* Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferND]
*/
public inline fun <T, reified R : Any> StructureND<T>.mapToBuffer(
factory: BufferFactory<R> = Buffer.Companion::auto,
public inline fun <T, R : Any> StructureND<T>.mapToBuffer(
factory: BufferFactory<R>,
crossinline transform: (T) -> R,
): BufferND<R> {
return if (this is BufferND<T>)
BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
else {
val strides = DefaultStrides(shape)
BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
}
): BufferND<R> = if (this is BufferND<T>)
BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
else {
val strides = DefaultStrides(shape)
BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
}
/**
* Transform structure to a new structure using inferred [BufferFactory]
*/
public inline fun <T, reified R : Any> StructureND<T>.mapToBuffer(
crossinline transform: (T) -> R,
): BufferND<R> = mapToBuffer(Buffer.Companion::auto, transform)
/**
* Represents [MutableStructureND] over [MutableBuffer].
*

View File

@ -5,7 +5,6 @@
package space.kscience.kmath.operations
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
@ -53,7 +52,7 @@ public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
*/
private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapInline(
buffer: Buffer<T>,
crossinline block: A.(T) -> T
crossinline block: A.(T) -> T,
): Buffer<T> = bufferFactory(buffer.size) { elementAlgebra.block(buffer[it]) }
/**
@ -61,7 +60,7 @@ private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapInline(
*/
private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapIndexedInline(
buffer: Buffer<T>,
crossinline block: A.(index: Int, arg: T) -> T
crossinline block: A.(index: Int, arg: T) -> T,
): Buffer<T> = bufferFactory(buffer.size) { elementAlgebra.block(it, buffer[it]) }
/**
@ -70,7 +69,7 @@ private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapIndexedInline(
private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.zipInline(
l: Buffer<T>,
r: Buffer<T>,
crossinline block: A.(l: T, r: T) -> T
crossinline block: A.(l: T, r: T) -> T,
): Buffer<T> {
require(l.size == r.size) { "Incompatible buffer sizes. left: ${l.size}, right: ${r.size}" }
return bufferFactory(l.size) { elementAlgebra.block(l[it], r[it]) }
@ -127,13 +126,13 @@ public fun <T, A : ExponentialOperations<T>> BufferAlgebra<T, A>.atanh(arg: Buff
mapInline(arg) { atanh(it) }
public fun <T, A : PowerOperations<T>> BufferAlgebra<T, A>.pow(arg: Buffer<T>, pow: Number): Buffer<T> =
mapInline(arg) {it.pow(pow) }
mapInline(arg) { it.pow(pow) }
public open class BufferRingOps<T, A: Ring<T>>(
public open class BufferRingOps<T, A : Ring<T>>(
override val elementAlgebra: A,
override val bufferFactory: BufferFactory<T>,
) : BufferAlgebra<T, A>, RingOps<Buffer<T>>{
) : BufferAlgebra<T, A>, RingOps<Buffer<T>> {
override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l + r }
override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l * r }
@ -152,10 +151,11 @@ public val ShortRing.bufferAlgebra: BufferRingOps<Short, ShortRing>
public open class BufferFieldOps<T, A : Field<T>>(
elementAlgebra: A,
bufferFactory: BufferFactory<T>,
) : BufferRingOps<T, A>(elementAlgebra, bufferFactory), BufferAlgebra<T, A>, FieldOps<Buffer<T>>, ScaleOperations<Buffer<T>> {
) : BufferRingOps<T, A>(elementAlgebra, bufferFactory), BufferAlgebra<T, A>, FieldOps<Buffer<T>>,
ScaleOperations<Buffer<T>> {
override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l + r }
override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l * r }
// override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l + r }
// override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l * r }
override fun divide(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l / r }
override fun scale(a: Buffer<T>, value: Double): Buffer<T> = a.map { scale(it, value) }
@ -168,7 +168,7 @@ public open class BufferFieldOps<T, A : Field<T>>(
public class BufferField<T, A : Field<T>>(
elementAlgebra: A,
bufferFactory: BufferFactory<T>,
override val size: Int
override val size: Int,
) : BufferFieldOps<T, A>(elementAlgebra, bufferFactory), Field<Buffer<T>>, WithSize {
override val zero: Buffer<T> = bufferFactory(size) { elementAlgebra.zero }

View File

@ -105,6 +105,16 @@ public interface Buffer<out T> {
*/
public val Buffer<*>.indices: IntRange get() = 0 until size
public fun <T> Buffer<T>.first(): T {
require(size > 0) { "Can't get the first element of empty buffer" }
return get(0)
}
public fun <T> Buffer<T>.last(): T {
require(size > 0) { "Can't get the last element of empty buffer" }
return get(size - 1)
}
/**
* Immutable wrapper for [MutableBuffer].
*

View File

@ -6,14 +6,13 @@
package space.kscience.kmath.misc
import space.kscience.kmath.misc.PermSortTest.Platform.*
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import space.kscience.kmath.structures.IntBuffer
import space.kscience.kmath.structures.asBuffer
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class PermSortTest {
@ -29,9 +28,9 @@ class PermSortTest {
@Test
fun testOnEmptyBuffer() {
val emptyBuffer = IntBuffer(0) {it}
var permutations = emptyBuffer.permSort()
var permutations = emptyBuffer.indicesSorted()
assertTrue(permutations.isEmpty(), "permutation on an empty buffer should return an empty result")
permutations = emptyBuffer.permSortDescending()
permutations = emptyBuffer.indicesSortedDescending()
assertTrue(permutations.isEmpty(), "permutation on an empty buffer should return an empty result")
}
@ -47,25 +46,25 @@ class PermSortTest {
@Test
fun testPermSortBy() {
val permutations = platforms.permSortBy { it.name }
val permutations = platforms.indicesSortedBy { it.name }
val expected = listOf(ANDROID, JS, JVM, NATIVE, WASM)
assertContentEquals(expected, permutations.map { platforms[it] }, "Ascending PermSort by name")
}
@Test
fun testPermSortByDescending() {
val permutations = platforms.permSortByDescending { it.name }
val permutations = platforms.indicesSortedByDescending { it.name }
val expected = listOf(WASM, NATIVE, JVM, JS, ANDROID)
assertContentEquals(expected, permutations.map { platforms[it] }, "Descending PermSort by name")
}
@Test
fun testPermSortWith() {
var permutations = platforms.permSortWith { p1, p2 -> p1.name.length.compareTo(p2.name.length) }
var permutations = platforms.indicesSortedWith { p1, p2 -> p1.name.length.compareTo(p2.name.length) }
val expected = listOf(JS, JVM, WASM, NATIVE, ANDROID)
assertContentEquals(expected, permutations.map { platforms[it] }, "PermSort using custom ascending comparator")
permutations = platforms.permSortWith(compareByDescending { it.name.length })
permutations = platforms.indicesSortedWith(compareByDescending { it.name.length })
assertContentEquals(expected.reversed(), permutations.map { platforms[it] }, "PermSort using custom descending comparator")
}
@ -75,7 +74,7 @@ class PermSortTest {
println("Test randomization seed: $seed")
val buffer = Random(seed).buffer(bufferSize)
val indices = buffer.permSort()
val indices = buffer.indicesSorted()
assertEquals(bufferSize, indices.size)
// Ensure no doublon is present in indices
@ -87,7 +86,7 @@ class PermSortTest {
assertTrue(current <= next, "Permutation indices not properly sorted")
}
val descIndices = buffer.permSortDescending()
val descIndices = buffer.indicesSortedDescending()
assertEquals(bufferSize, descIndices.size)
// Ensure no doublon is present in indices
assertEquals(descIndices.toSet().size, descIndices.size)

View File

@ -0,0 +1,32 @@
# Module kmath-coroutines
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-coroutines:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-coroutines:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-coroutines:0.3.0")
}
```

View File

@ -0,0 +1,32 @@
# Module kmath-dimensions
A proof of concept module for adding type-safe dimensions to structures
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-dimensions:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-dimensions:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-dimensions:0.3.0")
}
```

View File

@ -9,17 +9,17 @@ EJML based linear algebra implementation.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-ejml:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-ejml:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-ejml:0.3.0-dev-17'
implementation 'space.kscience:kmath-ejml:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -30,6 +30,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-ejml:0.3.0-dev-17")
implementation("space.kscience:kmath-ejml:0.3.0")
}
```

View File

@ -9,17 +9,17 @@ Specialization of KMath APIs for Double numbers.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-for-real:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-for-real:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-for-real:0.3.0-dev-17'
implementation 'space.kscience:kmath-for-real:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -30,6 +30,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-for-real:0.3.0-dev-17")
implementation("space.kscience:kmath-for-real:0.3.0")
}
```

View File

@ -21,19 +21,22 @@ readme {
feature(
id = "DoubleVector",
description = "Numpy-like operations for Buffers/Points",
ref = "src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt"
)
){
"Numpy-like operations for Buffers/Points"
}
feature(
id = "DoubleMatrix",
description = "Numpy-like operations for 2d real structures",
ref = "src/commonMain/kotlin/space/kscience/kmath/real/DoubleMatrix.kt"
)
){
"Numpy-like operations for 2d real structures"
}
feature(
id = "grids",
description = "Uniform grid generators",
ref = "src/commonMain/kotlin/space/kscience/kmath/structures/grids.kt"
)
){
"Uniform grid generators"
}
}

View File

@ -11,17 +11,17 @@ Functions and interpolations.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-functions:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-functions:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-functions:0.3.0-dev-17'
implementation 'space.kscience:kmath-functions:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -32,6 +32,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-functions:0.3.0-dev-17")
implementation("space.kscience:kmath-functions:0.3.0")
}
```

View File

@ -7,6 +7,6 @@ package space.kscience.kmath.functions
import space.kscience.kmath.structures.Buffer
public typealias UnivariateFunction<T> = (T) -> T
public typealias Function1D<T> = (T) -> T
public typealias MultivariateFunction<T> = (Buffer<T>) -> T
public typealias FunctionND<T> = (Buffer<T>) -> T

View File

@ -28,6 +28,8 @@ public fun <T : Comparable<T>> PiecewisePolynomial<T>.integrate(algebra: Field<T
/**
* Compute definite integral of given [PiecewisePolynomial] piece by piece in a given [range]
* Requires [UnivariateIntegrationNodes] or [IntegrationRange] and [IntegrandMaxCalls]
*
* TODO use context receiver for algebra
*/
@UnstableKMathAPI
public fun <T : Comparable<T>> PiecewisePolynomial<T>.integrate(
@ -98,6 +100,7 @@ public object DoubleSplineIntegrator : UnivariateIntegrator<Double> {
}
}
@Suppress("unused")
@UnstableKMathAPI
public inline val DoubleField.splineIntegrator: UnivariateIntegrator<Double>
get() = DoubleSplineIntegrator

View File

@ -9,6 +9,7 @@ package space.kscience.kmath.interpolation
import space.kscience.kmath.data.XYColumnarData
import space.kscience.kmath.functions.PiecewisePolynomial
import space.kscience.kmath.functions.asFunction
import space.kscience.kmath.functions.value
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Ring
@ -59,3 +60,33 @@ public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
val pointSet = XYColumnarData.of(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer())
return interpolatePolynomials(pointSet)
}
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
x: Buffer<T>,
y: Buffer<T>,
): (T) -> T? = interpolatePolynomials(x, y).asFunction(algebra)
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
data: Map<T, T>,
): (T) -> T? = interpolatePolynomials(data).asFunction(algebra)
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
data: List<Pair<T, T>>,
): (T) -> T? = interpolatePolynomials(data).asFunction(algebra)
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
x: Buffer<T>,
y: Buffer<T>,
defaultValue: T,
): (T) -> T = interpolatePolynomials(x, y).asFunction(algebra, defaultValue)
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
data: Map<T, T>,
defaultValue: T,
): (T) -> T = interpolatePolynomials(data).asFunction(algebra, defaultValue)
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
data: List<Pair<T, T>>,
defaultValue: T,
): (T) -> T = interpolatePolynomials(data).asFunction(algebra, defaultValue)

View File

@ -22,6 +22,7 @@ internal fun <T : Comparable<T>> insureSorted(points: XYColumnarData<*, T, *>) {
* Reference JVM implementation: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/analysis/interpolation/LinearInterpolator.java
*/
public class LinearInterpolator<T : Comparable<T>>(override val algebra: Field<T>) : PolynomialInterpolator<T> {
@OptIn(UnstableKMathAPI::class)
override fun interpolatePolynomials(points: XYColumnarData<T, T, T>): PiecewisePolynomial<T> = algebra {
require(points.size > 0) { "Point array should not be empty" }
@ -37,3 +38,6 @@ public class LinearInterpolator<T : Comparable<T>>(override val algebra: Field<T
}
}
}
public val <T : Comparable<T>> Field<T>.linearInterpolator: LinearInterpolator<T>
get() = LinearInterpolator(this)

View File

@ -63,8 +63,8 @@ public class SplineInterpolator<T : Comparable<T>>(
//Shift coefficients to represent absolute polynomial instead of one with an offset
val polynomial = Polynomial(
a - b * x0 + c * x02 - d * x03,
b - 2*c*x0 + 3*d*x02,
c - 3*d*x0,
b - 2 * c * x0 + 3 * d * x02,
c - 3 * d * x0,
d
)
cOld = c
@ -72,8 +72,12 @@ public class SplineInterpolator<T : Comparable<T>>(
}
}
}
public companion object {
public val double: SplineInterpolator<Double> = SplineInterpolator(DoubleField, ::DoubleBuffer)
}
}
public fun <T : Comparable<T>> Field<T>.splineInterpolator(
bufferFactory: MutableBufferFactory<T>,
): SplineInterpolator<T> = SplineInterpolator(this, bufferFactory)
public val DoubleField.splineInterpolator: SplineInterpolator<Double>
get() = SplineInterpolator(this, ::DoubleBuffer)

View File

@ -5,8 +5,6 @@
package space.kscience.kmath.interpolation
import space.kscience.kmath.functions.PiecewisePolynomial
import space.kscience.kmath.functions.asFunction
import space.kscience.kmath.operations.DoubleField
import kotlin.test.Test
import kotlin.test.assertEquals
@ -21,8 +19,8 @@ internal class LinearInterpolatorTest {
3.0 to 4.0
)
val polynomial: PiecewisePolynomial<Double> = LinearInterpolator(DoubleField).interpolatePolynomials(data)
val function = polynomial.asFunction(DoubleField)
//val polynomial: PiecewisePolynomial<Double> = DoubleField.linearInterpolator.interpolatePolynomials(data)
val function = DoubleField.linearInterpolator.interpolate(data)
assertEquals(null, function(-1.0))
assertEquals(0.5, function(0.5))
assertEquals(2.0, function(1.5))

View File

@ -5,8 +5,6 @@
package space.kscience.kmath.interpolation
import space.kscience.kmath.functions.PiecewisePolynomial
import space.kscience.kmath.functions.asFunction
import space.kscience.kmath.operations.DoubleField
import kotlin.math.PI
import kotlin.math.sin
@ -21,9 +19,10 @@ internal class SplineInterpolatorTest {
x to sin(x)
}
val polynomial: PiecewisePolynomial<Double> = SplineInterpolator.double.interpolatePolynomials(data)
//val polynomial: PiecewisePolynomial<Double> = DoubleField.splineInterpolator.interpolatePolynomials(data)
val function = DoubleField.splineInterpolator.interpolate(data, Double.NaN)
val function = polynomial.asFunction(DoubleField, Double.NaN)
assertEquals(Double.NaN, function(-1.0))
assertEquals(sin(0.5), function(0.5), 0.1)
assertEquals(sin(1.5), function(1.5), 0.1)

32
kmath-geometry/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module kmath-geometry
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-geometry:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-geometry:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-geometry:0.3.0")
}
```

View File

@ -0,0 +1,32 @@
# Module kmath-histograms
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-histograms:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-histograms:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-histograms:0.3.0")
}
```

View File

@ -17,6 +17,8 @@ kotlin.sourceSets {
commonTest {
dependencies {
implementation(project(":kmath-for-real"))
implementation(projects.kmath.kmathStat)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.0")
}
}
}

View File

@ -18,7 +18,8 @@ public interface Counter<T : Any> {
public val value: T
public companion object {
public fun double(): ObjectCounter<Double> = ObjectCounter(DoubleField)
public fun ofDouble(): ObjectCounter<Double> = ObjectCounter(DoubleField)
public fun <T: Any> of(group: Group<T>): ObjectCounter<T> = ObjectCounter(group)
}
}

View File

@ -1,130 +0,0 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.histogram
import space.kscience.kmath.domains.Domain
import space.kscience.kmath.domains.HyperSquareDomain
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.*
import kotlin.math.floor
public class DoubleHistogramSpace(
private val lower: Buffer<Double>,
private val upper: Buffer<Double>,
private val binNums: IntArray = IntArray(lower.size) { 20 },
) : IndexedHistogramSpace<Double, Double> {
init {
// argument checks
require(lower.size == upper.size) { "Dimension mismatch in histogram lower and upper limits." }
require(lower.size == binNums.size) { "Dimension mismatch in bin count." }
require(!lower.indices.any { upper[it] - lower[it] < 0 }) { "Range for one of axis is not strictly positive" }
}
public val dimension: Int get() = lower.size
override val shape: IntArray = IntArray(binNums.size) { binNums[it] + 2 }
override val histogramValueSpace: DoubleFieldND = DoubleField.ndAlgebra(*shape)
private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] }
/**
* Get internal [StructureND] bin index for given axis
*/
private fun getIndex(axis: Int, value: Double): Int = when {
value >= upper[axis] -> binNums[axis] + 1 // overflow
value < lower[axis] -> 0 // underflow
else -> floor((value - lower[axis]) / binSize[axis]).toInt()
}
override fun getIndex(point: Buffer<Double>): IntArray = IntArray(dimension) {
getIndex(it, point[it])
}
@OptIn(UnstableKMathAPI::class)
override fun getDomain(index: IntArray): Domain<Double> {
val lowerBoundary = index.mapIndexed { axis, i ->
when (i) {
0 -> Double.NEGATIVE_INFINITY
shape[axis] - 1 -> upper[axis]
else -> lower[axis] + (i.toDouble()) * binSize[axis]
}
}.asBuffer()
val upperBoundary = index.mapIndexed { axis, i ->
when (i) {
0 -> lower[axis]
shape[axis] - 1 -> Double.POSITIVE_INFINITY
else -> lower[axis] + (i.toDouble() + 1) * binSize[axis]
}
}.asBuffer()
return HyperSquareDomain(lowerBoundary, upperBoundary)
}
override fun produceBin(index: IntArray, value: Double): Bin<Double> {
val domain = getDomain(index)
return DomainBin(domain, value)
}
override fun produce(builder: HistogramBuilder<Double>.() -> Unit): IndexedHistogram<Double, Double> {
val ndCounter = StructureND.auto(shape) { Counter.double() }
val hBuilder = HistogramBuilder<Double> { point, value ->
val index = getIndex(point)
ndCounter[index].add(value.toDouble())
}
hBuilder.apply(builder)
val values: BufferND<Double> = ndCounter.mapToBuffer { it.value }
return IndexedHistogram(this, values)
}
override fun IndexedHistogram<Double, Double>.unaryMinus(): IndexedHistogram<Double, Double> = this * (-1)
public companion object {
/**
* Use it like
* ```
*FastHistogram.fromRanges(
* (-1.0..1.0),
* (-1.0..1.0)
*)
*```
*/
public fun fromRanges(vararg ranges: ClosedFloatingPointRange<Double>): DoubleHistogramSpace = DoubleHistogramSpace(
ranges.map(ClosedFloatingPointRange<Double>::start).asBuffer(),
ranges.map(ClosedFloatingPointRange<Double>::endInclusive).asBuffer()
)
/**
* Use it like
* ```
*FastHistogram.fromRanges(
* (-1.0..1.0) to 50,
* (-1.0..1.0) to 32
*)
*```
*/
public fun fromRanges(vararg ranges: Pair<ClosedFloatingPointRange<Double>, Int>): DoubleHistogramSpace =
DoubleHistogramSpace(
ListBuffer(
ranges
.map(Pair<ClosedFloatingPointRange<Double>, Int>::first)
.map(ClosedFloatingPointRange<Double>::start)
),
ListBuffer(
ranges
.map(Pair<ClosedFloatingPointRange<Double>, Int>::first)
.map(ClosedFloatingPointRange<Double>::endInclusive)
),
ranges.map(Pair<ClosedFloatingPointRange<Double>, Int>::second).toIntArray()
)
}
}

View File

@ -13,14 +13,23 @@ import space.kscience.kmath.structures.asBuffer
/**
* The binned data element. Could be a histogram bin with a number of counts or an artificial construct.
*/
public interface Bin<in T : Any> : Domain<T> {
public interface Bin<in T : Any, out V> : Domain<T> {
/**
* The value of this bin.
*/
public val value: Number
public val binValue: V
}
public interface Histogram<in T : Any, out B : Bin<T>> {
/**
* A simple histogram bin based on domain
*/
public data class DomainBin<in T : Comparable<T>, D : Domain<T>, out V>(
public val domain: D,
override val binValue: V,
) : Bin<T, V>, Domain<T> by domain
public interface Histogram<in T : Any, out V, out B : Bin<T, V>> {
/**
* Find existing bin, corresponding to given coordinates
*/
@ -32,29 +41,38 @@ public interface Histogram<in T : Any, out B : Bin<T>> {
public val dimension: Int
public val bins: Iterable<B>
public companion object {
//A discoverability root
}
}
public fun interface HistogramBuilder<in T : Any> {
public interface HistogramBuilder<in T : Any, V : Any> {
/**
* Increment appropriate bin
* The default value increment for a bin
*/
public fun putValue(point: Point<out T>, value: Number)
public val defaultValue: V
/**
* Increment appropriate bin with given value
*/
public fun putValue(point: Point<out T>, value: V = defaultValue)
}
public fun <T : Any, B : Bin<T>> HistogramBuilder<T>.put(point: Point<out T>): Unit = putValue(point, 1.0)
public fun <T : Any> HistogramBuilder<T, *>.put(point: Point<out T>): Unit = putValue(point)
public fun <T : Any> HistogramBuilder<T>.put(vararg point: T): Unit = put(point.asBuffer())
public fun <T : Any> HistogramBuilder<T, *>.put(vararg point: T): Unit = put(point.asBuffer())
public fun HistogramBuilder<Double>.put(vararg point: Number): Unit =
public fun HistogramBuilder<Double, *>.put(vararg point: Number): Unit =
put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray()))
public fun HistogramBuilder<Double>.put(vararg point: Double): Unit = put(DoubleBuffer(point))
public fun <T : Any> HistogramBuilder<T>.fill(sequence: Iterable<Point<T>>): Unit = sequence.forEach { put(it) }
public fun HistogramBuilder<Double, *>.put(vararg point: Double): Unit = put(DoubleBuffer(point))
public fun <T : Any> HistogramBuilder<T, *>.fill(sequence: Iterable<Point<T>>): Unit = sequence.forEach { put(it) }
/**
* Pass a sequence builder into histogram
*/
public fun <T : Any> HistogramBuilder<T>.fill(block: suspend SequenceScope<Point<T>>.() -> Unit): Unit =
public fun <T : Any> HistogramBuilder<T, *>.fill(block: suspend SequenceScope<Point<T>>.() -> Unit): Unit =
fill(sequence(block).asIterable())

View File

@ -0,0 +1,64 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.histogram
import space.kscience.kmath.domains.Domain1D
import space.kscience.kmath.domains.center
import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.asSequence
import space.kscience.kmath.structures.Buffer
/**
* A univariate bin based on a range
*
* @property binValue The value of histogram including weighting
*/
@UnstableKMathAPI
public data class Bin1D<T : Comparable<T>, out V>(
public val domain: Domain1D<T>,
override val binValue: V,
) : Bin<T, V>, ClosedRange<T> by domain.range {
override val dimension: Int get() = 1
override fun contains(point: Buffer<T>): Boolean = point.size == 1 && contains(point[0])
}
@OptIn(UnstableKMathAPI::class)
public interface Histogram1D<T : Comparable<T>, V> : Histogram<T, V, Bin1D<T, V>> {
override val dimension: Int get() = 1
public operator fun get(value: T): Bin1D<T, V>?
override operator fun get(point: Buffer<T>): Bin1D<T, V>? = get(point[0])
}
public interface Histogram1DBuilder<in T : Any, V : Any> : HistogramBuilder<T, V> {
/**
* Thread safe put operation
*/
public fun putValue(at: T, value: V = defaultValue)
override fun putValue(point: Point<out T>, value: V) {
require(point.size == 1) { "Only points with single value could be used in Histogram1D" }
putValue(point[0], value)
}
}
@UnstableKMathAPI
public fun Histogram1DBuilder<Double, *>.fill(items: Iterable<Double>): Unit =
items.forEach(this::putValue)
@UnstableKMathAPI
public fun Histogram1DBuilder<Double, *>.fill(array: DoubleArray): Unit =
array.forEach(this::putValue)
@UnstableKMathAPI
public fun <T : Any> Histogram1DBuilder<T, *>.fill(buffer: Buffer<T>): Unit =
buffer.asSequence().forEach(this::putValue)
@OptIn(UnstableKMathAPI::class)
public val Bin1D<Double, *>.center: Double get() = domain.center

View File

@ -0,0 +1,76 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.histogram
import space.kscience.kmath.domains.Domain
import space.kscience.kmath.linear.Point
import space.kscience.kmath.nd.DefaultStrides
import space.kscience.kmath.nd.FieldOpsND
import space.kscience.kmath.nd.Shape
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke
/**
* @param T the type of the argument space
* @param V the type of bin value
*/
public class HistogramND<T : Comparable<T>, D : Domain<T>, V : Any>(
public val group: HistogramGroupND<T, D, V>,
internal val values: StructureND<V>,
) : Histogram<T, V, DomainBin<T, D, V>> {
override fun get(point: Point<T>): DomainBin<T, D, V>? {
val index = group.getIndexOrNull(point) ?: return null
return group.produceBin(index, values[index])
}
override val dimension: Int get() = group.shape.size
override val bins: Iterable<DomainBin<T, D, V>>
get() = DefaultStrides(group.shape).asSequence().map {
group.produceBin(it, values[it])
}.asIterable()
}
/**
* A space for producing histograms with values in a NDStructure
*/
public interface HistogramGroupND<T : Comparable<T>, D : Domain<T>, V : Any> :
Group<HistogramND<T, D, V>>, ScaleOperations<HistogramND<T, D, V>> {
public val shape: Shape
public val valueAlgebraND: FieldOpsND<V, *> //= NDAlgebra.space(valueSpace, Buffer.Companion::boxing, *shape),
/**
* Resolve index of the bin including given [point]. Return null if point is outside histogram area
*/
public fun getIndexOrNull(point: Point<T>): IntArray?
/**
* Get a bin domain represented by given index
*/
public fun getDomain(index: IntArray): Domain<T>?
public fun produceBin(index: IntArray, value: V): DomainBin<T, D, V>
public fun produce(builder: HistogramBuilder<T, V>.() -> Unit): HistogramND<T, D, V>
override fun add(left: HistogramND<T, D, V>, right: HistogramND<T, D, V>): HistogramND<T, D, V> {
require(left.group == this && right.group == this) {
"A histogram belonging to a different group cannot be operated."
}
return HistogramND(this, valueAlgebraND { left.values + right.values })
}
override fun scale(a: HistogramND<T, D, V>, value: Double): HistogramND<T, D, V> {
require(a.group == this) { "A histogram belonging to a different group cannot be operated." }
return HistogramND(this, valueAlgebraND { a.values * value })
}
override val zero: HistogramND<T, D, V> get() = produce { }
}

View File

@ -1,82 +0,0 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.histogram
import space.kscience.kmath.domains.Domain
import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.DefaultStrides
import space.kscience.kmath.nd.FieldND
import space.kscience.kmath.nd.Shape
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke
/**
* A simple histogram bin based on domain
*/
public data class DomainBin<in T : Comparable<T>>(
public val domain: Domain<T>,
override val value: Number,
) : Bin<T>, Domain<T> by domain
@OptIn(UnstableKMathAPI::class)
public class IndexedHistogram<T : Comparable<T>, V : Any>(
public val context: IndexedHistogramSpace<T, V>,
public val values: StructureND<V>,
) : Histogram<T, Bin<T>> {
override fun get(point: Point<T>): Bin<T>? {
val index = context.getIndex(point) ?: return null
return context.produceBin(index, values[index])
}
override val dimension: Int get() = context.shape.size
override val bins: Iterable<Bin<T>>
get() = DefaultStrides(context.shape).asSequence().map {
context.produceBin(it, values[it])
}.asIterable()
}
/**
* A space for producing histograms with values in a NDStructure
*/
public interface IndexedHistogramSpace<T : Comparable<T>, V : Any>
: Group<IndexedHistogram<T, V>>, ScaleOperations<IndexedHistogram<T, V>> {
//public val valueSpace: Space<V>
public val shape: Shape
public val histogramValueSpace: FieldND<V, *> //= NDAlgebra.space(valueSpace, Buffer.Companion::boxing, *shape),
/**
* Resolve index of the bin including given [point]
*/
public fun getIndex(point: Point<T>): IntArray?
/**
* Get a bin domain represented by given index
*/
public fun getDomain(index: IntArray): Domain<T>?
public fun produceBin(index: IntArray, value: V): Bin<T>
public fun produce(builder: HistogramBuilder<T>.() -> Unit): IndexedHistogram<T, V>
override fun add(left: IndexedHistogram<T, V>, right: IndexedHistogram<T, V>): IndexedHistogram<T, V> {
require(left.context == this) { "Can't operate on a histogram produced by external space" }
require(right.context == this) { "Can't operate on a histogram produced by external space" }
return IndexedHistogram(this, histogramValueSpace { left.values + right.values })
}
override fun scale(a: IndexedHistogram<T, V>, value: Double): IndexedHistogram<T, V> {
require(a.context == this) { "Can't operate on a histogram produced by external space" }
return IndexedHistogram(this, histogramValueSpace { a.values * value })
}
override val zero: IndexedHistogram<T, V> get() = produce { }
}

View File

@ -0,0 +1,156 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.histogram
import space.kscience.kmath.domains.DoubleDomain1D
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import kotlin.math.floor
@OptIn(UnstableKMathAPI::class)
public class UniformHistogram1D<V : Any>(
public val group: UniformHistogram1DGroup<V, *>,
internal val values: Map<Int, V>,
) : Histogram1D<Double, V> {
private val startPoint get() = group.startPoint
private val binSize get() = group.binSize
private fun produceBin(index: Int, value: V): Bin1D<Double, V> {
val domain = DoubleDomain1D((startPoint + index * binSize)..(startPoint + (index + 1) * binSize))
return Bin1D(domain, value)
}
override val bins: Iterable<Bin1D<Double, V>> get() = values.map { produceBin(it.key, it.value) }
override fun get(value: Double): Bin1D<Double, V>? {
val index: Int = group.getIndex(value)
val v = values[index]
return v?.let { produceBin(index, it) }
}
}
/**
* An algebra for uniform histograms in 1D real space
*/
public class UniformHistogram1DGroup<V : Any, A>(
public val valueAlgebra: A,
public val binSize: Double,
public val startPoint: Double = 0.0,
) : Group<Histogram1D<Double, V>>, ScaleOperations<Histogram1D<Double, V>> where A : Ring<V>, A : ScaleOperations<V> {
override val zero: UniformHistogram1D<V> = UniformHistogram1D(this, emptyMap())
/**
* Get index of a bin
*/
@PublishedApi
internal fun getIndex(at: Double): Int = floor((at - startPoint) / binSize).toInt()
override fun add(
left: Histogram1D<Double, V>,
right: Histogram1D<Double, V>,
): UniformHistogram1D<V> = valueAlgebra {
val leftUniform = produceFrom(left)
val rightUniform = produceFrom(right)
val keys = leftUniform.values.keys + rightUniform.values.keys
UniformHistogram1D(
this@UniformHistogram1DGroup,
keys.associateWith {
(leftUniform.values[it] ?: valueAlgebra.zero) + (rightUniform.values[it] ?: valueAlgebra.zero)
}
)
}
override fun Histogram1D<Double, V>.unaryMinus(): UniformHistogram1D<V> = valueAlgebra {
UniformHistogram1D(this@UniformHistogram1DGroup, produceFrom(this@unaryMinus).values.mapValues { -it.value })
}
override fun scale(
a: Histogram1D<Double, V>,
value: Double,
): UniformHistogram1D<V> = UniformHistogram1D(
this@UniformHistogram1DGroup,
produceFrom(a).values.mapValues { valueAlgebra.scale(it.value, value) }
)
/**
* Fill histogram.
*/
public inline fun produce(block: Histogram1DBuilder<Double, V>.() -> Unit): UniformHistogram1D<V> {
val map = HashMap<Int, V>()
val builder = object : Histogram1DBuilder<Double, V> {
override val defaultValue: V get() = valueAlgebra.zero
override fun putValue(at: Double, value: V) {
val index = getIndex(at)
map[index] = with(valueAlgebra) { (map[index] ?: zero) + one }
}
}
builder.block()
return UniformHistogram1D(this, map)
}
/**
* Re-bin given histogram to be compatible if exiting bin fully falls inside existing bin, this bin value
* is increased by one. If not, all bins including values from this bin are increased by fraction
* (conserving the norming).
*/
@OptIn(UnstableKMathAPI::class)
public fun produceFrom(
histogram: Histogram1D<Double, V>,
): UniformHistogram1D<V> = if ((histogram as? UniformHistogram1D)?.group == this) {
histogram
} else {
val map = HashMap<Int, V>()
histogram.bins.forEach { bin ->
val range = bin.domain.range
val indexOfLeft = getIndex(range.start)
val indexOfRight = getIndex(range.endInclusive)
val numBins = indexOfRight - indexOfLeft + 1
for (i in indexOfLeft..indexOfRight) {
map[indexOfLeft] = with(valueAlgebra) {
(map[indexOfLeft] ?: zero) + bin.binValue / numBins
}
}
}
UniformHistogram1D(this, map)
}
}
public fun <V : Any, A> Histogram.Companion.uniform1D(
valueAlgebra: A,
binSize: Double,
startPoint: Double = 0.0,
): UniformHistogram1DGroup<V, A> where A : Ring<V>, A : ScaleOperations<V> =
UniformHistogram1DGroup(valueAlgebra, binSize, startPoint)
@UnstableKMathAPI
public fun <V : Any> UniformHistogram1DGroup<V, *>.produce(
buffer: Buffer<Double>,
): UniformHistogram1D<V> = produce { fill(buffer) }
/**
* Map of bin centers to bin values
*/
@OptIn(UnstableKMathAPI::class)
public val <V : Any> UniformHistogram1D<V>.binValues: Map<Double, V>
get() = bins.associate { it.center to it.binValue }
//TODO add normalized values inside Field-based histogram spaces with context receivers
///**
// * Map of bin centers to normalized bin values (bin size as normalization)
// */
//@OptIn(UnstableKMathAPI::class)
//public val <V : Any> UniformHistogram1D<V>.binValuesNormalized: Map<Double, V>
// get() = group.valueAlgebra {
// bins.associate { it.center to it.binValue / group.binSize }
// }

View File

@ -0,0 +1,163 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(UnstableKMathAPI::class)
package space.kscience.kmath.histogram
import space.kscience.kmath.domains.HyperSquareDomain
import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Field
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.*
import kotlin.math.floor
public typealias HyperSquareBin<V> = DomainBin<Double, HyperSquareDomain, V>
/**
* Multivariate histogram space for hyper-square real-field bins.
* @param bufferFactory is an optional parameter used to optimize buffer production.
*/
public class UniformHistogramGroupND<V : Any, A : Field<V>>(
override val valueAlgebraND: FieldOpsND<V, A>,
private val lower: Buffer<Double>,
private val upper: Buffer<Double>,
private val binNums: IntArray = IntArray(lower.size) { 20 },
private val bufferFactory: BufferFactory<V> = Buffer.Companion::boxing,
) : HistogramGroupND<Double, HyperSquareDomain, V> {
init {
// argument checks
require(lower.size == upper.size) { "Dimension mismatch in histogram lower and upper limits." }
require(lower.size == binNums.size) { "Dimension mismatch in bin count." }
require(!lower.indices.any { upper[it] - lower[it] < 0 }) { "Range for one of axis is not strictly positive" }
}
public val dimension: Int get() = lower.size
override val shape: IntArray = IntArray(binNums.size) { binNums[it] + 2 }
private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] }
/**
* Get internal [StructureND] bin index for given axis
*/
private fun getIndex(axis: Int, value: Double): Int = when {
value >= upper[axis] -> binNums[axis] + 1 // overflow
value < lower[axis] -> 0 // underflow
else -> floor((value - lower[axis]) / binSize[axis]).toInt()
}
override fun getIndexOrNull(point: Buffer<Double>): IntArray = IntArray(dimension) {
getIndex(it, point[it])
}
override fun getDomain(index: IntArray): HyperSquareDomain {
val lowerBoundary = index.mapIndexed { axis, i ->
when (i) {
0 -> Double.NEGATIVE_INFINITY
shape[axis] - 1 -> upper[axis]
else -> lower[axis] + (i.toDouble()) * binSize[axis]
}
}.asBuffer()
val upperBoundary = index.mapIndexed { axis, i ->
when (i) {
0 -> lower[axis]
shape[axis] - 1 -> Double.POSITIVE_INFINITY
else -> lower[axis] + (i.toDouble() + 1) * binSize[axis]
}
}.asBuffer()
return HyperSquareDomain(lowerBoundary, upperBoundary)
}
override fun produceBin(index: IntArray, value: V): HyperSquareBin<V> {
val domain = getDomain(index)
return DomainBin(domain, value)
}
override fun produce(builder: HistogramBuilder<Double, V>.() -> Unit): HistogramND<Double, HyperSquareDomain, V> {
val ndCounter = StructureND.buffered(shape) { Counter.of(valueAlgebraND.elementAlgebra) }
val hBuilder = object : HistogramBuilder<Double, V> {
override val defaultValue: V get() = valueAlgebraND.elementAlgebra.one
override fun putValue(point: Point<out Double>, value: V) = with(valueAlgebraND.elementAlgebra) {
val index = getIndexOrNull(point)
ndCounter[index].add(value)
}
}
hBuilder.apply(builder)
val values: BufferND<V> = ndCounter.mapToBuffer(bufferFactory) { it.value }
return HistogramND(this, values)
}
override fun HistogramND<Double, HyperSquareDomain, V>.unaryMinus(): HistogramND<Double, HyperSquareDomain, V> =
this * (-1)
}
/**
* Use it like
* ```
*FastHistogram.fromRanges(
* (-1.0..1.0),
* (-1.0..1.0)
*)
*```
*/
public fun <V : Any, A : Field<V>> Histogram.Companion.uniformNDFromRanges(
valueAlgebraND: FieldOpsND<V, A>,
vararg ranges: ClosedFloatingPointRange<Double>,
bufferFactory: BufferFactory<V> = Buffer.Companion::boxing,
): UniformHistogramGroupND<V, A> = UniformHistogramGroupND(
valueAlgebraND,
ranges.map(ClosedFloatingPointRange<Double>::start).asBuffer(),
ranges.map(ClosedFloatingPointRange<Double>::endInclusive).asBuffer(),
bufferFactory = bufferFactory
)
public fun Histogram.Companion.uniformDoubleNDFromRanges(
vararg ranges: ClosedFloatingPointRange<Double>,
): UniformHistogramGroupND<Double, DoubleField> =
uniformNDFromRanges(DoubleFieldOpsND, *ranges, bufferFactory = ::DoubleBuffer)
/**
* Use it like
* ```
*FastHistogram.fromRanges(
* (-1.0..1.0) to 50,
* (-1.0..1.0) to 32
*)
*```
*/
public fun <V : Any, A : Field<V>> Histogram.Companion.uniformNDFromRanges(
valueAlgebraND: FieldOpsND<V, A>,
vararg ranges: Pair<ClosedFloatingPointRange<Double>, Int>,
bufferFactory: BufferFactory<V> = Buffer.Companion::boxing,
): UniformHistogramGroupND<V, A> = UniformHistogramGroupND(
valueAlgebraND,
ListBuffer(
ranges
.map(Pair<ClosedFloatingPointRange<Double>, Int>::first)
.map(ClosedFloatingPointRange<Double>::start)
),
ListBuffer(
ranges
.map(Pair<ClosedFloatingPointRange<Double>, Int>::first)
.map(ClosedFloatingPointRange<Double>::endInclusive)
),
ranges.map(Pair<ClosedFloatingPointRange<Double>, Int>::second).toIntArray(),
bufferFactory = bufferFactory
)
public fun Histogram.Companion.uniformDoubleNDFromRanges(
vararg ranges: Pair<ClosedFloatingPointRange<Double>, Int>,
): UniformHistogramGroupND<Double, DoubleField> =
uniformNDFromRanges(DoubleFieldOpsND, *ranges, bufferFactory = ::DoubleBuffer)

View File

@ -3,8 +3,11 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(UnstableKMathAPI::class)
package space.kscience.kmath.histogram
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.DefaultStrides
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.real.DoubleVector
@ -14,14 +17,14 @@ import kotlin.test.*
internal class MultivariateHistogramTest {
@Test
fun testSinglePutHistogram() {
val hSpace = DoubleHistogramSpace.fromRanges(
val hSpace = Histogram.uniformDoubleNDFromRanges(
(-1.0..1.0),
(-1.0..1.0)
)
val histogram = hSpace.produce {
put(0.55, 0.55)
}
val bin = histogram.bins.find { it.value.toInt() > 0 } ?: fail()
val bin = histogram.bins.find { it.binValue.toInt() > 0 } ?: fail()
assertTrue { bin.contains(DoubleVector(0.55, 0.55)) }
assertTrue { bin.contains(DoubleVector(0.6, 0.5)) }
assertFalse { bin.contains(DoubleVector(-0.55, 0.55)) }
@ -29,7 +32,7 @@ internal class MultivariateHistogramTest {
@Test
fun testSequentialPut() {
val hSpace = DoubleHistogramSpace.fromRanges(
val hSpace = Histogram.uniformDoubleNDFromRanges(
(-1.0..1.0),
(-1.0..1.0),
(-1.0..1.0)
@ -44,12 +47,12 @@ internal class MultivariateHistogramTest {
put(nextDouble(), nextDouble(), nextDouble())
}
}
assertEquals(n, histogram.bins.sumOf { it.value.toInt() })
assertEquals(n, histogram.bins.sumOf { it.binValue.toInt() })
}
@Test
fun testHistogramAlgebra() {
DoubleHistogramSpace.fromRanges(
Histogram.uniformDoubleNDFromRanges(
(-1.0..1.0),
(-1.0..1.0),
(-1.0..1.0)
@ -77,7 +80,7 @@ internal class MultivariateHistogramTest {
assertTrue {
res.bins.count() >= histogram1.bins.count()
}
assertEquals(0.0, res.bins.sumOf { it.value.toDouble() })
assertEquals(0.0, res.bins.sumOf { it.binValue.toDouble() })
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.histogram
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.stat.nextBuffer
import kotlin.native.concurrent.ThreadLocal
import kotlin.test.Test
import kotlin.test.assertEquals
@OptIn(ExperimentalCoroutinesApi::class, UnstableKMathAPI::class)
internal class UniformHistogram1DTest {
@Test
fun normal() = runTest {
val distribution = NormalDistribution(0.0, 1.0)
with(Histogram.uniform1D(DoubleField, 0.1)) {
val h1 = produce(distribution.nextBuffer(generator, 10000))
val h2 = produce(distribution.nextBuffer(generator, 50000))
val h3 = h1 + h2
assertEquals(60000, h3.bins.sumOf { it.binValue }.toInt())
}
}
@Test
fun rebinDown() = runTest {
val h1 = Histogram.uniform1D(DoubleField, 0.01).produce(generator.nextDoubleBuffer(10000))
val h2 = Histogram.uniform1D(DoubleField,0.03).produceFrom(h1)
assertEquals(10000, h2.bins.sumOf { it.binValue }.toInt())
}
@Test
fun rebinUp() = runTest {
val h1 = Histogram.uniform1D(DoubleField, 0.03).produce(generator.nextDoubleBuffer(10000))
val h2 = Histogram.uniform1D(DoubleField,0.01).produceFrom(h1)
assertEquals(10000, h2.bins.sumOf { it.binValue }.toInt())
}
@ThreadLocal
companion object{
private val generator = RandomGenerator.default(123)
}
}

View File

@ -0,0 +1,179 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(UnstableKMathAPI::class)
package space.kscience.kmath.histogram
import space.kscience.kmath.domains.DoubleDomain1D
import space.kscience.kmath.domains.center
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.misc.sorted
import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.first
import space.kscience.kmath.structures.indices
import space.kscience.kmath.structures.last
import java.util.*
private fun <B : ClosedRange<Double>> TreeMap<Double, B>.getBin(value: Double): B? {
// check ceiling entry and return it if it is what needed
val ceil = ceilingEntry(value)?.value
if (ceil != null && value in ceil) return ceil
//check floor entry
val floor = floorEntry(value)?.value
if (floor != null && value in floor) return floor
//neither is valid, not found
return null
}
//public data class ValueAndError(val value: Double, val error: Double)
//
//public typealias WeightedBin1D = Bin1D<Double, ValueAndError>
/**
* A histogram based on a tree map of values
*/
public class TreeHistogram<V : Any>(
private val binMap: TreeMap<Double, Bin1D<Double, V>>,
) : Histogram1D<Double, V> {
override fun get(value: Double): Bin1D<Double, V>? = binMap.getBin(value)
override val bins: Collection<Bin1D<Double, V>> get() = binMap.values
}
/**
* A space for univariate histograms with variable bin borders based on a tree map
*/
public class TreeHistogramGroup<V : Any, A>(
public val valueAlgebra: A,
@PublishedApi internal val binFactory: (Double) -> DoubleDomain1D,
) : Group<TreeHistogram<V>>, ScaleOperations<TreeHistogram<V>> where A : Ring<V>, A : ScaleOperations<V> {
internal inner class DomainCounter(val domain: DoubleDomain1D, val counter: Counter<V> = Counter.of(valueAlgebra)) :
ClosedRange<Double> by domain.range
@PublishedApi
internal inner class TreeHistogramBuilder : Histogram1DBuilder<Double, V> {
override val defaultValue: V get() = valueAlgebra.one
private val bins: TreeMap<Double, DomainCounter> = TreeMap()
private fun createBin(value: Double): DomainCounter {
val binDefinition: DoubleDomain1D = binFactory(value)
val newBin = DomainCounter(binDefinition)
synchronized(this) {
bins[binDefinition.center] = newBin
}
return newBin
}
/**
* Thread safe put operation
*/
override fun putValue(at: Double, value: V) {
(bins.getBin(at) ?: createBin(at)).counter.add(value)
}
fun build(): TreeHistogram<V> {
val map = bins.mapValuesTo(TreeMap<Double, Bin1D<Double, V>>()) { (_, binCounter) ->
Bin1D(binCounter.domain, binCounter.counter.value)
}
return TreeHistogram(map)
}
}
public inline fun produce(block: Histogram1DBuilder<Double, V>.() -> Unit): TreeHistogram<V> =
TreeHistogramBuilder().apply(block).build()
override fun add(
left: TreeHistogram<V>,
right: TreeHistogram<V>,
): TreeHistogram<V> {
val bins = TreeMap<Double, Bin1D<Double, V>>().apply {
(left.bins.map { it.domain } union right.bins.map { it.domain }).forEach { def ->
put(
def.center,
Bin1D(
def,
with(valueAlgebra) {
(left[def.center]?.binValue ?: zero) + (right[def.center]?.binValue ?: zero)
}
)
)
}
}
return TreeHistogram(bins)
}
override fun scale(a: TreeHistogram<V>, value: Double): TreeHistogram<V> {
val bins = TreeMap<Double, Bin1D<Double, V>>().apply {
a.bins.forEach { bin ->
put(
bin.domain.center,
Bin1D(bin.domain, valueAlgebra.scale(bin.binValue, value))
)
}
}
return TreeHistogram(bins)
}
override fun TreeHistogram<V>.unaryMinus(): TreeHistogram<V> = this * (-1)
override val zero: TreeHistogram<V> = produce { }
}
///**
// * Build and fill a histogram with custom borders. Returns a read-only histogram.
// */
//public inline fun Histogram.custom(
// borders: DoubleArray,
// builder: Histogram1DBuilder<Double, Double>.() -> Unit,
//): TreeHistogram = custom(borders).fill(builder)
//
//
///**
// * Build and fill a [DoubleHistogram1D]. Returns a read-only histogram.
// */
//public fun uniform(
// binSize: Double,
// start: Double = 0.0,
//): TreeHistogramSpace = TreeHistogramSpace { value ->
// val center = start + binSize * floor((value - start) / binSize + 0.5)
// DoubleDomain1D((center - binSize / 2)..(center + binSize / 2))
//}
/**
* Create a histogram group with custom cell borders
*/
public fun <V : Any, A> Histogram.Companion.custom1D(
valueAlgebra: A,
borders: Buffer<Double>,
): TreeHistogramGroup<V, A> where A : Ring<V>, A : ScaleOperations<V> {
val sorted = borders.sorted()
return TreeHistogramGroup(valueAlgebra) { value ->
when {
value <= sorted.first() -> DoubleDomain1D(
Double.NEGATIVE_INFINITY..sorted.first()
)
value > sorted.last() -> DoubleDomain1D(
sorted.last()..Double.POSITIVE_INFINITY
)
else -> {
val index = sorted.indices.first { value <= sorted[it] }
val left = sorted[index - 1]
val right = sorted[index]
DoubleDomain1D(left..right)
}
}
}
}

View File

@ -1,171 +0,0 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.histogram
import space.kscience.kmath.domains.UnivariateDomain
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.structures.Buffer
import java.util.*
import kotlin.math.abs
import kotlin.math.floor
import kotlin.math.sqrt
private fun <B : ClosedFloatingPointRange<Double>> TreeMap<Double, B>.getBin(value: Double): B? {
// check ceiling entry and return it if it is what needed
val ceil = ceilingEntry(value)?.value
if (ceil != null && value in ceil) return ceil
//check floor entry
val floor = floorEntry(value)?.value
if (floor != null && value in floor) return floor
//neither is valid, not found
return null
}
@UnstableKMathAPI
public class TreeHistogram(
private val binMap: TreeMap<Double, out UnivariateBin>,
) : UnivariateHistogram {
override fun get(value: Double): UnivariateBin? = binMap.getBin(value)
override val dimension: Int get() = 1
override val bins: Collection<UnivariateBin> get() = binMap.values
}
@OptIn(UnstableKMathAPI::class)
@PublishedApi
internal class TreeHistogramBuilder(val binFactory: (Double) -> UnivariateDomain) : UnivariateHistogramBuilder {
internal class BinCounter(val domain: UnivariateDomain, val counter: Counter<Double> = Counter.double()) :
ClosedFloatingPointRange<Double> by domain.range
private val bins: TreeMap<Double, BinCounter> = TreeMap()
fun get(value: Double): BinCounter? = bins.getBin(value)
fun createBin(value: Double): BinCounter {
val binDefinition = binFactory(value)
val newBin = BinCounter(binDefinition)
synchronized(this) { bins[binDefinition.center] = newBin }
return newBin
}
/**
* Thread safe put operation
*/
override fun putValue(at: Double, value: Double) {
(get(at) ?: createBin(at)).apply {
counter.add(value)
}
}
override fun putValue(point: Buffer<Double>, value: Number) {
require(point.size == 1) { "Only points with single value could be used in univariate histogram" }
putValue(point[0], value.toDouble())
}
fun build(): TreeHistogram {
val map = bins.mapValuesTo(TreeMap<Double, UnivariateBin>()) { (_, binCounter) ->
val count = binCounter.counter.value
UnivariateBin(binCounter.domain, count, sqrt(count))
}
return TreeHistogram(map)
}
}
/**
* A space for univariate histograms with variable bin borders based on a tree map
*/
@UnstableKMathAPI
public class TreeHistogramSpace(
@PublishedApi internal val binFactory: (Double) -> UnivariateDomain,
) : Group<UnivariateHistogram>, ScaleOperations<UnivariateHistogram> {
public inline fun fill(block: UnivariateHistogramBuilder.() -> Unit): UnivariateHistogram =
TreeHistogramBuilder(binFactory).apply(block).build()
override fun add(
left: UnivariateHistogram,
right: UnivariateHistogram,
): UnivariateHistogram {
// require(a.context == this) { "Histogram $a does not belong to this context" }
// require(b.context == this) { "Histogram $b does not belong to this context" }
val bins = TreeMap<Double, UnivariateBin>().apply {
(left.bins.map { it.domain } union right.bins.map { it.domain }).forEach { def ->
put(
def.center,
UnivariateBin(
def,
value = (left[def.center]?.value ?: 0.0) + (right[def.center]?.value ?: 0.0),
standardDeviation = (left[def.center]?.standardDeviation
?: 0.0) + (right[def.center]?.standardDeviation ?: 0.0)
)
)
}
}
return TreeHistogram(bins)
}
override fun scale(a: UnivariateHistogram, value: Double): UnivariateHistogram {
val bins = TreeMap<Double, UnivariateBin>().apply {
a.bins.forEach { bin ->
put(
bin.domain.center,
UnivariateBin(
bin.domain,
value = bin.value * value,
standardDeviation = abs(bin.standardDeviation * value)
)
)
}
}
return TreeHistogram(bins)
}
override fun UnivariateHistogram.unaryMinus(): UnivariateHistogram = this * (-1)
override val zero: UnivariateHistogram by lazy { fill { } }
public companion object {
/**
* Build and fill a [UnivariateHistogram]. Returns a read-only histogram.
*/
public fun uniform(
binSize: Double,
start: Double = 0.0,
): TreeHistogramSpace = TreeHistogramSpace { value ->
val center = start + binSize * floor((value - start) / binSize + 0.5)
UnivariateDomain((center - binSize / 2)..(center + binSize / 2))
}
/**
* Create a histogram with custom cell borders
*/
public fun custom(borders: DoubleArray): TreeHistogramSpace {
val sorted = borders.sortedArray()
return TreeHistogramSpace { value ->
when {
value < sorted.first() -> UnivariateDomain(
Double.NEGATIVE_INFINITY..sorted.first()
)
value > sorted.last() -> UnivariateDomain(
sorted.last()..Double.POSITIVE_INFINITY
)
else -> {
val index = sorted.indices.first { value > sorted[it] }
val left = sorted[index]
val right = sorted[index + 1]
UnivariateDomain(left..right)
}
}
}
}
}
}

View File

@ -1,79 +0,0 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.histogram
import space.kscience.kmath.domains.UnivariateDomain
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.asSequence
import space.kscience.kmath.structures.Buffer
@UnstableKMathAPI
public val UnivariateDomain.center: Double
get() = (range.endInclusive + range.start) / 2
/**
* A univariate bin based on a range
*
* @property value The value of histogram including weighting
* @property standardDeviation Standard deviation of the bin value. Zero or negative if not applicable
*/
@UnstableKMathAPI
public class UnivariateBin(
public val domain: UnivariateDomain,
override val value: Double,
public val standardDeviation: Double,
) : Bin<Double>, ClosedFloatingPointRange<Double> by domain.range {
override val dimension: Int get() = 1
override fun contains(point: Buffer<Double>): Boolean = point.size == 1 && contains(point[0])
}
@OptIn(UnstableKMathAPI::class)
public interface UnivariateHistogram : Histogram<Double, UnivariateBin> {
public operator fun get(value: Double): UnivariateBin?
override operator fun get(point: Buffer<Double>): UnivariateBin? = get(point[0])
public companion object {
/**
* Build and fill a [UnivariateHistogram]. Returns a read-only histogram.
*/
public inline fun uniform(
binSize: Double,
start: Double = 0.0,
builder: UnivariateHistogramBuilder.() -> Unit,
): UnivariateHistogram = TreeHistogramSpace.uniform(binSize, start).fill(builder)
/**
* Build and fill a histogram with custom borders. Returns a read-only histogram.
*/
public inline fun custom(
borders: DoubleArray,
builder: UnivariateHistogramBuilder.() -> Unit,
): UnivariateHistogram = TreeHistogramSpace.custom(borders).fill(builder)
}
}
@UnstableKMathAPI
public interface UnivariateHistogramBuilder : HistogramBuilder<Double> {
/**
* Thread safe put operation
*/
public fun putValue(at: Double, value: Double = 1.0)
override fun putValue(point: Buffer<Double>, value: Number)
}
@UnstableKMathAPI
public fun UnivariateHistogramBuilder.fill(items: Iterable<Double>): Unit = items.forEach(this::putValue)
@UnstableKMathAPI
public fun UnivariateHistogramBuilder.fill(array: DoubleArray): Unit = array.forEach(this::putValue)
@UnstableKMathAPI
public fun UnivariateHistogramBuilder.fill(buffer: Buffer<Double>): Unit = buffer.asSequence().forEach(this::putValue)

View File

@ -6,19 +6,24 @@
package space.kscience.kmath.histogram
import org.junit.jupiter.api.Test
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.real.step
import kotlin.random.Random
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class TreeHistogramTest {
@Test
fun normalFill() {
val histogram = UnivariateHistogram.uniform(0.1) {
val random = Random(123)
val histogram = Histogram.custom1D(DoubleField, 0.0..1.0 step 0.1).produce {
repeat(100_000) {
putValue(Random.nextDouble())
putValue(random.nextDouble())
}
}
assertTrue { histogram.bins.count() > 10 }
assertTrue { histogram.bins.count() > 8}
assertEquals(100_000, histogram.bins.sumOf { it.binValue }.toInt())
}
}

View File

@ -7,17 +7,17 @@ Integration with [Jafama](https://github.com/jeffhain/jafama).
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-jafama:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-jafama:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-jafama:0.3.0-dev-17'
implementation 'space.kscience:kmath-jafama:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -28,7 +28,7 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-jafama:0.3.0-dev-17")
implementation("space.kscience:kmath-jafama:0.3.0")
}
```

32
kmath-jupyter/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module kmath-jupyter
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-jupyter:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-jupyter:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-jupyter:0.3.0")
}
```

View File

@ -0,0 +1,4 @@
public final class space/kscience/kmath/jupyter/KMathJupyterKt {
public static final fun toMst (Ljava/lang/Number;)Lspace/kscience/kmath/expressions/MST$Numeric;
}

View File

@ -8,17 +8,17 @@
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-kotlingrad:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-kotlingrad:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-kotlingrad:0.3.0-dev-17'
implementation 'space.kscience:kmath-kotlingrad:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -29,6 +29,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-kotlingrad:0.3.0-dev-17")
implementation("space.kscience:kmath-kotlingrad:0.3.0")
}
```

32
kmath-memory/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module kmath-memory
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-memory:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-memory:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-memory:0.3.0")
}
```

32
kmath-multik/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module kmath-multik
JetBrains Multik connector
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-multik:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-multik:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-multik:0.3.0")
}
```

View File

@ -6,13 +6,38 @@
package space.kscience.kmath.multik
import org.junit.jupiter.api.Test
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.one
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.tensorAlgebra
import kotlin.test.assertTrue
internal class MultikNDTest {
@Test
fun basicAlgebra(): Unit = DoubleField.multikAlgebra{
one(2,2) + 1.0
}
@Test
fun dotResult(){
val dim = 100
val tensor1 = DoubleTensorAlgebra.randomNormal(shape = intArrayOf(dim, dim), 12224)
val tensor2 = DoubleTensorAlgebra.randomNormal(shape = intArrayOf(dim, dim), 12225)
val multikResult = with(DoubleField.multikAlgebra){
tensor1 dot tensor2
}
val defaultResult = with(DoubleField.tensorAlgebra){
tensor1 dot tensor2
}
assertTrue {
StructureND.contentEquals(multikResult, defaultResult)
}
}
}

View File

@ -9,17 +9,17 @@ ND4J based implementations of KMath abstractions.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-nd4j:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-nd4j:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-nd4j:0.3.0-dev-17'
implementation 'space.kscience:kmath-nd4j:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -30,7 +30,7 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-nd4j:0.3.0-dev-17")
implementation("space.kscience:kmath-nd4j:0.3.0")
}
```

View File

@ -0,0 +1,32 @@
# Module kmath-optimization
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-optimization:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-optimization:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-optimization:0.3.0")
}
```

32
kmath-stat/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module kmath-stat
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-stat:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-stat:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-stat:0.3.0")
}
```

View File

@ -27,7 +27,7 @@ public interface Distribution<T : Any> : Sampler<T> {
public companion object
}
public interface UnivariateDistribution<T : Comparable<T>> : Distribution<T> {
public interface Distribution1D<T : Comparable<T>> : Distribution<T> {
/**
* Cumulative distribution for ordered parameter (CDF)
*/
@ -37,7 +37,7 @@ public interface UnivariateDistribution<T : Comparable<T>> : Distribution<T> {
/**
* Compute probability integral in an interval
*/
public fun <T : Comparable<T>> UnivariateDistribution<T>.integral(from: T, to: T): Double {
public fun <T : Comparable<T>> Distribution1D<T>.integral(from: T, to: T): Double {
require(to > from)
return cumulative(to) - cumulative(from)
}

View File

@ -14,14 +14,9 @@ import space.kscience.kmath.stat.RandomGenerator
import kotlin.math.*
/**
* Implements [UnivariateDistribution] for the normal (gaussian) distribution.
* Implements [Distribution1D] for the normal (gaussian) distribution.
*/
public class NormalDistribution(public val sampler: GaussianSampler) : UnivariateDistribution<Double> {
public constructor(
mean: Double,
standardDeviation: Double,
normalized: NormalizedGaussianSampler = ZigguratNormalizedGaussianSampler,
) : this(GaussianSampler(mean, standardDeviation, normalized))
public class NormalDistribution(public val sampler: GaussianSampler) : Distribution1D<Double> {
override fun probability(arg: Double): Double {
val x1 = (arg - sampler.mean) / sampler.standardDeviation
@ -43,3 +38,9 @@ public class NormalDistribution(public val sampler: GaussianSampler) : Univariat
private val SQRT2 = sqrt(2.0)
}
}
public fun NormalDistribution(
mean: Double,
standardDeviation: Double,
normalized: NormalizedGaussianSampler = ZigguratNormalizedGaussianSampler,
): NormalDistribution = NormalDistribution(GaussianSampler(mean, standardDeviation, normalized))

View File

@ -67,3 +67,12 @@ public fun Sampler<Double>.sampleBuffer(generator: RandomGenerator, size: Int):
@JvmName("sampleIntBuffer")
public fun Sampler<Int>.sampleBuffer(generator: RandomGenerator, size: Int): Chain<Buffer<Int>> =
sampleBuffer(generator, size, ::IntBuffer)
/**
* Samples a [Buffer] of values from this [Sampler].
*/
public suspend fun Sampler<Double>.nextBuffer(generator: RandomGenerator, size: Int): Buffer<Double> =
sampleBuffer(generator, size).first()
//TODO add `context(RandomGenerator) Sampler.nextBuffer

View File

@ -8,9 +8,9 @@ package space.kscience.kmath.stat
import space.kscience.kmath.chains.Chain
import space.kscience.kmath.chains.SimpleChain
import space.kscience.kmath.distributions.Distribution
import space.kscience.kmath.distributions.UnivariateDistribution
import space.kscience.kmath.distributions.Distribution1D
public class UniformDistribution(public val range: ClosedFloatingPointRange<Double>) : UnivariateDistribution<Double> {
public class UniformDistribution(public val range: ClosedFloatingPointRange<Double>) : Distribution1D<Double> {
private val length: Double = range.endInclusive - range.start
override fun probability(arg: Double): Double = if (arg in range) 1.0 / length else 0.0

View File

@ -0,0 +1,56 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.stat
import space.kscience.kmath.operations.Field
import kotlin.math.pow
import kotlin.math.sqrt
/**
* A combination of a random [value] and its [dispersion].
*
* [dispersion] must be positive.
*/
public data class ValueAndError(val value: Double, val dispersion: Double) {
init {
require(dispersion >= 0) { "Dispersion must be non-negative" }
}
val error: Double get() = sqrt(dispersion)
}
/**
* An algebra for double value + its error combination. The multiplication assumes linear error propagation
*/
public object ValueAndErrorField : Field<ValueAndError> {
override val zero: ValueAndError = ValueAndError(0.0, 0.0)
override val one: ValueAndError = ValueAndError(1.0, 0.0)
override fun add(left: ValueAndError, right: ValueAndError): ValueAndError =
ValueAndError(left.value + right.value, left.dispersion + right.dispersion)
override fun ValueAndError.unaryMinus(): ValueAndError =
ValueAndError(-value, dispersion)
//TODO study performance impact of pow(2). On JVM it does not exist: https://stackoverflow.com/questions/29144275/xx-vs-math-powx-2-java-performance
override fun multiply(left: ValueAndError, right: ValueAndError): ValueAndError {
val value = left.value * right.value
val dispersion = (left.dispersion / left.value.pow(2) + right.dispersion / right.value.pow(2)) * value.pow(2)
return ValueAndError(value, dispersion)
}
override fun divide(left: ValueAndError, right: ValueAndError): ValueAndError {
val value = left.value / right.value
val dispersion = (left.dispersion / left.value.pow(2) + right.dispersion / right.value.pow(2)) * value.pow(2)
return ValueAndError(value, dispersion)
}
override fun scale(a: ValueAndError, value: Double): ValueAndError =
ValueAndError(a.value * value, a.dispersion * value.pow(2))
}

32
kmath-symja/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module kmath-symja
Symja integration module
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-symja:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-symja:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-symja:0.3.0")
}
```

View File

@ -0,0 +1,32 @@
# Module kmath-tensorflow
Google tensorflow connector
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-tensorflow:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-tensorflow:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-tensorflow:0.3.0")
}
```

View File

@ -6,11 +6,9 @@ import org.tensorflow.Operand
import org.tensorflow.Output
import org.tensorflow.Session
import org.tensorflow.ndarray.NdArray
import org.tensorflow.ndarray.index.Indices
import org.tensorflow.op.Ops
import org.tensorflow.op.core.Constant
import org.tensorflow.op.core.Max
import org.tensorflow.op.core.Min
import org.tensorflow.op.core.Sum
import org.tensorflow.op.core.*
import org.tensorflow.types.TInt32
import org.tensorflow.types.family.TNumber
import org.tensorflow.types.family.TType
@ -182,7 +180,7 @@ public abstract class TensorFlowAlgebra<T, TT : TNumber, A : Ring<T>> internal c
override fun StructureND<T>.unaryMinus(): TensorFlowOutput<T, TT> = operate(ops.math::neg)
override fun Tensor<T>.get(i: Int): Tensor<T> = operate {
TODO("Not yet implemented")
StridedSliceHelper.stridedSlice(ops.scope(), it, Indices.at(i.toLong()))
}
override fun Tensor<T>.transpose(i: Int, j: Int): Tensor<T> = operate {
@ -210,7 +208,13 @@ public abstract class TensorFlowAlgebra<T, TT : TNumber, A : Ring<T>> internal c
dim1: Int,
dim2: Int,
): TensorFlowOutput<T, TT> = diagonalEntries.operate {
TODO("Not yet implemented")
ops.linalg.matrixDiagV3(
/* diagonal = */ it,
/* k = */ ops.constant(offset),
/* numRows = */ ops.constant(dim1),
/* numCols = */ ops.constant(dim2),
/* paddingValue = */ const(elementAlgebra.zero)
)
}
override fun StructureND<T>.sum(): T = operate {

View File

@ -6,7 +6,6 @@ import space.kscience.kmath.nd.structureND
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.sum
import kotlin.random.Random
import kotlin.test.assertEquals
class DoubleTensorFlowOps {
@ -23,7 +22,6 @@ class DoubleTensorFlowOps {
@Test
fun dot(){
val random = Random(12224)
val dim = 1000
val tensor1 = DoubleTensorAlgebra.randomNormal(shape = intArrayOf(dim, dim), 12224)

View File

@ -9,17 +9,17 @@ Common linear algebra operations on tensors.
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-tensors:0.3.0-dev-17`.
The Maven coordinates of this project are `space.kscience:kmath-tensors:0.3.0`.
**Gradle:**
```gradle
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-tensors:0.3.0-dev-17'
implementation 'space.kscience:kmath-tensors:0.3.0'
}
```
**Gradle Kotlin DSL:**
@ -30,6 +30,6 @@ repositories {
}
dependencies {
implementation("space.kscience:kmath-tensors:0.3.0-dev-17")
implementation("space.kscience:kmath-tensors:0.3.0")
}
```

View File

@ -27,7 +27,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
val newThis = broadcast[0]
val newOther = broadcast[1]
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
newThis.mutableBuffer.array()[i] + newOther.mutableBuffer.array()[i]
newThis.mutableBuffer[i] + newOther.mutableBuffer[i]
}
return DoubleTensor(newThis.shape, resBuffer)
}
@ -35,8 +35,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
override fun Tensor<Double>.plusAssign(arg: StructureND<Double>) {
val newOther = broadcastTo(arg.tensor, tensor.shape)
for (i in 0 until tensor.indices.linearSize) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] +=
newOther.mutableBuffer.array()[tensor.bufferStart + i]
tensor.mutableBuffer[tensor.bufferStart + i] +=
newOther.mutableBuffer[tensor.bufferStart + i]
}
}
@ -45,7 +45,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
val newThis = broadcast[0]
val newOther = broadcast[1]
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
newThis.mutableBuffer.array()[i] - newOther.mutableBuffer.array()[i]
newThis.mutableBuffer[i] - newOther.mutableBuffer[i]
}
return DoubleTensor(newThis.shape, resBuffer)
}
@ -53,8 +53,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
override fun Tensor<Double>.minusAssign(arg: StructureND<Double>) {
val newOther = broadcastTo(arg.tensor, tensor.shape)
for (i in 0 until tensor.indices.linearSize) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] -=
newOther.mutableBuffer.array()[tensor.bufferStart + i]
tensor.mutableBuffer[tensor.bufferStart + i] -=
newOther.mutableBuffer[tensor.bufferStart + i]
}
}
@ -63,8 +63,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
val newThis = broadcast[0]
val newOther = broadcast[1]
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
newThis.mutableBuffer.array()[newThis.bufferStart + i] *
newOther.mutableBuffer.array()[newOther.bufferStart + i]
newThis.mutableBuffer[newThis.bufferStart + i] *
newOther.mutableBuffer[newOther.bufferStart + i]
}
return DoubleTensor(newThis.shape, resBuffer)
}
@ -72,8 +72,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
override fun Tensor<Double>.timesAssign(arg: StructureND<Double>) {
val newOther = broadcastTo(arg.tensor, tensor.shape)
for (i in 0 until tensor.indices.linearSize) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] *=
newOther.mutableBuffer.array()[tensor.bufferStart + i]
tensor.mutableBuffer[tensor.bufferStart + i] *=
newOther.mutableBuffer[tensor.bufferStart + i]
}
}
@ -82,8 +82,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
val newThis = broadcast[0]
val newOther = broadcast[1]
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
newThis.mutableBuffer.array()[newOther.bufferStart + i] /
newOther.mutableBuffer.array()[newOther.bufferStart + i]
newThis.mutableBuffer[newOther.bufferStart + i] /
newOther.mutableBuffer[newOther.bufferStart + i]
}
return DoubleTensor(newThis.shape, resBuffer)
}
@ -91,8 +91,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
override fun Tensor<Double>.divAssign(arg: StructureND<Double>) {
val newOther = broadcastTo(arg.tensor, tensor.shape)
for (i in 0 until tensor.indices.linearSize) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] /=
newOther.mutableBuffer.array()[tensor.bufferStart + i]
tensor.mutableBuffer[tensor.bufferStart + i] /=
newOther.mutableBuffer[tensor.bufferStart + i]
}
}
}

View File

@ -89,7 +89,7 @@ public open class DoubleTensorAlgebra :
}
override fun StructureND<Double>.valueOrNull(): Double? = if (tensor.shape contentEquals intArrayOf(1))
tensor.mutableBuffer.array()[tensor.bufferStart] else null
tensor.mutableBuffer[tensor.bufferStart] else null
override fun StructureND<Double>.value(): Double = valueOrNull()
?: throw IllegalArgumentException("The tensor shape is $shape, but value method is allowed only for shape [1]")
@ -208,7 +208,7 @@ public open class DoubleTensorAlgebra :
override fun Double.plus(arg: StructureND<Double>): DoubleTensor {
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i] + this
arg.tensor.mutableBuffer[arg.tensor.bufferStart + i] + this
}
return DoubleTensor(arg.shape, resBuffer)
}
@ -218,35 +218,35 @@ public open class DoubleTensorAlgebra :
override fun StructureND<Double>.plus(arg: StructureND<Double>): DoubleTensor {
checkShapesCompatible(tensor, arg.tensor)
val resBuffer = DoubleArray(tensor.numElements) { i ->
tensor.mutableBuffer.array()[i] + arg.tensor.mutableBuffer.array()[i]
tensor.mutableBuffer[i] + arg.tensor.mutableBuffer[i]
}
return DoubleTensor(tensor.shape, resBuffer)
}
override fun Tensor<Double>.plusAssign(value: Double) {
for (i in 0 until tensor.numElements) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] += value
tensor.mutableBuffer[tensor.bufferStart + i] += value
}
}
override fun Tensor<Double>.plusAssign(arg: StructureND<Double>) {
checkShapesCompatible(tensor, arg.tensor)
for (i in 0 until tensor.numElements) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] +=
arg.tensor.mutableBuffer.array()[tensor.bufferStart + i]
tensor.mutableBuffer[tensor.bufferStart + i] +=
arg.tensor.mutableBuffer[tensor.bufferStart + i]
}
}
override fun Double.minus(arg: StructureND<Double>): DoubleTensor {
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
this - arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i]
this - arg.tensor.mutableBuffer[arg.tensor.bufferStart + i]
}
return DoubleTensor(arg.shape, resBuffer)
}
override fun StructureND<Double>.minus(arg: Double): DoubleTensor {
val resBuffer = DoubleArray(tensor.numElements) { i ->
tensor.mutableBuffer.array()[tensor.bufferStart + i] - arg
tensor.mutableBuffer[tensor.bufferStart + i] - arg
}
return DoubleTensor(tensor.shape, resBuffer)
}
@ -254,28 +254,28 @@ public open class DoubleTensorAlgebra :
override fun StructureND<Double>.minus(arg: StructureND<Double>): DoubleTensor {
checkShapesCompatible(tensor, arg)
val resBuffer = DoubleArray(tensor.numElements) { i ->
tensor.mutableBuffer.array()[i] - arg.tensor.mutableBuffer.array()[i]
tensor.mutableBuffer[i] - arg.tensor.mutableBuffer[i]
}
return DoubleTensor(tensor.shape, resBuffer)
}
override fun Tensor<Double>.minusAssign(value: Double) {
for (i in 0 until tensor.numElements) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] -= value
tensor.mutableBuffer[tensor.bufferStart + i] -= value
}
}
override fun Tensor<Double>.minusAssign(arg: StructureND<Double>) {
checkShapesCompatible(tensor, arg)
for (i in 0 until tensor.numElements) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] -=
arg.tensor.mutableBuffer.array()[tensor.bufferStart + i]
tensor.mutableBuffer[tensor.bufferStart + i] -=
arg.tensor.mutableBuffer[tensor.bufferStart + i]
}
}
override fun Double.times(arg: StructureND<Double>): DoubleTensor {
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i] * this
arg.tensor.mutableBuffer[arg.tensor.bufferStart + i] * this
}
return DoubleTensor(arg.shape, resBuffer)
}
@ -285,36 +285,36 @@ public open class DoubleTensorAlgebra :
override fun StructureND<Double>.times(arg: StructureND<Double>): DoubleTensor {
checkShapesCompatible(tensor, arg)
val resBuffer = DoubleArray(tensor.numElements) { i ->
tensor.mutableBuffer.array()[tensor.bufferStart + i] *
arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i]
tensor.mutableBuffer[tensor.bufferStart + i] *
arg.tensor.mutableBuffer[arg.tensor.bufferStart + i]
}
return DoubleTensor(tensor.shape, resBuffer)
}
override fun Tensor<Double>.timesAssign(value: Double) {
for (i in 0 until tensor.numElements) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] *= value
tensor.mutableBuffer[tensor.bufferStart + i] *= value
}
}
override fun Tensor<Double>.timesAssign(arg: StructureND<Double>) {
checkShapesCompatible(tensor, arg)
for (i in 0 until tensor.numElements) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] *=
arg.tensor.mutableBuffer.array()[tensor.bufferStart + i]
tensor.mutableBuffer[tensor.bufferStart + i] *=
arg.tensor.mutableBuffer[tensor.bufferStart + i]
}
}
override fun Double.div(arg: StructureND<Double>): DoubleTensor {
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
this / arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i]
this / arg.tensor.mutableBuffer[arg.tensor.bufferStart + i]
}
return DoubleTensor(arg.shape, resBuffer)
}
override fun StructureND<Double>.div(arg: Double): DoubleTensor {
val resBuffer = DoubleArray(tensor.numElements) { i ->
tensor.mutableBuffer.array()[tensor.bufferStart + i] / arg
tensor.mutableBuffer[tensor.bufferStart + i] / arg
}
return DoubleTensor(shape, resBuffer)
}
@ -322,29 +322,29 @@ public open class DoubleTensorAlgebra :
override fun StructureND<Double>.div(arg: StructureND<Double>): DoubleTensor {
checkShapesCompatible(tensor, arg)
val resBuffer = DoubleArray(tensor.numElements) { i ->
tensor.mutableBuffer.array()[arg.tensor.bufferStart + i] /
arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i]
tensor.mutableBuffer[arg.tensor.bufferStart + i] /
arg.tensor.mutableBuffer[arg.tensor.bufferStart + i]
}
return DoubleTensor(tensor.shape, resBuffer)
}
override fun Tensor<Double>.divAssign(value: Double) {
for (i in 0 until tensor.numElements) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] /= value
tensor.mutableBuffer[tensor.bufferStart + i] /= value
}
}
override fun Tensor<Double>.divAssign(arg: StructureND<Double>) {
checkShapesCompatible(tensor, arg)
for (i in 0 until tensor.numElements) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] /=
arg.tensor.mutableBuffer.array()[tensor.bufferStart + i]
tensor.mutableBuffer[tensor.bufferStart + i] /=
arg.tensor.mutableBuffer[tensor.bufferStart + i]
}
}
override fun StructureND<Double>.unaryMinus(): DoubleTensor {
val resBuffer = DoubleArray(tensor.numElements) { i ->
tensor.mutableBuffer.array()[tensor.bufferStart + i].unaryMinus()
tensor.mutableBuffer[tensor.bufferStart + i].unaryMinus()
}
return DoubleTensor(tensor.shape, resBuffer)
}
@ -367,8 +367,8 @@ public open class DoubleTensorAlgebra :
newMultiIndex[ii] = newMultiIndex[jj].also { newMultiIndex[jj] = newMultiIndex[ii] }
val linearIndex = resTensor.indices.offset(newMultiIndex)
resTensor.mutableBuffer.array()[linearIndex] =
tensor.mutableBuffer.array()[tensor.bufferStart + offset]
resTensor.mutableBuffer[linearIndex] =
tensor.mutableBuffer[tensor.bufferStart + offset]
}
return resTensor
}
@ -833,8 +833,38 @@ public open class DoubleTensorAlgebra :
return qTensor to rTensor
}
override fun StructureND<Double>.svd(): Triple<DoubleTensor, DoubleTensor, DoubleTensor> =
svd(epsilon = 1e-10)
override fun StructureND<Double>.svd(): Triple<DoubleTensor, DoubleTensor, DoubleTensor> {
return this.svdGolubKahan()
}
public fun StructureND<Double>.svdGolubKahan(iterations: Int = 30, epsilon: Double = 1e-10): Triple<DoubleTensor, DoubleTensor, DoubleTensor> {
val size = tensor.dimension
val commonShape = tensor.shape.sliceArray(0 until size - 2)
val (n, m) = tensor.shape.sliceArray(size - 2 until size)
val uTensor = zeros(commonShape + intArrayOf(n, m))
val sTensor = zeros(commonShape + intArrayOf(m))
val vTensor = zeros(commonShape + intArrayOf(m, m))
val matrices = tensor.matrices
val uTensors = uTensor.matrices
val sTensorVectors = sTensor.vectors
val vTensors = vTensor.matrices
for (index in matrices.indices) {
val matrix = matrices[index]
val matrixSize = matrix.shape.reduce { acc, i -> acc * i }
val curMatrix = DoubleTensor(
matrix.shape,
matrix.mutableBuffer.array()
.slice(matrix.bufferStart until matrix.bufferStart + matrixSize)
.toDoubleArray()
)
curMatrix.as2D().svdGolubKahanHelper(uTensors[index].as2D(), sTensorVectors[index], vTensors[index].as2D(),
iterations, epsilon)
}
return Triple(uTensor, sTensor, vTensor)
}
/**
* Singular Value Decomposition.
@ -849,7 +879,7 @@ public open class DoubleTensorAlgebra :
* i.e., the precision with which the cosine approaches 1 in an iterative algorithm.
* @return a triple `Triple(U, S, V)`.
*/
public fun StructureND<Double>.svd(epsilon: Double): Triple<DoubleTensor, DoubleTensor, DoubleTensor> {
public fun StructureND<Double>.svdPowerMethod(epsilon: Double = 1e-10): Triple<DoubleTensor, DoubleTensor, DoubleTensor> {
val size = tensor.dimension
val commonShape = tensor.shape.sliceArray(0 until size - 2)
val (n, m) = tensor.shape.sliceArray(size - 2 until size)
@ -876,7 +906,7 @@ public open class DoubleTensorAlgebra :
.slice(matrix.bufferStart until matrix.bufferStart + matrixSize)
.toDoubleArray()
)
svdHelper(curMatrix, usv, m, n, epsilon)
svdPowerMethodHelper(curMatrix, usv, m, n, epsilon)
}
return Triple(uTensor.transpose(), sTensor, vTensor.transpose())
@ -907,7 +937,7 @@ public open class DoubleTensorAlgebra :
}
}
val (u, s, v) = tensor.svd(epsilon)
val (u, s, v) = tensor.svd()
val shp = s.shape + intArrayOf(1)
val utv = u.transpose() dot v
val n = s.shape.last()
@ -928,18 +958,21 @@ public open class DoubleTensorAlgebra :
var eigenvalueStart = 0
var eigenvectorStart = 0
val eigenvaluesBuffer = eigenvalues.mutableBuffer
val eigenvectorsBuffer = eigenvectors.mutableBuffer
for (matrix in tensor.matrixSequence()) {
val matrix2D = matrix.as2D()
val (d, v) = matrix2D.jacobiHelper(maxIteration, epsilon)
for (i in 0 until matrix2D.rowNum) {
for (j in 0 until matrix2D.colNum) {
eigenvectors.mutableBuffer.array()[eigenvectorStart + i * matrix2D.rowNum + j] = v[i, j]
eigenvectorsBuffer[eigenvectorStart + i * matrix2D.rowNum + j] = v[i, j]
}
}
for (i in 0 until matrix2D.rowNum) {
eigenvalues.mutableBuffer.array()[eigenvalueStart + i] = d[i]
eigenvaluesBuffer[eigenvalueStart + i] = d[i]
}
eigenvalueStart += this.shape.last()
@ -962,11 +995,11 @@ public open class DoubleTensorAlgebra :
// assume that buffered tensor is square matrix
operator fun BufferedTensor<Double>.get(i: Int, j: Int): Double {
return this.mutableBuffer.array()[bufferStart + i * this.shape[0] + j]
return this.mutableBuffer[bufferStart + i * this.shape[0] + j]
}
operator fun BufferedTensor<Double>.set(i: Int, j: Int, value: Double) {
this.mutableBuffer.array()[bufferStart + i * this.shape[0] + j] = value
this.mutableBuffer[bufferStart + i * this.shape[0] + j] = value
}
fun maxOffDiagonal(matrix: BufferedTensor<Double>): Double {

View File

@ -17,6 +17,7 @@ import space.kscience.kmath.tensors.core.DoubleTensor
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.IntTensor
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt
@ -299,7 +300,7 @@ internal fun DoubleTensorAlgebra.svd1d(a: DoubleTensor, epsilon: Double = 1e-10)
}
}
internal fun DoubleTensorAlgebra.svdHelper(
internal fun DoubleTensorAlgebra.svdPowerMethodHelper(
matrix: DoubleTensor,
USV: Triple<BufferedTensor<Double>, BufferedTensor<Double>, BufferedTensor<Double>>,
m: Int, n: Int, epsilon: Double,
@ -307,6 +308,14 @@ internal fun DoubleTensorAlgebra.svdHelper(
val res = ArrayList<Triple<Double, DoubleTensor, DoubleTensor>>(0)
val (matrixU, matrixS, matrixV) = USV
val matrixUStart = matrixU.bufferStart
val matrixSStart = matrixS.bufferStart
val matrixVStart = matrixV.bufferStart
val matrixUBuffer = matrixU.mutableBuffer
val matrixSBuffer = matrixS.mutableBuffer
val matrixVBuffer = matrixV.mutableBuffer
for (k in 0 until min(n, m)) {
var a = matrix.copy()
for ((singularValue, u, v) in res.slice(0 until k)) {
@ -340,12 +349,307 @@ internal fun DoubleTensorAlgebra.svdHelper(
val uBuffer = res.map { it.second }.flatMap { it.mutableBuffer.array().toList() }.toDoubleArray()
val vBuffer = res.map { it.third }.flatMap { it.mutableBuffer.array().toList() }.toDoubleArray()
for (i in uBuffer.indices) {
matrixU.mutableBuffer.array()[matrixU.bufferStart + i] = uBuffer[i]
matrixUBuffer[matrixUStart + i] = uBuffer[i]
}
for (i in s.indices) {
matrixS.mutableBuffer.array()[matrixS.bufferStart + i] = s[i]
matrixSBuffer[matrixSStart + i] = s[i]
}
for (i in vBuffer.indices) {
matrixV.mutableBuffer.array()[matrixV.bufferStart + i] = vBuffer[i]
matrixVBuffer[matrixVStart + i] = vBuffer[i]
}
}
private fun pythag(a: Double, b: Double): Double {
val at: Double = abs(a)
val bt: Double = abs(b)
val ct: Double
val result: Double
if (at > bt) {
ct = bt / at
result = at * sqrt(1.0 + ct * ct)
} else if (bt > 0.0) {
ct = at / bt
result = bt * sqrt(1.0 + ct * ct)
} else result = 0.0
return result
}
private fun SIGN(a: Double, b: Double): Double {
if (b >= 0.0)
return abs(a)
else
return -abs(a)
}
internal fun MutableStructure2D<Double>.svdGolubKahanHelper(u: MutableStructure2D<Double>, w: BufferedTensor<Double>,
v: MutableStructure2D<Double>, iterations: Int, epsilon: Double) {
val shape = this.shape
val m = shape.component1()
val n = shape.component2()
var f = 0.0
val rv1 = DoubleArray(n)
var s = 0.0
var scale = 0.0
var anorm = 0.0
var g = 0.0
var l = 0
val wStart = w.bufferStart
val wBuffer = w.mutableBuffer
for (i in 0 until n) {
/* left-hand reduction */
l = i + 1
rv1[i] = scale * g
g = 0.0
s = 0.0
scale = 0.0
if (i < m) {
for (k in i until m) {
scale += abs(this[k, i]);
}
if (abs(scale) > epsilon) {
for (k in i until m) {
this[k, i] = (this[k, i] / scale)
s += this[k, i] * this[k, i]
}
f = this[i, i]
if (f >= 0) {
g = (-1) * abs(sqrt(s))
} else {
g = abs(sqrt(s))
}
val h = f * g - s
this[i, i] = f - g
if (i != n - 1) {
for (j in l until n) {
s = 0.0
for (k in i until m) {
s += this[k, i] * this[k, j]
}
f = s / h
for (k in i until m) {
this[k, j] += f * this[k, i]
}
}
}
for (k in i until m) {
this[k, i] = this[k, i] * scale
}
}
}
wBuffer[wStart + i] = scale * g
/* right-hand reduction */
g = 0.0
s = 0.0
scale = 0.0
if (i < m && i != n - 1) {
for (k in l until n) {
scale += abs(this[i, k])
}
if (abs(scale) > epsilon) {
for (k in l until n) {
this[i, k] = this[i, k] / scale
s += this[i, k] * this[i, k]
}
f = this[i, l]
if (f >= 0) {
g = (-1) * abs(sqrt(s))
} else {
g = abs(sqrt(s))
}
val h = f * g - s
this[i, l] = f - g
for (k in l until n) {
rv1[k] = this[i, k] / h
}
if (i != m - 1) {
for (j in l until m) {
s = 0.0
for (k in l until n) {
s += this[j, k] * this[i, k]
}
for (k in l until n) {
this[j, k] += s * rv1[k]
}
}
}
for (k in l until n) {
this[i, k] = this[i, k] * scale
}
}
}
anorm = max(anorm, (abs(wBuffer[wStart + i]) + abs(rv1[i])));
}
for (i in n - 1 downTo 0) {
if (i < n - 1) {
if (abs(g) > epsilon) {
for (j in l until n) {
v[j, i] = (this[i, j] / this[i, l]) / g
}
for (j in l until n) {
s = 0.0
for (k in l until n)
s += this[i, k] * v[k, j]
for (k in l until n)
v[k, j] += s * v[k, i]
}
}
for (j in l until n) {
v[i, j] = 0.0
v[j, i] = 0.0
}
}
v[i, i] = 1.0
g = rv1[i]
l = i
}
for (i in min(n, m) - 1 downTo 0) {
l = i + 1
g = wBuffer[wStart + i]
for (j in l until n) {
this[i, j] = 0.0
}
if (abs(g) > epsilon) {
g = 1.0 / g
for (j in l until n) {
s = 0.0
for (k in l until m) {
s += this[k, i] * this[k, j]
}
f = (s / this[i, i]) * g
for (k in i until m) {
this[k, j] += f * this[k, i]
}
}
for (j in i until m) {
this[j, i] *= g
}
} else {
for (j in i until m) {
this[j, i] = 0.0
}
}
this[i, i] += 1.0
}
var flag = 0
var nm = 0
var c = 0.0
var h = 0.0
var y = 0.0
var z = 0.0
var x = 0.0
for (k in n - 1 downTo 0) {
for (its in 1 until iterations) {
flag = 1
for (newl in k downTo 0) {
nm = newl - 1
if (abs(rv1[newl]) + anorm == anorm) {
flag = 0
l = newl
break
}
if (abs(wBuffer[wStart + nm]) + anorm == anorm) {
l = newl
break
}
}
if (flag != 0) {
c = 0.0
s = 1.0
for (i in l until k + 1) {
f = s * rv1[i]
rv1[i] = c * rv1[i]
if (abs(f) + anorm == anorm) {
break
}
g = wBuffer[wStart + i]
h = pythag(f, g)
wBuffer[wStart + i] = h
h = 1.0 / h
c = g * h
s = (-f) * h
for (j in 0 until m) {
y = this[j, nm]
z = this[j, i]
this[j, nm] = y * c + z * s
this[j, i] = z * c - y * s
}
}
}
z = wBuffer[wStart + k]
if (l == k) {
if (z < 0.0) {
wBuffer[wStart + k] = -z
for (j in 0 until n)
v[j, k] = -v[j, k]
}
break
}
x = wBuffer[wStart + l]
nm = k - 1
y = wBuffer[wStart + nm]
g = rv1[nm]
h = rv1[k]
f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y)
g = pythag(f, 1.0)
f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x
c = 1.0
s = 1.0
var i = 0
for (j in l until nm + 1) {
i = j + 1
g = rv1[i]
y = wBuffer[wStart + i]
h = s * g
g = c * g
z = pythag(f, h)
rv1[j] = z
c = f / z
s = h / z
f = x * c + g * s
g = g * c - x * s
h = y * s
y *= c
for (jj in 0 until n) {
x = v[jj, j];
z = v[jj, i];
v[jj, j] = x * c + z * s;
v[jj, i] = z * c - x * s;
}
z = pythag(f, h)
wBuffer[wStart + j] = z
if (abs(z) > epsilon) {
z = 1.0 / z
c = f * z
s = h * z
}
f = c * g + s * y
x = c * y - s * g
for (jj in 0 until m) {
y = this[jj, j]
z = this[jj, i]
this[jj, j] = y * c + z * s
this[jj, i] = z * c - y * s
}
}
rv1[l] = 0.0
rv1[k] = f
wBuffer[wStart + k] = x
}
}
for (i in 0 until n) {
for (j in 0 until m) {
u[j, i] = this[j, i]
}
}
}

View File

@ -156,23 +156,12 @@ internal class TestDoubleLinearOpsTensorAlgebra {
val res = svd1d(tensor2)
val resBuffer = res.mutableBuffer
val resStart = res.bufferStart
assertTrue(res.shape contentEquals intArrayOf(2))
assertTrue { abs(abs(res.mutableBuffer.array()[res.bufferStart]) - 0.386) < 0.01 }
assertTrue { abs(abs(res.mutableBuffer.array()[res.bufferStart + 1]) - 0.922) < 0.01 }
}
@Test
fun testSVD() = DoubleTensorAlgebra{
testSVDFor(fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))
testSVDFor(fromArray(intArrayOf(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0)))
}
@Test
fun testBatchedSVD() = DoubleTensorAlgebra {
val tensor = randomNormal(intArrayOf(2, 5, 3), 0)
val (tensorU, tensorS, tensorV) = tensor.svd()
val tensorSVD = tensorU dot (diagonalEmbedding(tensorS) dot tensorV.transpose())
assertTrue(tensor.eq(tensorSVD))
assertTrue { abs(abs(resBuffer[resStart]) - 0.386) < 0.01 }
assertTrue { abs(abs(resBuffer[resStart + 1]) - 0.922) < 0.01 }
}
@Test
@ -184,13 +173,130 @@ internal class TestDoubleLinearOpsTensorAlgebra {
assertTrue(tensorSigma.eq(tensorSigmaCalc))
}
@Test
fun testSVD() = DoubleTensorAlgebra{
testSVDFor(fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))
testSVDFor(fromArray(intArrayOf(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0)))
val buffer1 = doubleArrayOf(
1.000000, 2.000000, 3.000000,
2.000000, 3.000000, 4.000000,
3.000000, 4.000000, 5.000000,
4.000000, 5.000000, 6.000000,
5.000000, 6.000000, 9.000000
)
testSVDFor(fromArray(intArrayOf(5, 3), buffer1))
val buffer2 = doubleArrayOf(
1.0, 2.0, 3.0, 2.0, 3.0,
4.0, 3.0, 4.0, 5.0, 4.0,
5.0, 6.0, 5.0, 6.0, 7.0
)
testSVDFor(fromArray(intArrayOf(3, 5), buffer2))
}
@Test
fun testBatchedSVD() = DoubleTensorAlgebra{
val tensor1 = randomNormal(intArrayOf(2, 5, 3), 0)
testSVDFor(tensor1)
val tensor2 = DoubleTensorAlgebra.randomNormal(intArrayOf(30, 30, 30), 0)
testSVDFor(tensor2)
}
@Test
fun testSVDGolubKahan() = DoubleTensorAlgebra{
testSVDGolubKahanFor(fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))
testSVDGolubKahanFor(fromArray(intArrayOf(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0)))
val buffer1 = doubleArrayOf(
1.000000, 2.000000, 3.000000,
2.000000, 3.000000, 4.000000,
3.000000, 4.000000, 5.000000,
4.000000, 5.000000, 6.000000,
5.000000, 6.000000, 9.000000
)
testSVDGolubKahanFor(fromArray(intArrayOf(5, 3), buffer1))
val buffer2 = doubleArrayOf(
1.0, 2.0, 3.0, 2.0, 3.0,
4.0, 3.0, 4.0, 5.0, 4.0,
5.0, 6.0, 5.0, 6.0, 7.0
)
testSVDGolubKahanFor(fromArray(intArrayOf(3, 5), buffer2))
}
@Test
fun testBatchedSVDGolubKahan() = DoubleTensorAlgebra{
val tensor1 = randomNormal(intArrayOf(2, 5, 3), 0)
testSVDGolubKahanFor(tensor1)
val tensor2 = DoubleTensorAlgebra.randomNormal(intArrayOf(30, 30, 30), 0)
testSVDGolubKahanFor(tensor2)
}
@Test
fun testSVDPowerMethod() = DoubleTensorAlgebra{
testSVDPowerMethodFor(fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))
testSVDPowerMethodFor(fromArray(intArrayOf(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0)))
val buffer1 = doubleArrayOf(
1.000000, 2.000000, 3.000000,
2.000000, 3.000000, 4.000000,
3.000000, 4.000000, 5.000000,
4.000000, 5.000000, 6.000000,
5.000000, 6.000000, 9.000000
)
testSVDPowerMethodFor(fromArray(intArrayOf(5, 3), buffer1))
val buffer2 = doubleArrayOf(
1.0, 2.0, 3.0, 2.0, 3.0,
4.0, 3.0, 4.0, 5.0, 4.0,
5.0, 6.0, 5.0, 6.0, 7.0
)
testSVDPowerMethodFor(fromArray(intArrayOf(3, 5), buffer2))
}
@Test
fun testBatchedSVDPowerMethod() = DoubleTensorAlgebra {
val tensor1 = randomNormal(intArrayOf(2, 5, 3), 0)
testSVDPowerMethodFor(tensor1)
val tensor2 = DoubleTensorAlgebra.randomNormal(intArrayOf(30, 30, 30), 0)
testSVDPowerMethodFor(tensor2)
}
// @Test
// fun testSVDPowerMethodError() = DoubleTensorAlgebra{
// val buffer = doubleArrayOf(
// 1.000000, 2.000000, 3.000000,
// 2.000000, 3.000000, 4.000000,
// 3.000000, 4.000000, 5.000000,
// 4.000000, 5.000000, 6.000000,
// 5.000000, 6.000000, 7.000000
// )
// testSVDPowerMethodFor(fromArray(intArrayOf(5, 3), buffer))
// }
}
private fun DoubleTensorAlgebra.testSVDFor(tensor: DoubleTensor, epsilon: Double = 1e-10) {
private fun DoubleTensorAlgebra.testSVDFor(tensor: DoubleTensor) {
val svd = tensor.svd()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensor.eq(tensorSVD))
}
private fun DoubleTensorAlgebra.testSVDGolubKahanFor(tensor: DoubleTensor, epsilon: Double = 1e-10) {
val svd = tensor.svdGolubKahan()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensor.eq(tensorSVD, epsilon))
}
private fun DoubleTensorAlgebra.testSVDPowerMethodFor(tensor: DoubleTensor, epsilon: Double = 1e-10) {
val svd = tensor.svdPowerMethod()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)

32
kmath-viktor/README.md Normal file
View File

@ -0,0 +1,32 @@
# Module kmath-viktor
Binding for https://github.com/JetBrains-Research/viktor
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:kmath-viktor:0.3.0`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:kmath-viktor:0.3.0'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:kmath-viktor:0.3.0")
}
```

View File

@ -1,40 +1,69 @@
public final class space/kscience/kmath/viktor/ViktorBuffer : space/kscience/kmath/structures/MutableBuffer {
public fun <init> (Lorg/jetbrains/bio/viktor/F64FlatArray;)V
public static final synthetic fun box-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;)Lspace/kscience/kmath/viktor/ViktorBuffer;
public static fun constructor-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;)Lorg/jetbrains/bio/viktor/F64FlatArray;
public fun copy ()Lspace/kscience/kmath/structures/MutableBuffer;
public static fun copy-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;)Lspace/kscience/kmath/structures/MutableBuffer;
public fun equals (Ljava/lang/Object;)Z
public static fun equals-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;Ljava/lang/Object;)Z
public static final fun equals-impl0 (Lorg/jetbrains/bio/viktor/F64FlatArray;Lorg/jetbrains/bio/viktor/F64FlatArray;)Z
public fun get (I)Ljava/lang/Double;
public synthetic fun get (I)Ljava/lang/Object;
public static fun get-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;I)Ljava/lang/Double;
public final fun getFlatArray ()Lorg/jetbrains/bio/viktor/F64FlatArray;
public fun getSize ()I
public static fun getSize-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;)I
public fun hashCode ()I
public static fun hashCode-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;)I
public fun iterator ()Ljava/util/Iterator;
public static fun iterator-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;)Ljava/util/Iterator;
public fun set (ID)V
public synthetic fun set (ILjava/lang/Object;)V
public static fun set-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;ID)V
public fun toString ()Ljava/lang/String;
public static fun toString-impl (Lorg/jetbrains/bio/viktor/F64FlatArray;)Ljava/lang/String;
public final synthetic fun unbox-impl ()Lorg/jetbrains/bio/viktor/F64FlatArray;
}
public final class space/kscience/kmath/viktor/ViktorFieldND : space/kscience/kmath/nd/FieldND, space/kscience/kmath/operations/ExtendedField, space/kscience/kmath/operations/NumbersAddOperations, space/kscience/kmath/operations/ScaleOperations {
public class space/kscience/kmath/viktor/ViktorFieldND : space/kscience/kmath/viktor/ViktorFieldOpsND, space/kscience/kmath/nd/FieldND, space/kscience/kmath/operations/NumbersAddOps {
public fun <init> ([I)V
public synthetic fun getOne ()Ljava/lang/Object;
public synthetic fun getOne ()Lspace/kscience/kmath/nd/StructureND;
public fun getOne ()Lspace/kscience/kmath/viktor/ViktorStructureND;
public fun getShape ()[I
public synthetic fun getZero ()Ljava/lang/Object;
public synthetic fun getZero ()Lspace/kscience/kmath/nd/StructureND;
public fun getZero ()Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun number (Ljava/lang/Number;)Ljava/lang/Object;
public fun number (Ljava/lang/Number;)Lspace/kscience/kmath/viktor/ViktorStructureND;
}
public class space/kscience/kmath/viktor/ViktorFieldOpsND : space/kscience/kmath/nd/FieldOpsND, space/kscience/kmath/operations/ExtendedFieldOps, space/kscience/kmath/operations/PowerOperations {
public static final field Companion Lspace/kscience/kmath/viktor/ViktorFieldOpsND$Companion;
public fun <init> ()V
public synthetic fun acos (Ljava/lang/Object;)Ljava/lang/Object;
public fun acos (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun acosh (Ljava/lang/Object;)Ljava/lang/Object;
public fun acosh (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun add (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
public synthetic fun add (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/nd/StructureND;
public fun add (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun asin (Ljava/lang/Object;)Ljava/lang/Object;
public fun asin (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun asinh (Ljava/lang/Object;)Ljava/lang/Object;
public fun asinh (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun atan (Ljava/lang/Object;)Ljava/lang/Object;
public fun atan (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun combine (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;Lkotlin/jvm/functions/Function3;)Lspace/kscience/kmath/nd/StructureND;
public fun combine (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;Lkotlin/jvm/functions/Function3;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun atanh (Ljava/lang/Object;)Ljava/lang/Object;
public fun atanh (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun cos (Ljava/lang/Object;)Ljava/lang/Object;
public fun cos (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun cosh (Ljava/lang/Object;)Ljava/lang/Object;
public fun cosh (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun exp (Ljava/lang/Object;)Ljava/lang/Object;
public fun exp (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun getElementContext ()Lspace/kscience/kmath/operations/Algebra;
public fun getElementContext ()Lspace/kscience/kmath/operations/DoubleField;
public synthetic fun getElementAlgebra ()Lspace/kscience/kmath/operations/Algebra;
public fun getElementAlgebra ()Lspace/kscience/kmath/operations/DoubleField;
public final fun getF64Buffer (Lspace/kscience/kmath/nd/StructureND;)Lorg/jetbrains/bio/viktor/F64Array;
public synthetic fun getOne ()Ljava/lang/Object;
public fun getOne ()Lspace/kscience/kmath/viktor/ViktorStructureND;
public fun getShape ()[I
public synthetic fun getZero ()Ljava/lang/Object;
public fun getZero ()Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun ln (Ljava/lang/Object;)Ljava/lang/Object;
public fun ln (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun map (Lspace/kscience/kmath/nd/StructureND;Lkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/nd/StructureND;
@ -43,26 +72,38 @@ public final class space/kscience/kmath/viktor/ViktorFieldND : space/kscience/km
public fun mapIndexed (Lspace/kscience/kmath/nd/StructureND;Lkotlin/jvm/functions/Function3;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun minus (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
public fun minus (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun number (Ljava/lang/Number;)Ljava/lang/Object;
public fun number (Ljava/lang/Number;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun plus (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
public fun plus (Lspace/kscience/kmath/nd/StructureND;D)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun plus (Lspace/kscience/kmath/nd/StructureND;Ljava/lang/Object;)Lspace/kscience/kmath/nd/StructureND;
public fun plus (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun power (Ljava/lang/Object;Ljava/lang/Number;)Ljava/lang/Object;
public fun power (Lspace/kscience/kmath/nd/StructureND;Ljava/lang/Number;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun produce (Lkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/nd/StructureND;
public fun produce (Lkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun scale (Ljava/lang/Object;D)Ljava/lang/Object;
public synthetic fun scale (Lspace/kscience/kmath/nd/StructureND;D)Lspace/kscience/kmath/nd/StructureND;
public fun scale (Lspace/kscience/kmath/nd/StructureND;D)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun sin (Ljava/lang/Object;)Ljava/lang/Object;
public fun sin (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun sinh (Ljava/lang/Object;)Ljava/lang/Object;
public fun sinh (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun structureND ([ILkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/nd/StructureND;
public fun structureND ([ILkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun tan (Ljava/lang/Object;)Ljava/lang/Object;
public fun tan (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun times (Ljava/lang/Object;Ljava/lang/Number;)Ljava/lang/Object;
public fun times (Lspace/kscience/kmath/nd/StructureND;Ljava/lang/Number;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun unaryMinus (Ljava/lang/Object;)Ljava/lang/Object;
public fun unaryMinus (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/nd/StructureND;
public synthetic fun zip (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;Lkotlin/jvm/functions/Function3;)Lspace/kscience/kmath/nd/StructureND;
public fun zip (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;Lkotlin/jvm/functions/Function3;)Lspace/kscience/kmath/viktor/ViktorStructureND;
}
public final class space/kscience/kmath/viktor/ViktorFieldOpsND$Companion : space/kscience/kmath/viktor/ViktorFieldOpsND {
}
public final class space/kscience/kmath/viktor/ViktorFieldOpsNDKt {
public static final fun ViktorFieldND ([I)Lspace/kscience/kmath/viktor/ViktorFieldND;
public static final fun getViktorAlgebra (Lspace/kscience/kmath/operations/DoubleField;)Lspace/kscience/kmath/viktor/ViktorFieldOpsND;
public static final fun viktorAlgebra (Lspace/kscience/kmath/operations/DoubleField;[I)Lspace/kscience/kmath/viktor/ViktorFieldND;
}
public final class space/kscience/kmath/viktor/ViktorStructureND : space/kscience/kmath/nd/MutableStructureND {
@ -77,7 +118,6 @@ public final class space/kscience/kmath/viktor/ViktorStructureND : space/kscienc
}
public final class space/kscience/kmath/viktor/ViktorStructureNDKt {
public static final fun ViktorNDField ([I)Lspace/kscience/kmath/viktor/ViktorFieldND;
public static final fun asStructure (Lorg/jetbrains/bio/viktor/F64Array;)Lspace/kscience/kmath/viktor/ViktorStructureND;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,6 @@
rootProject.name = "kmath"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
enableFeaturePreview("VERSION_CATALOGS")
dependencyResolutionManagement {
val toolsVersion: String by extra
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
versionCatalogs {
create("npmlibs") {
from("ru.mipt.npm:version-catalog:$toolsVersion")
}
}
}
include(
":kmath-memory",
":kmath-complex",