forked from kscience/kmath
commit
9e141db871
28
.github/workflows/build.yml
vendored
28
.github/workflows/build.yml
vendored
@ -7,26 +7,18 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
runs-on: windows-latest
|
||||||
matrix:
|
timeout-minutes: 20
|
||||||
os: [ macOS-latest, windows-latest ]
|
|
||||||
runs-on: ${{matrix.os}}
|
|
||||||
timeout-minutes: 40
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.0.0
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-java@v3.0.0
|
- uses: actions/setup-java@v3.5.1
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: '11'
|
||||||
distribution: liberica
|
distribution: 'liberica'
|
||||||
- name: Cache konan
|
cache: 'gradle'
|
||||||
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
|
- name: Gradle Wrapper Validation
|
||||||
uses: gradle/wrapper-validation-action@v1.0.4
|
uses: gradle/wrapper-validation-action@v1.0.4
|
||||||
- uses: gradle/gradle-build-action@v2.1.5
|
- name: Gradle Build
|
||||||
|
uses: gradle/gradle-build-action@v2.3.2
|
||||||
with:
|
with:
|
||||||
arguments: build
|
arguments: test jvmTest
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
- Type-aliases for numbers like `Float64`
|
||||||
- 2D optimal trajectory computation in a separate module `kmath-trajectory`
|
- 2D optimal trajectory computation in a separate module `kmath-trajectory`
|
||||||
- Autodiff for generic algebra elements in core!
|
- Autodiff for generic algebra elements in core!
|
||||||
- Algebra now has an obligatory `bufferFactory` (#477).
|
- Algebra now has an obligatory `bufferFactory` (#477).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- Tensor operations switched to prefix notation
|
||||||
|
- Row-wise and column-wise ND shapes in the core
|
||||||
|
- Shape is read-only
|
||||||
- Major refactor of tensors (only minor API changes)
|
- Major refactor of tensors (only minor API changes)
|
||||||
- Kotlin 1.7.20
|
- Kotlin 1.7.20
|
||||||
- `LazyStructure` `deffered` -> `async` to comply with coroutines code style
|
- `LazyStructure` `deffered` -> `async` to comply with coroutines code style
|
||||||
@ -16,6 +20,7 @@
|
|||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
- Polynomials moved to https://github.com/SciProgCentre/kmath-polynomial
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
@ -6,34 +6,75 @@
|
|||||||
package space.kscience.kmath.benchmarks
|
package space.kscience.kmath.benchmarks
|
||||||
|
|
||||||
import kotlinx.benchmark.Benchmark
|
import kotlinx.benchmark.Benchmark
|
||||||
|
import kotlinx.benchmark.Blackhole
|
||||||
import kotlinx.benchmark.Scope
|
import kotlinx.benchmark.Scope
|
||||||
import kotlinx.benchmark.State
|
import kotlinx.benchmark.State
|
||||||
import space.kscience.kmath.complex.Complex
|
import space.kscience.kmath.complex.Complex
|
||||||
|
import space.kscience.kmath.complex.ComplexField
|
||||||
import space.kscience.kmath.complex.complex
|
import space.kscience.kmath.complex.complex
|
||||||
|
import space.kscience.kmath.operations.invoke
|
||||||
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
import space.kscience.kmath.structures.DoubleBuffer
|
||||||
import space.kscience.kmath.structures.MutableBuffer
|
import space.kscience.kmath.structures.getDouble
|
||||||
|
import space.kscience.kmath.structures.permute
|
||||||
|
|
||||||
@State(Scope.Benchmark)
|
@State(Scope.Benchmark)
|
||||||
internal class BufferBenchmark {
|
internal class BufferBenchmark {
|
||||||
@Benchmark
|
|
||||||
fun genericDoubleBufferReadWrite() {
|
|
||||||
val buffer = DoubleBuffer(size) { it.toDouble() }
|
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun doubleArrayReadWrite(blackhole: Blackhole) {
|
||||||
|
val buffer = DoubleArray(size) { it.toDouble() }
|
||||||
|
var res = 0.0
|
||||||
(0 until size).forEach {
|
(0 until size).forEach {
|
||||||
buffer[it]
|
res += buffer[it]
|
||||||
}
|
}
|
||||||
|
blackhole.consume(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
fun complexBufferReadWrite() {
|
fun doubleBufferReadWrite(blackhole: Blackhole) {
|
||||||
val buffer = MutableBuffer.complex(size / 2) { Complex(it.toDouble(), -it.toDouble()) }
|
val buffer = DoubleBuffer(size) { it.toDouble() }
|
||||||
|
var res = 0.0
|
||||||
(0 until size / 2).forEach {
|
(0 until size).forEach {
|
||||||
buffer[it]
|
res += buffer[it]
|
||||||
}
|
}
|
||||||
|
blackhole.consume(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun bufferViewReadWrite(blackhole: Blackhole) {
|
||||||
|
val buffer = DoubleBuffer(size) { it.toDouble() }.permute(reversedIndices)
|
||||||
|
var res = 0.0
|
||||||
|
(0 until size).forEach {
|
||||||
|
res += buffer[it]
|
||||||
|
}
|
||||||
|
blackhole.consume(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun bufferViewReadWriteSpecialized(blackhole: Blackhole) {
|
||||||
|
val buffer = DoubleBuffer(size) { it.toDouble() }.permute(reversedIndices)
|
||||||
|
var res = 0.0
|
||||||
|
(0 until size).forEach {
|
||||||
|
res += buffer.getDouble(it)
|
||||||
|
}
|
||||||
|
blackhole.consume(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun complexBufferReadWrite(blackhole: Blackhole) = ComplexField {
|
||||||
|
val buffer = Buffer.complex(size / 2) { Complex(it.toDouble(), -it.toDouble()) }
|
||||||
|
|
||||||
|
var res = zero
|
||||||
|
(0 until size / 2).forEach {
|
||||||
|
res += buffer[it]
|
||||||
|
}
|
||||||
|
|
||||||
|
blackhole.consume(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
private const val size = 100
|
private const val size = 100
|
||||||
|
private val reversedIndices = IntArray(size){it}.apply { reverse() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,8 @@ import org.jetbrains.kotlinx.multik.api.Multik
|
|||||||
import org.jetbrains.kotlinx.multik.api.ones
|
import org.jetbrains.kotlinx.multik.api.ones
|
||||||
import org.jetbrains.kotlinx.multik.ndarray.data.DN
|
import org.jetbrains.kotlinx.multik.ndarray.data.DN
|
||||||
import org.jetbrains.kotlinx.multik.ndarray.data.DataType
|
import org.jetbrains.kotlinx.multik.ndarray.data.DataType
|
||||||
import space.kscience.kmath.nd.BufferedFieldOpsND
|
import space.kscience.kmath.misc.UnsafeKMathAPI
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.nd.ndAlgebra
|
|
||||||
import space.kscience.kmath.nd.one
|
|
||||||
import space.kscience.kmath.nd4j.nd4j
|
import space.kscience.kmath.nd4j.nd4j
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
import space.kscience.kmath.tensors.core.DoubleTensor
|
import space.kscience.kmath.tensors.core.DoubleTensor
|
||||||
@ -69,9 +67,10 @@ internal class NDFieldBenchmark {
|
|||||||
blackhole.consume(res)
|
blackhole.consume(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(UnsafeKMathAPI::class)
|
||||||
@Benchmark
|
@Benchmark
|
||||||
fun multikInPlaceAdd(blackhole: Blackhole) = with(multikAlgebra) {
|
fun multikInPlaceAdd(blackhole: Blackhole) = with(multikAlgebra) {
|
||||||
val res = Multik.ones<Double, DN>(shape, DataType.DoubleDataType).wrap()
|
val res = Multik.ones<Double, DN>(shape.asArray(), DataType.DoubleDataType).wrap()
|
||||||
repeat(n) { res += 1.0 }
|
repeat(n) { res += 1.0 }
|
||||||
blackhole.consume(res)
|
blackhole.consume(res)
|
||||||
}
|
}
|
||||||
@ -86,7 +85,7 @@ internal class NDFieldBenchmark {
|
|||||||
private companion object {
|
private companion object {
|
||||||
private const val dim = 1000
|
private const val dim = 1000
|
||||||
private const val n = 100
|
private const val n = 100
|
||||||
private val shape = intArrayOf(dim, dim)
|
private val shape = ShapeND(dim, dim)
|
||||||
private val specializedField = DoubleField.ndAlgebra
|
private val specializedField = DoubleField.ndAlgebra
|
||||||
private val genericField = BufferedFieldOpsND(DoubleField)
|
private val genericField = BufferedFieldOpsND(DoubleField)
|
||||||
private val nd4jField = DoubleField.nd4j
|
private val nd4jField = DoubleField.nd4j
|
||||||
|
@ -13,6 +13,8 @@ import space.kscience.kmath.linear.linearSpace
|
|||||||
import space.kscience.kmath.linear.matrix
|
import space.kscience.kmath.linear.matrix
|
||||||
import space.kscience.kmath.linear.symmetric
|
import space.kscience.kmath.linear.symmetric
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
|
import space.kscience.kmath.tensors.core.symEigJacobi
|
||||||
|
import space.kscience.kmath.tensors.core.symEigSvd
|
||||||
import space.kscience.kmath.tensors.core.tensorAlgebra
|
import space.kscience.kmath.tensors.core.tensorAlgebra
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -27,11 +29,11 @@ internal class TensorAlgebraBenchmark {
|
|||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
fun tensorSymEigSvd(blackhole: Blackhole) = with(Double.tensorAlgebra) {
|
fun tensorSymEigSvd(blackhole: Blackhole) = with(Double.tensorAlgebra) {
|
||||||
blackhole.consume(matrix.symEigSvd(1e-10))
|
blackhole.consume(symEigSvd(matrix, 1e-10))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
fun tensorSymEigJacobi(blackhole: Blackhole) = with(Double.tensorAlgebra) {
|
fun tensorSymEigJacobi(blackhole: Blackhole) = with(Double.tensorAlgebra) {
|
||||||
blackhole.consume(matrix.symEigJacobi(50, 1e-10))
|
blackhole.consume(symEigJacobi(matrix, 50, 1e-10))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ import kotlinx.benchmark.Blackhole
|
|||||||
import kotlinx.benchmark.Scope
|
import kotlinx.benchmark.Scope
|
||||||
import kotlinx.benchmark.State
|
import kotlinx.benchmark.State
|
||||||
import org.jetbrains.bio.viktor.F64Array
|
import org.jetbrains.bio.viktor.F64Array
|
||||||
import space.kscience.kmath.nd.Shape
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.nd.ndAlgebra
|
import space.kscience.kmath.nd.ndAlgebra
|
||||||
import space.kscience.kmath.nd.one
|
import space.kscience.kmath.nd.one
|
||||||
@ -49,7 +49,7 @@ internal class ViktorBenchmark {
|
|||||||
private companion object {
|
private companion object {
|
||||||
private const val dim = 1000
|
private const val dim = 1000
|
||||||
private const val n = 100
|
private const val n = 100
|
||||||
private val shape = Shape(dim, dim)
|
private val shape = ShapeND(dim, dim)
|
||||||
|
|
||||||
// automatically build context most suited for given type.
|
// automatically build context most suited for given type.
|
||||||
private val doubleField = DoubleField.ndAlgebra
|
private val doubleField = DoubleField.ndAlgebra
|
||||||
|
@ -10,7 +10,7 @@ import kotlinx.benchmark.Blackhole
|
|||||||
import kotlinx.benchmark.Scope
|
import kotlinx.benchmark.Scope
|
||||||
import kotlinx.benchmark.State
|
import kotlinx.benchmark.State
|
||||||
import org.jetbrains.bio.viktor.F64Array
|
import org.jetbrains.bio.viktor.F64Array
|
||||||
import space.kscience.kmath.nd.Shape
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.nd.ndAlgebra
|
import space.kscience.kmath.nd.ndAlgebra
|
||||||
import space.kscience.kmath.nd.one
|
import space.kscience.kmath.nd.one
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
@ -49,7 +49,7 @@ internal class ViktorLogBenchmark {
|
|||||||
private companion object {
|
private companion object {
|
||||||
private const val dim = 1000
|
private const val dim = 1000
|
||||||
private const val n = 100
|
private const val n = 100
|
||||||
private val shape = Shape(dim, dim)
|
private val shape = ShapeND(dim, dim)
|
||||||
|
|
||||||
// automatically build context most suited for given type.
|
// automatically build context most suited for given type.
|
||||||
private val doubleField = DoubleField.ndAlgebra
|
private val doubleField = DoubleField.ndAlgebra
|
||||||
|
@ -15,7 +15,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.3.1-dev-4"
|
version = "0.3.1-dev-7"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
@ -79,9 +79,9 @@ ksciencePublish {
|
|||||||
github("kmath", "SciProgCentre")
|
github("kmath", "SciProgCentre")
|
||||||
space(
|
space(
|
||||||
if (isInDevelopment) {
|
if (isInDevelopment) {
|
||||||
"https://maven.pkg.jetbrains.space/mipt-npm/p/sci/dev"
|
"https://maven.pkg.jetbrains.space/spc/p/sci/dev"
|
||||||
} else {
|
} else {
|
||||||
"https://maven.pkg.jetbrains.space/mipt-npm/p/sci/release"
|
"https://maven.pkg.jetbrains.space/spc/p/sci/maven"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sonatype()
|
sonatype()
|
||||||
|
6
docs/templates/ARTIFACT-TEMPLATE.md
vendored
6
docs/templates/ARTIFACT-TEMPLATE.md
vendored
@ -3,10 +3,12 @@
|
|||||||
The Maven coordinates of this project are `${group}:${name}:${version}`.
|
The Maven coordinates of this project are `${group}:${name}:${version}`.
|
||||||
|
|
||||||
**Gradle:**
|
**Gradle:**
|
||||||
```gradle
|
```groovy
|
||||||
repositories {
|
repositories {
|
||||||
maven { url 'https://repo.kotlin.link' }
|
maven { url 'https://repo.kotlin.link' }
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
// development and snapshot versions
|
||||||
|
maven { url 'https://maven.pkg.jetbrains.space/spc/p/sci/dev' }
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -18,6 +20,8 @@ dependencies {
|
|||||||
repositories {
|
repositories {
|
||||||
maven("https://repo.kotlin.link")
|
maven("https://repo.kotlin.link")
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
// development and snapshot versions
|
||||||
|
maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -18,7 +18,6 @@ dependencies {
|
|||||||
implementation(project(":kmath-commons"))
|
implementation(project(":kmath-commons"))
|
||||||
implementation(project(":kmath-complex"))
|
implementation(project(":kmath-complex"))
|
||||||
implementation(project(":kmath-functions"))
|
implementation(project(":kmath-functions"))
|
||||||
implementation(project(":kmath-polynomial"))
|
|
||||||
implementation(project(":kmath-optimization"))
|
implementation(project(":kmath-optimization"))
|
||||||
implementation(project(":kmath-stat"))
|
implementation(project(":kmath-stat"))
|
||||||
implementation(project(":kmath-viktor"))
|
implementation(project(":kmath-viktor"))
|
||||||
|
@ -18,10 +18,10 @@ import space.kscience.kmath.optimization.FunctionOptimizationTarget
|
|||||||
import space.kscience.kmath.optimization.optimizeWith
|
import space.kscience.kmath.optimization.optimizeWith
|
||||||
import space.kscience.kmath.optimization.resultPoint
|
import space.kscience.kmath.optimization.resultPoint
|
||||||
import space.kscience.kmath.optimization.resultValue
|
import space.kscience.kmath.optimization.resultValue
|
||||||
|
import space.kscience.kmath.random.RandomGenerator
|
||||||
import space.kscience.kmath.real.DoubleVector
|
import space.kscience.kmath.real.DoubleVector
|
||||||
import space.kscience.kmath.real.map
|
import space.kscience.kmath.real.map
|
||||||
import space.kscience.kmath.real.step
|
import space.kscience.kmath.real.step
|
||||||
import space.kscience.kmath.stat.RandomGenerator
|
|
||||||
import space.kscience.plotly.*
|
import space.kscience.plotly.*
|
||||||
import space.kscience.plotly.models.ScatterMode
|
import space.kscience.plotly.models.ScatterMode
|
||||||
import space.kscience.plotly.models.TraceValues
|
import space.kscience.plotly.models.TraceValues
|
||||||
|
@ -19,9 +19,9 @@ import space.kscience.kmath.optimization.QowOptimizer
|
|||||||
import space.kscience.kmath.optimization.chiSquaredOrNull
|
import space.kscience.kmath.optimization.chiSquaredOrNull
|
||||||
import space.kscience.kmath.optimization.fitWith
|
import space.kscience.kmath.optimization.fitWith
|
||||||
import space.kscience.kmath.optimization.resultPoint
|
import space.kscience.kmath.optimization.resultPoint
|
||||||
|
import space.kscience.kmath.random.RandomGenerator
|
||||||
import space.kscience.kmath.real.map
|
import space.kscience.kmath.real.map
|
||||||
import space.kscience.kmath.real.step
|
import space.kscience.kmath.real.step
|
||||||
import space.kscience.kmath.stat.RandomGenerator
|
|
||||||
import space.kscience.plotly.*
|
import space.kscience.plotly.*
|
||||||
import space.kscience.plotly.models.ScatterMode
|
import space.kscience.plotly.models.ScatterMode
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
@ -1,399 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018-2022 KMath contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("LocalVariableName")
|
|
||||||
|
|
||||||
package space.kscience.kmath.functions
|
|
||||||
|
|
||||||
import space.kscience.kmath.expressions.Symbol
|
|
||||||
import space.kscience.kmath.expressions.symbol
|
|
||||||
import space.kscience.kmath.operations.algebra
|
|
||||||
import space.kscience.kmath.operations.invoke
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows [ListPolynomial]s' and [ListRationalFunction]s' capabilities.
|
|
||||||
*/
|
|
||||||
fun listPolynomialsExample() {
|
|
||||||
// [ListPolynomial] is a representation of a univariate polynomial as a list of coefficients from the least term to
|
|
||||||
// the greatest term. For example,
|
|
||||||
val polynomial1: ListPolynomial<Int> = ListPolynomial(listOf(2, -3, 1))
|
|
||||||
// represents polynomial 2 + (-3) x + x^2
|
|
||||||
|
|
||||||
// There are also shortcut fabrics:
|
|
||||||
val polynomial2: ListPolynomial<Int> = ListPolynomial(2, -3, 1)
|
|
||||||
println(polynomial1 == polynomial2) // true
|
|
||||||
// and even
|
|
||||||
val polynomial3: ListPolynomial<Int> = 57.asListPolynomial()
|
|
||||||
val polynomial4: ListPolynomial<Int> = ListPolynomial(listOf(57))
|
|
||||||
println(polynomial3 == polynomial4) // true
|
|
||||||
|
|
||||||
val polynomial5: ListPolynomial<Int> = ListPolynomial(3, -1)
|
|
||||||
// For every ring there can be provided a polynomial ring:
|
|
||||||
Int.algebra.listPolynomialSpace {
|
|
||||||
println(-polynomial5 == ListPolynomial(-3, 1)) // true
|
|
||||||
println(polynomial1 + polynomial5 == ListPolynomial(5, -4, 1)) // true
|
|
||||||
println(polynomial1 - polynomial5 == ListPolynomial(-1, -2, 1)) // true
|
|
||||||
println(polynomial1 * polynomial5 == ListPolynomial(6, -11, 6, -1)) // true
|
|
||||||
}
|
|
||||||
// You can even write
|
|
||||||
val x: ListPolynomial<Double> = ListPolynomial(0.0, 1.0)
|
|
||||||
val polynomial6: ListPolynomial<Double> = ListPolynomial(2.0, -3.0, 1.0)
|
|
||||||
Double.algebra.listPolynomialSpace {
|
|
||||||
println(2 - 3 * x + x * x == polynomial6)
|
|
||||||
println(2.0 - 3.0 * x + x * x == polynomial6)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also there are some utilities for polynomials:
|
|
||||||
println(polynomial1.substitute(Int.algebra, 1) == 0) // true, because 2 + (-3) * 1 + 1^2 = 0
|
|
||||||
println(polynomial1.substitute(Int.algebra, polynomial5) == polynomial1) // true, because 2 + (-3) * (3-x) + (3-x)^2 = 2 - 3x + x^2
|
|
||||||
println(polynomial1.derivative(Int.algebra) == ListPolynomial(-3, 2)) // true, (2 - 3x + x^2)' = -3 + 2x
|
|
||||||
println(polynomial1.nthDerivative(Int.algebra, 2) == 2.asListPolynomial()) // true, (2 - 3x + x^2)'' = 2
|
|
||||||
|
|
||||||
// Lastly, there are rational functions and some other utilities:
|
|
||||||
Double.algebra.listRationalFunctionSpace {
|
|
||||||
val rationalFunction1: ListRationalFunction<Double> = ListRationalFunction(listOf(2.0, -3.0, 1.0), listOf(3.0, -1.0))
|
|
||||||
// It's just (2 - 3x + x^2)/(3 - x)
|
|
||||||
|
|
||||||
val rationalFunction2 : ListRationalFunction<Double> = ListRationalFunction(listOf(5.0, -4.0, 1.0), listOf(3.0, -1.0))
|
|
||||||
// It's just (5 - 4x + x^2)/(3 - x)
|
|
||||||
|
|
||||||
println(rationalFunction1 + 1 == rationalFunction2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows [NumberedPolynomial]s' and [NumberedRationalFunction]s' capabilities.
|
|
||||||
*/
|
|
||||||
fun numberedPolynomialsExample() {
|
|
||||||
// Consider polynomial
|
|
||||||
// 3 + 5 x_2 - 7 x_1^2 x_3
|
|
||||||
// Consider, for example, its term -7 x_1^2 x_3. -7 is a coefficient of the term, whereas (2, 0, 1, 0, 0, ...) is
|
|
||||||
// description of degrees of variables x_1, x_2, ... in the term. Such description with removed leading zeros
|
|
||||||
// [2, 0, 1] is called "signature" of the term -7 x_1^2 x_3.
|
|
||||||
|
|
||||||
val polynomial1: NumberedPolynomial<Int>
|
|
||||||
with(Int.algebra) {
|
|
||||||
// [NumberedPolynomial] is a representation of a multivariate polynomial, that stores terms in a map with terms'
|
|
||||||
// signatures as the map's keys and terms' coefficients as corresponding values. For example,
|
|
||||||
polynomial1 = NumberedPolynomial(
|
|
||||||
mapOf(
|
|
||||||
listOf<UInt>() to 3,
|
|
||||||
listOf(0u, 1u) to 5,
|
|
||||||
listOf(2u, 0u, 1u) to -7,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// represents polynomial 3 + 5 x_2 - 7 x_1^2 x_3
|
|
||||||
|
|
||||||
// This `NumberedPolynomial` function needs context of either ring of constant (as `Int.algebra` in this example)
|
|
||||||
// or space of NumberedPolynomials over it. To understand why it is like this see documentations of functions
|
|
||||||
// NumberedPolynomial and NumberedPolynomialWithoutCheck
|
|
||||||
|
|
||||||
// There are also shortcut fabrics:
|
|
||||||
val polynomial2: NumberedPolynomial<Int> = NumberedPolynomial(
|
|
||||||
listOf<UInt>() to 3,
|
|
||||||
listOf(0u, 1u) to 5,
|
|
||||||
listOf(2u, 0u, 1u) to -7,
|
|
||||||
)
|
|
||||||
println(polynomial1 == polynomial2) // true
|
|
||||||
// and even
|
|
||||||
val polynomial3: NumberedPolynomial<Int> = 57.asNumberedPolynomial() // This one actually does not algebraic context!
|
|
||||||
val polynomial4: NumberedPolynomial<Int> = NumberedPolynomial(listOf<UInt>() to 57)
|
|
||||||
println(polynomial3 == polynomial4) // true
|
|
||||||
|
|
||||||
numberedPolynomialSpace {
|
|
||||||
// Also there is DSL for constructing NumberedPolynomials:
|
|
||||||
val polynomial5: NumberedPolynomial<Int> = NumberedPolynomialDSL1 {
|
|
||||||
3 {}
|
|
||||||
5 { 1 inPowerOf 1u }
|
|
||||||
-7 with { 0 pow 2u; 2 pow 1u }
|
|
||||||
// `pow` and `inPowerOf` are the same
|
|
||||||
// `with` is omittable
|
|
||||||
}
|
|
||||||
println(polynomial1 == polynomial5) // true
|
|
||||||
|
|
||||||
// Unfortunately the DSL does not work good in bare context of constants' ring, so for now it's disabled and
|
|
||||||
// works only in NumberedPolynomialSpace and NumberedRationalFunctionSpace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val polynomial6: NumberedPolynomial<Int> = Int.algebra {
|
|
||||||
NumberedPolynomial(
|
|
||||||
listOf<UInt>() to 7,
|
|
||||||
listOf(0u, 1u) to -5,
|
|
||||||
listOf(2u, 0u, 1u) to 0,
|
|
||||||
listOf(0u, 0u, 0u, 4u) to 4,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// For every ring there can be provided a polynomial ring:
|
|
||||||
Int.algebra.numberedPolynomialSpace {
|
|
||||||
println(
|
|
||||||
-polynomial6 == NumberedPolynomial(
|
|
||||||
listOf<UInt>() to -7,
|
|
||||||
listOf(0u, 1u) to 5,
|
|
||||||
listOf(2u, 0u, 1u) to 0,
|
|
||||||
listOf(0u, 0u, 0u, 4u) to (-4),
|
|
||||||
)
|
|
||||||
) // true
|
|
||||||
println(
|
|
||||||
polynomial1 + polynomial6 == NumberedPolynomial(
|
|
||||||
listOf<UInt>() to 10,
|
|
||||||
listOf(0u, 1u) to 0,
|
|
||||||
listOf(2u, 0u, 1u) to -7,
|
|
||||||
listOf(0u, 0u, 0u, 4u) to 4,
|
|
||||||
)
|
|
||||||
) // true
|
|
||||||
println(
|
|
||||||
polynomial1 - polynomial6 == NumberedPolynomial(
|
|
||||||
listOf<UInt>() to -4,
|
|
||||||
listOf(0u, 1u) to 10,
|
|
||||||
listOf(2u, 0u, 1u) to -7,
|
|
||||||
listOf(0u, 0u, 0u, 4u) to -4,
|
|
||||||
)
|
|
||||||
) // true
|
|
||||||
|
|
||||||
polynomial1 * polynomial6 // Multiplication works too
|
|
||||||
}
|
|
||||||
|
|
||||||
Double.algebra.numberedPolynomialSpace {
|
|
||||||
// You can even write
|
|
||||||
val x_1: NumberedPolynomial<Double> = NumberedPolynomial(listOf(1u) to 1.0)
|
|
||||||
val x_2: NumberedPolynomial<Double> = NumberedPolynomial(listOf(0u, 1u) to 1.0)
|
|
||||||
val x_3: NumberedPolynomial<Double> = NumberedPolynomial(listOf(0u, 0u, 1u) to 1.0)
|
|
||||||
val polynomial7: NumberedPolynomial<Double> = NumberedPolynomial(
|
|
||||||
listOf<UInt>() to 3.0,
|
|
||||||
listOf(0u, 1u) to 5.0,
|
|
||||||
listOf(2u, 0u, 1u) to -7.0,
|
|
||||||
)
|
|
||||||
Double.algebra.listPolynomialSpace {
|
|
||||||
println(3 + 5 * x_2 - 7 * x_1 * x_1 * x_3 == polynomial7)
|
|
||||||
println(3.0 + 5.0 * x_2 - 7.0 * x_1 * x_1 * x_3 == polynomial7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Int.algebra.numberedPolynomialSpace {
|
|
||||||
val x_4: NumberedPolynomial<Int> = NumberedPolynomial(listOf(0u, 0u, 0u, 4u) to 1)
|
|
||||||
// Also there are some utilities for polynomials:
|
|
||||||
println(polynomial1.substitute(mapOf(0 to 1, 1 to -2, 2 to -1)) == 0.asNumberedPolynomial()) // true,
|
|
||||||
// because it's substitution x_1 -> 1, x_2 -> -2, x_3 -> -1,
|
|
||||||
// so 3 + 5 x_2 - 7 x_1^2 x_3 = 3 + 5 * (-2) - 7 * 1^2 * (-1) = 3 - 10 + 7 = 0
|
|
||||||
println(
|
|
||||||
polynomial1.substitute(mapOf(1 to x_4)) == NumberedPolynomial(
|
|
||||||
listOf<UInt>() to 3,
|
|
||||||
listOf(0u, 1u) to 5,
|
|
||||||
listOf(2u, 0u, 1u) to -7,
|
|
||||||
)
|
|
||||||
) // true, because it's substitution x_2 -> x_4, so result is 3 + 5 x_4 - 7 x_1^2 x_3
|
|
||||||
println(
|
|
||||||
polynomial1.derivativeWithRespectTo(Int.algebra, 1) ==
|
|
||||||
NumberedPolynomial(listOf<UInt>() to 5)
|
|
||||||
) // true, d/dx_2 (3 + 5 x_2 - 7 x_1^2 x_3) = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lastly, there are rational functions and some other utilities:
|
|
||||||
Double.algebra.numberedRationalFunctionSpace {
|
|
||||||
val rationalFunction1: NumberedRationalFunction<Double> = NumberedRationalFunction(
|
|
||||||
NumberedPolynomial(
|
|
||||||
listOf<UInt>() to 2.0,
|
|
||||||
listOf(1u) to -3.0,
|
|
||||||
listOf(2u) to 1.0,
|
|
||||||
),
|
|
||||||
NumberedPolynomial(
|
|
||||||
listOf<UInt>() to 3.0,
|
|
||||||
listOf(1u) to -1.0,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// It's just (2 - 3x + x^2)/(3 - x) where x = x_1
|
|
||||||
|
|
||||||
val rationalFunction2: NumberedRationalFunction<Double> = NumberedRationalFunction(
|
|
||||||
NumberedPolynomial(
|
|
||||||
listOf<UInt>() to 5.0,
|
|
||||||
listOf(1u) to -4.0,
|
|
||||||
listOf(2u) to 1.0,
|
|
||||||
),
|
|
||||||
NumberedPolynomial(
|
|
||||||
listOf<UInt>() to 3.0,
|
|
||||||
listOf(1u) to -1.0,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// It's just (5 - 4x + x^2)/(3 - x) where x = x_1
|
|
||||||
|
|
||||||
println(rationalFunction1 + 1 == rationalFunction2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows [LabeledPolynomial]s' and [LabeledRationalFunction]s' capabilities.
|
|
||||||
*/
|
|
||||||
fun labeledPolynomialsExample() {
|
|
||||||
val x by symbol
|
|
||||||
val y by symbol
|
|
||||||
val z by symbol
|
|
||||||
val t by symbol
|
|
||||||
|
|
||||||
// Consider polynomial
|
|
||||||
// 3 + 5 y - 7 x^2 z
|
|
||||||
// Consider, for example, its term -7 x^2 z. -7 is a coefficient of the term, whereas matching (x -> 2, z -> 3) is
|
|
||||||
// description of degrees of variables x_1, x_2, ... in the term. Such description is called "signature" of the
|
|
||||||
// term -7 x_1^2 x_3.
|
|
||||||
|
|
||||||
val polynomial1: LabeledPolynomial<Int>
|
|
||||||
with(Int.algebra) {
|
|
||||||
// [LabeledPolynomial] is a representation of a multivariate polynomial, that stores terms in a map with terms'
|
|
||||||
// signatures as the map's keys and terms' coefficients as corresponding values. For example,
|
|
||||||
polynomial1 = LabeledPolynomial(
|
|
||||||
mapOf(
|
|
||||||
mapOf<Symbol, UInt>() to 3,
|
|
||||||
mapOf(y to 1u) to 5,
|
|
||||||
mapOf(x to 2u, z to 1u) to -7,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// represents polynomial 3 + 5 y - 7 x^2 z
|
|
||||||
|
|
||||||
// This `LabeledPolynomial` function needs context of either ring of constant (as `Int.algebra` in this example)
|
|
||||||
// or space of LabeledPolynomials over it. To understand why it is like this see documentations of functions
|
|
||||||
// LabeledPolynomial and LabeledPolynomialWithoutCheck
|
|
||||||
|
|
||||||
// There are also shortcut fabrics:
|
|
||||||
val polynomial2: LabeledPolynomial<Int> = LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to 3,
|
|
||||||
mapOf(y to 1u) to 5,
|
|
||||||
mapOf(x to 2u, z to 1u) to -7,
|
|
||||||
)
|
|
||||||
println(polynomial1 == polynomial2) // true
|
|
||||||
// and even
|
|
||||||
val polynomial3: LabeledPolynomial<Int> = 57.asLabeledPolynomial() // This one actually does not algebraic context!
|
|
||||||
val polynomial4: LabeledPolynomial<Int> = LabeledPolynomial(mapOf<Symbol, UInt>() to 57)
|
|
||||||
println(polynomial3 == polynomial4) // true
|
|
||||||
|
|
||||||
labeledPolynomialSpace {
|
|
||||||
// Also there is DSL for constructing NumberedPolynomials:
|
|
||||||
val polynomial5: LabeledPolynomial<Int> = LabeledPolynomialDSL1 {
|
|
||||||
3 {}
|
|
||||||
5 { y inPowerOf 1u }
|
|
||||||
-7 with { x pow 2u; z pow 1u }
|
|
||||||
// `pow` and `inPowerOf` are the same
|
|
||||||
// `with` is omittable
|
|
||||||
}
|
|
||||||
println(polynomial1 == polynomial5) // true
|
|
||||||
|
|
||||||
// Unfortunately the DSL does not work good in bare context of constants' ring, so for now it's disabled and
|
|
||||||
// works only in NumberedPolynomialSpace and NumberedRationalFunctionSpace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val polynomial6: LabeledPolynomial<Int> = Int.algebra {
|
|
||||||
LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to 7,
|
|
||||||
mapOf(y to 1u) to -5,
|
|
||||||
mapOf(x to 2u, z to 1u) to 0,
|
|
||||||
mapOf(t to 4u) to 4,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// For every ring there can be provided a polynomial ring:
|
|
||||||
Int.algebra.labeledPolynomialSpace {
|
|
||||||
println(
|
|
||||||
-polynomial6 == LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to -7,
|
|
||||||
mapOf(y to 1u) to 5,
|
|
||||||
mapOf(x to 2u, z to 1u) to 0,
|
|
||||||
mapOf(t to 4u) to -4,
|
|
||||||
)
|
|
||||||
) // true
|
|
||||||
println(
|
|
||||||
polynomial1 + polynomial6 == LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to 10,
|
|
||||||
mapOf(y to 1u) to 0,
|
|
||||||
mapOf(x to 2u, z to 1u) to -7,
|
|
||||||
mapOf(t to 4u) to 4,
|
|
||||||
)
|
|
||||||
) // true
|
|
||||||
println(
|
|
||||||
polynomial1 - polynomial6 == LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to -4,
|
|
||||||
mapOf(y to 1u) to 10,
|
|
||||||
mapOf(x to 2u, z to 1u) to -7,
|
|
||||||
mapOf(t to 4u) to -4,
|
|
||||||
)
|
|
||||||
) // true
|
|
||||||
|
|
||||||
polynomial1 * polynomial6 // Multiplication works too
|
|
||||||
}
|
|
||||||
|
|
||||||
Double.algebra.labeledPolynomialSpace {
|
|
||||||
// You can even write
|
|
||||||
val polynomial7: LabeledPolynomial<Double> = LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to 3.0,
|
|
||||||
mapOf(y to 1u) to 5.0,
|
|
||||||
mapOf(x to 2u, z to 1u) to -7.0,
|
|
||||||
)
|
|
||||||
Double.algebra.listPolynomialSpace {
|
|
||||||
println(3 + 5 * y - 7 * x * x * z == polynomial7)
|
|
||||||
println(3.0 + 5.0 * y - 7.0 * x * x * z == polynomial7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Int.algebra.labeledPolynomialSpace {
|
|
||||||
// Also there are some utilities for polynomials:
|
|
||||||
println(polynomial1.substitute(mapOf(x to 1, y to -2, z to -1)) == 0.asLabeledPolynomial()) // true,
|
|
||||||
// because it's substitution x -> 1, y -> -2, z -> -1,
|
|
||||||
// so 3 + 5 y - 7 x^2 z = 3 + 5 * (-2) - 7 * 1^2 * (-1) = 3 - 10 + 7 = 0
|
|
||||||
println(
|
|
||||||
polynomial1.substitute(mapOf(y to t.asPolynomial())) == LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to 3,
|
|
||||||
mapOf(t to 1u) to 5,
|
|
||||||
mapOf(x to 2u, z to 1u) to -7,
|
|
||||||
)
|
|
||||||
) // true, because it's substitution y -> t, so result is 3 + 5 t - 7 x^2 z
|
|
||||||
println(
|
|
||||||
polynomial1.derivativeWithRespectTo(Int.algebra, y) == LabeledPolynomial(mapOf<Symbol, UInt>() to 5)
|
|
||||||
) // true, d/dy (3 + 5 y - 7 x^2 z) = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lastly, there are rational functions and some other utilities:
|
|
||||||
Double.algebra.labeledRationalFunctionSpace {
|
|
||||||
val rationalFunction1: LabeledRationalFunction<Double> = LabeledRationalFunction(
|
|
||||||
LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to 2.0,
|
|
||||||
mapOf(x to 1u) to -3.0,
|
|
||||||
mapOf(x to 2u) to 1.0,
|
|
||||||
),
|
|
||||||
LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to 3.0,
|
|
||||||
mapOf(x to 1u) to -1.0,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// It's just (2 - 3x + x^2)/(3 - x)
|
|
||||||
|
|
||||||
val rationalFunction2: LabeledRationalFunction<Double> = LabeledRationalFunction(
|
|
||||||
LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to 5.0,
|
|
||||||
mapOf(x to 1u) to -4.0,
|
|
||||||
mapOf(x to 2u) to 1.0,
|
|
||||||
),
|
|
||||||
LabeledPolynomial(
|
|
||||||
mapOf<Symbol, UInt>() to 3.0,
|
|
||||||
mapOf(x to 1u) to -1.0,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// It's just (5 - 4x + x^2)/(3 - x)
|
|
||||||
|
|
||||||
println(rationalFunction1 + 1 == rationalFunction2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
println("ListPolynomials:")
|
|
||||||
listPolynomialsExample()
|
|
||||||
println()
|
|
||||||
|
|
||||||
println("NumberedPolynomials:")
|
|
||||||
numberedPolynomialsExample()
|
|
||||||
println()
|
|
||||||
|
|
||||||
println("ListPolynomials:")
|
|
||||||
labeledPolynomialsExample()
|
|
||||||
println()
|
|
||||||
}
|
|
@ -8,14 +8,14 @@ package space.kscience.kmath.operations
|
|||||||
import space.kscience.kmath.commons.linear.CMLinearSpace
|
import space.kscience.kmath.commons.linear.CMLinearSpace
|
||||||
import space.kscience.kmath.linear.matrix
|
import space.kscience.kmath.linear.matrix
|
||||||
import space.kscience.kmath.nd.DoubleBufferND
|
import space.kscience.kmath.nd.DoubleBufferND
|
||||||
import space.kscience.kmath.nd.Shape
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.nd.Structure2D
|
import space.kscience.kmath.nd.Structure2D
|
||||||
import space.kscience.kmath.nd.ndAlgebra
|
import space.kscience.kmath.nd.ndAlgebra
|
||||||
import space.kscience.kmath.viktor.ViktorStructureND
|
import space.kscience.kmath.viktor.ViktorStructureND
|
||||||
import space.kscience.kmath.viktor.viktorAlgebra
|
import space.kscience.kmath.viktor.viktorAlgebra
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val viktorStructure: ViktorStructureND = DoubleField.viktorAlgebra.structureND(Shape(2, 2)) { (i, j) ->
|
val viktorStructure: ViktorStructureND = DoubleField.viktorAlgebra.structureND(ShapeND(2, 2)) { (i, j) ->
|
||||||
if (i == j) 2.0 else 0.0
|
if (i == j) 2.0 else 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package space.kscience.kmath.series
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.html.FlowContent
|
||||||
|
import kotlinx.html.h1
|
||||||
|
import space.kscience.kmath.operations.DoubleBufferOps
|
||||||
|
import space.kscience.kmath.operations.algebra
|
||||||
|
import space.kscience.kmath.operations.bufferAlgebra
|
||||||
|
import space.kscience.kmath.operations.toList
|
||||||
|
import space.kscience.kmath.stat.KMComparisonResult
|
||||||
|
import space.kscience.kmath.stat.ksComparisonStatistic
|
||||||
|
import space.kscience.kmath.structures.Buffer
|
||||||
|
import space.kscience.kmath.structures.slice
|
||||||
|
import space.kscience.plotly.*
|
||||||
|
import kotlin.math.PI
|
||||||
|
|
||||||
|
fun main() = with(Double.algebra.bufferAlgebra.seriesAlgebra()) {
|
||||||
|
fun FlowContent.plotSeries(buffer: Buffer<Double>) {
|
||||||
|
val ls = buffer.labels
|
||||||
|
plot {
|
||||||
|
scatter {
|
||||||
|
x.numbers = ls
|
||||||
|
y.numbers = buffer.toList()
|
||||||
|
}
|
||||||
|
layout {
|
||||||
|
xaxis {
|
||||||
|
range(0.0..100.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val s1 = series(100) { sin(2 * PI * it / 100) + 1.0 }
|
||||||
|
val s2 = s1.slice(20..50).moveTo(40)
|
||||||
|
|
||||||
|
val s3: Buffer<Double> = s1.zip(s2) { l, r -> l + r } //s1 + s2
|
||||||
|
val s4 = DoubleBufferOps.ln(s3)
|
||||||
|
|
||||||
|
val kmTest: KMComparisonResult<Double> = ksComparisonStatistic(s1, s2)
|
||||||
|
|
||||||
|
Plotly.page {
|
||||||
|
h1 { +"This is my plot" }
|
||||||
|
plotSeries(s1)
|
||||||
|
plotSeries(s2)
|
||||||
|
plotSeries(s4)
|
||||||
|
}.makeFile()
|
||||||
|
|
||||||
|
}
|
@ -10,6 +10,7 @@ import kotlinx.coroutines.async
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.apache.commons.rng.sampling.distribution.BoxMullerNormalizedGaussianSampler
|
import org.apache.commons.rng.sampling.distribution.BoxMullerNormalizedGaussianSampler
|
||||||
import org.apache.commons.rng.simple.RandomSource
|
import org.apache.commons.rng.simple.RandomSource
|
||||||
|
import space.kscience.kmath.random.RandomGenerator
|
||||||
import space.kscience.kmath.samplers.GaussianSampler
|
import space.kscience.kmath.samplers.GaussianSampler
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
@ -9,6 +9,7 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import space.kscience.kmath.chains.Chain
|
import space.kscience.kmath.chains.Chain
|
||||||
import space.kscience.kmath.chains.combineWithState
|
import space.kscience.kmath.chains.combineWithState
|
||||||
import space.kscience.kmath.distributions.NormalDistribution
|
import space.kscience.kmath.distributions.NormalDistribution
|
||||||
|
import space.kscience.kmath.random.RandomGenerator
|
||||||
|
|
||||||
private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0)
|
private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0)
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ fun main() {
|
|||||||
Nd4j.zeros(0)
|
Nd4j.zeros(0)
|
||||||
val dim = 1000
|
val dim = 1000
|
||||||
val n = 1000
|
val n = 1000
|
||||||
val shape = Shape(dim, dim)
|
val shape = ShapeND(dim, dim)
|
||||||
|
|
||||||
|
|
||||||
// specialized nd-field for Double. It works as generic Double field as well.
|
// specialized nd-field for Double. It works as generic Double field as well.
|
||||||
|
@ -17,11 +17,11 @@ import java.util.stream.IntStream
|
|||||||
* A demonstration implementation of NDField over Real using Java [java.util.stream.DoubleStream] for parallel
|
* A demonstration implementation of NDField over Real using Java [java.util.stream.DoubleStream] for parallel
|
||||||
* execution.
|
* execution.
|
||||||
*/
|
*/
|
||||||
class StreamDoubleFieldND(override val shape: IntArray) : FieldND<Double, DoubleField>,
|
class StreamDoubleFieldND(override val shape: ShapeND) : FieldND<Double, DoubleField>,
|
||||||
NumbersAddOps<StructureND<Double>>,
|
NumbersAddOps<StructureND<Double>>,
|
||||||
ExtendedField<StructureND<Double>> {
|
ExtendedField<StructureND<Double>> {
|
||||||
|
|
||||||
private val strides = DefaultStrides(shape)
|
private val strides = ColumnStrides(shape)
|
||||||
override val elementAlgebra: DoubleField get() = DoubleField
|
override val elementAlgebra: DoubleField get() = DoubleField
|
||||||
override val zero: BufferND<Double> by lazy { structureND(shape) { zero } }
|
override val zero: BufferND<Double> by lazy { structureND(shape) { zero } }
|
||||||
override val one: BufferND<Double> by lazy { structureND(shape) { one } }
|
override val one: BufferND<Double> by lazy { structureND(shape) { one } }
|
||||||
@ -31,17 +31,19 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND<Double, Double
|
|||||||
return structureND(shape) { d }
|
return structureND(shape) { d }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
private val StructureND<Double>.buffer: DoubleBuffer
|
private val StructureND<Double>.buffer: DoubleBuffer
|
||||||
get() = when {
|
get() = when {
|
||||||
!shape.contentEquals(this@StreamDoubleFieldND.shape) -> throw ShapeMismatchException(
|
!shape.contentEquals(this@StreamDoubleFieldND.shape) -> throw ShapeMismatchException(
|
||||||
this@StreamDoubleFieldND.shape,
|
this@StreamDoubleFieldND.shape,
|
||||||
shape
|
shape
|
||||||
)
|
)
|
||||||
this is BufferND && this.indices == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer
|
|
||||||
|
this is BufferND && indices == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer
|
||||||
else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
|
else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): BufferND<Double> {
|
override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): BufferND<Double> {
|
||||||
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
|
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
|
||||||
val index = strides.index(offset)
|
val index = strides.index(offset)
|
||||||
DoubleField.initializer(index)
|
DoubleField.initializer(index)
|
||||||
@ -109,4 +111,4 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND<Double, Double
|
|||||||
override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
|
override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DoubleField.ndStreaming(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(shape)
|
fun DoubleField.ndStreaming(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(ShapeND(shape))
|
||||||
|
@ -5,16 +5,19 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.structures
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.nd.BufferND
|
import space.kscience.kmath.nd.BufferND
|
||||||
import space.kscience.kmath.nd.DefaultStrides
|
import space.kscience.kmath.nd.ColumnStrides
|
||||||
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
fun main() {
|
fun main() {
|
||||||
val n = 6000
|
val n = 6000
|
||||||
val array = DoubleArray(n * n) { 1.0 }
|
val array = DoubleArray(n * n) { 1.0 }
|
||||||
val buffer = DoubleBuffer(array)
|
val buffer = DoubleBuffer(array)
|
||||||
val strides = DefaultStrides(intArrayOf(n, n))
|
val strides = ColumnStrides(ShapeND(n, n))
|
||||||
val structure = BufferND(strides, buffer)
|
val structure = BufferND(strides, buffer)
|
||||||
|
|
||||||
measureTimeMillis {
|
measureTimeMillis {
|
||||||
|
@ -5,16 +5,23 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.structures
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
|
import space.kscience.kmath.nd.BufferND
|
||||||
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.nd.mapToBuffer
|
import space.kscience.kmath.operations.mapToBuffer
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
private inline fun <T, reified R : Any> BufferND<T>.mapToBufferND(
|
||||||
|
bufferFactory: BufferFactory<R> = BufferFactory.auto(),
|
||||||
|
crossinline block: (T) -> R,
|
||||||
|
): BufferND<R> = BufferND(indices, buffer.mapToBuffer(bufferFactory, block))
|
||||||
|
|
||||||
@Suppress("UNUSED_VARIABLE")
|
@Suppress("UNUSED_VARIABLE")
|
||||||
fun main() {
|
fun main() {
|
||||||
val n = 6000
|
val n = 6000
|
||||||
val structure = StructureND.buffered(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 }
|
val structure = StructureND.buffered(ShapeND(n, n), Buffer.Companion::auto) { 1.0 }
|
||||||
structure.mapToBuffer { it + 1 } // warm-up
|
structure.mapToBufferND { it + 1 } // warm-up
|
||||||
val time1 = measureTimeMillis { val res = structure.mapToBuffer { it + 1 } }
|
val time1 = measureTimeMillis { val res = structure.mapToBufferND { it + 1 } }
|
||||||
println("Structure mapping finished in $time1 millis")
|
println("Structure mapping finished in $time1 millis")
|
||||||
val array = DoubleArray(n * n) { 1.0 }
|
val array = DoubleArray(n * n) { 1.0 }
|
||||||
|
|
||||||
|
@ -5,16 +5,17 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.tensors
|
package space.kscience.kmath.tensors
|
||||||
|
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.nd.ShapeND
|
||||||
|
import space.kscience.kmath.nd.contentEquals
|
||||||
import space.kscience.kmath.operations.invoke
|
import space.kscience.kmath.operations.invoke
|
||||||
import space.kscience.kmath.tensors.core.DoubleTensor
|
import space.kscience.kmath.tensors.core.DoubleTensor
|
||||||
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
||||||
|
import space.kscience.kmath.tensors.core.randomNormal
|
||||||
|
import space.kscience.kmath.tensors.core.randomNormalLike
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
// OLS estimator using SVD
|
// OLS estimator using SVD
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
|
||||||
fun main() {
|
fun main() {
|
||||||
//seed for random
|
//seed for random
|
||||||
val randSeed = 100500L
|
val randSeed = 100500L
|
||||||
@ -23,10 +24,10 @@ fun main() {
|
|||||||
DoubleTensorAlgebra {
|
DoubleTensorAlgebra {
|
||||||
// take coefficient vector from normal distribution
|
// take coefficient vector from normal distribution
|
||||||
val alpha = randomNormal(
|
val alpha = randomNormal(
|
||||||
intArrayOf(5),
|
ShapeND(5),
|
||||||
randSeed
|
randSeed
|
||||||
) + fromArray(
|
) + fromArray(
|
||||||
intArrayOf(5),
|
ShapeND(5),
|
||||||
doubleArrayOf(1.0, 2.5, 3.4, 5.0, 10.1)
|
doubleArrayOf(1.0, 2.5, 3.4, 5.0, 10.1)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,27 +35,29 @@ fun main() {
|
|||||||
|
|
||||||
// also take sample of size 20 from normal distribution for x
|
// also take sample of size 20 from normal distribution for x
|
||||||
val x = randomNormal(
|
val x = randomNormal(
|
||||||
intArrayOf(20, 5),
|
ShapeND(20, 5),
|
||||||
randSeed
|
randSeed
|
||||||
)
|
)
|
||||||
|
|
||||||
// calculate y and add gaussian noise (N(0, 0.05))
|
// calculate y and add gaussian noise (N(0, 0.05))
|
||||||
val y = x dot alpha
|
val y = x dot alpha
|
||||||
y += y.randomNormalLike(randSeed) * 0.05
|
y += randomNormalLike(y, randSeed) * 0.05
|
||||||
|
|
||||||
// now restore the coefficient vector with OSL estimator with SVD
|
// now restore the coefficient vector with OSL estimator with SVD
|
||||||
val (u, singValues, v) = x.svd()
|
val (u, singValues, v) = svd(x)
|
||||||
|
|
||||||
// we have to make sure the singular values of the matrix are not close to zero
|
// we have to make sure the singular values of the matrix are not close to zero
|
||||||
println("Singular values:\n$singValues")
|
println("Singular values:\n$singValues")
|
||||||
|
|
||||||
|
|
||||||
// inverse Sigma matrix can be restored from singular values with diagonalEmbedding function
|
// inverse Sigma matrix can be restored from singular values with diagonalEmbedding function
|
||||||
val sigma = diagonalEmbedding(singValues.map{ if (abs(it) < 1e-3) 0.0 else 1.0/it })
|
val sigma = diagonalEmbedding(singValues.map { if (abs(it) < 1e-3) 0.0 else 1.0 / it })
|
||||||
|
|
||||||
val alphaOLS = v dot sigma dot u.transposed() dot y
|
val alphaOLS = v dot sigma dot u.transposed() dot y
|
||||||
println("Estimated alpha:\n" +
|
println(
|
||||||
"$alphaOLS")
|
"Estimated alpha:\n" +
|
||||||
|
"$alphaOLS"
|
||||||
|
)
|
||||||
|
|
||||||
// figure out MSE of approximation
|
// figure out MSE of approximation
|
||||||
fun mse(yTrue: DoubleTensor, yPred: DoubleTensor): Double {
|
fun mse(yTrue: DoubleTensor, yPred: DoubleTensor): Double {
|
||||||
@ -62,7 +65,7 @@ fun main() {
|
|||||||
require(yTrue.shape contentEquals yPred.shape)
|
require(yTrue.shape contentEquals yPred.shape)
|
||||||
|
|
||||||
val diff = yTrue - yPred
|
val diff = yTrue - yPred
|
||||||
return diff.dot(diff).sqrt().value()
|
return sqrt(diff.dot(diff)).value()
|
||||||
}
|
}
|
||||||
|
|
||||||
println("MSE: ${mse(alpha, alphaOLS)}")
|
println("MSE: ${mse(alpha, alphaOLS)}")
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.tensors
|
package space.kscience.kmath.tensors
|
||||||
|
|
||||||
import space.kscience.kmath.tensors.core.tensorAlgebra
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.tensors.core.withBroadcast
|
import space.kscience.kmath.tensors.core.*
|
||||||
|
|
||||||
|
|
||||||
// simple PCA
|
// simple PCA
|
||||||
@ -16,12 +16,12 @@ fun main(): Unit = Double.tensorAlgebra.withBroadcast { // work in context with
|
|||||||
|
|
||||||
// assume x is range from 0 until 10
|
// assume x is range from 0 until 10
|
||||||
val x = fromArray(
|
val x = fromArray(
|
||||||
intArrayOf(10),
|
ShapeND(10),
|
||||||
DoubleArray(10) { it.toDouble() }
|
DoubleArray(10) { it.toDouble() }
|
||||||
)
|
)
|
||||||
|
|
||||||
// take y dependent on x with noise
|
// take y dependent on x with noise
|
||||||
val y = 2.0 * x + (3.0 + x.randomNormalLike(seed) * 1.5)
|
val y = 2.0 * x + (3.0 + randomNormalLike(x, seed) * 1.5)
|
||||||
|
|
||||||
println("x:\n$x")
|
println("x:\n$x")
|
||||||
println("y:\n$y")
|
println("y:\n$y")
|
||||||
@ -30,34 +30,34 @@ fun main(): Unit = Double.tensorAlgebra.withBroadcast { // work in context with
|
|||||||
val dataset = stack(listOf(x, y)).transposed()
|
val dataset = stack(listOf(x, y)).transposed()
|
||||||
|
|
||||||
// normalize both x and y
|
// normalize both x and y
|
||||||
val xMean = x.mean()
|
val xMean = mean(x)
|
||||||
val yMean = y.mean()
|
val yMean = mean(y)
|
||||||
|
|
||||||
val xStd = x.std()
|
val xStd = std(x)
|
||||||
val yStd = y.std()
|
val yStd = std(y)
|
||||||
|
|
||||||
val xScaled = (x - xMean) / xStd
|
val xScaled: DoubleTensor = (x - xMean) / xStd
|
||||||
val yScaled = (y - yMean) / yStd
|
val yScaled: DoubleTensor = (y - yMean) / yStd
|
||||||
|
|
||||||
// save means ans standard deviations for further recovery
|
// save means ans standard deviations for further recovery
|
||||||
val mean = fromArray(
|
val mean = fromArray(
|
||||||
intArrayOf(2),
|
ShapeND(2),
|
||||||
doubleArrayOf(xMean, yMean)
|
doubleArrayOf(xMean, yMean)
|
||||||
)
|
)
|
||||||
println("Means:\n$mean")
|
println("Means:\n$mean")
|
||||||
|
|
||||||
val std = fromArray(
|
val std = fromArray(
|
||||||
intArrayOf(2),
|
ShapeND(2),
|
||||||
doubleArrayOf(xStd, yStd)
|
doubleArrayOf(xStd, yStd)
|
||||||
)
|
)
|
||||||
println("Standard deviations:\n$std")
|
println("Standard deviations:\n$std")
|
||||||
|
|
||||||
// calculate the covariance matrix of scaled x and y
|
// calculate the covariance matrix of scaled x and y
|
||||||
val covMatrix = cov(listOf(xScaled, yScaled))
|
val covMatrix = covariance(listOf(xScaled.asDoubleTensor1D(), yScaled.asDoubleTensor1D()))
|
||||||
println("Covariance matrix:\n$covMatrix")
|
println("Covariance matrix:\n$covMatrix")
|
||||||
|
|
||||||
// and find out eigenvector of it
|
// and find out eigenvector of it
|
||||||
val (_, evecs) = covMatrix.symEig()
|
val (_, evecs) = symEig(covMatrix)
|
||||||
val v = evecs.getTensor(0)
|
val v = evecs.getTensor(0)
|
||||||
println("Eigenvector:\n$v")
|
println("Eigenvector:\n$v")
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ fun main(): Unit = Double.tensorAlgebra.withBroadcast { // work in context with
|
|||||||
// we can restore original data from reduced data;
|
// we can restore original data from reduced data;
|
||||||
// for example, find 7th element of dataset.
|
// for example, find 7th element of dataset.
|
||||||
val n = 7
|
val n = 7
|
||||||
val restored = (datasetReduced.getTensor(n) dot v.view(intArrayOf(1, 2))) * std + mean
|
val restored = (datasetReduced.getTensor(n) dot v.view(ShapeND(1, 2))) * std + mean
|
||||||
println("Original value:\n${dataset.getTensor(n)}")
|
println("Original value:\n${dataset.getTensor(n)}")
|
||||||
println("Restored value:\n$restored")
|
println("Restored value:\n$restored")
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.tensors
|
package space.kscience.kmath.tensors
|
||||||
|
|
||||||
|
import space.kscience.kmath.nd.ShapeND
|
||||||
|
import space.kscience.kmath.tensors.core.randomNormal
|
||||||
import space.kscience.kmath.tensors.core.tensorAlgebra
|
import space.kscience.kmath.tensors.core.tensorAlgebra
|
||||||
import space.kscience.kmath.tensors.core.withBroadcast
|
import space.kscience.kmath.tensors.core.withBroadcast
|
||||||
|
|
||||||
@ -13,17 +15,17 @@ import space.kscience.kmath.tensors.core.withBroadcast
|
|||||||
|
|
||||||
fun main() = Double.tensorAlgebra.withBroadcast { // work in context with broadcast methods
|
fun main() = Double.tensorAlgebra.withBroadcast { // work in context with broadcast methods
|
||||||
// take dataset of 5-element vectors from normal distribution
|
// take dataset of 5-element vectors from normal distribution
|
||||||
val dataset = randomNormal(intArrayOf(100, 5)) * 1.5 // all elements from N(0, 1.5)
|
val dataset = randomNormal(ShapeND(100, 5)) * 1.5 // all elements from N(0, 1.5)
|
||||||
|
|
||||||
dataset += fromArray(
|
dataset += fromArray(
|
||||||
intArrayOf(5),
|
ShapeND(5),
|
||||||
doubleArrayOf(0.0, 1.0, 1.5, 3.0, 5.0) // row means
|
doubleArrayOf(0.0, 1.0, 1.5, 3.0, 5.0) // row means
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// find out mean and standard deviation of each column
|
// find out mean and standard deviation of each column
|
||||||
val mean = dataset.mean(0, false)
|
val mean = mean(dataset, 0, false)
|
||||||
val std = dataset.std(0, false)
|
val std = std(dataset, 0, false)
|
||||||
|
|
||||||
println("Mean:\n$mean")
|
println("Mean:\n$mean")
|
||||||
println("Standard deviation:\n$std")
|
println("Standard deviation:\n$std")
|
||||||
@ -35,8 +37,8 @@ fun main() = Double.tensorAlgebra.withBroadcast { // work in context with broad
|
|||||||
// now we can scale dataset with mean normalization
|
// now we can scale dataset with mean normalization
|
||||||
val datasetScaled = (dataset - mean) / std
|
val datasetScaled = (dataset - mean) / std
|
||||||
|
|
||||||
// find out mean and std of scaled dataset
|
// find out mean and standardDiviation of scaled dataset
|
||||||
|
|
||||||
println("Mean of scaled:\n${datasetScaled.mean(0, false)}")
|
println("Mean of scaled:\n${mean(datasetScaled, 0, false)}")
|
||||||
println("Mean of scaled:\n${datasetScaled.std(0, false)}")
|
println("Mean of scaled:\n${std(datasetScaled, 0, false)}")
|
||||||
}
|
}
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.tensors
|
package space.kscience.kmath.tensors
|
||||||
|
|
||||||
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.tensors.core.DoubleTensor
|
import space.kscience.kmath.tensors.core.DoubleTensor
|
||||||
import space.kscience.kmath.tensors.core.tensorAlgebra
|
import space.kscience.kmath.tensors.core.tensorAlgebra
|
||||||
import space.kscience.kmath.tensors.core.withBroadcast
|
import space.kscience.kmath.tensors.core.withBroadcast
|
||||||
@ -15,13 +16,13 @@ fun main() = Double.tensorAlgebra.withBroadcast {// work in context with linear
|
|||||||
|
|
||||||
// set true value of x
|
// set true value of x
|
||||||
val trueX = fromArray(
|
val trueX = fromArray(
|
||||||
intArrayOf(4),
|
ShapeND(4),
|
||||||
doubleArrayOf(-2.0, 1.5, 6.8, -2.4)
|
doubleArrayOf(-2.0, 1.5, 6.8, -2.4)
|
||||||
)
|
)
|
||||||
|
|
||||||
// and A matrix
|
// and A matrix
|
||||||
val a = fromArray(
|
val a = fromArray(
|
||||||
intArrayOf(4, 4),
|
ShapeND(4, 4),
|
||||||
doubleArrayOf(
|
doubleArrayOf(
|
||||||
0.5, 10.5, 4.5, 1.0,
|
0.5, 10.5, 4.5, 1.0,
|
||||||
8.5, 0.9, 12.8, 0.1,
|
8.5, 0.9, 12.8, 0.1,
|
||||||
@ -40,7 +41,7 @@ fun main() = Double.tensorAlgebra.withBroadcast {// work in context with linear
|
|||||||
// solve `Ax = b` system using LUP decomposition
|
// solve `Ax = b` system using LUP decomposition
|
||||||
|
|
||||||
// get P, L, U such that PA = LU
|
// get P, L, U such that PA = LU
|
||||||
val (p, l, u) = a.lu()
|
val (p, l, u) = lu(a)
|
||||||
|
|
||||||
// check P is permutation matrix
|
// check P is permutation matrix
|
||||||
println("P:\n$p")
|
println("P:\n$p")
|
||||||
@ -64,7 +65,7 @@ fun main() = Double.tensorAlgebra.withBroadcast {// work in context with linear
|
|||||||
// this function returns solution x of a system lx = b, l should be lower triangular
|
// this function returns solution x of a system lx = b, l should be lower triangular
|
||||||
fun solveLT(l: DoubleTensor, b: DoubleTensor): DoubleTensor {
|
fun solveLT(l: DoubleTensor, b: DoubleTensor): DoubleTensor {
|
||||||
val n = l.shape[0]
|
val n = l.shape[0]
|
||||||
val x = zeros(intArrayOf(n))
|
val x = zeros(ShapeND(n))
|
||||||
for (i in 0 until n) {
|
for (i in 0 until n) {
|
||||||
x[intArrayOf(i)] = (b[intArrayOf(i)] - l.getTensor(i).dot(x).value()) / l[intArrayOf(i, i)]
|
x[intArrayOf(i)] = (b[intArrayOf(i)] - l.getTensor(i).dot(x).value()) / l[intArrayOf(i, i)]
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,11 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.tensors
|
package space.kscience.kmath.tensors
|
||||||
|
|
||||||
|
import space.kscience.kmath.nd.ShapeND
|
||||||
|
import space.kscience.kmath.nd.contentEquals
|
||||||
import space.kscience.kmath.operations.asIterable
|
import space.kscience.kmath.operations.asIterable
|
||||||
import space.kscience.kmath.operations.invoke
|
import space.kscience.kmath.operations.invoke
|
||||||
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra
|
import space.kscience.kmath.tensors.core.*
|
||||||
import space.kscience.kmath.tensors.core.DoubleTensor
|
|
||||||
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
|
||||||
import space.kscience.kmath.tensors.core.toDoubleTensor
|
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
const val seed = 100500L
|
const val seed = 100500L
|
||||||
@ -49,7 +48,7 @@ fun reluDer(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
|||||||
class ReLU : Activation(::relu, ::reluDer)
|
class ReLU : Activation(::relu, ::reluDer)
|
||||||
|
|
||||||
fun sigmoid(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
fun sigmoid(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
||||||
1.0 / (1.0 + (-x).exp())
|
1.0 / (1.0 + exp((-x)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sigmoidDer(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
fun sigmoidDer(x: DoubleTensor): DoubleTensor = DoubleTensorAlgebra {
|
||||||
@ -68,12 +67,12 @@ class Dense(
|
|||||||
|
|
||||||
private val weights: DoubleTensor = DoubleTensorAlgebra {
|
private val weights: DoubleTensor = DoubleTensorAlgebra {
|
||||||
randomNormal(
|
randomNormal(
|
||||||
intArrayOf(inputUnits, outputUnits),
|
ShapeND(inputUnits, outputUnits),
|
||||||
seed
|
seed
|
||||||
) * sqrt(2.0 / (inputUnits + outputUnits))
|
) * sqrt(2.0 / (inputUnits + outputUnits))
|
||||||
}
|
}
|
||||||
|
|
||||||
private val bias: DoubleTensor = DoubleTensorAlgebra { zeros(intArrayOf(outputUnits)) }
|
private val bias: DoubleTensor = DoubleTensorAlgebra { zeros(ShapeND(outputUnits)) }
|
||||||
|
|
||||||
override fun forward(input: DoubleTensor): DoubleTensor = BroadcastDoubleTensorAlgebra {
|
override fun forward(input: DoubleTensor): DoubleTensor = BroadcastDoubleTensorAlgebra {
|
||||||
(input dot weights) + bias
|
(input dot weights) + bias
|
||||||
@ -83,7 +82,7 @@ class Dense(
|
|||||||
val gradInput = outputError dot weights.transposed()
|
val gradInput = outputError dot weights.transposed()
|
||||||
|
|
||||||
val gradW = input.transposed() dot outputError
|
val gradW = input.transposed() dot outputError
|
||||||
val gradBias = outputError.mean(dim = 0, keepDim = false) * input.shape[0].toDouble()
|
val gradBias = mean(structureND = outputError, dim = 0, keepDim = false) * input.shape[0].toDouble()
|
||||||
|
|
||||||
weights -= learningRate * gradW
|
weights -= learningRate * gradW
|
||||||
bias -= learningRate * gradBias
|
bias -= learningRate * gradBias
|
||||||
@ -116,7 +115,7 @@ class NeuralNetwork(private val layers: List<Layer>) {
|
|||||||
onesForAnswers[intArrayOf(index, label)] = 1.0
|
onesForAnswers[intArrayOf(index, label)] = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
val softmaxValue = yPred.exp() / yPred.exp().sum(dim = 1, keepDim = true)
|
val softmaxValue = exp(yPred) / exp(yPred).sum(dim = 1, keepDim = true)
|
||||||
|
|
||||||
(-onesForAnswers + softmaxValue) / (yPred.shape[0].toDouble())
|
(-onesForAnswers + softmaxValue) / (yPred.shape[0].toDouble())
|
||||||
}
|
}
|
||||||
@ -174,7 +173,6 @@ class NeuralNetwork(private val layers: List<Layer>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
|
||||||
fun main() = BroadcastDoubleTensorAlgebra {
|
fun main() = BroadcastDoubleTensorAlgebra {
|
||||||
val features = 5
|
val features = 5
|
||||||
val sampleSize = 250
|
val sampleSize = 250
|
||||||
@ -182,17 +180,17 @@ fun main() = BroadcastDoubleTensorAlgebra {
|
|||||||
//val testSize = sampleSize - trainSize
|
//val testSize = sampleSize - trainSize
|
||||||
|
|
||||||
// take sample of features from normal distribution
|
// take sample of features from normal distribution
|
||||||
val x = randomNormal(intArrayOf(sampleSize, features), seed) * 2.5
|
val x = randomNormal(ShapeND(sampleSize, features), seed) * 2.5
|
||||||
|
|
||||||
x += fromArray(
|
x += fromArray(
|
||||||
intArrayOf(5),
|
ShapeND(5),
|
||||||
doubleArrayOf(0.0, -1.0, -2.5, -3.0, 5.5) // row means
|
doubleArrayOf(0.0, -1.0, -2.5, -3.0, 5.5) // row means
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// define class like '1' if the sum of features > 0 and '0' otherwise
|
// define class like '1' if the sum of features > 0 and '0' otherwise
|
||||||
val y = fromArray(
|
val y = fromArray(
|
||||||
intArrayOf(sampleSize, 1),
|
ShapeND(sampleSize, 1),
|
||||||
DoubleArray(sampleSize) { i ->
|
DoubleArray(sampleSize) { i ->
|
||||||
if (x.getTensor(i).sum() > 0.0) {
|
if (x.getTensor(i).sum() > 0.0) {
|
||||||
1.0
|
1.0
|
||||||
|
@ -3,13 +3,15 @@
|
|||||||
# Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
# Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
#
|
#
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
kotlin.jupyter.add.scanner=false
|
|
||||||
kotlin.mpp.stability.nowarn=true
|
kotlin.mpp.stability.nowarn=true
|
||||||
kotlin.native.ignoreDisabledTargets=true
|
kotlin.native.ignoreDisabledTargets=true
|
||||||
kotlin.incremental.js.ir=true
|
kotlin.incremental.js.ir=true
|
||||||
|
|
||||||
org.gradle.configureondemand=true
|
org.gradle.configureondemand=true
|
||||||
org.gradle.parallel=true
|
|
||||||
org.gradle.jvmargs=-Xmx4096m
|
org.gradle.jvmargs=-Xmx4096m
|
||||||
|
|
||||||
toolsVersion=0.13.0-kotlin-1.7.20-Beta
|
toolsVersion=0.13.1-kotlin-1.7.20
|
||||||
|
|
||||||
|
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.workers.max=4
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
package space.kscience.kmath.commons.random
|
package space.kscience.kmath.commons.random
|
||||||
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
|
||||||
import space.kscience.kmath.samplers.GaussianSampler
|
|
||||||
import space.kscience.kmath.misc.toIntExact
|
import space.kscience.kmath.misc.toIntExact
|
||||||
import space.kscience.kmath.stat.RandomGenerator
|
import space.kscience.kmath.random.RandomGenerator
|
||||||
|
import space.kscience.kmath.samplers.GaussianSampler
|
||||||
import space.kscience.kmath.stat.next
|
import space.kscience.kmath.stat.next
|
||||||
|
|
||||||
|
|
||||||
public class CMRandomGeneratorWrapper(
|
public class CMRandomGeneratorWrapper(
|
||||||
public val factory: (IntArray) -> RandomGenerator,
|
public val factory: (IntArray) -> RandomGenerator,
|
||||||
) : org.apache.commons.math3.random.RandomGenerator {
|
) : org.apache.commons.math3.random.RandomGenerator {
|
||||||
|
@ -10,28 +10,18 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import org.apache.commons.math3.transform.*
|
import org.apache.commons.math3.transform.*
|
||||||
import space.kscience.kmath.complex.Complex
|
import space.kscience.kmath.complex.Complex
|
||||||
import space.kscience.kmath.operations.SuspendBufferTransform
|
import space.kscience.kmath.operations.BufferTransform
|
||||||
import space.kscience.kmath.streaming.chunked
|
import space.kscience.kmath.streaming.chunked
|
||||||
import space.kscience.kmath.streaming.spread
|
import space.kscience.kmath.streaming.spread
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.*
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
|
||||||
import space.kscience.kmath.structures.VirtualBuffer
|
|
||||||
import space.kscience.kmath.structures.asBuffer
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streaming and buffer transformations
|
* Streaming and buffer transformations with Commons-math algorithms
|
||||||
*/
|
*/
|
||||||
public object Transformations {
|
public object Transformations {
|
||||||
private fun Buffer<Complex>.toArray(): Array<org.apache.commons.math3.complex.Complex> =
|
private fun Buffer<Complex>.toCmComplexArray(): Array<org.apache.commons.math3.complex.Complex> =
|
||||||
Array(size) { org.apache.commons.math3.complex.Complex(get(it).re, get(it).im) }
|
Array(size) { org.apache.commons.math3.complex.Complex(get(it).re, get(it).im) }
|
||||||
|
|
||||||
private fun Buffer<Double>.asArray() = if (this is DoubleBuffer) {
|
|
||||||
array
|
|
||||||
} else {
|
|
||||||
DoubleArray(size) { i -> get(i) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a virtual buffer on top of array
|
* Create a virtual buffer on top of array
|
||||||
*/
|
*/
|
||||||
@ -43,70 +33,67 @@ public object Transformations {
|
|||||||
public fun fourier(
|
public fun fourier(
|
||||||
normalization: DftNormalization = DftNormalization.STANDARD,
|
normalization: DftNormalization = DftNormalization.STANDARD,
|
||||||
direction: TransformType = TransformType.FORWARD,
|
direction: TransformType = TransformType.FORWARD,
|
||||||
): SuspendBufferTransform<Complex, Complex> = {
|
): BufferTransform<Complex, Complex> = BufferTransform {
|
||||||
FastFourierTransformer(normalization).transform(it.toArray(), direction).asBuffer()
|
FastFourierTransformer(normalization).transform(it.toCmComplexArray(), direction).asBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun realFourier(
|
public fun realFourier(
|
||||||
normalization: DftNormalization = DftNormalization.STANDARD,
|
normalization: DftNormalization = DftNormalization.STANDARD,
|
||||||
direction: TransformType = TransformType.FORWARD,
|
direction: TransformType = TransformType.FORWARD,
|
||||||
): SuspendBufferTransform<Double, Complex> = {
|
): BufferTransform<Double, Complex> = BufferTransform {
|
||||||
FastFourierTransformer(normalization).transform(it.asArray(), direction).asBuffer()
|
FastFourierTransformer(normalization).transform(it.toDoubleArray(), direction).asBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun sine(
|
public fun sine(
|
||||||
normalization: DstNormalization = DstNormalization.STANDARD_DST_I,
|
normalization: DstNormalization = DstNormalization.STANDARD_DST_I,
|
||||||
direction: TransformType = TransformType.FORWARD,
|
direction: TransformType = TransformType.FORWARD,
|
||||||
): SuspendBufferTransform<Double, Double> = {
|
): BufferTransform<Double, Double> = DoubleBufferTransform {
|
||||||
FastSineTransformer(normalization).transform(it.asArray(), direction).asBuffer()
|
FastSineTransformer(normalization).transform(it.array, direction).asBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun cosine(
|
public fun cosine(
|
||||||
normalization: DctNormalization = DctNormalization.STANDARD_DCT_I,
|
normalization: DctNormalization = DctNormalization.STANDARD_DCT_I,
|
||||||
direction: TransformType = TransformType.FORWARD,
|
direction: TransformType = TransformType.FORWARD,
|
||||||
): SuspendBufferTransform<Double, Double> = {
|
): BufferTransform<Double, Double> = BufferTransform {
|
||||||
FastCosineTransformer(normalization).transform(it.asArray(), direction).asBuffer()
|
FastCosineTransformer(normalization).transform(it.toDoubleArray(), direction).asBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun hadamard(
|
public fun hadamard(
|
||||||
direction: TransformType = TransformType.FORWARD,
|
direction: TransformType = TransformType.FORWARD,
|
||||||
): SuspendBufferTransform<Double, Double> = {
|
): BufferTransform<Double, Double> = DoubleBufferTransform {
|
||||||
FastHadamardTransformer().transform(it.asArray(), direction).asBuffer()
|
FastHadamardTransformer().transform(it.array, direction).asBuffer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process given [Flow] with commons-math fft transformation
|
* Process given [Flow] with commons-math fft transformation
|
||||||
*/
|
*/
|
||||||
@FlowPreview
|
public fun Flow<Buffer<Complex>>.fft(
|
||||||
public fun Flow<Buffer<Complex>>.FFT(
|
|
||||||
normalization: DftNormalization = DftNormalization.STANDARD,
|
normalization: DftNormalization = DftNormalization.STANDARD,
|
||||||
direction: TransformType = TransformType.FORWARD,
|
direction: TransformType = TransformType.FORWARD,
|
||||||
): Flow<Buffer<Complex>> {
|
): Flow<Buffer<Complex>> {
|
||||||
val transform = Transformations.fourier(normalization, direction)
|
val transform = Transformations.fourier(normalization, direction)
|
||||||
return map { transform(it) }
|
return map(transform::transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
@FlowPreview
|
|
||||||
@JvmName("realFFT")
|
@JvmName("realFFT")
|
||||||
public fun Flow<Buffer<Double>>.FFT(
|
public fun Flow<Buffer<Double>>.fft(
|
||||||
normalization: DftNormalization = DftNormalization.STANDARD,
|
normalization: DftNormalization = DftNormalization.STANDARD,
|
||||||
direction: TransformType = TransformType.FORWARD,
|
direction: TransformType = TransformType.FORWARD,
|
||||||
): Flow<Buffer<Complex>> {
|
): Flow<Buffer<Complex>> {
|
||||||
val transform = Transformations.realFourier(normalization, direction)
|
val transform = Transformations.realFourier(normalization, direction)
|
||||||
return map(transform)
|
return map(transform::transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a continuous flow of real numbers in FFT splitting it in chunks of [bufferSize].
|
* Process a continuous flow of real numbers in FFT splitting it in chunks of [bufferSize].
|
||||||
*/
|
*/
|
||||||
@FlowPreview
|
|
||||||
@JvmName("realFFT")
|
@JvmName("realFFT")
|
||||||
public fun Flow<Double>.FFT(
|
public fun Flow<Double>.fft(
|
||||||
bufferSize: Int = Int.MAX_VALUE,
|
bufferSize: Int = Int.MAX_VALUE,
|
||||||
normalization: DftNormalization = DftNormalization.STANDARD,
|
normalization: DftNormalization = DftNormalization.STANDARD,
|
||||||
direction: TransformType = TransformType.FORWARD,
|
direction: TransformType = TransformType.FORWARD,
|
||||||
): Flow<Complex> = chunked(bufferSize).FFT(normalization, direction).spread()
|
): Flow<Complex> = chunked(bufferSize).fft(normalization, direction).spread()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map a complex flow into real flow by taking real part of each number
|
* Map a complex flow into real flow by taking real part of each number
|
||||||
|
@ -13,12 +13,11 @@ import space.kscience.kmath.expressions.Symbol.Companion.x
|
|||||||
import space.kscience.kmath.expressions.Symbol.Companion.y
|
import space.kscience.kmath.expressions.Symbol.Companion.y
|
||||||
import space.kscience.kmath.expressions.chiSquaredExpression
|
import space.kscience.kmath.expressions.chiSquaredExpression
|
||||||
import space.kscience.kmath.expressions.symbol
|
import space.kscience.kmath.expressions.symbol
|
||||||
import space.kscience.kmath.operations.map
|
import space.kscience.kmath.operations.DoubleBufferOps.Companion.map
|
||||||
import space.kscience.kmath.optimization.*
|
import space.kscience.kmath.optimization.*
|
||||||
import space.kscience.kmath.stat.RandomGenerator
|
import space.kscience.kmath.random.RandomGenerator
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
import space.kscience.kmath.structures.DoubleBuffer
|
||||||
import space.kscience.kmath.structures.asBuffer
|
import space.kscience.kmath.structures.asBuffer
|
||||||
import kotlin.math.pow
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
internal class OptimizeTest {
|
internal class OptimizeTest {
|
||||||
|
@ -193,7 +193,6 @@ public object ComplexField :
|
|||||||
* @property re The real part.
|
* @property re The real part.
|
||||||
* @property im The imaginary part.
|
* @property im The imaginary part.
|
||||||
*/
|
*/
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
public data class Complex(val re: Double, val im: Double) {
|
public data class Complex(val re: Double, val im: Double) {
|
||||||
public constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble())
|
public constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble())
|
||||||
public constructor(re: Number) : this(re.toDouble(), 0.0)
|
public constructor(re: Number) : this(re.toDouble(), 0.0)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.complex
|
package space.kscience.kmath.complex
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.nd.*
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.operations.*
|
import space.kscience.kmath.operations.*
|
||||||
@ -20,6 +21,7 @@ import kotlin.contracts.contract
|
|||||||
public sealed class ComplexFieldOpsND : BufferedFieldOpsND<Complex, ComplexField>(ComplexField.bufferAlgebra),
|
public sealed class ComplexFieldOpsND : BufferedFieldOpsND<Complex, ComplexField>(ComplexField.bufferAlgebra),
|
||||||
ScaleOperations<StructureND<Complex>>, ExtendedFieldOps<StructureND<Complex>>, PowerOperations<StructureND<Complex>> {
|
ScaleOperations<StructureND<Complex>>, ExtendedFieldOps<StructureND<Complex>>, PowerOperations<StructureND<Complex>> {
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun StructureND<Complex>.toBufferND(): BufferND<Complex> = when (this) {
|
override fun StructureND<Complex>.toBufferND(): BufferND<Complex> = when (this) {
|
||||||
is BufferND -> this
|
is BufferND -> this
|
||||||
else -> {
|
else -> {
|
||||||
@ -57,7 +59,7 @@ public sealed class ComplexFieldOpsND : BufferedFieldOpsND<Complex, ComplexField
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
public class ComplexFieldND(override val shape: Shape) :
|
public class ComplexFieldND(override val shape: ShapeND) :
|
||||||
ComplexFieldOpsND(), FieldND<Complex, ComplexField>,
|
ComplexFieldOpsND(), FieldND<Complex, ComplexField>,
|
||||||
NumbersAddOps<StructureND<Complex>> {
|
NumbersAddOps<StructureND<Complex>> {
|
||||||
|
|
||||||
@ -69,12 +71,12 @@ public class ComplexFieldND(override val shape: Shape) :
|
|||||||
|
|
||||||
public val ComplexField.ndAlgebra: ComplexFieldOpsND get() = ComplexFieldOpsND
|
public val ComplexField.ndAlgebra: ComplexFieldOpsND get() = ComplexFieldOpsND
|
||||||
|
|
||||||
public fun ComplexField.ndAlgebra(vararg shape: Int): ComplexFieldND = ComplexFieldND(shape)
|
public fun ComplexField.ndAlgebra(vararg shape: Int): ComplexFieldND = ComplexFieldND(ShapeND(shape))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a context for n-dimensional operations inside this real field
|
* Produce a context for n-dimensional operations inside this real field
|
||||||
*/
|
*/
|
||||||
public inline fun <R> ComplexField.withNdAlgebra(vararg shape: Int, action: ComplexFieldND.() -> R): R {
|
public inline fun <R> ComplexField.withNdAlgebra(vararg shape: Int, action: ComplexFieldND.() -> R): R {
|
||||||
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
|
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
|
||||||
return ComplexFieldND(shape).action()
|
return ComplexFieldND(ShapeND(shape)).action()
|
||||||
}
|
}
|
||||||
|
@ -268,7 +268,7 @@ public open class DSRing<T, A>(
|
|||||||
|
|
||||||
protected fun DS<T, A>.mapData(block: A.(T) -> T): DS<T, A> {
|
protected fun DS<T, A>.mapData(block: A.(T) -> T): DS<T, A> {
|
||||||
require(derivativeAlgebra == this@DSRing) { "All derivative operations should be done in the same algebra" }
|
require(derivativeAlgebra == this@DSRing) { "All derivative operations should be done in the same algebra" }
|
||||||
val newData: Buffer<T> = data.map(valueBufferFactory) {
|
val newData: Buffer<T> = data.mapToBuffer(valueBufferFactory) {
|
||||||
algebra.block(it)
|
algebra.block(it)
|
||||||
}
|
}
|
||||||
return DS(newData)
|
return DS(newData)
|
||||||
@ -276,7 +276,7 @@ public open class DSRing<T, A>(
|
|||||||
|
|
||||||
protected fun DS<T, A>.mapDataIndexed(block: (Int, T) -> T): DS<T, A> {
|
protected fun DS<T, A>.mapDataIndexed(block: (Int, T) -> T): DS<T, A> {
|
||||||
require(derivativeAlgebra == this@DSRing) { "All derivative operations should be done in the same algebra" }
|
require(derivativeAlgebra == this@DSRing) { "All derivative operations should be done in the same algebra" }
|
||||||
val newData: Buffer<T> = data.mapIndexed(valueBufferFactory, block)
|
val newData: Buffer<T> = data.mapIndexedToBuffer(valueBufferFactory, block)
|
||||||
return DS(newData)
|
return DS(newData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ import space.kscience.kmath.structures.Buffer
|
|||||||
import space.kscience.kmath.structures.indices
|
import space.kscience.kmath.structures.indices
|
||||||
import kotlin.jvm.JvmName
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
//TODO move to stat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic
|
* Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic
|
||||||
* differentiation.
|
* differentiation.
|
||||||
|
@ -6,9 +6,7 @@
|
|||||||
package space.kscience.kmath.linear
|
package space.kscience.kmath.linear
|
||||||
|
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.nd.BufferedRingOpsND
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.nd.as2D
|
|
||||||
import space.kscience.kmath.nd.asND
|
|
||||||
import space.kscience.kmath.operations.*
|
import space.kscience.kmath.operations.*
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.VirtualBuffer
|
import space.kscience.kmath.structures.VirtualBuffer
|
||||||
@ -23,7 +21,7 @@ public class BufferedLinearSpace<T, out A : Ring<T>>(
|
|||||||
private val ndAlgebra = BufferedRingOpsND(bufferAlgebra)
|
private val ndAlgebra = BufferedRingOpsND(bufferAlgebra)
|
||||||
|
|
||||||
override fun buildMatrix(rows: Int, columns: Int, initializer: A.(i: Int, j: Int) -> T): Matrix<T> =
|
override fun buildMatrix(rows: Int, columns: Int, initializer: A.(i: Int, j: Int) -> T): Matrix<T> =
|
||||||
ndAlgebra.structureND(intArrayOf(rows, columns)) { (i, j) -> elementAlgebra.initializer(i, j) }.as2D()
|
ndAlgebra.structureND(ShapeND(rows, columns)) { (i, j) -> elementAlgebra.initializer(i, j) }.as2D()
|
||||||
|
|
||||||
override fun buildVector(size: Int, initializer: A.(Int) -> T): Point<T> =
|
override fun buildVector(size: Int, initializer: A.(Int) -> T): Point<T> =
|
||||||
bufferAlgebra.buffer(size) { elementAlgebra.initializer(it) }
|
bufferAlgebra.buffer(size) { elementAlgebra.initializer(it) }
|
||||||
|
@ -6,9 +6,7 @@
|
|||||||
package space.kscience.kmath.linear
|
package space.kscience.kmath.linear
|
||||||
|
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.nd.DoubleFieldOpsND
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.nd.as2D
|
|
||||||
import space.kscience.kmath.nd.asND
|
|
||||||
import space.kscience.kmath.operations.DoubleBufferOps
|
import space.kscience.kmath.operations.DoubleBufferOps
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
import space.kscience.kmath.operations.invoke
|
import space.kscience.kmath.operations.invoke
|
||||||
@ -23,7 +21,7 @@ public object DoubleLinearSpace : LinearSpace<Double, DoubleField> {
|
|||||||
rows: Int,
|
rows: Int,
|
||||||
columns: Int,
|
columns: Int,
|
||||||
initializer: DoubleField.(i: Int, j: Int) -> Double
|
initializer: DoubleField.(i: Int, j: Int) -> Double
|
||||||
): Matrix<Double> = DoubleFieldOpsND.structureND(intArrayOf(rows, columns)) { (i, j) ->
|
): Matrix<Double> = DoubleFieldOpsND.structureND(ShapeND(rows, columns)) { (i, j) ->
|
||||||
DoubleField.initializer(i, j)
|
DoubleField.initializer(i, j)
|
||||||
}.as2D()
|
}.as2D()
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.linear
|
package space.kscience.kmath.linear
|
||||||
|
|
||||||
|
import space.kscience.kmath.nd.ShapeND
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The matrix where each element is evaluated each time when is being accessed.
|
* The matrix where each element is evaluated each time when is being accessed.
|
||||||
*
|
*
|
||||||
@ -16,7 +19,7 @@ public class VirtualMatrix<out T : Any>(
|
|||||||
public val generator: (i: Int, j: Int) -> T,
|
public val generator: (i: Int, j: Int) -> T,
|
||||||
) : Matrix<T> {
|
) : Matrix<T> {
|
||||||
|
|
||||||
override val shape: IntArray get() = intArrayOf(rowNum, colNum)
|
override val shape: ShapeND get() = ShapeND(rowNum, colNum)
|
||||||
|
|
||||||
override operator fun get(i: Int, j: Int): T = generator(i, j)
|
override operator fun get(i: Int, j: Int): T = generator(i, j)
|
||||||
}
|
}
|
||||||
|
@ -29,3 +29,16 @@ public annotation class UnstableKMathAPI
|
|||||||
public annotation class PerformancePitfall(
|
public annotation class PerformancePitfall(
|
||||||
val message: String = "Potential performance problem",
|
val message: String = "Potential performance problem",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks API that is public, but should not be used without clear understanding what it does.
|
||||||
|
*/
|
||||||
|
@MustBeDocumented
|
||||||
|
@Retention(value = AnnotationRetention.BINARY)
|
||||||
|
@RequiresOptIn(
|
||||||
|
"This API is unsafe and should be used carefully",
|
||||||
|
RequiresOptIn.Level.ERROR,
|
||||||
|
)
|
||||||
|
public annotation class UnsafeKMathAPI(
|
||||||
|
val message: String = "Unsafe API",
|
||||||
|
)
|
||||||
|
@ -25,7 +25,7 @@ public interface AlgebraND<T, out C : Algebra<T>>: Algebra<StructureND<T>> {
|
|||||||
/**
|
/**
|
||||||
* Produces a new [StructureND] using given initializer function.
|
* Produces a new [StructureND] using given initializer function.
|
||||||
*/
|
*/
|
||||||
public fun structureND(shape: Shape, initializer: C.(IntArray) -> T): StructureND<T>
|
public fun structureND(shape: ShapeND, initializer: C.(IntArray) -> T): StructureND<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps elements from one structure to another one by applying [transform] to them.
|
* Maps elements from one structure to another one by applying [transform] to them.
|
||||||
|
@ -12,11 +12,11 @@ import space.kscience.kmath.misc.UnstableKMathAPI
|
|||||||
import space.kscience.kmath.operations.*
|
import space.kscience.kmath.operations.*
|
||||||
|
|
||||||
public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
|
public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
|
||||||
public val indexerBuilder: (IntArray) -> ShapeIndexer
|
public val indexerBuilder: (ShapeND) -> ShapeIndexer
|
||||||
public val bufferAlgebra: BufferAlgebra<T, A>
|
public val bufferAlgebra: BufferAlgebra<T, A>
|
||||||
override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
|
override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
|
||||||
|
|
||||||
override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): BufferND<T> {
|
override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): BufferND<T> {
|
||||||
val indexer = indexerBuilder(shape)
|
val indexer = indexerBuilder(shape)
|
||||||
return BufferND(
|
return BufferND(
|
||||||
indexer,
|
indexer,
|
||||||
@ -26,6 +26,7 @@ public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
public fun StructureND<T>.toBufferND(): BufferND<T> = when (this) {
|
public fun StructureND<T>.toBufferND(): BufferND<T> = when (this) {
|
||||||
is BufferND -> this
|
is BufferND -> this
|
||||||
else -> {
|
else -> {
|
||||||
@ -46,7 +47,7 @@ public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
|
|||||||
zipInline(left.toBufferND(), right.toBufferND(), transform)
|
zipInline(left.toBufferND(), right.toBufferND(), transform)
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public val defaultIndexerBuilder: (IntArray) -> ShapeIndexer = ::Strides
|
public val defaultIndexerBuilder: (ShapeND) -> ShapeIndexer = ::Strides
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,24 +99,24 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.zipInline(
|
|||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class)
|
||||||
public open class BufferedGroupNDOps<T, out A : Group<T>>(
|
public open class BufferedGroupNDOps<T, out A : Group<T>>(
|
||||||
override val bufferAlgebra: BufferAlgebra<T, A>,
|
override val bufferAlgebra: BufferAlgebra<T, A>,
|
||||||
override val indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
override val indexerBuilder: (ShapeND) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
||||||
) : GroupOpsND<T, A>, BufferAlgebraND<T, A> {
|
) : GroupOpsND<T, A>, BufferAlgebraND<T, A> {
|
||||||
override fun StructureND<T>.unaryMinus(): StructureND<T> = map { -it }
|
override fun StructureND<T>.unaryMinus(): StructureND<T> = map { -it }
|
||||||
}
|
}
|
||||||
|
|
||||||
public open class BufferedRingOpsND<T, out A : Ring<T>>(
|
public open class BufferedRingOpsND<T, out A : Ring<T>>(
|
||||||
bufferAlgebra: BufferAlgebra<T, A>,
|
bufferAlgebra: BufferAlgebra<T, A>,
|
||||||
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
indexerBuilder: (ShapeND) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
||||||
) : BufferedGroupNDOps<T, A>(bufferAlgebra, indexerBuilder), RingOpsND<T, A>
|
) : BufferedGroupNDOps<T, A>(bufferAlgebra, indexerBuilder), RingOpsND<T, A>
|
||||||
|
|
||||||
public open class BufferedFieldOpsND<T, out A : Field<T>>(
|
public open class BufferedFieldOpsND<T, out A : Field<T>>(
|
||||||
bufferAlgebra: BufferAlgebra<T, A>,
|
bufferAlgebra: BufferAlgebra<T, A>,
|
||||||
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
indexerBuilder: (ShapeND) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
||||||
) : BufferedRingOpsND<T, A>(bufferAlgebra, indexerBuilder), FieldOpsND<T, A> {
|
) : BufferedRingOpsND<T, A>(bufferAlgebra, indexerBuilder), FieldOpsND<T, A> {
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
elementAlgebra: A,
|
elementAlgebra: A,
|
||||||
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
indexerBuilder: (ShapeND) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
|
||||||
) : this(BufferFieldOps(elementAlgebra), indexerBuilder)
|
) : this(BufferFieldOps(elementAlgebra), indexerBuilder)
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class)
|
||||||
@ -130,7 +131,7 @@ public val <T, A : Field<T>> BufferAlgebra<T, A>.nd: BufferedFieldOpsND<T, A> ge
|
|||||||
public fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.structureND(
|
public fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.structureND(
|
||||||
vararg shape: Int,
|
vararg shape: Int,
|
||||||
initializer: A.(IntArray) -> T,
|
initializer: A.(IntArray) -> T,
|
||||||
): BufferND<T> = structureND(shape, initializer)
|
): BufferND<T> = structureND(ShapeND(shape), initializer)
|
||||||
|
|
||||||
public fun <T, EA : Algebra<T>, A> A.structureND(
|
public fun <T, EA : Algebra<T>, A> A.structureND(
|
||||||
initializer: EA.(IntArray) -> T,
|
initializer: EA.(IntArray) -> T,
|
||||||
|
@ -5,10 +5,9 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.nd
|
package space.kscience.kmath.nd
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.BufferFactory
|
|
||||||
import space.kscience.kmath.structures.MutableBuffer
|
import space.kscience.kmath.structures.MutableBuffer
|
||||||
import space.kscience.kmath.structures.MutableBufferFactory
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents [StructureND] over [Buffer].
|
* Represents [StructureND] over [Buffer].
|
||||||
@ -22,32 +21,33 @@ public open class BufferND<out T>(
|
|||||||
public open val buffer: Buffer<T>,
|
public open val buffer: Buffer<T>,
|
||||||
) : StructureND<T> {
|
) : StructureND<T> {
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override operator fun get(index: IntArray): T = buffer[indices.offset(index)]
|
override operator fun get(index: IntArray): T = buffer[indices.offset(index)]
|
||||||
|
|
||||||
override val shape: IntArray get() = indices.shape
|
override val shape: ShapeND get() = indices.shape
|
||||||
|
|
||||||
override fun toString(): String = StructureND.toString(this)
|
override fun toString(): String = StructureND.toString(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
///**
|
||||||
* Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferND]
|
// * Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferND]
|
||||||
*/
|
// */
|
||||||
public inline fun <T, R : Any> StructureND<T>.mapToBuffer(
|
//public inline fun <T, R : Any> StructureND<T>.mapToBuffer(
|
||||||
factory: BufferFactory<R>,
|
// factory: BufferFactory<R>,
|
||||||
crossinline transform: (T) -> R,
|
// crossinline transform: (T) -> R,
|
||||||
): BufferND<R> = if (this is BufferND<T>)
|
//): BufferND<R> = if (this is BufferND<T>)
|
||||||
BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
// BufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
||||||
else {
|
//else {
|
||||||
val strides = DefaultStrides(shape)
|
// val strides = ColumnStrides(shape)
|
||||||
BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
|
// BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* Transform structure to a new structure using inferred [BufferFactory]
|
// * Transform structure to a new structure using inferred [BufferFactory]
|
||||||
*/
|
// */
|
||||||
public inline fun <T, reified R : Any> StructureND<T>.mapToBuffer(
|
//public inline fun <T, reified R : Any> StructureND<T>.mapToBuffer(
|
||||||
crossinline transform: (T) -> R,
|
// crossinline transform: (T) -> R,
|
||||||
): BufferND<R> = mapToBuffer(Buffer.Companion::auto, transform)
|
//): BufferND<R> = mapToBuffer(Buffer.Companion::auto, transform)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents [MutableStructureND] over [MutableBuffer].
|
* Represents [MutableStructureND] over [MutableBuffer].
|
||||||
@ -60,22 +60,24 @@ public open class MutableBufferND<T>(
|
|||||||
strides: ShapeIndexer,
|
strides: ShapeIndexer,
|
||||||
override val buffer: MutableBuffer<T>,
|
override val buffer: MutableBuffer<T>,
|
||||||
) : MutableStructureND<T>, BufferND<T>(strides, buffer) {
|
) : MutableStructureND<T>, BufferND<T>(strides, buffer) {
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override fun set(index: IntArray, value: T) {
|
override fun set(index: IntArray, value: T) {
|
||||||
buffer[indices.offset(index)] = value
|
buffer[indices.offset(index)] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
///**
|
||||||
* Transform structure to a new structure using provided [MutableBufferFactory] and optimizing if argument is [MutableBufferND]
|
// * Transform structure to a new structure using provided [MutableBufferFactory] and optimizing if argument is [MutableBufferND]
|
||||||
*/
|
// */
|
||||||
public inline fun <T, reified R : Any> MutableStructureND<T>.mapToMutableBuffer(
|
//public inline fun <T, reified R : Any> MutableStructureND<T>.mapToMutableBuffer(
|
||||||
factory: MutableBufferFactory<R> = MutableBufferFactory(MutableBuffer.Companion::auto),
|
// factory: MutableBufferFactory<R> = MutableBufferFactory(MutableBuffer.Companion::auto),
|
||||||
crossinline transform: (T) -> R,
|
// crossinline transform: (T) -> R,
|
||||||
): MutableBufferND<R> {
|
//): MutableBufferND<R> {
|
||||||
return if (this is MutableBufferND<T>)
|
// return if (this is MutableBufferND<T>)
|
||||||
MutableBufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
// MutableBufferND(this.indices, factory.invoke(indices.linearSize) { transform(buffer[it]) })
|
||||||
else {
|
// else {
|
||||||
val strides = DefaultStrides(shape)
|
// val strides = ColumnStrides(shape)
|
||||||
MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
|
// MutableBufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
|
||||||
}
|
// }
|
||||||
}
|
//}
|
@ -14,15 +14,25 @@ import kotlin.contracts.contract
|
|||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import kotlin.math.pow as kpow
|
import kotlin.math.pow as kpow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple mutable [StructureND] of doubles
|
||||||
|
*/
|
||||||
public class DoubleBufferND(
|
public class DoubleBufferND(
|
||||||
indexes: ShapeIndexer,
|
indexes: ShapeIndexer,
|
||||||
override val buffer: DoubleBuffer,
|
override val buffer: DoubleBuffer,
|
||||||
) : MutableBufferND<Double>(indexes, buffer)
|
) : MutableBufferND<Double>(indexes, buffer), MutableStructureNDOfDouble{
|
||||||
|
override fun getDouble(index: IntArray): Double = buffer[indices.offset(index)]
|
||||||
|
|
||||||
|
override fun setDouble(index: IntArray, value: Double) {
|
||||||
|
buffer[indices.offset(index)] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(DoubleField.bufferAlgebra),
|
public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(DoubleField.bufferAlgebra),
|
||||||
ScaleOperations<StructureND<Double>>, ExtendedFieldOps<StructureND<Double>> {
|
ScaleOperations<StructureND<Double>>, ExtendedFieldOps<StructureND<Double>> {
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun StructureND<Double>.toBufferND(): DoubleBufferND = when (this) {
|
override fun StructureND<Double>.toBufferND(): DoubleBufferND = when (this) {
|
||||||
is DoubleBufferND -> this
|
is DoubleBufferND -> this
|
||||||
else -> {
|
else -> {
|
||||||
@ -64,7 +74,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
|
|||||||
transform: DoubleField.(Double, Double) -> Double,
|
transform: DoubleField.(Double, Double) -> Double,
|
||||||
): BufferND<Double> = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> DoubleField.transform(l, r) }
|
): BufferND<Double> = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> DoubleField.transform(l, r) }
|
||||||
|
|
||||||
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND {
|
override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND {
|
||||||
val indexer = indexerBuilder(shape)
|
val indexer = indexerBuilder(shape)
|
||||||
return DoubleBufferND(
|
return DoubleBufferND(
|
||||||
indexer,
|
indexer,
|
||||||
@ -179,7 +189,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
public class DoubleFieldND(override val shape: Shape) :
|
public class DoubleFieldND(override val shape: ShapeND) :
|
||||||
DoubleFieldOpsND(), FieldND<Double, DoubleField>, NumbersAddOps<StructureND<Double>>,
|
DoubleFieldOpsND(), FieldND<Double, DoubleField>, NumbersAddOps<StructureND<Double>>,
|
||||||
ExtendedField<StructureND<Double>> {
|
ExtendedField<StructureND<Double>> {
|
||||||
|
|
||||||
@ -221,7 +231,8 @@ public class DoubleFieldND(override val shape: Shape) :
|
|||||||
|
|
||||||
public val DoubleField.ndAlgebra: DoubleFieldOpsND get() = DoubleFieldOpsND
|
public val DoubleField.ndAlgebra: DoubleFieldOpsND get() = DoubleFieldOpsND
|
||||||
|
|
||||||
public fun DoubleField.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleFieldND(shape)
|
public fun DoubleField.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleFieldND(ShapeND(shape))
|
||||||
|
public fun DoubleField.ndAlgebra(shape: ShapeND): DoubleFieldND = DoubleFieldND(shape)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a context for n-dimensional operations inside this real field
|
* Produce a context for n-dimensional operations inside this real field
|
||||||
@ -229,5 +240,5 @@ public fun DoubleField.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleField
|
|||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public inline fun <R> DoubleField.withNdAlgebra(vararg shape: Int, action: DoubleFieldND.() -> R): R {
|
public inline fun <R> DoubleField.withNdAlgebra(vararg shape: Int, action: DoubleFieldND.() -> R): R {
|
||||||
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
|
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
|
||||||
return DoubleFieldND(shape).run(action)
|
return DoubleFieldND(ShapeND(shape)).run(action)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ public class IntBufferND(
|
|||||||
|
|
||||||
public sealed class IntRingOpsND : BufferedRingOpsND<Int, IntRing>(IntRing.bufferAlgebra) {
|
public sealed class IntRingOpsND : BufferedRingOpsND<Int, IntRing>(IntRing.bufferAlgebra) {
|
||||||
|
|
||||||
override fun structureND(shape: Shape, initializer: IntRing.(IntArray) -> Int): IntBufferND {
|
override fun structureND(shape: ShapeND, initializer: IntRing.(IntArray) -> Int): IntBufferND {
|
||||||
val indexer = indexerBuilder(shape)
|
val indexer = indexerBuilder(shape)
|
||||||
return IntBufferND(
|
return IntBufferND(
|
||||||
indexer,
|
indexer,
|
||||||
@ -35,7 +35,7 @@ public sealed class IntRingOpsND : BufferedRingOpsND<Int, IntRing>(IntRing.buffe
|
|||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
public class IntRingND(
|
public class IntRingND(
|
||||||
override val shape: Shape
|
override val shape: ShapeND
|
||||||
) : IntRingOpsND(), RingND<Int, IntRing>, NumbersAddOps<StructureND<Int>> {
|
) : IntRingOpsND(), RingND<Int, IntRing>, NumbersAddOps<StructureND<Int>> {
|
||||||
|
|
||||||
override fun number(value: Number): BufferND<Int> {
|
override fun number(value: Number): BufferND<Int> {
|
||||||
@ -46,5 +46,5 @@ public class IntRingND(
|
|||||||
|
|
||||||
public inline fun <R> IntRing.withNdAlgebra(vararg shape: Int, action: IntRingND.() -> R): R {
|
public inline fun <R> IntRing.withNdAlgebra(vararg shape: Int, action: IntRingND.() -> R): R {
|
||||||
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
|
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
|
||||||
return IntRingND(shape).run(action)
|
return IntRingND(ShapeND(shape)).run(action)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2022 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.nd
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
|
|
||||||
|
|
||||||
|
public class PermutedStructureND<T>(
|
||||||
|
public val origin: StructureND<T>,
|
||||||
|
public val permutation: (IntArray) -> IntArray,
|
||||||
|
) : StructureND<T> {
|
||||||
|
|
||||||
|
override val shape: ShapeND
|
||||||
|
get() = origin.shape
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
|
override fun get(index: IntArray): T {
|
||||||
|
return origin[permutation(index)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T> StructureND<T>.permute(
|
||||||
|
permutation: (IntArray) -> IntArray,
|
||||||
|
): PermutedStructureND<T> = PermutedStructureND(this, permutation)
|
||||||
|
|
||||||
|
public class PermutedMutableStructureND<T>(
|
||||||
|
public val origin: MutableStructureND<T>,
|
||||||
|
override val shape: ShapeND = origin.shape,
|
||||||
|
public val permutation: (IntArray) -> IntArray,
|
||||||
|
) : MutableStructureND<T> {
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
|
override fun set(index: IntArray, value: T) {
|
||||||
|
origin[permutation(index)] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
|
override fun get(index: IntArray): T {
|
||||||
|
return origin[permutation(index)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T> MutableStructureND<T>.permute(
|
||||||
|
newShape: ShapeND = shape,
|
||||||
|
permutation: (IntArray) -> IntArray,
|
||||||
|
): PermutedMutableStructureND<T> = PermutedMutableStructureND(this, newShape, permutation)
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018-2022 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.nd
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception is thrown when the expected and actual shape of NDArray differ.
|
|
||||||
*
|
|
||||||
* @property expected the expected shape.
|
|
||||||
* @property actual the actual shape.
|
|
||||||
*/
|
|
||||||
public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) :
|
|
||||||
RuntimeException("Shape ${actual.contentToString()} doesn't fit in expected shape ${expected.contentToString()}.")
|
|
||||||
|
|
||||||
public class IndexOutOfShapeException(public val shape: Shape, public val index: IntArray) :
|
|
||||||
RuntimeException("Index ${index.contentToString()} is out of shape ${shape.contentToString()}")
|
|
||||||
|
|
||||||
public typealias Shape = IntArray
|
|
||||||
|
|
||||||
public fun Shape(shapeFirst: Int, vararg shapeRest: Int): Shape = intArrayOf(shapeFirst, *shapeRest)
|
|
||||||
|
|
||||||
public interface WithShape {
|
|
||||||
public val shape: Shape
|
|
||||||
|
|
||||||
public val indices: ShapeIndexer get() = DefaultStrides(shape)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun requireIndexInShape(index: IntArray, shape: Shape) {
|
|
||||||
if (index.size != shape.size) throw IndexOutOfShapeException(index, shape)
|
|
||||||
shape.forEachIndexed { axis, axisShape ->
|
|
||||||
if (index[axis] !in 0 until axisShape) throw IndexOutOfShapeException(index, shape)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018-2022 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.nd
|
|
||||||
|
|
||||||
import kotlin.native.concurrent.ThreadLocal
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A converter from linear index to multivariate index
|
|
||||||
*/
|
|
||||||
public interface ShapeIndexer : Iterable<IntArray> {
|
|
||||||
public val shape: Shape
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get linear index from multidimensional index
|
|
||||||
*/
|
|
||||||
public fun offset(index: IntArray): Int
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get multidimensional from linear
|
|
||||||
*/
|
|
||||||
public fun index(offset: Int): IntArray
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The size of linear buffer to accommodate all elements of ND-structure corresponding to strides
|
|
||||||
*/
|
|
||||||
public val linearSize: Int
|
|
||||||
|
|
||||||
// TODO introduce a fast way to calculate index of the next element?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over ND indices in a natural order
|
|
||||||
*/
|
|
||||||
public fun asSequence(): Sequence<IntArray>
|
|
||||||
|
|
||||||
override fun iterator(): Iterator<IntArray> = asSequence().iterator()
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean
|
|
||||||
override fun hashCode(): Int
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Linear transformation of indexes
|
|
||||||
*/
|
|
||||||
public abstract class Strides : ShapeIndexer {
|
|
||||||
/**
|
|
||||||
* Array strides
|
|
||||||
*/
|
|
||||||
public abstract val strides: IntArray
|
|
||||||
|
|
||||||
public override fun offset(index: IntArray): Int = index.mapIndexed { i, value ->
|
|
||||||
if (value < 0 || value >= shape[i]) throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${this.shape[i]})")
|
|
||||||
value * strides[i]
|
|
||||||
}.sum()
|
|
||||||
|
|
||||||
// TODO introduce a fast way to calculate index of the next element?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over ND indices in a natural order
|
|
||||||
*/
|
|
||||||
public override fun asSequence(): Sequence<IntArray> = (0 until linearSize).asSequence().map(::index)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple implementation of [Strides].
|
|
||||||
*/
|
|
||||||
public class DefaultStrides(override val shape: IntArray) : Strides() {
|
|
||||||
override val linearSize: Int get() = strides[shape.size]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strides for memory access
|
|
||||||
*/
|
|
||||||
override val strides: IntArray by lazy {
|
|
||||||
sequence {
|
|
||||||
var current = 1
|
|
||||||
yield(1)
|
|
||||||
|
|
||||||
shape.forEach {
|
|
||||||
current *= it
|
|
||||||
yield(current)
|
|
||||||
}
|
|
||||||
}.toList().toIntArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun index(offset: Int): IntArray {
|
|
||||||
val res = IntArray(shape.size)
|
|
||||||
var current = offset
|
|
||||||
var strideIndex = strides.size - 2
|
|
||||||
|
|
||||||
while (strideIndex >= 0) {
|
|
||||||
res[strideIndex] = (current / strides[strideIndex])
|
|
||||||
current %= strides[strideIndex]
|
|
||||||
strideIndex--
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other !is DefaultStrides) return false
|
|
||||||
if (!shape.contentEquals(other.shape)) return false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = shape.contentHashCode()
|
|
||||||
|
|
||||||
|
|
||||||
public companion object {
|
|
||||||
/**
|
|
||||||
* Cached builder for default strides
|
|
||||||
*/
|
|
||||||
@Deprecated("Replace by Strides(shape)")
|
|
||||||
public operator fun invoke(shape: IntArray): Strides =
|
|
||||||
defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ThreadLocal
|
|
||||||
private val defaultStridesCache = HashMap<IntArray, Strides>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cached builder for default strides
|
|
||||||
*/
|
|
||||||
public fun Strides(shape: IntArray): Strides = defaultStridesCache.getOrPut(shape) { DefaultStrides(shape) }
|
|
@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2022 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.nd
|
||||||
|
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.native.concurrent.ThreadLocal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A converter from linear index to multivariate index
|
||||||
|
*/
|
||||||
|
public interface ShapeIndexer : Iterable<IntArray> {
|
||||||
|
public val shape: ShapeND
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get linear index from multidimensional index
|
||||||
|
*/
|
||||||
|
public fun offset(index: IntArray): Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get multidimensional from linear
|
||||||
|
*/
|
||||||
|
public fun index(offset: Int): IntArray
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of linear buffer to accommodate all elements of ND-structure corresponding to strides
|
||||||
|
*/
|
||||||
|
public val linearSize: Int
|
||||||
|
|
||||||
|
// TODO introduce a fast way to calculate index of the next element?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over ND indices in a natural order
|
||||||
|
*/
|
||||||
|
public fun asSequence(): Sequence<IntArray>
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<IntArray> = asSequence().iterator()
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean
|
||||||
|
override fun hashCode(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linear transformation of indexes
|
||||||
|
*/
|
||||||
|
public abstract class Strides : ShapeIndexer {
|
||||||
|
/**
|
||||||
|
* Array strides
|
||||||
|
*/
|
||||||
|
internal abstract val strides: IntArray
|
||||||
|
|
||||||
|
public override fun offset(index: IntArray): Int {
|
||||||
|
var res = 0
|
||||||
|
index.forEachIndexed { i, value ->
|
||||||
|
if (value !in 0 until shape[i]) throw IndexOutOfBoundsException("Index $value out of shape bounds: (0, ${this.shape[i]})")
|
||||||
|
res += value * strides[i]
|
||||||
|
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO introduce a fast way to calculate index of the next element?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over ND indices in a natural order
|
||||||
|
*/
|
||||||
|
public override fun asSequence(): Sequence<IntArray> = (0 until linearSize).asSequence().map(::index)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column-first [Strides]. Columns are represented as continuous arrays
|
||||||
|
*/
|
||||||
|
public class ColumnStrides(override val shape: ShapeND) : Strides() {
|
||||||
|
override val linearSize: Int get() = strides[shape.size]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strides for memory access
|
||||||
|
*/
|
||||||
|
override val strides: IntArray = sequence {
|
||||||
|
var current = 1
|
||||||
|
yield(1)
|
||||||
|
|
||||||
|
shape.forEach {
|
||||||
|
current *= it
|
||||||
|
yield(current)
|
||||||
|
}
|
||||||
|
}.toList().toIntArray()
|
||||||
|
|
||||||
|
override fun index(offset: Int): IntArray {
|
||||||
|
val res = IntArray(shape.size)
|
||||||
|
var current = offset
|
||||||
|
var strideIndex = strides.size - 2
|
||||||
|
|
||||||
|
while (strideIndex >= 0) {
|
||||||
|
res[strideIndex] = (current / strides[strideIndex])
|
||||||
|
current %= strides[strideIndex]
|
||||||
|
strideIndex--
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is ColumnStrides) return false
|
||||||
|
return shape.contentEquals(other.shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = shape.contentHashCode()
|
||||||
|
|
||||||
|
|
||||||
|
public companion object
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This [Strides] implementation follows the last dimension first convention
|
||||||
|
* For more information: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.strides.html
|
||||||
|
*
|
||||||
|
* @param shape the shape of the tensor.
|
||||||
|
*/
|
||||||
|
public class RowStrides(override val shape: ShapeND) : Strides() {
|
||||||
|
|
||||||
|
override val strides: IntArray = run {
|
||||||
|
val nDim = shape.size
|
||||||
|
val res = IntArray(nDim)
|
||||||
|
if (nDim == 0) return@run res
|
||||||
|
|
||||||
|
var current = nDim - 1
|
||||||
|
res[current] = 1
|
||||||
|
|
||||||
|
while (current > 0) {
|
||||||
|
res[current - 1] = max(1, shape[current]) * res[current]
|
||||||
|
current--
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun index(offset: Int): IntArray {
|
||||||
|
val res = IntArray(shape.size)
|
||||||
|
var current = offset
|
||||||
|
var strideIndex = 0
|
||||||
|
|
||||||
|
while (strideIndex < shape.size) {
|
||||||
|
res[strideIndex] = (current / strides[strideIndex])
|
||||||
|
current %= strides[strideIndex]
|
||||||
|
strideIndex++
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
override val linearSize: Int get() = shape.linearSize
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is RowStrides) return false
|
||||||
|
return shape.contentEquals(other.shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = shape.contentHashCode()
|
||||||
|
|
||||||
|
public companion object
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ThreadLocal
|
||||||
|
private val defaultStridesCache = HashMap<ShapeND, Strides>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached builder for default strides
|
||||||
|
*/
|
||||||
|
public fun Strides(shape: ShapeND): Strides = defaultStridesCache.getOrPut(shape) { RowStrides(shape) }
|
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2022 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.nd
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.UnsafeKMathAPI
|
||||||
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A read-only ND shape
|
||||||
|
*/
|
||||||
|
@JvmInline
|
||||||
|
public value class ShapeND(@PublishedApi internal val array: IntArray) {
|
||||||
|
public val size: Int get() = array.size
|
||||||
|
public operator fun get(index: Int): Int = array[index]
|
||||||
|
override fun toString(): String = array.contentToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun ShapeND.forEach(block: (value: Int) -> Unit): Unit = array.forEach(block)
|
||||||
|
|
||||||
|
public inline fun ShapeND.forEachIndexed(block: (index: Int, value: Int) -> Unit): Unit = array.forEachIndexed(block)
|
||||||
|
|
||||||
|
public infix fun ShapeND.contentEquals(other: ShapeND): Boolean = array.contentEquals(other.array)
|
||||||
|
|
||||||
|
public fun ShapeND.contentHashCode(): Int = array.contentHashCode()
|
||||||
|
|
||||||
|
public val ShapeND.indices: IntRange get() = array.indices
|
||||||
|
public val ShapeND.linearSize: Int get() = array.reduce(Int::times)
|
||||||
|
|
||||||
|
public fun ShapeND.slice(range: IntRange): ShapeND = ShapeND(array.sliceArray(range))
|
||||||
|
|
||||||
|
public fun ShapeND.last(): Int = array.last()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shape including last [n] dimensions of this shape
|
||||||
|
*/
|
||||||
|
public fun ShapeND.last(n: Int): ShapeND = ShapeND(array.copyOfRange(size - n, size))
|
||||||
|
|
||||||
|
public fun ShapeND.first(): Int = array.first()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shape including first [n] dimensions of this shape
|
||||||
|
*/
|
||||||
|
public fun ShapeND.first(n: Int): ShapeND = ShapeND(array.copyOfRange(0, n))
|
||||||
|
|
||||||
|
public operator fun ShapeND.plus(add: IntArray): ShapeND = ShapeND(array + add)
|
||||||
|
|
||||||
|
public operator fun ShapeND.plus(add: ShapeND): ShapeND = ShapeND(array + add.array)
|
||||||
|
|
||||||
|
public fun ShapeND.isEmpty(): Boolean = size == 0
|
||||||
|
public fun ShapeND.isNotEmpty(): Boolean = size > 0
|
||||||
|
|
||||||
|
public fun ShapeND.transposed(i: Int, j: Int): ShapeND = ShapeND(array.copyOf().apply {
|
||||||
|
val ith = get(i)
|
||||||
|
val jth = get(j)
|
||||||
|
set(i, jth)
|
||||||
|
set(j, ith)
|
||||||
|
})
|
||||||
|
|
||||||
|
public operator fun ShapeND.component1(): Int = get(0)
|
||||||
|
public operator fun ShapeND.component2(): Int = get(1)
|
||||||
|
public operator fun ShapeND.component3(): Int = get(2)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to array with protective copy
|
||||||
|
*/
|
||||||
|
public fun ShapeND.toArray(): IntArray = array.copyOf()
|
||||||
|
|
||||||
|
@UnsafeKMathAPI
|
||||||
|
public fun ShapeND.asArray(): IntArray = array
|
||||||
|
|
||||||
|
public fun ShapeND.asList(): List<Int> = array.asList()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception is thrown when the expected and actual shape of NDArray differ.
|
||||||
|
*
|
||||||
|
* @property expected the expected shape.
|
||||||
|
* @property actual the actual shape.
|
||||||
|
*/
|
||||||
|
public class ShapeMismatchException(public val expected: ShapeND, public val actual: ShapeND) :
|
||||||
|
RuntimeException("Shape $actual doesn't fit in expected shape ${expected}.")
|
||||||
|
|
||||||
|
public class IndexOutOfShapeException(public val shape: ShapeND, public val index: IntArray) :
|
||||||
|
RuntimeException("Index ${index.contentToString()} is out of shape ${shape}")
|
||||||
|
|
||||||
|
public fun ShapeND(shapeFirst: Int, vararg shapeRest: Int): ShapeND = ShapeND(intArrayOf(shapeFirst, *shapeRest))
|
||||||
|
|
||||||
|
public interface WithShape {
|
||||||
|
public val shape: ShapeND
|
||||||
|
|
||||||
|
public val indices: ShapeIndexer get() = ColumnStrides(shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun requireIndexInShape(index: IntArray, shape: ShapeND) {
|
||||||
|
if (index.size != shape.size) throw IndexOutOfShapeException(shape, index)
|
||||||
|
shape.forEachIndexed { axis, axisShape ->
|
||||||
|
if (index[axis] !in 0 until axisShape) throw IndexOutOfShapeException(shape, index)
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ public sealed class ShortRingOpsND : BufferedRingOpsND<Short, ShortRing>(ShortRi
|
|||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
public class ShortRingND(
|
public class ShortRingND(
|
||||||
override val shape: Shape
|
override val shape: ShapeND
|
||||||
) : ShortRingOpsND(), RingND<Short, ShortRing>, NumbersAddOps<StructureND<Short>> {
|
) : ShortRingOpsND(), RingND<Short, ShortRing>, NumbersAddOps<StructureND<Short>> {
|
||||||
|
|
||||||
override fun number(value: Number): BufferND<Short> {
|
override fun number(value: Number): BufferND<Short> {
|
||||||
@ -30,5 +30,5 @@ public class ShortRingND(
|
|||||||
|
|
||||||
public inline fun <R> ShortRing.withNdAlgebra(vararg shape: Int, action: ShortRingND.() -> R): R {
|
public inline fun <R> ShortRing.withNdAlgebra(vararg shape: Int, action: ShortRingND.() -> R): R {
|
||||||
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
|
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
|
||||||
return ShortRingND(shape).run(action)
|
return ShortRingND(ShapeND(shape)).run(action)
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import kotlin.jvm.JvmInline
|
|||||||
public interface Structure1D<out T> : StructureND<T>, Buffer<T> {
|
public interface Structure1D<out T> : StructureND<T>, Buffer<T> {
|
||||||
override val dimension: Int get() = 1
|
override val dimension: Int get() = 1
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override operator fun get(index: IntArray): T {
|
override operator fun get(index: IntArray): T {
|
||||||
require(index.size == 1) { "Index dimension mismatch. Expected 1 but found ${index.size}" }
|
require(index.size == 1) { "Index dimension mismatch. Expected 1 but found ${index.size}" }
|
||||||
return get(index[0])
|
return get(index[0])
|
||||||
@ -32,6 +33,8 @@ public interface Structure1D<out T> : StructureND<T>, Buffer<T> {
|
|||||||
* A mutable structure that is guaranteed to be one-dimensional
|
* A mutable structure that is guaranteed to be one-dimensional
|
||||||
*/
|
*/
|
||||||
public interface MutableStructure1D<T> : Structure1D<T>, MutableStructureND<T>, MutableBuffer<T> {
|
public interface MutableStructure1D<T> : Structure1D<T>, MutableStructureND<T>, MutableBuffer<T> {
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override operator fun set(index: IntArray, value: T) {
|
override operator fun set(index: IntArray, value: T) {
|
||||||
require(index.size == 1) { "Index dimension mismatch. Expected 1 but found ${index.size}" }
|
require(index.size == 1) { "Index dimension mismatch. Expected 1 but found ${index.size}" }
|
||||||
set(index[0], value)
|
set(index[0], value)
|
||||||
@ -43,9 +46,10 @@ public interface MutableStructure1D<T> : Structure1D<T>, MutableStructureND<T>,
|
|||||||
*/
|
*/
|
||||||
@JvmInline
|
@JvmInline
|
||||||
private value class Structure1DWrapper<out T>(val structure: StructureND<T>) : Structure1D<T> {
|
private value class Structure1DWrapper<out T>(val structure: StructureND<T>) : Structure1D<T> {
|
||||||
override val shape: IntArray get() = structure.shape
|
override val shape: ShapeND get() = structure.shape
|
||||||
override val size: Int get() = structure.shape[0]
|
override val size: Int get() = structure.shape[0]
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override operator fun get(index: Int): T = structure[index]
|
override operator fun get(index: Int): T = structure[index]
|
||||||
|
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
@ -56,13 +60,16 @@ private value class Structure1DWrapper<out T>(val structure: StructureND<T>) : S
|
|||||||
* A 1D wrapper for a mutable nd-structure
|
* A 1D wrapper for a mutable nd-structure
|
||||||
*/
|
*/
|
||||||
private class MutableStructure1DWrapper<T>(val structure: MutableStructureND<T>) : MutableStructure1D<T> {
|
private class MutableStructure1DWrapper<T>(val structure: MutableStructureND<T>) : MutableStructure1D<T> {
|
||||||
override val shape: IntArray get() = structure.shape
|
override val shape: ShapeND get() = structure.shape
|
||||||
override val size: Int get() = structure.shape[0]
|
override val size: Int get() = structure.shape[0]
|
||||||
|
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements()
|
override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements()
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override fun get(index: Int): T = structure[index]
|
override fun get(index: Int): T = structure[index]
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override fun set(index: Int, value: T) {
|
override fun set(index: Int, value: T) {
|
||||||
structure[intArrayOf(index)] = value
|
structure[intArrayOf(index)] = value
|
||||||
}
|
}
|
||||||
@ -83,7 +90,7 @@ private class MutableStructure1DWrapper<T>(val structure: MutableStructureND<T>)
|
|||||||
*/
|
*/
|
||||||
@JvmInline
|
@JvmInline
|
||||||
private value class Buffer1DWrapper<out T>(val buffer: Buffer<T>) : Structure1D<T> {
|
private value class Buffer1DWrapper<out T>(val buffer: Buffer<T>) : Structure1D<T> {
|
||||||
override val shape: IntArray get() = intArrayOf(buffer.size)
|
override val shape: ShapeND get() = ShapeND(buffer.size)
|
||||||
override val size: Int get() = buffer.size
|
override val size: Int get() = buffer.size
|
||||||
|
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
@ -95,7 +102,7 @@ private value class Buffer1DWrapper<out T>(val buffer: Buffer<T>) : Structure1D<
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class MutableBuffer1DWrapper<T>(val buffer: MutableBuffer<T>) : MutableStructure1D<T> {
|
internal class MutableBuffer1DWrapper<T>(val buffer: MutableBuffer<T>) : MutableStructure1D<T> {
|
||||||
override val shape: IntArray get() = intArrayOf(buffer.size)
|
override val shape: ShapeND get() = ShapeND(buffer.size)
|
||||||
override val size: Int get() = buffer.size
|
override val size: Int get() = buffer.size
|
||||||
|
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
|
@ -7,6 +7,7 @@ package space.kscience.kmath.nd
|
|||||||
|
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
|
import space.kscience.kmath.structures.MutableBuffer
|
||||||
import space.kscience.kmath.structures.MutableListBuffer
|
import space.kscience.kmath.structures.MutableListBuffer
|
||||||
import space.kscience.kmath.structures.VirtualBuffer
|
import space.kscience.kmath.structures.VirtualBuffer
|
||||||
import kotlin.jvm.JvmInline
|
import kotlin.jvm.JvmInline
|
||||||
@ -28,7 +29,7 @@ public interface Structure2D<out T> : StructureND<T> {
|
|||||||
*/
|
*/
|
||||||
public val colNum: Int
|
public val colNum: Int
|
||||||
|
|
||||||
override val shape: IntArray get() = intArrayOf(rowNum, colNum)
|
override val shape: ShapeND get() = ShapeND(rowNum, colNum)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The buffer of rows of this structure. It gets elements from the structure dynamically.
|
* The buffer of rows of this structure. It gets elements from the structure dynamically.
|
||||||
@ -53,6 +54,7 @@ public interface Structure2D<out T> : StructureND<T> {
|
|||||||
*/
|
*/
|
||||||
public operator fun get(i: Int, j: Int): T
|
public operator fun get(i: Int, j: Int): T
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override operator fun get(index: IntArray): T {
|
override operator fun get(index: IntArray): T {
|
||||||
require(index.size == 2) { "Index dimension mismatch. Expected 2 but found ${index.size}" }
|
require(index.size == 2) { "Index dimension mismatch. Expected 2 but found ${index.size}" }
|
||||||
return get(index[0], index[1])
|
return get(index[0], index[1])
|
||||||
@ -84,15 +86,15 @@ public interface MutableStructure2D<T> : Structure2D<T>, MutableStructureND<T> {
|
|||||||
* The buffer of rows of this structure. It gets elements from the structure dynamically.
|
* The buffer of rows of this structure. It gets elements from the structure dynamically.
|
||||||
*/
|
*/
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
override val rows: List<MutableStructure1D<T>>
|
override val rows: List<MutableBuffer<T>>
|
||||||
get() = List(rowNum) { i -> MutableBuffer1DWrapper(MutableListBuffer(colNum) { j -> get(i, j) }) }
|
get() = List(rowNum) { i -> MutableListBuffer(colNum) { j -> get(i, j) } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The buffer of columns of this structure. It gets elements from the structure dynamically.
|
* The buffer of columns of this structure. It gets elements from the structure dynamically.
|
||||||
*/
|
*/
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
override val columns: List<MutableStructure1D<T>>
|
override val columns: List<MutableBuffer<T>>
|
||||||
get() = List(colNum) { j -> MutableBuffer1DWrapper(MutableListBuffer(rowNum) { i -> get(i, j) }) }
|
get() = List(colNum) { j -> MutableListBuffer(rowNum) { i -> get(i, j) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,11 +102,12 @@ public interface MutableStructure2D<T> : Structure2D<T>, MutableStructureND<T> {
|
|||||||
*/
|
*/
|
||||||
@JvmInline
|
@JvmInline
|
||||||
private value class Structure2DWrapper<out T>(val structure: StructureND<T>) : Structure2D<T> {
|
private value class Structure2DWrapper<out T>(val structure: StructureND<T>) : Structure2D<T> {
|
||||||
override val shape: Shape get() = structure.shape
|
override val shape: ShapeND get() = structure.shape
|
||||||
|
|
||||||
override val rowNum: Int get() = shape[0]
|
override val rowNum: Int get() = shape[0]
|
||||||
override val colNum: Int get() = shape[1]
|
override val colNum: Int get() = shape[1]
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override operator fun get(i: Int, j: Int): T = structure[i, j]
|
override operator fun get(i: Int, j: Int): T = structure[i, j]
|
||||||
|
|
||||||
override fun <F : StructureFeature> getFeature(type: KClass<out F>): F? = structure.getFeature(type)
|
override fun <F : StructureFeature> getFeature(type: KClass<out F>): F? = structure.getFeature(type)
|
||||||
@ -117,17 +120,20 @@ private value class Structure2DWrapper<out T>(val structure: StructureND<T>) : S
|
|||||||
* A 2D wrapper for a mutable nd-structure
|
* A 2D wrapper for a mutable nd-structure
|
||||||
*/
|
*/
|
||||||
private class MutableStructure2DWrapper<T>(val structure: MutableStructureND<T>) : MutableStructure2D<T> {
|
private class MutableStructure2DWrapper<T>(val structure: MutableStructureND<T>) : MutableStructure2D<T> {
|
||||||
override val shape: Shape get() = structure.shape
|
override val shape: ShapeND get() = structure.shape
|
||||||
|
|
||||||
override val rowNum: Int get() = shape[0]
|
override val rowNum: Int get() = shape[0]
|
||||||
override val colNum: Int get() = shape[1]
|
override val colNum: Int get() = shape[1]
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override operator fun get(i: Int, j: Int): T = structure[i, j]
|
override operator fun get(i: Int, j: Int): T = structure[i, j]
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override fun set(index: IntArray, value: T) {
|
override fun set(index: IntArray, value: T) {
|
||||||
structure[index] = value
|
structure[index] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override operator fun set(i: Int, j: Int, value: T) {
|
override operator fun set(i: Int, j: Int, value: T) {
|
||||||
structure[intArrayOf(i, j)] = value
|
structure[intArrayOf(i, j)] = value
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
|
|||||||
* The shape of structure i.e., non-empty sequence of non-negative integers that specify sizes of dimensions of
|
* The shape of structure i.e., non-empty sequence of non-negative integers that specify sizes of dimensions of
|
||||||
* this structure.
|
* this structure.
|
||||||
*/
|
*/
|
||||||
override val shape: Shape
|
override val shape: ShapeND
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The count of dimensions in this structure. It should be equal to size of [shape].
|
* The count of dimensions in this structure. It should be equal to size of [shape].
|
||||||
@ -46,6 +46,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
|
|||||||
* @param index the indices.
|
* @param index the indices.
|
||||||
* @return the value.
|
* @return the value.
|
||||||
*/
|
*/
|
||||||
|
@PerformancePitfall
|
||||||
public operator fun get(index: IntArray): T
|
public operator fun get(index: IntArray): T
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,6 +98,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
|
|||||||
/**
|
/**
|
||||||
* Debug output to string
|
* Debug output to string
|
||||||
*/
|
*/
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
public fun toString(structure: StructureND<*>): String {
|
public fun toString(structure: StructureND<*>): String {
|
||||||
val bufferRepr: String = when (structure.shape.size) {
|
val bufferRepr: String = when (structure.shape.size) {
|
||||||
1 -> (0 until structure.shape[0]).map { structure[it] }
|
1 -> (0 until structure.shape[0]).map { structure[it] }
|
||||||
@ -116,7 +118,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
|
|||||||
}
|
}
|
||||||
val className = structure::class.simpleName ?: "StructureND"
|
val className = structure::class.simpleName ?: "StructureND"
|
||||||
|
|
||||||
return "$className(shape=${structure.shape.contentToString()}, buffer=$bufferRepr)"
|
return "$className(shape=${structure.shape}, buffer=$bufferRepr)"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,28 +147,28 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
|
|||||||
): BufferND<T> = BufferND(strides, Buffer.auto(type, strides.linearSize) { i -> initializer(strides.index(i)) })
|
): BufferND<T> = BufferND(strides, Buffer.auto(type, strides.linearSize) { i -> initializer(strides.index(i)) })
|
||||||
|
|
||||||
public fun <T> buffered(
|
public fun <T> buffered(
|
||||||
shape: IntArray,
|
shape: ShapeND,
|
||||||
bufferFactory: BufferFactory<T> = BufferFactory.boxing(),
|
bufferFactory: BufferFactory<T> = BufferFactory.boxing(),
|
||||||
initializer: (IntArray) -> T,
|
initializer: (IntArray) -> T,
|
||||||
): BufferND<T> = buffered(DefaultStrides(shape), bufferFactory, initializer)
|
): BufferND<T> = buffered(ColumnStrides(shape), bufferFactory, initializer)
|
||||||
|
|
||||||
public inline fun <reified T : Any> auto(
|
public inline fun <reified T : Any> auto(
|
||||||
shape: IntArray,
|
shape: ShapeND,
|
||||||
crossinline initializer: (IntArray) -> T,
|
crossinline initializer: (IntArray) -> T,
|
||||||
): BufferND<T> = auto(DefaultStrides(shape), initializer)
|
): BufferND<T> = auto(ColumnStrides(shape), initializer)
|
||||||
|
|
||||||
@JvmName("autoVarArg")
|
@JvmName("autoVarArg")
|
||||||
public inline fun <reified T : Any> auto(
|
public inline fun <reified T : Any> auto(
|
||||||
vararg shape: Int,
|
vararg shape: Int,
|
||||||
crossinline initializer: (IntArray) -> T,
|
crossinline initializer: (IntArray) -> T,
|
||||||
): BufferND<T> =
|
): BufferND<T> =
|
||||||
auto(DefaultStrides(shape), initializer)
|
auto(ColumnStrides(ShapeND(shape)), initializer)
|
||||||
|
|
||||||
public inline fun <T : Any> auto(
|
public inline fun <T : Any> auto(
|
||||||
type: KClass<T>,
|
type: KClass<T>,
|
||||||
vararg shape: Int,
|
vararg shape: Int,
|
||||||
crossinline initializer: (IntArray) -> T,
|
crossinline initializer: (IntArray) -> T,
|
||||||
): BufferND<T> = auto(type, DefaultStrides(shape), initializer)
|
): BufferND<T> = auto(type, ColumnStrides(ShapeND(shape)), initializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,8 +216,13 @@ public fun <T : Comparable<T>> LinearSpace<T, Ring<T>>.contentEquals(
|
|||||||
* @param index the indices.
|
* @param index the indices.
|
||||||
* @return the value.
|
* @return the value.
|
||||||
*/
|
*/
|
||||||
|
@PerformancePitfall
|
||||||
public operator fun <T> StructureND<T>.get(vararg index: Int): T = get(index)
|
public operator fun <T> StructureND<T>.get(vararg index: Int): T = get(index)
|
||||||
|
|
||||||
|
public operator fun StructureND<Double>.get(vararg index: Int): Double = getDouble(index)
|
||||||
|
|
||||||
|
public operator fun StructureND<Int>.get(vararg index: Int): Int = getInt(index)
|
||||||
|
|
||||||
//@UnstableKMathAPI
|
//@UnstableKMathAPI
|
||||||
//public inline fun <reified T : StructureFeature> StructureND<*>.getFeature(): T? = getFeature(T::class)
|
//public inline fun <reified T : StructureFeature> StructureND<*>.getFeature(): T? = getFeature(T::class)
|
||||||
|
|
||||||
@ -229,12 +236,14 @@ public interface MutableStructureND<T> : StructureND<T> {
|
|||||||
* @param index the indices.
|
* @param index the indices.
|
||||||
* @param value the value.
|
* @param value the value.
|
||||||
*/
|
*/
|
||||||
|
@PerformancePitfall
|
||||||
public operator fun set(index: IntArray, value: T)
|
public operator fun set(index: IntArray, value: T)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set value at specified indices
|
* Set value at specified indices
|
||||||
*/
|
*/
|
||||||
|
@PerformancePitfall
|
||||||
public operator fun <T> MutableStructureND<T>.set(vararg index: Int, value: T) {
|
public operator fun <T> MutableStructureND<T>.set(vararg index: Int, value: T) {
|
||||||
set(index, value)
|
set(index, value)
|
||||||
}
|
}
|
@ -5,12 +5,15 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.nd
|
package space.kscience.kmath.nd
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
|
|
||||||
public open class VirtualStructureND<T>(
|
public open class VirtualStructureND<T>(
|
||||||
override val shape: Shape,
|
override val shape: ShapeND,
|
||||||
public val producer: (IntArray) -> T,
|
public val producer: (IntArray) -> T,
|
||||||
) : StructureND<T> {
|
) : StructureND<T> {
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override fun get(index: IntArray): T {
|
override fun get(index: IntArray): T {
|
||||||
requireIndexInShape(index, shape)
|
requireIndexInShape(index, shape)
|
||||||
return producer(index)
|
return producer(index)
|
||||||
@ -19,12 +22,12 @@ public open class VirtualStructureND<T>(
|
|||||||
|
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public class VirtualDoubleStructureND(
|
public class VirtualDoubleStructureND(
|
||||||
shape: Shape,
|
shape: ShapeND,
|
||||||
producer: (IntArray) -> Double,
|
producer: (IntArray) -> Double,
|
||||||
) : VirtualStructureND<Double>(shape, producer)
|
) : VirtualStructureND<Double>(shape, producer)
|
||||||
|
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public class VirtualIntStructureND(
|
public class VirtualIntStructureND(
|
||||||
shape: Shape,
|
shape: ShapeND,
|
||||||
producer: (IntArray) -> Int,
|
producer: (IntArray) -> Int,
|
||||||
) : VirtualStructureND<Int>(shape, producer)
|
) : VirtualStructureND<Int>(shape, producer)
|
@ -15,9 +15,9 @@ public fun <T, A : Algebra<T>> AlgebraND<T, A>.structureND(
|
|||||||
shapeFirst: Int,
|
shapeFirst: Int,
|
||||||
vararg shapeRest: Int,
|
vararg shapeRest: Int,
|
||||||
initializer: A.(IntArray) -> T
|
initializer: A.(IntArray) -> T
|
||||||
): StructureND<T> = structureND(Shape(shapeFirst, *shapeRest), initializer)
|
): StructureND<T> = structureND(ShapeND(shapeFirst, *shapeRest), initializer)
|
||||||
|
|
||||||
public fun <T, A : Group<T>> AlgebraND<T, A>.zero(shape: Shape): StructureND<T> = structureND(shape) { zero }
|
public fun <T, A : Group<T>> AlgebraND<T, A>.zero(shape: ShapeND): StructureND<T> = structureND(shape) { zero }
|
||||||
|
|
||||||
@JvmName("zeroVarArg")
|
@JvmName("zeroVarArg")
|
||||||
public fun <T, A : Group<T>> AlgebraND<T, A>.zero(
|
public fun <T, A : Group<T>> AlgebraND<T, A>.zero(
|
||||||
@ -25,7 +25,7 @@ public fun <T, A : Group<T>> AlgebraND<T, A>.zero(
|
|||||||
vararg shapeRest: Int,
|
vararg shapeRest: Int,
|
||||||
): StructureND<T> = structureND(shapeFirst, *shapeRest) { zero }
|
): StructureND<T> = structureND(shapeFirst, *shapeRest) { zero }
|
||||||
|
|
||||||
public fun <T, A : Ring<T>> AlgebraND<T, A>.one(shape: Shape): StructureND<T> = structureND(shape) { one }
|
public fun <T, A : Ring<T>> AlgebraND<T, A>.one(shape: ShapeND): StructureND<T> = structureND(shape) { one }
|
||||||
|
|
||||||
@JvmName("oneVarArg")
|
@JvmName("oneVarArg")
|
||||||
public fun <T, A : Ring<T>> AlgebraND<T, A>.one(
|
public fun <T, A : Ring<T>> AlgebraND<T, A>.one(
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.nd
|
package space.kscience.kmath.nd
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
public fun <T> StructureND<T>.roll(axis: Int, step: Int = 1): StructureND<T> {
|
public fun <T> StructureND<T>.roll(axis: Int, step: Int = 1): StructureND<T> {
|
||||||
require(axis in shape.indices) { "Axis $axis is outside of shape dimensions: [0, ${shape.size})" }
|
require(axis in shape.indices) { "Axis $axis is outside of shape dimensions: [0, ${shape.size})" }
|
||||||
return VirtualStructureND(shape) { index ->
|
return VirtualStructureND(shape) { index ->
|
||||||
@ -19,6 +22,7 @@ public fun <T> StructureND<T>.roll(axis: Int, step: Int = 1): StructureND<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
public fun <T> StructureND<T>.roll(pair: Pair<Int, Int>, vararg others: Pair<Int, Int>): StructureND<T> {
|
public fun <T> StructureND<T>.roll(pair: Pair<Int, Int>, vararg others: Pair<Int, Int>): StructureND<T> {
|
||||||
val axisMap: Map<Int, Int> = mapOf(pair, *others)
|
val axisMap: Map<Int, Int> = mapOf(pair, *others)
|
||||||
require(axisMap.keys.all { it in shape.indices }) { "Some of axes ${axisMap.keys} is outside of shape dimensions: [0, ${shape.size})" }
|
require(axisMap.keys.all { it in shape.indices }) { "Some of axes ${axisMap.keys} is outside of shape dimensions: [0, ${shape.size})" }
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2022 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.nd
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
|
|
||||||
|
public interface StructureNDOfDouble : StructureND<Double> {
|
||||||
|
/**
|
||||||
|
* Guaranteed non-blocking access to content
|
||||||
|
*/
|
||||||
|
public fun getDouble(index: IntArray): Double
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimized method to access primitive without boxing if possible
|
||||||
|
*/
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
|
public fun StructureND<Double>.getDouble(index: IntArray): Double =
|
||||||
|
if (this is StructureNDOfDouble) getDouble(index) else get(index)
|
||||||
|
|
||||||
|
public interface MutableStructureNDOfDouble : StructureNDOfDouble, MutableStructureND<Double> {
|
||||||
|
/**
|
||||||
|
* Guaranteed non-blocking access to content
|
||||||
|
*/
|
||||||
|
public fun setDouble(index: IntArray, value: Double)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
|
public fun MutableStructureND<Double>.getDouble(index: IntArray): Double =
|
||||||
|
if (this is StructureNDOfDouble) getDouble(index) else get(index)
|
||||||
|
|
||||||
|
|
||||||
|
public interface StructureNDOfInt : StructureND<Int> {
|
||||||
|
/**
|
||||||
|
* Guaranteed non-blocking access to content
|
||||||
|
*/
|
||||||
|
public fun getInt(index: IntArray): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
|
public fun StructureND<Int>.getInt(index: IntArray): Int =
|
||||||
|
if (this is StructureNDOfInt) getInt(index) else get(index)
|
@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.operations
|
package space.kscience.kmath.operations
|
||||||
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.DoubleField.pow
|
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
import space.kscience.kmath.structures.DoubleBuffer
|
||||||
|
|
||||||
@ -32,9 +30,9 @@ public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Doub
|
|||||||
override fun atanh(arg: Buffer<Double>): DoubleBuffer = super<DoubleBufferOps>.atanh(arg)
|
override fun atanh(arg: Buffer<Double>): DoubleBuffer = super<DoubleBufferOps>.atanh(arg)
|
||||||
|
|
||||||
override fun power(arg: Buffer<Double>, pow: Number): DoubleBuffer = if (pow.isInteger()) {
|
override fun power(arg: Buffer<Double>, pow: Number): DoubleBuffer = if (pow.isInteger()) {
|
||||||
arg.mapInline { it.pow(pow.toInt()) }
|
arg.map { it.pow(pow.toInt()) }
|
||||||
} else {
|
} else {
|
||||||
arg.mapInline {
|
arg.map {
|
||||||
if(it<0) throw IllegalArgumentException("Negative argument $it could not be raised to the fractional power")
|
if(it<0) throw IllegalArgumentException("Negative argument $it could not be raised to the fractional power")
|
||||||
it.pow(pow.toDouble())
|
it.pow(pow.toDouble())
|
||||||
}
|
}
|
||||||
@ -42,103 +40,4 @@ public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Doub
|
|||||||
|
|
||||||
override fun unaryOperationFunction(operation: String): (arg: Buffer<Double>) -> Buffer<Double> =
|
override fun unaryOperationFunction(operation: String): (arg: Buffer<Double>) -> Buffer<Double> =
|
||||||
super<ExtendedField>.unaryOperationFunction(operation)
|
super<ExtendedField>.unaryOperationFunction(operation)
|
||||||
|
|
||||||
// override fun number(value: Number): Buffer<Double> = DoubleBuffer(size) { value.toDouble() }
|
|
||||||
//
|
|
||||||
// override fun Buffer<Double>.unaryMinus(): Buffer<Double> = DoubleBufferOperations.run {
|
|
||||||
// -this@unaryMinus
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun add(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.add(a, b)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// override fun multiply(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.multiply(a, b)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun divide(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.divide(a, b)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun sin(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.sin(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun cos(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.cos(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun tan(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.tan(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun asin(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.asin(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun acos(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.acos(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun atan(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.atan(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun sinh(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.sinh(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun cosh(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.cosh(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun tanh(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.tanh(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun asinh(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.asinh(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun acosh(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.acosh(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun atanh(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.atanh(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun power(arg: Buffer<Double>, pow: Number): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.power(arg, pow)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun exp(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.exp(arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun ln(arg: Buffer<Double>): DoubleBuffer {
|
|
||||||
// require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
|
||||||
// return DoubleBufferOperations.ln(arg)
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
@ -6,10 +6,8 @@
|
|||||||
package space.kscience.kmath.operations
|
package space.kscience.kmath.operations
|
||||||
|
|
||||||
import space.kscience.kmath.linear.Point
|
import space.kscience.kmath.linear.Point
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
import space.kscience.kmath.structures.*
|
||||||
import space.kscience.kmath.structures.MutableBufferFactory
|
|
||||||
import space.kscience.kmath.structures.asBuffer
|
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,10 +17,29 @@ public abstract class DoubleBufferOps : BufferAlgebra<Double, DoubleField>, Exte
|
|||||||
Norm<Buffer<Double>, Double> {
|
Norm<Buffer<Double>, Double> {
|
||||||
|
|
||||||
override val elementAlgebra: DoubleField get() = DoubleField
|
override val elementAlgebra: DoubleField get() = DoubleField
|
||||||
|
|
||||||
override val elementBufferFactory: MutableBufferFactory<Double> get() = elementAlgebra.bufferFactory
|
override val elementBufferFactory: MutableBufferFactory<Double> get() = elementAlgebra.bufferFactory
|
||||||
|
|
||||||
override fun Buffer<Double>.map(block: DoubleField.(Double) -> Double): DoubleBuffer =
|
@Suppress("OVERRIDE_BY_INLINE")
|
||||||
mapInline { DoubleField.block(it) }
|
@OptIn(UnstableKMathAPI::class)
|
||||||
|
final override inline fun Buffer<Double>.map(block: DoubleField.(Double) -> Double): DoubleBuffer =
|
||||||
|
DoubleArray(size) { DoubleField.block(getDouble(it)) }.asBuffer()
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(UnstableKMathAPI::class)
|
||||||
|
@Suppress("OVERRIDE_BY_INLINE")
|
||||||
|
final override inline fun Buffer<Double>.mapIndexed(block: DoubleField.(index: Int, arg: Double) -> Double): DoubleBuffer =
|
||||||
|
DoubleBuffer(size) { DoubleField.block(it, getDouble(it)) }
|
||||||
|
|
||||||
|
@OptIn(UnstableKMathAPI::class)
|
||||||
|
@Suppress("OVERRIDE_BY_INLINE")
|
||||||
|
final override inline fun Buffer<Double>.zip(
|
||||||
|
other: Buffer<Double>,
|
||||||
|
block: DoubleField.(left: Double, right: Double) -> Double,
|
||||||
|
): DoubleBuffer {
|
||||||
|
require(size == other.size) { "Incompatible buffer sizes. left: ${size}, right: ${other.size}" }
|
||||||
|
return DoubleBuffer(size) { DoubleField.block(getDouble(it), other.getDouble(it)) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun unaryOperationFunction(operation: String): (arg: Buffer<Double>) -> Buffer<Double> =
|
override fun unaryOperationFunction(operation: String): (arg: Buffer<Double>) -> Buffer<Double> =
|
||||||
super<ExtendedFieldOps>.unaryOperationFunction(operation)
|
super<ExtendedFieldOps>.unaryOperationFunction(operation)
|
||||||
@ -30,7 +47,7 @@ public abstract class DoubleBufferOps : BufferAlgebra<Double, DoubleField>, Exte
|
|||||||
override fun binaryOperationFunction(operation: String): (left: Buffer<Double>, right: Buffer<Double>) -> Buffer<Double> =
|
override fun binaryOperationFunction(operation: String): (left: Buffer<Double>, right: Buffer<Double>) -> Buffer<Double> =
|
||||||
super<ExtendedFieldOps>.binaryOperationFunction(operation)
|
super<ExtendedFieldOps>.binaryOperationFunction(operation)
|
||||||
|
|
||||||
override fun Buffer<Double>.unaryMinus(): DoubleBuffer = mapInline { -it }
|
override fun Buffer<Double>.unaryMinus(): DoubleBuffer = map { -it }
|
||||||
|
|
||||||
override fun add(left: Buffer<Double>, right: Buffer<Double>): DoubleBuffer {
|
override fun add(left: Buffer<Double>, right: Buffer<Double>): DoubleBuffer {
|
||||||
require(right.size == left.size) {
|
require(right.size == left.size) {
|
||||||
@ -77,6 +94,7 @@ public abstract class DoubleBufferOps : BufferAlgebra<Double, DoubleField>, Exte
|
|||||||
// } else RealBuffer(DoubleArray(a.size) { a[it] / kValue })
|
// } else RealBuffer(DoubleArray(a.size) { a[it] / kValue })
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
override fun multiply(left: Buffer<Double>, right: Buffer<Double>): DoubleBuffer {
|
override fun multiply(left: Buffer<Double>, right: Buffer<Double>): DoubleBuffer {
|
||||||
require(right.size == left.size) {
|
require(right.size == left.size) {
|
||||||
"The size of the first buffer ${left.size} should be the same as for second one: ${right.size} "
|
"The size of the first buffer ${left.size} should be the same as for second one: ${right.size} "
|
||||||
@ -101,55 +119,83 @@ public abstract class DoubleBufferOps : BufferAlgebra<Double, DoubleField>, Exte
|
|||||||
} else DoubleBuffer(DoubleArray(left.size) { left[it] / right[it] })
|
} else DoubleBuffer(DoubleArray(left.size) { left[it] / right[it] })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sin(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::sin)
|
override fun sin(arg: Buffer<Double>): DoubleBuffer = arg.map { sin(it) }
|
||||||
|
|
||||||
override fun cos(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::cos)
|
override fun cos(arg: Buffer<Double>): DoubleBuffer = arg.map { cos(it) }
|
||||||
|
|
||||||
override fun tan(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::tan)
|
override fun tan(arg: Buffer<Double>): DoubleBuffer = arg.map { tan(it) }
|
||||||
|
|
||||||
override fun asin(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::asin)
|
override fun asin(arg: Buffer<Double>): DoubleBuffer = arg.map { asin(it) }
|
||||||
|
|
||||||
override fun acos(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::acos)
|
override fun acos(arg: Buffer<Double>): DoubleBuffer = arg.map { acos(it) }
|
||||||
|
|
||||||
override fun atan(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::atan)
|
override fun atan(arg: Buffer<Double>): DoubleBuffer = arg.map { atan(it) }
|
||||||
|
|
||||||
override fun sinh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::sinh)
|
override fun sinh(arg: Buffer<Double>): DoubleBuffer = arg.map { sinh(it) }
|
||||||
|
|
||||||
override fun cosh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::cosh)
|
override fun cosh(arg: Buffer<Double>): DoubleBuffer = arg.map { cosh(it) }
|
||||||
|
|
||||||
override fun tanh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::tanh)
|
override fun tanh(arg: Buffer<Double>): DoubleBuffer = arg.map { tanh(it) }
|
||||||
|
|
||||||
override fun asinh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::asinh)
|
override fun asinh(arg: Buffer<Double>): DoubleBuffer = arg.map { asinh(it) }
|
||||||
|
|
||||||
override fun acosh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::acosh)
|
override fun acosh(arg: Buffer<Double>): DoubleBuffer = arg.map { acosh(it) }
|
||||||
|
|
||||||
override fun atanh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::atanh)
|
override fun atanh(arg: Buffer<Double>): DoubleBuffer = arg.map { atanh(it) }
|
||||||
|
|
||||||
override fun exp(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::exp)
|
override fun exp(arg: Buffer<Double>): DoubleBuffer = arg.map { exp(it) }
|
||||||
|
|
||||||
override fun ln(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::ln)
|
override fun ln(arg: Buffer<Double>): DoubleBuffer = arg.map { ln(it) }
|
||||||
|
|
||||||
override fun norm(arg: Buffer<Double>): Double = DoubleL2Norm.norm(arg)
|
override fun norm(arg: Buffer<Double>): Double = DoubleL2Norm.norm(arg)
|
||||||
|
|
||||||
override fun scale(a: Buffer<Double>, value: Double): DoubleBuffer = a.mapInline { it * value }
|
override fun scale(a: Buffer<Double>, value: Double): DoubleBuffer = a.map { it * value }
|
||||||
|
|
||||||
override fun power(arg: Buffer<Double>, pow: Number): Buffer<Double> = if (pow is Int) {
|
override fun power(arg: Buffer<Double>, pow: Number): Buffer<Double> = if (pow is Int) {
|
||||||
arg.mapInline { it.pow(pow) }
|
arg.map { it.pow(pow) }
|
||||||
} else {
|
} else {
|
||||||
arg.mapInline { it.pow(pow.toDouble()) }
|
arg.map { it.pow(pow.toDouble()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
public companion object : DoubleBufferOps() {
|
public companion object : DoubleBufferOps()
|
||||||
public inline fun Buffer<Double>.mapInline(block: (Double) -> Double): DoubleBuffer =
|
|
||||||
if (this is DoubleBuffer) {
|
|
||||||
DoubleArray(size) { block(array[it]) }.asBuffer()
|
|
||||||
} else {
|
|
||||||
DoubleArray(size) { block(get(it)) }.asBuffer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object DoubleL2Norm : Norm<Point<Double>, Double> {
|
public object DoubleL2Norm : Norm<Point<Double>, Double> {
|
||||||
override fun norm(arg: Point<Double>): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) })
|
override fun norm(arg: Point<Double>): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun DoubleBufferOps.sum(buffer: Buffer<Double>): Double = buffer.reduce(Double::plus)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sum of elements using given [conversion]
|
||||||
|
*/
|
||||||
|
public inline fun <T> DoubleBufferOps.sumOf(buffer: Buffer<T>, conversion: (T) -> Double): Double =
|
||||||
|
buffer.fold(0.0) { acc, value -> acc + conversion(value) }
|
||||||
|
|
||||||
|
public fun DoubleBufferOps.average(buffer: Buffer<Double>): Double = sum(buffer) / buffer.size
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Average of elements using given [conversion]
|
||||||
|
*/
|
||||||
|
public inline fun <T> DoubleBufferOps.averageOf(buffer: Buffer<T>, conversion: (T) -> Double): Double =
|
||||||
|
sumOf(buffer, conversion) / buffer.size
|
||||||
|
|
||||||
|
public fun DoubleBufferOps.dispersion(buffer: Buffer<Double>): Double {
|
||||||
|
val av = average(buffer)
|
||||||
|
return buffer.fold(0.0) { acc, value -> acc + (value - av).pow(2) } / buffer.size
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun DoubleBufferOps.std(buffer: Buffer<Double>): Double = sqrt(dispersion(buffer))
|
||||||
|
|
||||||
|
public fun DoubleBufferOps.covariance(x: Buffer<Double>, y: Buffer<Double>): Double {
|
||||||
|
require(x.size == y.size) { "Expected buffers of the same size, but x.size == ${x.size} and y.size == ${y.size}" }
|
||||||
|
val xMean = average(x)
|
||||||
|
val yMean = average(y)
|
||||||
|
var sum = 0.0
|
||||||
|
x.indices.forEach {
|
||||||
|
sum += (x[it] - xMean) * (y[it] - yMean)
|
||||||
|
}
|
||||||
|
return sum / (x.size - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,21 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.operations
|
package space.kscience.kmath.operations
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
|
import space.kscience.kmath.structures.Buffer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sum of all elements in the iterable in this [Group].
|
||||||
|
*
|
||||||
|
* @receiver the algebra that provides addition.
|
||||||
|
* @param data the iterable to sum up.
|
||||||
|
* @return the sum.
|
||||||
|
*/
|
||||||
|
@PerformancePitfall("Potential boxing access to buffer elements")
|
||||||
|
public fun <T> Group<T>.sum(data: Buffer<T>): T = data.fold(zero) { left, right ->
|
||||||
|
add(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the sum of all elements in the iterable in this [Group].
|
* Returns the sum of all elements in the iterable in this [Group].
|
||||||
*
|
*
|
||||||
@ -29,6 +44,18 @@ public fun <T> Group<T>.sum(data: Sequence<T>): T = data.fold(zero) { left, righ
|
|||||||
add(left, right)
|
add(left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an average value of elements in the iterable in this [Group].
|
||||||
|
*
|
||||||
|
* @receiver the algebra that provides addition and division.
|
||||||
|
* @param data the iterable to find average.
|
||||||
|
* @return the average value.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
@PerformancePitfall("Potential boxing access to buffer elements")
|
||||||
|
public fun <T, S> S.average(data: Buffer<T>): T where S : Group<T>, S : ScaleOperations<T> =
|
||||||
|
sum(data) / data.size
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an average value of elements in the iterable in this [Group].
|
* Returns an average value of elements in the iterable in this [Group].
|
||||||
*
|
*
|
||||||
@ -95,4 +122,3 @@ public fun <T, S> Iterable<T>.averageWith(space: S): T where S : Group<T>, S : S
|
|||||||
*/
|
*/
|
||||||
public fun <T, S> Sequence<T>.averageWith(space: S): T where S : Group<T>, S : ScaleOperations<T> =
|
public fun <T, S> Sequence<T>.averageWith(space: S): T where S : Group<T>, S : ScaleOperations<T> =
|
||||||
space.average(this)
|
space.average(this)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.structures
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
|
import space.kscience.kmath.operations.WithSize
|
||||||
import space.kscience.kmath.operations.asSequence
|
import space.kscience.kmath.operations.asSequence
|
||||||
import kotlin.jvm.JvmInline
|
import kotlin.jvm.JvmInline
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@ -50,11 +51,11 @@ public fun interface MutableBufferFactory<T> : BufferFactory<T> {
|
|||||||
*
|
*
|
||||||
* @param T the type of elements contained in the buffer.
|
* @param T the type of elements contained in the buffer.
|
||||||
*/
|
*/
|
||||||
public interface Buffer<out T> {
|
public interface Buffer<out T> : WithSize {
|
||||||
/**
|
/**
|
||||||
* The size of this buffer.
|
* The size of this buffer.
|
||||||
*/
|
*/
|
||||||
public val size: Int
|
override val size: Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets element at given index.
|
* Gets element at given index.
|
||||||
@ -64,7 +65,7 @@ public interface Buffer<out T> {
|
|||||||
/**
|
/**
|
||||||
* Iterates over all elements.
|
* Iterates over all elements.
|
||||||
*/
|
*/
|
||||||
public operator fun iterator(): Iterator<T>
|
public operator fun iterator(): Iterator<T> = indices.asSequence().map(::get).iterator()
|
||||||
|
|
||||||
override fun toString(): String
|
override fun toString(): String
|
||||||
|
|
||||||
@ -122,7 +123,14 @@ public interface Buffer<out T> {
|
|||||||
/**
|
/**
|
||||||
* Returns an [IntRange] of the valid indices for this [Buffer].
|
* Returns an [IntRange] of the valid indices for this [Buffer].
|
||||||
*/
|
*/
|
||||||
public val Buffer<*>.indices: IntRange get() = 0 until size
|
public val <T> Buffer<T>.indices: IntRange get() = 0 until size
|
||||||
|
|
||||||
|
public operator fun <T> Buffer<T>.get(index: UInt): T = get(index.toInt())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if index is in range of buffer, return the value. Otherwise, return null.
|
||||||
|
*/
|
||||||
|
public fun <T> Buffer<T>.getOrNull(index: Int): T? = if (index in indices) get(index) else null
|
||||||
|
|
||||||
public fun <T> Buffer<T>.first(): T {
|
public fun <T> Buffer<T>.first(): T {
|
||||||
require(size > 0) { "Can't get the first element of empty buffer" }
|
require(size > 0) { "Can't get the first element of empty buffer" }
|
||||||
|
@ -5,10 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.structures
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
import space.kscience.kmath.nd.DefaultStrides
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.nd.Structure2D
|
|
||||||
import space.kscience.kmath.nd.StructureND
|
|
||||||
import space.kscience.kmath.nd.as2D
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A context that allows to operate on a [MutableBuffer] as on 2d array
|
* A context that allows to operate on a [MutableBuffer] as on 2d array
|
||||||
@ -31,7 +28,7 @@ internal class BufferAccessor2D<T>(
|
|||||||
|
|
||||||
//TODO optimize wrapper
|
//TODO optimize wrapper
|
||||||
fun MutableBuffer<T>.collect(): Structure2D<T> = StructureND.buffered(
|
fun MutableBuffer<T>.collect(): Structure2D<T> = StructureND.buffered(
|
||||||
DefaultStrides(intArrayOf(rowNum, colNum)),
|
ColumnStrides(ShapeND(rowNum, colNum)),
|
||||||
factory
|
factory
|
||||||
) { (i, j) ->
|
) { (i, j) ->
|
||||||
get(i, j)
|
get(i, j)
|
||||||
|
@ -0,0 +1,192 @@
|
|||||||
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A buffer that wraps an original buffer
|
||||||
|
*/
|
||||||
|
public interface BufferView<T> : Buffer<T> {
|
||||||
|
public val origin: Buffer<T>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index in [origin] buffer from index in this buffer.
|
||||||
|
* Return -1 if element not present in the original buffer
|
||||||
|
* This method should be used internally to optimize non-boxing access.
|
||||||
|
*/
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun originIndex(index: Int): Int
|
||||||
|
|
||||||
|
@OptIn(UnstableKMathAPI::class)
|
||||||
|
override fun get(index: Int): T = origin[originIndex(index)]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A zero-copy buffer that "sees" only part of original buffer. Slice can't go beyond original buffer borders.
|
||||||
|
*/
|
||||||
|
public class BufferSlice<T>(
|
||||||
|
override val origin: Buffer<T>,
|
||||||
|
public val offset: Int = 0,
|
||||||
|
override val size: Int,
|
||||||
|
) : BufferView<T> {
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(size > 0) { "Size must be positive" }
|
||||||
|
require(offset + size <= origin.size) {
|
||||||
|
"End of buffer ${offset + size} is beyond the end of origin buffer size ${origin.size}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(index: Int): T = if (index >= size) {
|
||||||
|
throw IndexOutOfBoundsException("$index is out of ${0 until size} rage")
|
||||||
|
} else {
|
||||||
|
origin[index + offset]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<T> =
|
||||||
|
(offset until (offset + size)).asSequence().map { origin[it] }.iterator()
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
override fun originIndex(index: Int): Int = if (index >= size) -1 else index - offset
|
||||||
|
|
||||||
|
override fun toString(): String = "$origin[$offset..${offset + size}"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An expanded buffer that could include the whole initial buffer or its part and fills all space beyond it borders with [defaultValue].
|
||||||
|
*
|
||||||
|
* The [offset] parameter shows the shift of expanded buffer start relative to origin start and could be both positive and negative.
|
||||||
|
*/
|
||||||
|
public class BufferExpanded<T>(
|
||||||
|
override val origin: Buffer<T>,
|
||||||
|
private val defaultValue: T,
|
||||||
|
public val offset: Int = 0,
|
||||||
|
override val size: Int = origin.size,
|
||||||
|
) : BufferView<T> {
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(size > 0) { "Size must be positive" }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(index: Int): T = when (index) {
|
||||||
|
!in 0 until size -> throw IndexOutOfBoundsException("Index $index is not in $indices")
|
||||||
|
in -offset until origin.size - offset -> origin[index + offset]
|
||||||
|
else -> defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
override fun originIndex(index: Int): Int = if (index in -offset until origin.size - offset) index + offset else -1
|
||||||
|
|
||||||
|
override fun toString(): String = "$origin[$offset..${offset + size}]"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero-copy select a slice inside the original buffer
|
||||||
|
*/
|
||||||
|
public fun <T> Buffer<T>.slice(range: IntRange): BufferView<T> = if (this is BufferSlice) {
|
||||||
|
BufferSlice(
|
||||||
|
origin,
|
||||||
|
this.offset + range.first,
|
||||||
|
(range.last - range.first) + 1
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
BufferSlice(
|
||||||
|
this,
|
||||||
|
range.first,
|
||||||
|
(range.last - range.first) + 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize original buffer to a given range using given [range], filling additional segments with [defaultValue].
|
||||||
|
* Range left border could be negative to designate adding new blank segment to the beginning of the buffer
|
||||||
|
*/
|
||||||
|
public fun <T> Buffer<T>.expand(
|
||||||
|
range: IntRange,
|
||||||
|
defaultValue: T,
|
||||||
|
): BufferView<T> = if (range.first >= 0 && range.last < size) {
|
||||||
|
BufferSlice(
|
||||||
|
this,
|
||||||
|
range.first,
|
||||||
|
(range.last - range.first) + 1
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
BufferExpanded(
|
||||||
|
this,
|
||||||
|
defaultValue,
|
||||||
|
range.first,
|
||||||
|
(range.last - range.first) + 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [BufferView] that overrides indexing of the original buffer
|
||||||
|
*/
|
||||||
|
public class PermutedBuffer<T>(
|
||||||
|
override val origin: Buffer<T>,
|
||||||
|
private val permutations: IntArray,
|
||||||
|
) : BufferView<T> {
|
||||||
|
init {
|
||||||
|
permutations.forEach { index ->
|
||||||
|
if (index !in origin.indices) {
|
||||||
|
throw IndexOutOfBoundsException("Index $index is not in ${origin.indices}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val size: Int get() = permutations.size
|
||||||
|
|
||||||
|
override fun get(index: Int): T = origin[permutations[index]]
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<T> = permutations.asSequence().map { origin[it] }.iterator()
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
override fun originIndex(index: Int): Int = if (index in permutations.indices) permutations[index] else -1
|
||||||
|
|
||||||
|
override fun toString(): String = Buffer.toString(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created a permuted view of given buffer using provided [indices]
|
||||||
|
*/
|
||||||
|
public fun <T> Buffer<T>.permute(indices: IntArray): PermutedBuffer<T> =
|
||||||
|
PermutedBuffer(this, indices)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [BufferView] that overrides indexing of the original buffer
|
||||||
|
*/
|
||||||
|
public class PermutedMutableBuffer<T>(
|
||||||
|
override val origin: MutableBuffer<T>,
|
||||||
|
private val permutations: IntArray,
|
||||||
|
) : BufferView<T>, MutableBuffer<T> {
|
||||||
|
init {
|
||||||
|
permutations.forEach { index ->
|
||||||
|
if (index !in origin.indices) {
|
||||||
|
throw IndexOutOfBoundsException("Index $index is not in ${origin.indices}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val size: Int get() = permutations.size
|
||||||
|
|
||||||
|
override fun get(index: Int): T = origin[permutations[index]]
|
||||||
|
|
||||||
|
override fun set(index: Int, value: T) {
|
||||||
|
origin[permutations[index]] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun copy(): MutableBuffer<T> = PermutedMutableBuffer(origin.copy(), permutations)
|
||||||
|
//TODO Probably could be optimized
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<T> = permutations.asSequence().map { origin[it] }.iterator()
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
override fun originIndex(index: Int): Int = if (index in permutations.indices) permutations[index] else -1
|
||||||
|
|
||||||
|
override fun toString(): String = Buffer.toString(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created a permuted mutable view of given buffer using provided [indices]
|
||||||
|
*/
|
||||||
|
public fun <T> MutableBuffer<T>.permute(indices: IntArray): PermutedMutableBuffer<T> =
|
||||||
|
PermutedMutableBuffer(this, indices)
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.structures
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
|
import space.kscience.kmath.operations.BufferTransform
|
||||||
import kotlin.jvm.JvmInline
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,7 +14,7 @@ import kotlin.jvm.JvmInline
|
|||||||
* @property array the underlying array.
|
* @property array the underlying array.
|
||||||
*/
|
*/
|
||||||
@JvmInline
|
@JvmInline
|
||||||
public value class DoubleBuffer(public val array: DoubleArray) : MutableBuffer<Double> {
|
public value class DoubleBuffer(public val array: DoubleArray) : PrimitiveBuffer<Double> {
|
||||||
override val size: Int get() = array.size
|
override val size: Int get() = array.size
|
||||||
|
|
||||||
override operator fun get(index: Int): Double = array[index]
|
override operator fun get(index: Int): Double = array[index]
|
||||||
@ -28,7 +29,7 @@ public value class DoubleBuffer(public val array: DoubleArray) : MutableBuffer<D
|
|||||||
|
|
||||||
override fun toString(): String = Buffer.toString(this)
|
override fun toString(): String = Buffer.toString(this)
|
||||||
|
|
||||||
public companion object{
|
public companion object {
|
||||||
public fun zero(size: Int): DoubleBuffer = DoubleArray(size).asBuffer()
|
public fun zero(size: Int): DoubleBuffer = DoubleArray(size).asBuffer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,7 +41,8 @@ public value class DoubleBuffer(public val array: DoubleArray) : MutableBuffer<D
|
|||||||
* The function [init] is called for each array element sequentially starting from the first one.
|
* The function [init] is called for each array element sequentially starting from the first one.
|
||||||
* It should return the value for a buffer element given its index.
|
* It should return the value for a buffer element given its index.
|
||||||
*/
|
*/
|
||||||
public inline fun DoubleBuffer(size: Int, init: (Int) -> Double): DoubleBuffer = DoubleBuffer(DoubleArray(size) { init(it) })
|
public inline fun DoubleBuffer(size: Int, init: (Int) -> Double): DoubleBuffer =
|
||||||
|
DoubleBuffer(DoubleArray(size) { init(it) })
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new [DoubleBuffer] of given elements.
|
* Returns a new [DoubleBuffer] of given elements.
|
||||||
@ -51,10 +53,18 @@ public fun DoubleBuffer(vararg doubles: Double): DoubleBuffer = DoubleBuffer(dou
|
|||||||
* Returns a new [DoubleArray] containing all the elements of this [Buffer].
|
* Returns a new [DoubleArray] containing all the elements of this [Buffer].
|
||||||
*/
|
*/
|
||||||
public fun Buffer<Double>.toDoubleArray(): DoubleArray = when (this) {
|
public fun Buffer<Double>.toDoubleArray(): DoubleArray = when (this) {
|
||||||
is DoubleBuffer -> array.copyOf()
|
is DoubleBuffer -> array
|
||||||
else -> DoubleArray(size, ::get)
|
else -> DoubleArray(size, ::get)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent this buffer as [DoubleBuffer]. Does not guarantee that changes in the original buffer are reflected on this buffer.
|
||||||
|
*/
|
||||||
|
public fun Buffer<Double>.toDoubleBuffer(): DoubleBuffer = when (this) {
|
||||||
|
is DoubleBuffer -> this
|
||||||
|
else -> DoubleArray(size, ::get).asBuffer()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns [DoubleBuffer] over this array.
|
* Returns [DoubleBuffer] over this array.
|
||||||
*
|
*
|
||||||
@ -62,3 +72,10 @@ public fun Buffer<Double>.toDoubleArray(): DoubleArray = when (this) {
|
|||||||
* @return the new buffer.
|
* @return the new buffer.
|
||||||
*/
|
*/
|
||||||
public fun DoubleArray.asBuffer(): DoubleBuffer = DoubleBuffer(this)
|
public fun DoubleArray.asBuffer(): DoubleBuffer = DoubleBuffer(this)
|
||||||
|
|
||||||
|
|
||||||
|
public fun interface DoubleBufferTransform : BufferTransform<Double, Double> {
|
||||||
|
public fun transform(arg: DoubleBuffer): DoubleBuffer
|
||||||
|
|
||||||
|
override fun transform(arg: Buffer<Double>): DoubleBuffer = arg.toDoubleBuffer()
|
||||||
|
}
|
||||||
|
@ -14,7 +14,7 @@ import kotlin.jvm.JvmInline
|
|||||||
* @author Iaroslav Postovalov
|
* @author Iaroslav Postovalov
|
||||||
*/
|
*/
|
||||||
@JvmInline
|
@JvmInline
|
||||||
public value class FloatBuffer(public val array: FloatArray) : MutableBuffer<Float> {
|
public value class FloatBuffer(public val array: FloatArray) : PrimitiveBuffer<Float> {
|
||||||
override val size: Int get() = array.size
|
override val size: Int get() = array.size
|
||||||
|
|
||||||
override operator fun get(index: Int): Float = array[index]
|
override operator fun get(index: Int): Float = array[index]
|
||||||
|
@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline
|
|||||||
* @property array the underlying array.
|
* @property array the underlying array.
|
||||||
*/
|
*/
|
||||||
@JvmInline
|
@JvmInline
|
||||||
public value class IntBuffer(public val array: IntArray) : MutableBuffer<Int> {
|
public value class IntBuffer(public val array: IntArray) : PrimitiveBuffer<Int> {
|
||||||
override val size: Int get() = array.size
|
override val size: Int get() = array.size
|
||||||
|
|
||||||
override operator fun get(index: Int): Int = array[index]
|
override operator fun get(index: Int): Int = array[index]
|
||||||
|
@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline
|
|||||||
* @property array the underlying array.
|
* @property array the underlying array.
|
||||||
*/
|
*/
|
||||||
@JvmInline
|
@JvmInline
|
||||||
public value class LongBuffer(public val array: LongArray) : MutableBuffer<Long> {
|
public value class LongBuffer(public val array: LongArray) : PrimitiveBuffer<Long> {
|
||||||
override val size: Int get() = array.size
|
override val size: Int get() = array.size
|
||||||
|
|
||||||
override operator fun get(index: Int): Long = array[index]
|
override operator fun get(index: Int): Long = array[index]
|
||||||
|
@ -94,4 +94,7 @@ public interface MutableBuffer<T> : Buffer<T> {
|
|||||||
public inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> =
|
public inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> =
|
||||||
auto(T::class, size, initializer)
|
auto(T::class, size, initializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public sealed interface PrimitiveBuffer<T>: MutableBuffer<T>
|
@ -9,14 +9,18 @@ import space.kscience.kmath.misc.UnstableKMathAPI
|
|||||||
import space.kscience.kmath.structures.*
|
import space.kscience.kmath.structures.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Typealias for buffer transformations.
|
* Type alias for buffer transformations.
|
||||||
*/
|
*/
|
||||||
public typealias BufferTransform<T, R> = (Buffer<T>) -> Buffer<R>
|
public fun interface BufferTransform<T, R> {
|
||||||
|
public fun transform(arg: Buffer<T>): Buffer<R>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
///**
|
||||||
* Typealias for buffer transformations with suspend function.
|
// * Type alias for buffer transformations with suspend function.
|
||||||
*/
|
// */
|
||||||
public typealias SuspendBufferTransform<T, R> = suspend (Buffer<T>) -> Buffer<R>
|
//public fun interface SuspendBufferTransform<T, R>{
|
||||||
|
// public suspend fun transform(arg: Buffer<T>): Buffer<R>
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,18 +61,18 @@ public fun <T> Buffer<T>.toMutableList(): MutableList<T> = when (this) {
|
|||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public inline fun <reified T> Buffer<T>.toTypedArray(): Array<T> = Array(size, ::get)
|
public inline fun <reified T> Buffer<T>.toTypedArray(): Array<T> = Array(size, ::get)
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* Create a new buffer from this one with the given mapping function and using [Buffer.Companion.auto] buffer factory.
|
// * Create a new buffer from this one with the given mapping function and using [Buffer.Companion.auto] buffer factory.
|
||||||
*/
|
// */
|
||||||
public inline fun <T, reified R : Any> Buffer<T>.map(block: (T) -> R): Buffer<R> =
|
//public inline fun <T, reified R : Any> Buffer<T>.map(block: (T) -> R): Buffer<R> =
|
||||||
Buffer.auto(size) { block(get(it)) }
|
// Buffer.auto(size) { block(get(it)) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new buffer from this one with the given mapping function.
|
* Create a new buffer from this one with the given mapping function.
|
||||||
* Provided [bufferFactory] is used to construct the new buffer.
|
* Provided [bufferFactory] is used to construct the new buffer.
|
||||||
*/
|
*/
|
||||||
public inline fun <T, R> Buffer<T>.map(
|
public inline fun <T, R> Buffer<T>.mapToBuffer(
|
||||||
bufferFactory: BufferFactory<R>,
|
bufferFactory: BufferFactory<R>,
|
||||||
crossinline block: (T) -> R,
|
crossinline block: (T) -> R,
|
||||||
): Buffer<R> = bufferFactory(size) { block(get(it)) }
|
): Buffer<R> = bufferFactory(size) { block(get(it)) }
|
||||||
@ -77,23 +81,24 @@ public inline fun <T, R> Buffer<T>.map(
|
|||||||
* Create a new buffer from this one with the given mapping (indexed) function.
|
* Create a new buffer from this one with the given mapping (indexed) function.
|
||||||
* Provided [bufferFactory] is used to construct the new buffer.
|
* Provided [bufferFactory] is used to construct the new buffer.
|
||||||
*/
|
*/
|
||||||
public inline fun <T, R> Buffer<T>.mapIndexed(
|
public inline fun <T, R> Buffer<T>.mapIndexedToBuffer(
|
||||||
bufferFactory: BufferFactory<R>,
|
bufferFactory: BufferFactory<R>,
|
||||||
crossinline block: (index: Int, value: T) -> R,
|
crossinline block: (index: Int, value: T) -> R,
|
||||||
): Buffer<R> = bufferFactory(size) { block(it, get(it)) }
|
): Buffer<R> = bufferFactory(size) { block(it, get(it)) }
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* Create a new buffer from this one with the given indexed mapping function.
|
// * Create a new buffer from this one with the given indexed mapping function.
|
||||||
* Provided [BufferFactory] is used to construct the new buffer.
|
// * Provided [BufferFactory] is used to construct the new buffer.
|
||||||
*/
|
// */
|
||||||
public inline fun <T, reified R : Any> Buffer<T>.mapIndexed(
|
//public inline fun <T, reified R : Any> Buffer<T>.mapIndexed(
|
||||||
crossinline block: (index: Int, value: T) -> R,
|
// crossinline block: (index: Int, value: T) -> R,
|
||||||
): Buffer<R> = Buffer.auto(size) { block(it, get(it)) }
|
//): Buffer<R> = Buffer.auto(size) { block(it, get(it)) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fold given buffer according to [operation]
|
* Fold given buffer according to [operation]
|
||||||
*/
|
*/
|
||||||
public inline fun <T, R> Buffer<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
|
public inline fun <T, R> Buffer<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
|
||||||
|
if (size == 0) return initial
|
||||||
var accumulator = initial
|
var accumulator = initial
|
||||||
for (index in this.indices) accumulator = operation(accumulator, get(index))
|
for (index in this.indices) accumulator = operation(accumulator, get(index))
|
||||||
return accumulator
|
return accumulator
|
||||||
@ -103,18 +108,31 @@ public inline fun <T, R> Buffer<T>.fold(initial: R, operation: (acc: R, T) -> R)
|
|||||||
* Fold given buffer according to indexed [operation]
|
* Fold given buffer according to indexed [operation]
|
||||||
*/
|
*/
|
||||||
public inline fun <T : Any, R> Buffer<T>.foldIndexed(initial: R, operation: (index: Int, acc: R, T) -> R): R {
|
public inline fun <T : Any, R> Buffer<T>.foldIndexed(initial: R, operation: (index: Int, acc: R, T) -> R): R {
|
||||||
|
if (size == 0) return initial
|
||||||
var accumulator = initial
|
var accumulator = initial
|
||||||
for (index in this.indices) accumulator = operation(index, accumulator, get(index))
|
for (index in this.indices) accumulator = operation(index, accumulator, get(index))
|
||||||
return accumulator
|
return accumulator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduce a buffer from left to right according to [operation]
|
||||||
|
*/
|
||||||
|
public inline fun <T> Buffer<T>.reduce(operation: (left: T, value: T) -> T): T {
|
||||||
|
require(size > 0) { "Buffer must have elements" }
|
||||||
|
var current = get(0)
|
||||||
|
for (i in 1 until size) {
|
||||||
|
current = operation(current, get(i))
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zip two buffers using given [transform].
|
* Zip two buffers using given [transform].
|
||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public inline fun <T1, T2 : Any, reified R : Any> Buffer<T1>.zip(
|
public inline fun <T1, T2, R> Buffer<T1>.combineToBuffer(
|
||||||
other: Buffer<T2>,
|
other: Buffer<T2>,
|
||||||
bufferFactory: BufferFactory<R> = BufferFactory.auto(),
|
bufferFactory: BufferFactory<R>,
|
||||||
crossinline transform: (T1, T2) -> R,
|
crossinline transform: (T1, T2) -> R,
|
||||||
): Buffer<R> {
|
): Buffer<R> {
|
||||||
require(size == other.size) { "Buffer size mismatch in zip: expected $size but found ${other.size}" }
|
require(size == other.size) { "Buffer size mismatch in zip: expected $size but found ${other.size}" }
|
@ -0,0 +1,38 @@
|
|||||||
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-boxing access to primitive [Double]
|
||||||
|
*/
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun Buffer<Double>.getDouble(index: Int): Double = if (this is BufferView) {
|
||||||
|
val originIndex = originIndex(index)
|
||||||
|
if (originIndex >= 0) {
|
||||||
|
origin.getDouble(originIndex)
|
||||||
|
} else {
|
||||||
|
get(index)
|
||||||
|
}
|
||||||
|
} else if (this is DoubleBuffer) {
|
||||||
|
array[index]
|
||||||
|
} else {
|
||||||
|
get(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-boxing access to primitive [Int]
|
||||||
|
*/
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun Buffer<Int>.getInt(index: Int): Int = if (this is BufferView) {
|
||||||
|
val originIndex = originIndex(index)
|
||||||
|
if (originIndex >= 0) {
|
||||||
|
origin.getInt(originIndex)
|
||||||
|
} else {
|
||||||
|
get(index)
|
||||||
|
}
|
||||||
|
} else if (this is IntBuffer) {
|
||||||
|
array[index]
|
||||||
|
} else {
|
||||||
|
get(index)
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2022 KMath contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
|
|
||||||
|
public typealias Float32 = Float
|
||||||
|
public typealias Float64 = Double
|
||||||
|
|
||||||
|
public typealias Int8 = Byte
|
||||||
|
public typealias Int16 = Short
|
||||||
|
public typealias Int32 = Int
|
||||||
|
public typealias Int64 = Long
|
||||||
|
|
||||||
|
public typealias UInt8 = UByte
|
||||||
|
public typealias UInt16 = UShort
|
||||||
|
public typealias UInt32 = UInt
|
||||||
|
public typealias UInt64 = ULong
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2022 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.nd
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
class StridesTest {
|
||||||
|
@Test
|
||||||
|
fun checkRowBasedStrides() {
|
||||||
|
val strides = RowStrides(ShapeND(3, 3))
|
||||||
|
var counter = 0
|
||||||
|
for(i in 0..2){
|
||||||
|
for(j in 0..2){
|
||||||
|
// print(strides.offset(intArrayOf(i,j)).toString() + "\t")
|
||||||
|
require(strides.offset(intArrayOf(i,j)) == counter)
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun checkColumnBasedStrides() {
|
||||||
|
val strides = ColumnStrides(ShapeND(3, 3))
|
||||||
|
var counter = 0
|
||||||
|
for(i in 0..2){
|
||||||
|
for(j in 0..2){
|
||||||
|
// print(strides.offset(intArrayOf(i,j)).toString() + "\t")
|
||||||
|
require(strides.offset(intArrayOf(j,i)) == counter)
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFails
|
||||||
|
|
||||||
|
internal class BufferExpandedTest {
|
||||||
|
private val buffer = (0..100).toList().asBuffer()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun shrink(){
|
||||||
|
val view = buffer.slice(20..30)
|
||||||
|
assertEquals(20, view[0])
|
||||||
|
assertEquals(30, view[10])
|
||||||
|
assertFails { view[11] }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun expandNegative(){
|
||||||
|
val view: BufferView<Int> = buffer.expand(-20..113,0)
|
||||||
|
assertEquals(0,view[4])
|
||||||
|
assertEquals(0,view[123])
|
||||||
|
assertEquals(100, view[120])
|
||||||
|
assertFails { view[-2] }
|
||||||
|
assertFails { view[134] }
|
||||||
|
}
|
||||||
|
}
|
@ -88,7 +88,7 @@ class NumberNDFieldTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testInternalContext() {
|
fun testInternalContext() {
|
||||||
algebra {
|
algebra {
|
||||||
(DoubleField.ndAlgebra(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } }
|
(DoubleField.ndAlgebra(array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2022 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.misc
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import space.kscience.kmath.operations.JBigDecimalField
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNotEquals
|
||||||
|
|
||||||
|
class JBigTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testExact() = with(JBigDecimalField) {
|
||||||
|
assertNotEquals(0.3, 0.1 + 0.2)
|
||||||
|
assertEquals(one * 0.3, one * 0.1 + one * 0.2)
|
||||||
|
}
|
||||||
|
}
|
@ -81,9 +81,7 @@ public suspend fun <T> AsyncFlow<T>.collect(concurrency: Int, collector: FlowCol
|
|||||||
public suspend inline fun <T> AsyncFlow<T>.collect(
|
public suspend inline fun <T> AsyncFlow<T>.collect(
|
||||||
concurrency: Int,
|
concurrency: Int,
|
||||||
crossinline action: suspend (value: T) -> Unit,
|
crossinline action: suspend (value: T) -> Unit,
|
||||||
): Unit = collect(concurrency, object : FlowCollector<T> {
|
): Unit = collect(concurrency, FlowCollector<T> { value -> action(value) })
|
||||||
override suspend fun emit(value: T): Unit = action(value)
|
|
||||||
})
|
|
||||||
|
|
||||||
public inline fun <T, R> Flow<T>.mapParallel(
|
public inline fun <T, R> Flow<T>.mapParallel(
|
||||||
dispatcher: CoroutineDispatcher = Dispatchers.Default,
|
dispatcher: CoroutineDispatcher = Dispatchers.Default,
|
||||||
|
@ -5,8 +5,10 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.streaming
|
package space.kscience.kmath.streaming
|
||||||
|
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
import kotlinx.coroutines.flow.flatMapConcat
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import space.kscience.kmath.chains.BlockingDoubleChain
|
import space.kscience.kmath.chains.BlockingDoubleChain
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.BufferFactory
|
import space.kscience.kmath.structures.BufferFactory
|
||||||
@ -20,7 +22,6 @@ public fun <T> Buffer<T>.asFlow(): Flow<T> = iterator().asFlow()
|
|||||||
/**
|
/**
|
||||||
* Flat map a [Flow] of [Buffer] into continuous [Flow] of elements
|
* Flat map a [Flow] of [Buffer] into continuous [Flow] of elements
|
||||||
*/
|
*/
|
||||||
@FlowPreview
|
|
||||||
public fun <T> Flow<Buffer<T>>.spread(): Flow<T> = flatMapConcat { it.asFlow() }
|
public fun <T> Flow<Buffer<T>>.spread(): Flow<T> = flatMapConcat { it.asFlow() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,12 +8,13 @@ package space.kscience.kmath.structures
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import space.kscience.kmath.coroutines.Math
|
import space.kscience.kmath.coroutines.Math
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.nd.DefaultStrides
|
import space.kscience.kmath.nd.ColumnStrides
|
||||||
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
|
|
||||||
public class LazyStructureND<out T>(
|
public class LazyStructureND<out T>(
|
||||||
public val scope: CoroutineScope,
|
public val scope: CoroutineScope,
|
||||||
override val shape: IntArray,
|
override val shape: ShapeND,
|
||||||
public val function: suspend (IntArray) -> T,
|
public val function: suspend (IntArray) -> T,
|
||||||
) : StructureND<T> {
|
) : StructureND<T> {
|
||||||
private val cache: MutableMap<IntArray, Deferred<T>> = HashMap()
|
private val cache: MutableMap<IntArray, Deferred<T>> = HashMap()
|
||||||
@ -23,30 +24,35 @@ public class LazyStructureND<out T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public suspend fun await(index: IntArray): T = async(index).await()
|
public suspend fun await(index: IntArray): T = async(index).await()
|
||||||
|
@PerformancePitfall
|
||||||
override operator fun get(index: IntArray): T = runBlocking { async(index).await() }
|
override operator fun get(index: IntArray): T = runBlocking { async(index).await() }
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun elements(): Sequence<Pair<IntArray, T>> {
|
override fun elements(): Sequence<Pair<IntArray, T>> {
|
||||||
val strides = DefaultStrides(shape)
|
val strides = ColumnStrides(shape)
|
||||||
val res = runBlocking { strides.asSequence().toList().map { index -> index to await(index) } }
|
val res = runBlocking { strides.asSequence().toList().map { index -> index to await(index) } }
|
||||||
return res.asSequence()
|
return res.asSequence()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
public fun <T> StructureND<T>.async(index: IntArray): Deferred<T> =
|
public fun <T> StructureND<T>.async(index: IntArray): Deferred<T> =
|
||||||
if (this is LazyStructureND<T>) this@async.async(index) else CompletableDeferred(get(index))
|
if (this is LazyStructureND<T>) this@async.async(index) else CompletableDeferred(get(index))
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
public suspend fun <T> StructureND<T>.await(index: IntArray): T =
|
public suspend fun <T> StructureND<T>.await(index: IntArray): T =
|
||||||
if (this is LazyStructureND<T>) await(index) else get(index)
|
if (this is LazyStructureND<T>) await(index) else get(index)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PENDING would benefit from KEEP-176
|
* PENDING would benefit from KEEP-176
|
||||||
*/
|
*/
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
public inline fun <T, R> StructureND<T>.mapAsyncIndexed(
|
public inline fun <T, R> StructureND<T>.mapAsyncIndexed(
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
crossinline function: suspend (T, index: IntArray) -> R,
|
crossinline function: suspend (T, index: IntArray) -> R,
|
||||||
): LazyStructureND<R> = LazyStructureND(scope, shape) { index -> function(get(index), index) }
|
): LazyStructureND<R> = LazyStructureND(scope, shape) { index -> function(get(index), index) }
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
public inline fun <T, R> StructureND<T>.mapAsync(
|
public inline fun <T, R> StructureND<T>.mapAsync(
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
crossinline function: suspend (T) -> R,
|
crossinline function: suspend (T) -> R,
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package space.kscience.kmath.dimensions
|
package space.kscience.kmath.dimensions
|
||||||
|
|
||||||
import space.kscience.kmath.linear.*
|
import space.kscience.kmath.linear.*
|
||||||
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.nd.Structure2D
|
import space.kscience.kmath.nd.Structure2D
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
import space.kscience.kmath.operations.Ring
|
import space.kscience.kmath.operations.Ring
|
||||||
@ -47,7 +48,7 @@ public interface DMatrix<out T, R : Dimension, C : Dimension> : Structure2D<T> {
|
|||||||
public value class DMatrixWrapper<out T, R : Dimension, C : Dimension>(
|
public value class DMatrixWrapper<out T, R : Dimension, C : Dimension>(
|
||||||
private val structure: Structure2D<T>,
|
private val structure: Structure2D<T>,
|
||||||
) : DMatrix<T, R, C> {
|
) : DMatrix<T, R, C> {
|
||||||
override val shape: IntArray get() = structure.shape
|
override val shape: ShapeND get() = structure.shape
|
||||||
override val rowNum: Int get() = shape[0]
|
override val rowNum: Int get() = shape[0]
|
||||||
override val colNum: Int get() = shape[1]
|
override val colNum: Int get() = shape[1]
|
||||||
override operator fun get(i: Int, j: Int): T = structure[i, j]
|
override operator fun get(i: Int, j: Int): T = structure[i, j]
|
||||||
|
@ -15,6 +15,7 @@ import space.kscience.kmath.linear.*
|
|||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
|
import space.kscience.kmath.nd.toArray
|
||||||
import space.kscience.kmath.operations.algebra
|
import space.kscience.kmath.operations.algebra
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.random.asJavaRandom
|
import kotlin.random.asJavaRandom
|
||||||
@ -52,7 +53,7 @@ internal class EjmlMatrixTest {
|
|||||||
fun shape() {
|
fun shape() {
|
||||||
val m = randomMatrix
|
val m = randomMatrix
|
||||||
val w = EjmlDoubleMatrix(m)
|
val w = EjmlDoubleMatrix(m)
|
||||||
assertContentEquals(intArrayOf(m.numRows, m.numCols), w.shape)
|
assertContentEquals(intArrayOf(m.numRows, m.numCols), w.shape.toArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
|
@ -131,7 +131,7 @@ public fun RealMatrix.extractColumn(columnIndex: Int): RealMatrix =
|
|||||||
extractColumns(columnIndex..columnIndex)
|
extractColumns(columnIndex..columnIndex)
|
||||||
|
|
||||||
public fun RealMatrix.sumByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j ->
|
public fun RealMatrix.sumByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j ->
|
||||||
columns[j].asIterable().sum()
|
columns[j].sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun RealMatrix.minByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j ->
|
public fun RealMatrix.minByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j ->
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.integration
|
package space.kscience.kmath.integration
|
||||||
|
|
||||||
import space.kscience.kmath.operations.map
|
import space.kscience.kmath.operations.mapToBuffer
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
import space.kscience.kmath.structures.DoubleBuffer
|
||||||
import space.kscience.kmath.structures.asBuffer
|
import space.kscience.kmath.structures.asBuffer
|
||||||
@ -30,14 +30,14 @@ public fun GaussIntegratorRuleFactory.build(
|
|||||||
numPoints: Int,
|
numPoints: Int,
|
||||||
range: ClosedRange<Double>,
|
range: ClosedRange<Double>,
|
||||||
): Pair<Buffer<Double>, Buffer<Double>> {
|
): Pair<Buffer<Double>, Buffer<Double>> {
|
||||||
val normalized = build(numPoints)
|
val normalized: Pair<Buffer<Double>, Buffer<Double>> = build(numPoints)
|
||||||
val length = range.endInclusive - range.start
|
val length = range.endInclusive - range.start
|
||||||
|
|
||||||
val points = normalized.first.map(::DoubleBuffer) {
|
val points = normalized.first.mapToBuffer(::DoubleBuffer) {
|
||||||
range.start + length / 2 + length / 2 * it
|
range.start + length / 2 + length / 2 * it
|
||||||
}
|
}
|
||||||
|
|
||||||
val weights = normalized.second.map(::DoubleBuffer) {
|
val weights = normalized.second.mapToBuffer(::DoubleBuffer) {
|
||||||
it * length / 2
|
it * length / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,9 +65,9 @@ public class SplineIntegrator<T : Comparable<T>>(
|
|||||||
DoubleBuffer(numPoints) { i -> range.start + i * step }
|
DoubleBuffer(numPoints) { i -> range.start + i * step }
|
||||||
}
|
}
|
||||||
|
|
||||||
val values = nodes.map(bufferFactory) { integrand.function(it) }
|
val values = nodes.mapToBuffer(bufferFactory) { integrand.function(it) }
|
||||||
val polynomials = interpolator.interpolatePolynomials(
|
val polynomials = interpolator.interpolatePolynomials(
|
||||||
nodes.map(bufferFactory) { number(it) },
|
nodes.mapToBuffer(bufferFactory) { number(it) },
|
||||||
values
|
values
|
||||||
)
|
)
|
||||||
val res = polynomials.integrate(algebra, number(range.start)..number(range.endInclusive))
|
val res = polynomials.integrate(algebra, number(range.start)..number(range.endInclusive))
|
||||||
@ -93,7 +93,7 @@ public object DoubleSplineIntegrator : UnivariateIntegrator<Double> {
|
|||||||
DoubleBuffer(numPoints) { i -> range.start + i * step }
|
DoubleBuffer(numPoints) { i -> range.start + i * step }
|
||||||
}
|
}
|
||||||
|
|
||||||
val values = nodes.map { integrand.function(it) }
|
val values = nodes.mapToBuffer(::DoubleBuffer) { integrand.function(it) }
|
||||||
val polynomials = interpolator.interpolatePolynomials(nodes, values)
|
val polynomials = interpolator.interpolatePolynomials(nodes, values)
|
||||||
val res = polynomials.integrate(DoubleField, range)
|
val res = polynomials.integrate(DoubleField, range)
|
||||||
return integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + nodes.size)
|
return integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + nodes.size)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.geometry
|
package space.kscience.kmath.geometry
|
||||||
|
|
||||||
|
|
||||||
import space.kscience.kmath.operations.toList
|
import space.kscience.kmath.operations.toList
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
@ -7,9 +7,10 @@ package space.kscience.kmath.histogram
|
|||||||
|
|
||||||
import space.kscience.kmath.domains.Domain
|
import space.kscience.kmath.domains.Domain
|
||||||
import space.kscience.kmath.linear.Point
|
import space.kscience.kmath.linear.Point
|
||||||
import space.kscience.kmath.nd.DefaultStrides
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
|
import space.kscience.kmath.nd.ColumnStrides
|
||||||
import space.kscience.kmath.nd.FieldOpsND
|
import space.kscience.kmath.nd.FieldOpsND
|
||||||
import space.kscience.kmath.nd.Shape
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.operations.Group
|
import space.kscience.kmath.operations.Group
|
||||||
import space.kscience.kmath.operations.ScaleOperations
|
import space.kscience.kmath.operations.ScaleOperations
|
||||||
@ -24,6 +25,7 @@ public class HistogramND<T : Comparable<T>, D : Domain<T>, V : Any>(
|
|||||||
internal val values: StructureND<V>,
|
internal val values: StructureND<V>,
|
||||||
) : Histogram<T, V, DomainBin<T, D, V>> {
|
) : Histogram<T, V, DomainBin<T, D, V>> {
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun get(point: Point<T>): DomainBin<T, D, V>? {
|
override fun get(point: Point<T>): DomainBin<T, D, V>? {
|
||||||
val index = group.getIndexOrNull(point) ?: return null
|
val index = group.getIndexOrNull(point) ?: return null
|
||||||
return group.produceBin(index, values[index])
|
return group.produceBin(index, values[index])
|
||||||
@ -31,8 +33,9 @@ public class HistogramND<T : Comparable<T>, D : Domain<T>, V : Any>(
|
|||||||
|
|
||||||
override val dimension: Int get() = group.shape.size
|
override val dimension: Int get() = group.shape.size
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override val bins: Iterable<DomainBin<T, D, V>>
|
override val bins: Iterable<DomainBin<T, D, V>>
|
||||||
get() = DefaultStrides(group.shape).asSequence().map {
|
get() = ColumnStrides(group.shape).asSequence().map {
|
||||||
group.produceBin(it, values[it])
|
group.produceBin(it, values[it])
|
||||||
}.asIterable()
|
}.asIterable()
|
||||||
}
|
}
|
||||||
@ -42,7 +45,7 @@ public class HistogramND<T : Comparable<T>, D : Domain<T>, V : Any>(
|
|||||||
*/
|
*/
|
||||||
public interface HistogramGroupND<T : Comparable<T>, D : Domain<T>, V : Any> :
|
public interface HistogramGroupND<T : Comparable<T>, D : Domain<T>, V : Any> :
|
||||||
Group<HistogramND<T, D, V>>, ScaleOperations<HistogramND<T, D, V>> {
|
Group<HistogramND<T, D, V>>, ScaleOperations<HistogramND<T, D, V>> {
|
||||||
public val shape: Shape
|
public val shape: ShapeND
|
||||||
public val valueAlgebraND: FieldOpsND<V, *> //= NDAlgebra.space(valueSpace, Buffer.Companion::boxing, *shape),
|
public val valueAlgebraND: FieldOpsND<V, *> //= NDAlgebra.space(valueSpace, Buffer.Companion::boxing, *shape),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,11 +9,10 @@ package space.kscience.kmath.histogram
|
|||||||
|
|
||||||
import space.kscience.kmath.domains.HyperSquareDomain
|
import space.kscience.kmath.domains.HyperSquareDomain
|
||||||
import space.kscience.kmath.linear.Point
|
import space.kscience.kmath.linear.Point
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.nd.*
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.*
|
||||||
import space.kscience.kmath.operations.Field
|
|
||||||
import space.kscience.kmath.operations.invoke
|
|
||||||
import space.kscience.kmath.structures.*
|
import space.kscience.kmath.structures.*
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ public class UniformHistogramGroupND<V : Any, A : Field<V>>(
|
|||||||
|
|
||||||
public val dimension: Int get() = lower.size
|
public val dimension: Int get() = lower.size
|
||||||
|
|
||||||
override val shape: IntArray = IntArray(binNums.size) { binNums[it] + 2 }
|
override val shape: ShapeND = ShapeND(IntArray(binNums.size) { binNums[it] + 2 })
|
||||||
|
|
||||||
private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] }
|
private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] }
|
||||||
|
|
||||||
@ -83,8 +82,12 @@ public class UniformHistogramGroupND<V : Any, A : Field<V>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun produce(builder: HistogramBuilder<Double, V>.() -> Unit): HistogramND<Double, HyperSquareDomain, V> {
|
@OptIn(PerformancePitfall::class)
|
||||||
val ndCounter = StructureND.buffered(shape) { Counter.of(valueAlgebraND.elementAlgebra) }
|
override fun produce(
|
||||||
|
builder: HistogramBuilder<Double, V>.() -> Unit,
|
||||||
|
): HistogramND<Double, HyperSquareDomain, V> {
|
||||||
|
val ndCounter: BufferND<ObjectCounter<V>> =
|
||||||
|
StructureND.buffered(shape) { Counter.of(valueAlgebraND.elementAlgebra) }
|
||||||
val hBuilder = object : HistogramBuilder<Double, V> {
|
val hBuilder = object : HistogramBuilder<Double, V> {
|
||||||
override val defaultValue: V get() = valueAlgebraND.elementAlgebra.one
|
override val defaultValue: V get() = valueAlgebraND.elementAlgebra.one
|
||||||
|
|
||||||
@ -94,7 +97,8 @@ public class UniformHistogramGroupND<V : Any, A : Field<V>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hBuilder.apply(builder)
|
hBuilder.apply(builder)
|
||||||
val values: BufferND<V> = ndCounter.mapToBuffer(valueBufferFactory) { it.value }
|
val values: BufferND<V> = BufferND(ndCounter.indices, ndCounter.buffer.mapToBuffer(valueBufferFactory) { it.value })
|
||||||
|
|
||||||
return HistogramND(this, values)
|
return HistogramND(this, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,9 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.histogram
|
package space.kscience.kmath.histogram
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.nd.DefaultStrides
|
import space.kscience.kmath.nd.ColumnStrides
|
||||||
import space.kscience.kmath.operations.invoke
|
import space.kscience.kmath.operations.invoke
|
||||||
import space.kscience.kmath.real.DoubleVector
|
import space.kscience.kmath.real.DoubleVector
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
@ -50,6 +51,7 @@ internal class MultivariateHistogramTest {
|
|||||||
assertEquals(n, histogram.bins.sumOf { it.binValue.toInt() })
|
assertEquals(n, histogram.bins.sumOf { it.binValue.toInt() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
@Test
|
@Test
|
||||||
fun testHistogramAlgebra() {
|
fun testHistogramAlgebra() {
|
||||||
Histogram.uniformDoubleNDFromRanges(
|
Histogram.uniformDoubleNDFromRanges(
|
||||||
@ -73,7 +75,7 @@ internal class MultivariateHistogramTest {
|
|||||||
}
|
}
|
||||||
val res = histogram1 - histogram2
|
val res = histogram1 - histogram2
|
||||||
assertTrue {
|
assertTrue {
|
||||||
DefaultStrides(shape).asSequence().all { index ->
|
ColumnStrides(shape).asSequence().all { index ->
|
||||||
res.values[index] <= histogram1.values[index]
|
res.values[index] <= histogram1.values[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import kotlinx.coroutines.test.runTest
|
|||||||
import space.kscience.kmath.distributions.NormalDistribution
|
import space.kscience.kmath.distributions.NormalDistribution
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
import space.kscience.kmath.stat.RandomGenerator
|
import space.kscience.kmath.random.RandomGenerator
|
||||||
import space.kscience.kmath.stat.nextBuffer
|
import space.kscience.kmath.stat.nextBuffer
|
||||||
import kotlin.native.concurrent.ThreadLocal
|
import kotlin.native.concurrent.ThreadLocal
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
@ -36,7 +36,7 @@ internal class UniformHistogram1DTest {
|
|||||||
@Test
|
@Test
|
||||||
fun rebinDown() = runTest {
|
fun rebinDown() = runTest {
|
||||||
val h1 = Histogram.uniform1D(DoubleField, 0.01).produce(generator.nextDoubleBuffer(10000))
|
val h1 = Histogram.uniform1D(DoubleField, 0.01).produce(generator.nextDoubleBuffer(10000))
|
||||||
val h2 = Histogram.uniform1D(DoubleField,0.03).produceFrom(h1)
|
val h2 = Histogram.uniform1D(DoubleField, 0.03).produceFrom(h1)
|
||||||
|
|
||||||
assertEquals(10000, h2.bins.sumOf { it.binValue }.toInt())
|
assertEquals(10000, h2.bins.sumOf { it.binValue }.toInt())
|
||||||
}
|
}
|
||||||
@ -44,13 +44,13 @@ internal class UniformHistogram1DTest {
|
|||||||
@Test
|
@Test
|
||||||
fun rebinUp() = runTest {
|
fun rebinUp() = runTest {
|
||||||
val h1 = Histogram.uniform1D(DoubleField, 0.03).produce(generator.nextDoubleBuffer(10000))
|
val h1 = Histogram.uniform1D(DoubleField, 0.03).produce(generator.nextDoubleBuffer(10000))
|
||||||
val h2 = Histogram.uniform1D(DoubleField,0.01).produceFrom(h1)
|
val h2 = Histogram.uniform1D(DoubleField, 0.01).produceFrom(h1)
|
||||||
|
|
||||||
assertEquals(10000, h2.bins.sumOf { it.binValue }.toInt())
|
assertEquals(10000, h2.bins.sumOf { it.binValue }.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadLocal
|
@ThreadLocal
|
||||||
companion object{
|
companion object {
|
||||||
private val generator = RandomGenerator.default(123)
|
private val generator = RandomGenerator.default(123)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -47,11 +47,13 @@ internal class KMathJupyter : JupyterIntegration() {
|
|||||||
syntaxRender.renderPart(mathRender.render(MST.Numeric(it)), s)
|
syntaxRender.renderPart(mathRender.render(MST.Numeric(it)), s)
|
||||||
+s.toString()
|
+s.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
is MST -> {
|
is MST -> {
|
||||||
val s = StringBuilder()
|
val s = StringBuilder()
|
||||||
syntaxRender.renderPart(mathRender.render(it), s)
|
syntaxRender.renderPart(mathRender.render(it), s)
|
||||||
+s.toString()
|
+s.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
+"<ms>"
|
+"<ms>"
|
||||||
+it.toString()
|
+it.toString()
|
||||||
|
@ -7,20 +7,22 @@ package space.kscience.kmath.multik
|
|||||||
|
|
||||||
import org.jetbrains.kotlinx.multik.ndarray.data.*
|
import org.jetbrains.kotlinx.multik.ndarray.data.*
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.nd.Shape
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.tensors.api.Tensor
|
import space.kscience.kmath.tensors.api.Tensor
|
||||||
import kotlin.jvm.JvmInline
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
public value class MultikTensor<T>(public val array: MutableMultiArray<T, DN>) : Tensor<T> {
|
public value class MultikTensor<T>(public val array: MutableMultiArray<T, DN>) : Tensor<T> {
|
||||||
override val shape: Shape get() = array.shape
|
override val shape: ShapeND get() = ShapeND(array.shape)
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override fun get(index: IntArray): T = array[index]
|
override fun get(index: IntArray): T = array[index]
|
||||||
|
|
||||||
@PerformancePitfall
|
@PerformancePitfall
|
||||||
override fun elements(): Sequence<Pair<IntArray, T>> =
|
override fun elements(): Sequence<Pair<IntArray, T>> =
|
||||||
array.multiIndices.iterator().asSequence().map { it to get(it) }
|
array.multiIndices.iterator().asSequence().map { it to get(it) }
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override fun set(index: IntArray, value: T) {
|
override fun set(index: IntArray, value: T) {
|
||||||
array[index] = value
|
array[index] = value
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import org.jetbrains.kotlinx.multik.api.stat.Statistics
|
|||||||
import org.jetbrains.kotlinx.multik.ndarray.data.*
|
import org.jetbrains.kotlinx.multik.ndarray.data.*
|
||||||
import org.jetbrains.kotlinx.multik.ndarray.operations.*
|
import org.jetbrains.kotlinx.multik.ndarray.operations.*
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
|
import space.kscience.kmath.misc.UnsafeKMathAPI
|
||||||
import space.kscience.kmath.nd.*
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.operations.*
|
import space.kscience.kmath.operations.*
|
||||||
import space.kscience.kmath.tensors.api.Tensor
|
import space.kscience.kmath.tensors.api.Tensor
|
||||||
@ -30,21 +31,22 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
protected val multikLinAl: LinAlg = multikEngine.getLinAlg()
|
protected val multikLinAl: LinAlg = multikEngine.getLinAlg()
|
||||||
protected val multikStat: Statistics = multikEngine.getStatistics()
|
protected val multikStat: Statistics = multikEngine.getStatistics()
|
||||||
|
|
||||||
override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): MultikTensor<T> {
|
@OptIn(UnsafeKMathAPI::class)
|
||||||
val strides = DefaultStrides(shape)
|
override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): MultikTensor<T> {
|
||||||
|
val strides = ColumnStrides(shape)
|
||||||
val memoryView = initMemoryView<T>(strides.linearSize, type)
|
val memoryView = initMemoryView<T>(strides.linearSize, type)
|
||||||
strides.asSequence().forEachIndexed { linearIndex, tensorIndex ->
|
strides.asSequence().forEachIndexed { linearIndex, tensorIndex ->
|
||||||
memoryView[linearIndex] = elementAlgebra.initializer(tensorIndex)
|
memoryView[linearIndex] = elementAlgebra.initializer(tensorIndex)
|
||||||
}
|
}
|
||||||
return MultikTensor(NDArray(memoryView, shape = shape, dim = DN(shape.size)))
|
return MultikTensor(NDArray(memoryView, shape = shape.asArray(), dim = DN(shape.size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class, UnsafeKMathAPI::class)
|
||||||
override fun StructureND<T>.map(transform: A.(T) -> T): MultikTensor<T> = if (this is MultikTensor) {
|
override fun StructureND<T>.map(transform: A.(T) -> T): MultikTensor<T> = if (this is MultikTensor) {
|
||||||
val data = initMemoryView<T>(array.size, type)
|
val data = initMemoryView<T>(array.size, type)
|
||||||
var count = 0
|
var count = 0
|
||||||
for (el in array) data[count++] = elementAlgebra.transform(el)
|
for (el in array) data[count++] = elementAlgebra.transform(el)
|
||||||
NDArray(data, shape = shape, dim = array.dim).wrap()
|
NDArray(data, shape = shape.asArray(), dim = array.dim).wrap()
|
||||||
} else {
|
} else {
|
||||||
structureND(shape) { index ->
|
structureND(shape) { index ->
|
||||||
transform(get(index))
|
transform(get(index))
|
||||||
@ -75,6 +77,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
/**
|
/**
|
||||||
* Transform a structure element-by element in place.
|
* Transform a structure element-by element in place.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
public inline fun <T> MutableStructureND<T>.mapIndexedInPlace(operation: (index: IntArray, t: T) -> T): Unit {
|
public inline fun <T> MutableStructureND<T>.mapIndexedInPlace(operation: (index: IntArray, t: T) -> T): Unit {
|
||||||
if (this is MultikTensor) {
|
if (this is MultikTensor) {
|
||||||
array.multiIndices.iterator().forEach {
|
array.multiIndices.iterator().forEach {
|
||||||
@ -106,10 +109,11 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
* Convert a tensor to [MultikTensor] if necessary. If tensor is converted, changes on the resulting tensor
|
* Convert a tensor to [MultikTensor] if necessary. If tensor is converted, changes on the resulting tensor
|
||||||
* are not reflected back onto the source
|
* are not reflected back onto the source
|
||||||
*/
|
*/
|
||||||
|
@OptIn(UnsafeKMathAPI::class, PerformancePitfall::class)
|
||||||
public fun StructureND<T>.asMultik(): MultikTensor<T> = if (this is MultikTensor) {
|
public fun StructureND<T>.asMultik(): MultikTensor<T> = if (this is MultikTensor) {
|
||||||
this
|
this
|
||||||
} else {
|
} else {
|
||||||
val res = mk.zeros<T, DN>(shape, type).asDNArray()
|
val res = mk.zeros<T, DN>(shape.asArray(), type).asDNArray()
|
||||||
for (index in res.multiIndices) {
|
for (index in res.multiIndices) {
|
||||||
res[index] = this[index]
|
res[index] = this[index]
|
||||||
}
|
}
|
||||||
@ -118,7 +122,8 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
|
|
||||||
public fun MutableMultiArray<T, *>.wrap(): MultikTensor<T> = MultikTensor(this.asDNArray())
|
public fun MutableMultiArray<T, *>.wrap(): MultikTensor<T> = MultikTensor(this.asDNArray())
|
||||||
|
|
||||||
override fun StructureND<T>.valueOrNull(): T? = if (shape contentEquals intArrayOf(1)) {
|
@OptIn(PerformancePitfall::class)
|
||||||
|
override fun StructureND<T>.valueOrNull(): T? = if (shape contentEquals ShapeND(1)) {
|
||||||
get(intArrayOf(0))
|
get(intArrayOf(0))
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
@ -139,6 +144,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun Tensor<T>.plusAssign(arg: StructureND<T>) {
|
override fun Tensor<T>.plusAssign(arg: StructureND<T>) {
|
||||||
if (this is MultikTensor) {
|
if (this is MultikTensor) {
|
||||||
array.plusAssign(arg.asMultik().array)
|
array.plusAssign(arg.asMultik().array)
|
||||||
@ -163,6 +169,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun Tensor<T>.minusAssign(arg: StructureND<T>) {
|
override fun Tensor<T>.minusAssign(arg: StructureND<T>) {
|
||||||
if (this is MultikTensor) {
|
if (this is MultikTensor) {
|
||||||
array.minusAssign(arg.asMultik().array)
|
array.minusAssign(arg.asMultik().array)
|
||||||
@ -188,6 +195,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun Tensor<T>.timesAssign(arg: StructureND<T>) {
|
override fun Tensor<T>.timesAssign(arg: StructureND<T>) {
|
||||||
if (this is MultikTensor) {
|
if (this is MultikTensor) {
|
||||||
array.timesAssign(arg.asMultik().array)
|
array.timesAssign(arg.asMultik().array)
|
||||||
@ -201,13 +209,13 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
|
|
||||||
override fun Tensor<T>.getTensor(i: Int): MultikTensor<T> = asMultik().array.mutableView(i).wrap()
|
override fun Tensor<T>.getTensor(i: Int): MultikTensor<T> = asMultik().array.mutableView(i).wrap()
|
||||||
|
|
||||||
override fun Tensor<T>.transposed(i: Int, j: Int): MultikTensor<T> = asMultik().array.transpose(i, j).wrap()
|
override fun StructureND<T>.transposed(i: Int, j: Int): MultikTensor<T> = asMultik().array.transpose(i, j).wrap()
|
||||||
|
|
||||||
override fun Tensor<T>.view(shape: IntArray): MultikTensor<T> {
|
override fun Tensor<T>.view(shape: ShapeND): MultikTensor<T> {
|
||||||
require(shape.all { it > 0 })
|
require(shape.asList().all { it > 0 })
|
||||||
require(shape.fold(1, Int::times) == this.shape.size) {
|
require(shape.linearSize == this.shape.size) {
|
||||||
"Cannot reshape array of size ${this.shape.size} into a new shape ${
|
"Cannot reshape array of size ${this.shape.size} into a new shape ${
|
||||||
shape.joinToString(
|
shape.asList().joinToString(
|
||||||
prefix = "(",
|
prefix = "(",
|
||||||
postfix = ")"
|
postfix = ")"
|
||||||
)
|
)
|
||||||
@ -215,10 +223,11 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val mt = asMultik().array
|
val mt = asMultik().array
|
||||||
return if (mt.shape.contentEquals(shape)) {
|
return if (ShapeND(mt.shape).contentEquals(shape)) {
|
||||||
mt
|
mt
|
||||||
} else {
|
} else {
|
||||||
NDArray(mt.data, mt.offset, shape, dim = DN(shape.size), base = mt.base ?: mt)
|
@OptIn(UnsafeKMathAPI::class)
|
||||||
|
NDArray(mt.data, mt.offset, shape.asArray(), dim = DN(shape.size), base = mt.base ?: mt)
|
||||||
}.wrap()
|
}.wrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +250,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>>(
|
|||||||
TODO("Not implemented for broadcasting")
|
TODO("Not implemented for broadcasting")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun diagonalEmbedding(diagonalEntries: Tensor<T>, offset: Int, dim1: Int, dim2: Int): MultikTensor<T> {
|
override fun diagonalEmbedding(diagonalEntries: StructureND<T>, offset: Int, dim1: Int, dim2: Int): MultikTensor<T> {
|
||||||
|
|
||||||
TODO("Diagonal embedding not implemented")
|
TODO("Diagonal embedding not implemented")
|
||||||
}
|
}
|
||||||
@ -284,8 +293,9 @@ public abstract class MultikDivisionTensorAlgebra<T, A : Field<T>>(
|
|||||||
multikEngine: Engine,
|
multikEngine: Engine,
|
||||||
) : MultikTensorAlgebra<T, A>(multikEngine), TensorPartialDivisionAlgebra<T, A> where T : Number, T : Comparable<T> {
|
) : MultikTensorAlgebra<T, A>(multikEngine), TensorPartialDivisionAlgebra<T, A> where T : Number, T : Comparable<T> {
|
||||||
|
|
||||||
|
@OptIn(UnsafeKMathAPI::class)
|
||||||
override fun T.div(arg: StructureND<T>): MultikTensor<T> =
|
override fun T.div(arg: StructureND<T>): MultikTensor<T> =
|
||||||
Multik.ones<T, DN>(arg.shape, type).apply { divAssign(arg.asMultik().array) }.wrap()
|
Multik.ones<T, DN>(arg.shape.asArray(), type).apply { divAssign(arg.asMultik().array) }.wrap()
|
||||||
|
|
||||||
override fun StructureND<T>.div(arg: T): MultikTensor<T> =
|
override fun StructureND<T>.div(arg: T): MultikTensor<T> =
|
||||||
asMultik().array.div(arg).wrap()
|
asMultik().array.div(arg).wrap()
|
||||||
@ -301,6 +311,7 @@ public abstract class MultikDivisionTensorAlgebra<T, A : Field<T>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun Tensor<T>.divAssign(arg: StructureND<T>) {
|
override fun Tensor<T>.divAssign(arg: StructureND<T>) {
|
||||||
if (this is MultikTensor) {
|
if (this is MultikTensor) {
|
||||||
array.divAssign(arg.asMultik().array)
|
array.divAssign(arg.asMultik().array)
|
||||||
|
@ -7,10 +7,12 @@ package space.kscience.kmath.multik
|
|||||||
|
|
||||||
import org.jetbrains.kotlinx.multik.default.DefaultEngine
|
import org.jetbrains.kotlinx.multik.default.DefaultEngine
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
|
import space.kscience.kmath.nd.ShapeND
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.nd.one
|
import space.kscience.kmath.nd.one
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
||||||
|
import space.kscience.kmath.tensors.core.randomNormal
|
||||||
import space.kscience.kmath.tensors.core.tensorAlgebra
|
import space.kscience.kmath.tensors.core.tensorAlgebra
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
@ -28,8 +30,8 @@ internal class MultikNDTest {
|
|||||||
fun dotResult() {
|
fun dotResult() {
|
||||||
val dim = 100
|
val dim = 100
|
||||||
|
|
||||||
val tensor1 = DoubleTensorAlgebra.randomNormal(shape = intArrayOf(dim, dim), 12224)
|
val tensor1 = DoubleTensorAlgebra.randomNormal(shape = ShapeND(dim, dim), 12224)
|
||||||
val tensor2 = DoubleTensorAlgebra.randomNormal(shape = intArrayOf(dim, dim), 12225)
|
val tensor2 = DoubleTensorAlgebra.randomNormal(shape = ShapeND(dim, dim), 12225)
|
||||||
|
|
||||||
val multikResult = with(multikAlgebra) {
|
val multikResult = with(multikAlgebra) {
|
||||||
tensor1 dot tensor2
|
tensor1 dot tensor2
|
||||||
|
@ -11,6 +11,7 @@ import org.nd4j.linalg.api.ops.impl.transforms.strict.ASinh
|
|||||||
import org.nd4j.linalg.factory.Nd4j
|
import org.nd4j.linalg.factory.Nd4j
|
||||||
import org.nd4j.linalg.ops.transforms.Transforms
|
import org.nd4j.linalg.ops.transforms.Transforms
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
|
import space.kscience.kmath.misc.UnsafeKMathAPI
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.nd.*
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.operations.*
|
import space.kscience.kmath.operations.*
|
||||||
@ -32,8 +33,10 @@ public sealed interface Nd4jArrayAlgebra<T, out C : Algebra<T>> : AlgebraND<T, C
|
|||||||
*/
|
*/
|
||||||
public val StructureND<T>.ndArray: INDArray
|
public val StructureND<T>.ndArray: INDArray
|
||||||
|
|
||||||
override fun structureND(shape: Shape, initializer: C.(IntArray) -> T): Nd4jArrayStructure<T> {
|
@OptIn(PerformancePitfall::class)
|
||||||
val struct = Nd4j.create(*shape)!!.wrap()
|
override fun structureND(shape: ShapeND, initializer: C.(IntArray) -> T): Nd4jArrayStructure<T> {
|
||||||
|
@OptIn(UnsafeKMathAPI::class)
|
||||||
|
val struct: Nd4jArrayStructure<T> = Nd4j.create(*shape.asArray())!!.wrap()
|
||||||
struct.indicesIterator().forEach { struct[it] = elementAlgebra.initializer(it) }
|
struct.indicesIterator().forEach { struct[it] = elementAlgebra.initializer(it) }
|
||||||
return struct
|
return struct
|
||||||
}
|
}
|
||||||
@ -45,23 +48,23 @@ public sealed interface Nd4jArrayAlgebra<T, out C : Algebra<T>> : AlgebraND<T, C
|
|||||||
return newStruct
|
return newStruct
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class, UnsafeKMathAPI::class)
|
||||||
override fun StructureND<T>.mapIndexed(
|
override fun StructureND<T>.mapIndexed(
|
||||||
transform: C.(index: IntArray, T) -> T,
|
transform: C.(index: IntArray, T) -> T,
|
||||||
): Nd4jArrayStructure<T> {
|
): Nd4jArrayStructure<T> {
|
||||||
val new = Nd4j.create(*shape).wrap()
|
val new = Nd4j.create(*shape.asArray()).wrap()
|
||||||
new.indicesIterator().forEach { idx -> new[idx] = elementAlgebra.transform(idx, this[idx]) }
|
new.indicesIterator().forEach { idx -> new[idx] = elementAlgebra.transform(idx, this[idx]) }
|
||||||
return new
|
return new
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class, UnsafeKMathAPI::class)
|
||||||
override fun zip(
|
override fun zip(
|
||||||
left: StructureND<T>,
|
left: StructureND<T>,
|
||||||
right: StructureND<T>,
|
right: StructureND<T>,
|
||||||
transform: C.(T, T) -> T,
|
transform: C.(T, T) -> T,
|
||||||
): Nd4jArrayStructure<T> {
|
): Nd4jArrayStructure<T> {
|
||||||
require(left.shape.contentEquals(right.shape)) { "Can't zip tow structures of shape ${left.shape} and ${right.shape}" }
|
require(left.shape.contentEquals(right.shape)) { "Can't zip tow structures of shape ${left.shape} and ${right.shape}" }
|
||||||
val new = Nd4j.create(*left.shape).wrap()
|
val new = Nd4j.create(*left.shape.asArray()).wrap()
|
||||||
new.indicesIterator().forEach { idx -> new[idx] = elementAlgebra.transform(left[idx], right[idx]) }
|
new.indicesIterator().forEach { idx -> new[idx] = elementAlgebra.transform(left[idx], right[idx]) }
|
||||||
return new
|
return new
|
||||||
}
|
}
|
||||||
@ -192,11 +195,11 @@ public open class DoubleNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps<Double, Do
|
|||||||
|
|
||||||
override fun INDArray.wrap(): Nd4jArrayStructure<Double> = asDoubleStructure()
|
override fun INDArray.wrap(): Nd4jArrayStructure<Double> = asDoubleStructure()
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class, UnsafeKMathAPI::class)
|
||||||
override val StructureND<Double>.ndArray: INDArray
|
override val StructureND<Double>.ndArray: INDArray
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
is Nd4jArrayStructure<Double> -> ndArray
|
is Nd4jArrayStructure<Double> -> ndArray
|
||||||
else -> Nd4j.zeros(*shape).also {
|
else -> Nd4j.zeros(*shape.asArray()).also {
|
||||||
elements().forEach { (idx, value) -> it.putScalar(idx, value) }
|
elements().forEach { (idx, value) -> it.putScalar(idx, value) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,10 +225,10 @@ public open class DoubleNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps<Double, Do
|
|||||||
|
|
||||||
public val DoubleField.nd4j: DoubleNd4jArrayFieldOps get() = DoubleNd4jArrayFieldOps
|
public val DoubleField.nd4j: DoubleNd4jArrayFieldOps get() = DoubleNd4jArrayFieldOps
|
||||||
|
|
||||||
public class DoubleNd4jArrayField(override val shape: Shape) : DoubleNd4jArrayFieldOps(), FieldND<Double, DoubleField>
|
public class DoubleNd4jArrayField(override val shape: ShapeND) : DoubleNd4jArrayFieldOps(), FieldND<Double, DoubleField>
|
||||||
|
|
||||||
public fun DoubleField.nd4j(shapeFirst: Int, vararg shapeRest: Int): DoubleNd4jArrayField =
|
public fun DoubleField.nd4j(shapeFirst: Int, vararg shapeRest: Int): DoubleNd4jArrayField =
|
||||||
DoubleNd4jArrayField(intArrayOf(shapeFirst, * shapeRest))
|
DoubleNd4jArrayField(ShapeND(shapeFirst, * shapeRest))
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -236,11 +239,11 @@ public open class FloatNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps<Float, Floa
|
|||||||
|
|
||||||
override fun INDArray.wrap(): Nd4jArrayStructure<Float> = asFloatStructure()
|
override fun INDArray.wrap(): Nd4jArrayStructure<Float> = asFloatStructure()
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class, UnsafeKMathAPI::class)
|
||||||
override val StructureND<Float>.ndArray: INDArray
|
override val StructureND<Float>.ndArray: INDArray
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
is Nd4jArrayStructure<Float> -> ndArray
|
is Nd4jArrayStructure<Float> -> ndArray
|
||||||
else -> Nd4j.zeros(*shape).also {
|
else -> Nd4j.zeros(*shape.asArray()).also {
|
||||||
elements().forEach { (idx, value) -> it.putScalar(idx, value) }
|
elements().forEach { (idx, value) -> it.putScalar(idx, value) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,12 +272,12 @@ public open class FloatNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps<Float, Floa
|
|||||||
public companion object : FloatNd4jArrayFieldOps()
|
public companion object : FloatNd4jArrayFieldOps()
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FloatNd4jArrayField(override val shape: Shape) : FloatNd4jArrayFieldOps(), RingND<Float, FloatField>
|
public class FloatNd4jArrayField(override val shape: ShapeND) : FloatNd4jArrayFieldOps(), RingND<Float, FloatField>
|
||||||
|
|
||||||
public val FloatField.nd4j: FloatNd4jArrayFieldOps get() = FloatNd4jArrayFieldOps
|
public val FloatField.nd4j: FloatNd4jArrayFieldOps get() = FloatNd4jArrayFieldOps
|
||||||
|
|
||||||
public fun FloatField.nd4j(shapeFirst: Int, vararg shapeRest: Int): FloatNd4jArrayField =
|
public fun FloatField.nd4j(shapeFirst: Int, vararg shapeRest: Int): FloatNd4jArrayField =
|
||||||
FloatNd4jArrayField(intArrayOf(shapeFirst, * shapeRest))
|
FloatNd4jArrayField(ShapeND(shapeFirst, * shapeRest))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents [RingND] over [Nd4jArrayIntStructure].
|
* Represents [RingND] over [Nd4jArrayIntStructure].
|
||||||
@ -284,11 +287,11 @@ public open class IntNd4jArrayRingOps : Nd4jArrayRingOps<Int, IntRing> {
|
|||||||
|
|
||||||
override fun INDArray.wrap(): Nd4jArrayStructure<Int> = asIntStructure()
|
override fun INDArray.wrap(): Nd4jArrayStructure<Int> = asIntStructure()
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class, UnsafeKMathAPI::class)
|
||||||
override val StructureND<Int>.ndArray: INDArray
|
override val StructureND<Int>.ndArray: INDArray
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
is Nd4jArrayStructure<Int> -> ndArray
|
is Nd4jArrayStructure<Int> -> ndArray
|
||||||
else -> Nd4j.zeros(*shape).also {
|
else -> Nd4j.zeros(*shape.asArray()).also {
|
||||||
elements().forEach { (idx, value) -> it.putScalar(idx, value) }
|
elements().forEach { (idx, value) -> it.putScalar(idx, value) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,7 +313,7 @@ public open class IntNd4jArrayRingOps : Nd4jArrayRingOps<Int, IntRing> {
|
|||||||
|
|
||||||
public val IntRing.nd4j: IntNd4jArrayRingOps get() = IntNd4jArrayRingOps
|
public val IntRing.nd4j: IntNd4jArrayRingOps get() = IntNd4jArrayRingOps
|
||||||
|
|
||||||
public class IntNd4jArrayRing(override val shape: Shape) : IntNd4jArrayRingOps(), RingND<Int, IntRing>
|
public class IntNd4jArrayRing(override val shape: ShapeND) : IntNd4jArrayRingOps(), RingND<Int, IntRing>
|
||||||
|
|
||||||
public fun IntRing.nd4j(shapeFirst: Int, vararg shapeRest: Int): IntNd4jArrayRing =
|
public fun IntRing.nd4j(shapeFirst: Int, vararg shapeRest: Int): IntNd4jArrayRing =
|
||||||
IntNd4jArrayRing(intArrayOf(shapeFirst, * shapeRest))
|
IntNd4jArrayRing(ShapeND(shapeFirst, * shapeRest))
|
@ -7,8 +7,7 @@ package space.kscience.kmath.nd4j
|
|||||||
|
|
||||||
import org.nd4j.linalg.api.ndarray.INDArray
|
import org.nd4j.linalg.api.ndarray.INDArray
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.nd.MutableStructureND
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.nd.StructureND
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a [StructureND] wrapping an [INDArray] object.
|
* Represents a [StructureND] wrapping an [INDArray] object.
|
||||||
@ -22,7 +21,7 @@ public sealed class Nd4jArrayStructure<T> : MutableStructureND<T> {
|
|||||||
*/
|
*/
|
||||||
public abstract val ndArray: INDArray
|
public abstract val ndArray: INDArray
|
||||||
|
|
||||||
override val shape: IntArray get() = ndArray.shape().toIntArray()
|
override val shape: ShapeND get() = ShapeND(ndArray.shape().toIntArray())
|
||||||
|
|
||||||
internal abstract fun elementsIterator(): Iterator<Pair<IntArray, T>>
|
internal abstract fun elementsIterator(): Iterator<Pair<IntArray, T>>
|
||||||
internal fun indicesIterator(): Iterator<IntArray> = ndArray.indicesIterator()
|
internal fun indicesIterator(): Iterator<IntArray> = ndArray.indicesIterator()
|
||||||
@ -31,20 +30,31 @@ public sealed class Nd4jArrayStructure<T> : MutableStructureND<T> {
|
|||||||
override fun elements(): Sequence<Pair<IntArray, T>> = Sequence(::elementsIterator)
|
override fun elements(): Sequence<Pair<IntArray, T>> = Sequence(::elementsIterator)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class Nd4jArrayIntStructure(override val ndArray: INDArray) : Nd4jArrayStructure<Int>() {
|
public data class Nd4jArrayIntStructure(override val ndArray: INDArray) : Nd4jArrayStructure<Int>(), StructureNDOfInt {
|
||||||
override fun elementsIterator(): Iterator<Pair<IntArray, Int>> = ndArray.intIterator()
|
override fun elementsIterator(): Iterator<Pair<IntArray, Int>> = ndArray.intIterator()
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun get(index: IntArray): Int = ndArray.getInt(*index)
|
override fun get(index: IntArray): Int = ndArray.getInt(*index)
|
||||||
|
|
||||||
|
override fun getInt(index: IntArray): Int = ndArray.getInt(*index)
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) }
|
override fun set(index: IntArray, value: Int): Unit = run { ndArray.putScalar(index, value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps this [INDArray] to [Nd4jArrayStructure].
|
* Wraps this [INDArray] to [Nd4jArrayStructure].
|
||||||
*/
|
*/
|
||||||
public fun INDArray.asIntStructure(): Nd4jArrayStructure<Int> = Nd4jArrayIntStructure(this)
|
public fun INDArray.asIntStructure(): Nd4jArrayIntStructure = Nd4jArrayIntStructure(this)
|
||||||
|
|
||||||
private data class Nd4jArrayDoubleStructure(override val ndArray: INDArray) : Nd4jArrayStructure<Double>() {
|
public data class Nd4jArrayDoubleStructure(override val ndArray: INDArray) : Nd4jArrayStructure<Double>(), StructureNDOfDouble {
|
||||||
override fun elementsIterator(): Iterator<Pair<IntArray, Double>> = ndArray.realIterator()
|
override fun elementsIterator(): Iterator<Pair<IntArray, Double>> = ndArray.realIterator()
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun get(index: IntArray): Double = ndArray.getDouble(*index)
|
override fun get(index: IntArray): Double = ndArray.getDouble(*index)
|
||||||
|
|
||||||
|
override fun getDouble(index: IntArray): Double = ndArray.getDouble(*index)
|
||||||
|
|
||||||
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) }
|
override fun set(index: IntArray, value: Double): Unit = run { ndArray.putScalar(index, value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,9 +63,12 @@ private data class Nd4jArrayDoubleStructure(override val ndArray: INDArray) : Nd
|
|||||||
*/
|
*/
|
||||||
public fun INDArray.asDoubleStructure(): Nd4jArrayStructure<Double> = Nd4jArrayDoubleStructure(this)
|
public fun INDArray.asDoubleStructure(): Nd4jArrayStructure<Double> = Nd4jArrayDoubleStructure(this)
|
||||||
|
|
||||||
private data class Nd4jArrayFloatStructure(override val ndArray: INDArray) : Nd4jArrayStructure<Float>() {
|
public data class Nd4jArrayFloatStructure(override val ndArray: INDArray) : Nd4jArrayStructure<Float>() {
|
||||||
override fun elementsIterator(): Iterator<Pair<IntArray, Float>> = ndArray.floatIterator()
|
override fun elementsIterator(): Iterator<Pair<IntArray, Float>> = ndArray.floatIterator()
|
||||||
|
@PerformancePitfall
|
||||||
override fun get(index: IntArray): Float = ndArray.getFloat(*index)
|
override fun get(index: IntArray): Float = ndArray.getFloat(*index)
|
||||||
|
|
||||||
|
@PerformancePitfall
|
||||||
override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) }
|
override fun set(index: IntArray, value: Float): Unit = run { ndArray.putScalar(index, value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,9 +13,8 @@ import org.nd4j.linalg.factory.Nd4j
|
|||||||
import org.nd4j.linalg.factory.ops.NDBase
|
import org.nd4j.linalg.factory.ops.NDBase
|
||||||
import org.nd4j.linalg.ops.transforms.Transforms
|
import org.nd4j.linalg.ops.transforms.Transforms
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
import space.kscience.kmath.nd.DefaultStrides
|
import space.kscience.kmath.misc.UnsafeKMathAPI
|
||||||
import space.kscience.kmath.nd.Shape
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.nd.StructureND
|
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
import space.kscience.kmath.operations.Field
|
import space.kscience.kmath.operations.Field
|
||||||
import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra
|
import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra
|
||||||
@ -38,7 +37,7 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
|
|||||||
*/
|
*/
|
||||||
public val StructureND<T>.ndArray: INDArray
|
public val StructureND<T>.ndArray: INDArray
|
||||||
|
|
||||||
override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): Nd4jArrayStructure<T>
|
override fun structureND(shape: ShapeND, initializer: A.(IntArray) -> T): Nd4jArrayStructure<T>
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class)
|
||||||
override fun StructureND<T>.map(transform: A.(T) -> T): Nd4jArrayStructure<T> =
|
override fun StructureND<T>.map(transform: A.(T) -> T): Nd4jArrayStructure<T> =
|
||||||
@ -96,7 +95,7 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
|
|||||||
|
|
||||||
override fun StructureND<T>.unaryMinus(): Nd4jArrayStructure<T> = ndArray.neg().wrap()
|
override fun StructureND<T>.unaryMinus(): Nd4jArrayStructure<T> = ndArray.neg().wrap()
|
||||||
override fun Tensor<T>.getTensor(i: Int): Nd4jArrayStructure<T> = ndArray.slice(i.toLong()).wrap()
|
override fun Tensor<T>.getTensor(i: Int): Nd4jArrayStructure<T> = ndArray.slice(i.toLong()).wrap()
|
||||||
override fun Tensor<T>.transposed(i: Int, j: Int): Nd4jArrayStructure<T> = ndArray.swapAxes(i, j).wrap()
|
override fun StructureND<T>.transposed(i: Int, j: Int): Nd4jArrayStructure<T> = ndArray.swapAxes(i, j).wrap()
|
||||||
override fun StructureND<T>.dot(other: StructureND<T>): Nd4jArrayStructure<T> = ndArray.mmul(other.ndArray).wrap()
|
override fun StructureND<T>.dot(other: StructureND<T>): Nd4jArrayStructure<T> = ndArray.mmul(other.ndArray).wrap()
|
||||||
|
|
||||||
override fun StructureND<T>.min(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
|
override fun StructureND<T>.min(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
|
||||||
@ -108,7 +107,9 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
|
|||||||
override fun StructureND<T>.max(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
|
override fun StructureND<T>.max(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
|
||||||
ndArray.max(keepDim, dim).wrap()
|
ndArray.max(keepDim, dim).wrap()
|
||||||
|
|
||||||
override fun Tensor<T>.view(shape: IntArray): Nd4jArrayStructure<T> = ndArray.reshape(shape).wrap()
|
@OptIn(UnsafeKMathAPI::class)
|
||||||
|
override fun Tensor<T>.view(shape: ShapeND): Nd4jArrayStructure<T> = ndArray.reshape(shape.asArray()).wrap()
|
||||||
|
|
||||||
override fun Tensor<T>.viewAs(other: StructureND<T>): Nd4jArrayStructure<T> = view(other.shape)
|
override fun Tensor<T>.viewAs(other: StructureND<T>): Nd4jArrayStructure<T> = view(other.shape)
|
||||||
|
|
||||||
override fun StructureND<T>.argMin(dim: Int, keepDim: Boolean): Tensor<Int> =
|
override fun StructureND<T>.argMin(dim: Int, keepDim: Boolean): Tensor<Int> =
|
||||||
@ -117,35 +118,35 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
|
|||||||
override fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Tensor<Int> =
|
override fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Tensor<Int> =
|
||||||
ndBase.get().argmax(ndArray, keepDim, dim).asIntStructure()
|
ndBase.get().argmax(ndArray, keepDim, dim).asIntStructure()
|
||||||
|
|
||||||
override fun StructureND<T>.mean(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
|
override fun mean(structureND: StructureND<T>, dim: Int, keepDim: Boolean): Tensor<T> =
|
||||||
ndArray.mean(keepDim, dim).wrap()
|
structureND.ndArray.mean(keepDim, dim).wrap()
|
||||||
|
|
||||||
override fun StructureND<T>.exp(): Nd4jArrayStructure<T> = Transforms.exp(ndArray).wrap()
|
override fun exp(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.exp(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.ln(): Nd4jArrayStructure<T> = Transforms.log(ndArray).wrap()
|
override fun ln(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.log(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.sqrt(): Nd4jArrayStructure<T> = Transforms.sqrt(ndArray).wrap()
|
override fun sqrt(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.sqrt(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.cos(): Nd4jArrayStructure<T> = Transforms.cos(ndArray).wrap()
|
override fun cos(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.cos(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.acos(): Nd4jArrayStructure<T> = Transforms.acos(ndArray).wrap()
|
override fun acos(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.acos(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.cosh(): Nd4jArrayStructure<T> = Transforms.cosh(ndArray).wrap()
|
override fun cosh(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.cosh(arg.ndArray).wrap()
|
||||||
|
|
||||||
override fun StructureND<T>.acosh(): Nd4jArrayStructure<T> =
|
override fun acosh(arg: StructureND<T>): Nd4jArrayStructure<T> =
|
||||||
Nd4j.getExecutioner().exec(ACosh(ndArray, ndArray.ulike())).wrap()
|
Nd4j.getExecutioner().exec(ACosh(arg.ndArray, arg.ndArray.ulike())).wrap()
|
||||||
|
|
||||||
override fun StructureND<T>.sin(): Nd4jArrayStructure<T> = Transforms.sin(ndArray).wrap()
|
override fun sin(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.sin(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.asin(): Nd4jArrayStructure<T> = Transforms.asin(ndArray).wrap()
|
override fun asin(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.asin(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.sinh(): Tensor<T> = Transforms.sinh(ndArray).wrap()
|
override fun sinh(arg: StructureND<T>): Tensor<T> = Transforms.sinh(arg.ndArray).wrap()
|
||||||
|
|
||||||
override fun StructureND<T>.asinh(): Nd4jArrayStructure<T> =
|
override fun asinh(arg: StructureND<T>): Nd4jArrayStructure<T> =
|
||||||
Nd4j.getExecutioner().exec(ASinh(ndArray, ndArray.ulike())).wrap()
|
Nd4j.getExecutioner().exec(ASinh(arg.ndArray, arg.ndArray.ulike())).wrap()
|
||||||
|
|
||||||
override fun StructureND<T>.tan(): Nd4jArrayStructure<T> = Transforms.tan(ndArray).wrap()
|
override fun tan(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.tan(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.atan(): Nd4jArrayStructure<T> = Transforms.atan(ndArray).wrap()
|
override fun atan(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.atan(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.tanh(): Nd4jArrayStructure<T> = Transforms.tanh(ndArray).wrap()
|
override fun tanh(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.tanh(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.atanh(): Nd4jArrayStructure<T> = Transforms.atanh(ndArray).wrap()
|
override fun atanh(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.atanh(arg.ndArray).wrap()
|
||||||
override fun power(arg: StructureND<T>, pow: Number): StructureND<T> = Transforms.pow(arg.ndArray, pow).wrap()
|
override fun power(arg: StructureND<T>, pow: Number): StructureND<T> = Transforms.pow(arg.ndArray, pow).wrap()
|
||||||
override fun StructureND<T>.ceil(): Nd4jArrayStructure<T> = Transforms.ceil(ndArray).wrap()
|
override fun ceil(arg: StructureND<T>): Nd4jArrayStructure<T> = Transforms.ceil(arg.ndArray).wrap()
|
||||||
override fun StructureND<T>.floor(): Nd4jArrayStructure<T> = Transforms.floor(ndArray).wrap()
|
override fun floor(structureND: StructureND<T>): Nd4jArrayStructure<T> = Transforms.floor(structureND.ndArray).wrap()
|
||||||
override fun StructureND<T>.std(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
|
override fun std(structureND: StructureND<T>, dim: Int, keepDim: Boolean): Tensor<T> =
|
||||||
ndArray.std(true, keepDim, dim).wrap()
|
structureND.ndArray.std(true, keepDim, dim).wrap()
|
||||||
|
|
||||||
override fun T.div(arg: StructureND<T>): Nd4jArrayStructure<T> = arg.ndArray.rdiv(this).wrap()
|
override fun T.div(arg: StructureND<T>): Nd4jArrayStructure<T> = arg.ndArray.rdiv(this).wrap()
|
||||||
override fun StructureND<T>.div(arg: T): Nd4jArrayStructure<T> = ndArray.div(arg).wrap()
|
override fun StructureND<T>.div(arg: T): Nd4jArrayStructure<T> = ndArray.div(arg).wrap()
|
||||||
@ -159,8 +160,8 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
|
|||||||
ndArray.divi(arg.ndArray)
|
ndArray.divi(arg.ndArray)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun StructureND<T>.variance(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
|
override fun variance(structureND: StructureND<T>, dim: Int, keepDim: Boolean): Tensor<T> =
|
||||||
Nd4j.getExecutioner().exec(Variance(ndArray, true, true, dim)).wrap()
|
Nd4j.getExecutioner().exec(Variance(structureND.ndArray, true, true, dim)).wrap()
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
private val ndBase: ThreadLocal<NDBase> = ThreadLocal.withInitial(::NDBase)
|
private val ndBase: ThreadLocal<NDBase> = ThreadLocal.withInitial(::NDBase)
|
||||||
@ -176,9 +177,10 @@ public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra<Double, DoubleField> {
|
|||||||
|
|
||||||
override fun INDArray.wrap(): Nd4jArrayStructure<Double> = asDoubleStructure()
|
override fun INDArray.wrap(): Nd4jArrayStructure<Double> = asDoubleStructure()
|
||||||
|
|
||||||
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): Nd4jArrayStructure<Double> {
|
@OptIn(UnsafeKMathAPI::class)
|
||||||
val array: INDArray = Nd4j.zeros(*shape)
|
override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): Nd4jArrayStructure<Double> {
|
||||||
val indices = DefaultStrides(shape)
|
val array: INDArray = Nd4j.zeros(*shape.asArray())
|
||||||
|
val indices = ColumnStrides(shape)
|
||||||
indices.asSequence().forEach { index ->
|
indices.asSequence().forEach { index ->
|
||||||
array.putScalar(index, elementAlgebra.initializer(index))
|
array.putScalar(index, elementAlgebra.initializer(index))
|
||||||
}
|
}
|
||||||
@ -186,21 +188,21 @@ public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra<Double, DoubleField> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@OptIn(PerformancePitfall::class)
|
@OptIn(PerformancePitfall::class, UnsafeKMathAPI::class)
|
||||||
override val StructureND<Double>.ndArray: INDArray
|
override val StructureND<Double>.ndArray: INDArray
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
is Nd4jArrayStructure<Double> -> ndArray
|
is Nd4jArrayStructure<Double> -> ndArray
|
||||||
else -> Nd4j.zeros(*shape).also {
|
else -> Nd4j.zeros(*shape.asArray()).also {
|
||||||
elements().forEach { (idx, value) -> it.putScalar(idx, value) }
|
elements().forEach { (idx, value) -> it.putScalar(idx, value) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun StructureND<Double>.valueOrNull(): Double? =
|
override fun StructureND<Double>.valueOrNull(): Double? =
|
||||||
if (shape contentEquals intArrayOf(1)) ndArray.getDouble(0) else null
|
if (shape contentEquals ShapeND(1)) ndArray.getDouble(0) else null
|
||||||
|
|
||||||
// TODO rewrite
|
// TODO rewrite
|
||||||
override fun diagonalEmbedding(
|
override fun diagonalEmbedding(
|
||||||
diagonalEntries: Tensor<Double>,
|
diagonalEntries: StructureND<Double>,
|
||||||
offset: Int,
|
offset: Int,
|
||||||
dim1: Int,
|
dim1: Int,
|
||||||
dim2: Int,
|
dim2: Int,
|
||||||
@ -209,7 +211,7 @@ public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra<Double, DoubleField> {
|
|||||||
override fun StructureND<Double>.sum(): Double = ndArray.sumNumber().toDouble()
|
override fun StructureND<Double>.sum(): Double = ndArray.sumNumber().toDouble()
|
||||||
override fun StructureND<Double>.min(): Double = ndArray.minNumber().toDouble()
|
override fun StructureND<Double>.min(): Double = ndArray.minNumber().toDouble()
|
||||||
override fun StructureND<Double>.max(): Double = ndArray.maxNumber().toDouble()
|
override fun StructureND<Double>.max(): Double = ndArray.maxNumber().toDouble()
|
||||||
override fun StructureND<Double>.mean(): Double = ndArray.meanNumber().toDouble()
|
override fun mean(structureND: StructureND<Double>): Double = structureND.ndArray.meanNumber().toDouble()
|
||||||
override fun StructureND<Double>.std(): Double = ndArray.stdNumber().toDouble()
|
override fun std(structureND: StructureND<Double>): Double = structureND.ndArray.stdNumber().toDouble()
|
||||||
override fun StructureND<Double>.variance(): Double = ndArray.varNumber().toDouble()
|
override fun variance(structureND: StructureND<Double>): Double = structureND.ndArray.varNumber().toDouble()
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ package space.kscience.kmath.nd4j
|
|||||||
|
|
||||||
import org.nd4j.linalg.factory.Nd4j
|
import org.nd4j.linalg.factory.Nd4j
|
||||||
import space.kscience.kmath.misc.PerformancePitfall
|
import space.kscience.kmath.misc.PerformancePitfall
|
||||||
|
import space.kscience.kmath.nd.asList
|
||||||
import space.kscience.kmath.nd.get
|
import space.kscience.kmath.nd.get
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -27,7 +28,7 @@ internal class Nd4jArrayStructureTest {
|
|||||||
fun testShape() {
|
fun testShape() {
|
||||||
val nd = Nd4j.rand(10, 2, 3, 6) ?: fail()
|
val nd = Nd4j.rand(10, 2, 3, 6) ?: fail()
|
||||||
val struct = nd.asDoubleStructure()
|
val struct = nd.asDoubleStructure()
|
||||||
assertEquals(intArrayOf(10, 2, 3, 6).toList(), struct.shape.toList())
|
assertEquals(intArrayOf(10, 2, 3, 6).toList(), struct.shape.asList())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -33,7 +33,7 @@ internal class HessianGradientCalculator(fcn: MnFcn, par: MnUserTransformation,
|
|||||||
val g2: RealVector = gradient.getGradientDerivative()
|
val g2: RealVector = gradient.getGradientDerivative()
|
||||||
val gstep: RealVector = gradient.getStep()
|
val gstep: RealVector = gradient.getStep()
|
||||||
val fcnmin: Double = par.fval()
|
val fcnmin: Double = par.fval()
|
||||||
// std::cout<<"fval: "<<fcnmin<<std::endl;
|
// standardDiviation::cout<<"fval: "<<fcnmin<<standardDiviation::endl;
|
||||||
val dfmin: Double = 4.0 * precision().eps2() * (abs(fcnmin) + theFcn.errorDef())
|
val dfmin: Double = 4.0 * precision().eps2() * (abs(fcnmin) + theFcn.errorDef())
|
||||||
val n: Int = x.getDimension()
|
val n: Int = x.getDimension()
|
||||||
val dgrd: RealVector = ArrayRealVector(n)
|
val dgrd: RealVector = ArrayRealVector(n)
|
||||||
|
@ -36,7 +36,7 @@ internal object MnPosDef {
|
|||||||
if (err.size() === 1 && err[0, 0] > prec.eps()) {
|
if (err.size() === 1 && err[0, 0] > prec.eps()) {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
// std::cout<<"MnPosDef init matrix= "<<err<<std::endl;
|
// standardDiviation::cout<<"MnPosDef init matrix= "<<err<<standardDiviation::endl;
|
||||||
val epspdf: Double = max(1e-6, prec.eps2())
|
val epspdf: Double = max(1e-6, prec.eps2())
|
||||||
var dgmin: Double = err[0, 0]
|
var dgmin: Double = err[0, 0]
|
||||||
for (i in 0 until err.nrow()) {
|
for (i in 0 until err.nrow()) {
|
||||||
@ -66,11 +66,11 @@ internal object MnPosDef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout<<"MnPosDef p: "<<p<<std::endl;
|
// standardDiviation::cout<<"MnPosDef p: "<<p<<standardDiviation::endl;
|
||||||
val eval: RealVector = p.eigenvalues()
|
val eval: RealVector = p.eigenvalues()
|
||||||
val pmin: Double = eval.getEntry(0)
|
val pmin: Double = eval.getEntry(0)
|
||||||
var pmax: Double = eval.getEntry(eval.getDimension() - 1)
|
var pmax: Double = eval.getEntry(eval.getDimension() - 1)
|
||||||
// std::cout<<"pmin= "<<pmin<<" pmax= "<<pmax<<std::endl;
|
// standardDiviation::cout<<"pmin= "<<pmin<<" pmax= "<<pmax<<standardDiviation::endl;
|
||||||
pmax = max(abs(pmax), 1.0)
|
pmax = max(abs(pmax), 1.0)
|
||||||
if (pmin > epspdf * pmax) {
|
if (pmin > epspdf * pmax) {
|
||||||
return e
|
return e
|
||||||
@ -81,9 +81,9 @@ internal object MnPosDef {
|
|||||||
err[i, i] = err[i, i] * (1.0 + padd)
|
err[i, i] = err[i, i] * (1.0 + padd)
|
||||||
MINUITPlugin.logStatic(java.lang.Double.toString(eval.getEntry(i)))
|
MINUITPlugin.logStatic(java.lang.Double.toString(eval.getEntry(i)))
|
||||||
}
|
}
|
||||||
// std::cout<<"MnPosDef final matrix: "<<err<<std::endl;
|
// standardDiviation::cout<<"MnPosDef final matrix: "<<err<<standardDiviation::endl;
|
||||||
MINUITPlugin.logStatic("matrix forced pos-def by adding $padd to diagonal")
|
MINUITPlugin.logStatic("matrix forced pos-def by adding $padd to diagonal")
|
||||||
// std::cout<<"eigenvalues: "<<eval<<std::endl;
|
// standardDiviation::cout<<"eigenvalues: "<<eval<<standardDiviation::endl;
|
||||||
return MinimumError(err, MnMadePosDef())
|
return MinimumError(err, MnMadePosDef())
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,50 +0,0 @@
|
|||||||
# Module kmath-polynomial
|
|
||||||
|
|
||||||
Polynomials, rational functions, and utilities
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- [polynomial abstraction](src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt) : Abstraction for polynomial spaces.
|
|
||||||
- [rational function abstraction](src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt) : Abstraction for rational functions spaces.
|
|
||||||
- ["list" polynomials](src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt) : List implementation of univariate polynomials.
|
|
||||||
- ["list" rational functions](src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt) : List implementation of univariate rational functions.
|
|
||||||
- ["list" polynomials and rational functions constructors](src/commonMain/kotlin/space/kscience/kmath/functions/listConstructors.kt) : Constructors for list polynomials and rational functions.
|
|
||||||
- ["list" polynomials and rational functions utilities](src/commonMain/kotlin/space/kscience/kmath/functions/listUtil.kt) : Utilities for list polynomials and rational functions.
|
|
||||||
- ["numbered" polynomials](src/commonMain/kotlin/space/kscience/kmath/functions/NumberedRationalFunction.kt) : Numbered implementation of multivariate polynomials.
|
|
||||||
- ["numbered" rational functions](src/commonMain/kotlin/space/kscience/kmath/functions/NumberedPolynomial.kt) : Numbered implementation of multivariate rational functions.
|
|
||||||
- ["numbered" polynomials and rational functions constructors](src/commonMain/kotlin/space/kscience/kmath/functions/numberedConstructors.kt) : Constructors for numbered polynomials and rational functions.
|
|
||||||
- ["numbered" polynomials and rational functions utilities](src/commonMain/kotlin/space/kscience/kmath/functions/numberedUtil.kt) : Utilities for numbered polynomials and rational functions.
|
|
||||||
- ["labeled" polynomials](src/commonMain/kotlin/space/kscience/kmath/functions/LabeledRationalFunction.kt) : Labeled implementation of multivariate polynomials.
|
|
||||||
- ["labeled" rational functions](src/commonMain/kotlin/space/kscience/kmath/functions/LabeledPolynomial.kt) : Labeled implementation of multivariate rational functions.
|
|
||||||
- ["labeled" polynomials and rational functions constructors](src/commonMain/kotlin/space/kscience/kmath/functions/labeledConstructors.kt) : Constructors for labeled polynomials and rational functions.
|
|
||||||
- ["labeled" polynomials and rational functions utilities](src/commonMain/kotlin/space/kscience/kmath/functions/labeledUtil.kt) : Utilities for labeled polynomials and rational functions.
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
## Artifact:
|
|
||||||
|
|
||||||
The Maven coordinates of this project are `space.kscience:kmath-polynomial:0.3.1-dev-1`.
|
|
||||||
|
|
||||||
**Gradle Groovy:**
|
|
||||||
```groovy
|
|
||||||
repositories {
|
|
||||||
maven { url 'https://repo.kotlin.link' }
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation 'space.kscience:kmath-polynomial:0.3.1-dev-1'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
**Gradle Kotlin DSL:**
|
|
||||||
```kotlin
|
|
||||||
repositories {
|
|
||||||
maven("https://repo.kotlin.link")
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation("space.kscience:kmath-polynomial:0.3.1-dev-1")
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,69 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("space.kscience.gradle.mpp")
|
|
||||||
}
|
|
||||||
|
|
||||||
kscience{
|
|
||||||
native()
|
|
||||||
}
|
|
||||||
|
|
||||||
description = "Polynomials, rational functions, and utilities"
|
|
||||||
|
|
||||||
kotlin.sourceSets {
|
|
||||||
commonMain {
|
|
||||||
dependencies {
|
|
||||||
api(projects.kmathCore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
dokkaPlugin("org.jetbrains.dokka:mathjax-plugin:${npmlibs.versions.dokka.get()}")
|
|
||||||
}
|
|
||||||
|
|
||||||
readme {
|
|
||||||
maturity = space.kscience.gradle.Maturity.PROTOTYPE
|
|
||||||
propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
|
|
||||||
|
|
||||||
feature("polynomial abstraction", "src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt") {
|
|
||||||
"Abstraction for polynomial spaces."
|
|
||||||
}
|
|
||||||
feature("rational function abstraction", "src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt") {
|
|
||||||
"Abstraction for rational functions spaces."
|
|
||||||
}
|
|
||||||
feature("\"list\" polynomials", "src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt") {
|
|
||||||
"List implementation of univariate polynomials."
|
|
||||||
}
|
|
||||||
feature("\"list\" rational functions", "src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt") {
|
|
||||||
"List implementation of univariate rational functions."
|
|
||||||
}
|
|
||||||
feature("\"list\" polynomials and rational functions constructors", "src/commonMain/kotlin/space/kscience/kmath/functions/listConstructors.kt") {
|
|
||||||
"Constructors for list polynomials and rational functions."
|
|
||||||
}
|
|
||||||
feature("\"list\" polynomials and rational functions utilities", "src/commonMain/kotlin/space/kscience/kmath/functions/listUtil.kt") {
|
|
||||||
"Utilities for list polynomials and rational functions."
|
|
||||||
}
|
|
||||||
feature("\"numbered\" polynomials", "src/commonMain/kotlin/space/kscience/kmath/functions/NumberedRationalFunction.kt") {
|
|
||||||
"Numbered implementation of multivariate polynomials."
|
|
||||||
}
|
|
||||||
feature("\"numbered\" rational functions", "src/commonMain/kotlin/space/kscience/kmath/functions/NumberedPolynomial.kt") {
|
|
||||||
"Numbered implementation of multivariate rational functions."
|
|
||||||
}
|
|
||||||
feature("\"numbered\" polynomials and rational functions constructors", "src/commonMain/kotlin/space/kscience/kmath/functions/numberedConstructors.kt") {
|
|
||||||
"Constructors for numbered polynomials and rational functions."
|
|
||||||
}
|
|
||||||
feature("\"numbered\" polynomials and rational functions utilities", "src/commonMain/kotlin/space/kscience/kmath/functions/numberedUtil.kt") {
|
|
||||||
"Utilities for numbered polynomials and rational functions."
|
|
||||||
}
|
|
||||||
feature("\"labeled\" polynomials", "src/commonMain/kotlin/space/kscience/kmath/functions/LabeledRationalFunction.kt") {
|
|
||||||
"Labeled implementation of multivariate polynomials."
|
|
||||||
}
|
|
||||||
feature("\"labeled\" rational functions", "src/commonMain/kotlin/space/kscience/kmath/functions/LabeledPolynomial.kt") {
|
|
||||||
"Labeled implementation of multivariate rational functions."
|
|
||||||
}
|
|
||||||
feature("\"labeled\" polynomials and rational functions constructors", "src/commonMain/kotlin/space/kscience/kmath/functions/labeledConstructors.kt") {
|
|
||||||
"Constructors for labeled polynomials and rational functions."
|
|
||||||
}
|
|
||||||
feature("\"labeled\" polynomials and rational functions utilities", "src/commonMain/kotlin/space/kscience/kmath/functions/labeledUtil.kt") {
|
|
||||||
"Utilities for labeled polynomials and rational functions."
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,529 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018-2022 KMath contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
|
||||||
|
|
||||||
package space.kscience.kmath.functions
|
|
||||||
|
|
||||||
import space.kscience.kmath.expressions.Symbol
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents multivariate polynomial that stores its coefficients in a [Map] and terms' signatures in a [Map] that
|
|
||||||
* associates variables (of type [Symbol]) with their degree.
|
|
||||||
*
|
|
||||||
* @param C the type of constants.
|
|
||||||
*/
|
|
||||||
public data class LabeledPolynomial<out C>
|
|
||||||
@PublishedApi
|
|
||||||
internal constructor(
|
|
||||||
/**
|
|
||||||
* Map that contains coefficients of the polynomial.
|
|
||||||
*
|
|
||||||
* Every monomial \(a x_1^{d_1} ... x_n^{d_n}\) is stored as a pair "key-value" in the map, where the value is the
|
|
||||||
* coefficient \(a\) and the key is a map that associates variables in the monomial with their degree in the monomial.
|
|
||||||
* For example, coefficients of a polynomial \(5 a^2 c^3 - 6 b\) can be represented as
|
|
||||||
* ```
|
|
||||||
* mapOf(
|
|
||||||
* mapOf(
|
|
||||||
* a to 2,
|
|
||||||
* c to 3
|
|
||||||
* ) to 5,
|
|
||||||
* mapOf(
|
|
||||||
* b to 1
|
|
||||||
* ) to (-6)
|
|
||||||
* )
|
|
||||||
* ```
|
|
||||||
* and also as
|
|
||||||
* ```
|
|
||||||
* mapOf(
|
|
||||||
* mapOf(
|
|
||||||
* a to 2,
|
|
||||||
* c to 3
|
|
||||||
* ) to 5,
|
|
||||||
* mapOf(
|
|
||||||
* b to 1
|
|
||||||
* ) to (-6),
|
|
||||||
* mapOf(
|
|
||||||
* b to 1,
|
|
||||||
* c to 1
|
|
||||||
* ) to 0
|
|
||||||
* )
|
|
||||||
* ```
|
|
||||||
* where \(a\), \(b\) and \(c\) are corresponding [Symbol] objects.
|
|
||||||
*
|
|
||||||
* It is not prohibited to put extra zero monomials into the map (as for \(0 b c\) in the example). But the
|
|
||||||
* bigger the coefficients map the worse performance of arithmetical operations performed on it. Thus, it is
|
|
||||||
* recommended not to put (or even to remove) extra (or useless) monomials in the coefficients map.
|
|
||||||
* @usesMathJax
|
|
||||||
*/
|
|
||||||
public val coefficients: Map<Map<Symbol, UInt>, C>
|
|
||||||
) {
|
|
||||||
override fun toString(): String = "LabeledPolynomial$coefficients"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Arithmetic context for multivariate polynomials with coefficients stored as a [Map] and terms' signatures stored as a
|
|
||||||
* [Map] constructed with the provided [ring] of constants.
|
|
||||||
*
|
|
||||||
* @param C the type of constants. Polynomials have them a coefficients in their terms.
|
|
||||||
* @param A type of provided underlying ring of constants. It's [Ring] of [C].
|
|
||||||
* @param ring underlying ring of constants of type [A].
|
|
||||||
*/
|
|
||||||
public class LabeledPolynomialSpace<C, out A : Ring<C>>(
|
|
||||||
public override val ring: A,
|
|
||||||
) : MultivariatePolynomialSpace<C, Symbol, LabeledPolynomial<C>>, PolynomialSpaceOverRing<C, LabeledPolynomial<C>, A> {
|
|
||||||
/**
|
|
||||||
* Returns sum of the variable represented as a monic monomial and the integer represented as a constant polynomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.plus(other: Int): LabeledPolynomial<C> =
|
|
||||||
if (other == 0) LabeledPolynomialAsIs(
|
|
||||||
mapOf(this@plus to 1U) to constantOne,
|
|
||||||
)
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
mapOf(this@plus to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to other.asConstant(),
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the variable represented as a monic monomial and the integer represented as a constant polynomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.minus(other: Int): LabeledPolynomial<C> =
|
|
||||||
if (other == 0) LabeledPolynomialAsIs(
|
|
||||||
mapOf(this@minus to 1U) to constantOne,
|
|
||||||
)
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
mapOf(this@minus to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to (-other).asConstant(),
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the variable represented as a monic monomial and the integer represented as a constant polynomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.times(other: Int): LabeledPolynomial<C> =
|
|
||||||
if (other == 0) zero
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
mapOf(this to 1U) to other.asConstant(),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the integer represented as a constant polynomial and the variable represented as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Int.plus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == 0) LabeledPolynomialAsIs(
|
|
||||||
mapOf(other to 1U) to constantOne,
|
|
||||||
)
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
mapOf(other to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to this@plus.asConstant(),
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the integer represented as a constant polynomial and the variable represented as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Int.minus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == 0) LabeledPolynomialAsIs(
|
|
||||||
mapOf(other to 1U) to -constantOne,
|
|
||||||
)
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
mapOf(other to 1U) to -constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to constantOne * this@minus,
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the integer represented as a constant polynomial and the variable represented as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Int.times(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == 0) zero
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
mapOf(other to 1U) to this@times.asConstant(),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the polynomial and the integer represented as a polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
|
|
||||||
*/
|
|
||||||
public override operator fun LabeledPolynomial<C>.plus(other: Int): LabeledPolynomial<C> =
|
|
||||||
when {
|
|
||||||
other == 0 -> this
|
|
||||||
coefficients.isEmpty() -> other.asPolynomial()
|
|
||||||
else -> LabeledPolynomialAsIs(
|
|
||||||
coefficients.withPutOrChanged(emptyMap(), other.asConstant()) { it -> it + other }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns difference between the polynomial and the integer represented as a polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
|
|
||||||
*/
|
|
||||||
public override operator fun LabeledPolynomial<C>.minus(other: Int): LabeledPolynomial<C> =
|
|
||||||
when {
|
|
||||||
other == 0 -> this
|
|
||||||
coefficients.isEmpty() -> other.asPolynomial()
|
|
||||||
else -> LabeledPolynomialAsIs(
|
|
||||||
coefficients.withPutOrChanged(emptyMap(), (-other).asConstant()) { it -> it - other }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns product of the polynomial and the integer represented as a polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to sum of [other] copies of [this].
|
|
||||||
*/
|
|
||||||
public override operator fun LabeledPolynomial<C>.times(other: Int): LabeledPolynomial<C> =
|
|
||||||
when(other) {
|
|
||||||
0 -> zero
|
|
||||||
1 -> this
|
|
||||||
else -> LabeledPolynomialAsIs(
|
|
||||||
coefficients.mapValues { (_, value) -> value * other }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the integer represented as a polynomial and the polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
|
|
||||||
*/
|
|
||||||
public override operator fun Int.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
when {
|
|
||||||
this == 0 -> other
|
|
||||||
other.coefficients.isEmpty() -> this@plus.asPolynomial()
|
|
||||||
else -> LabeledPolynomialAsIs(
|
|
||||||
other.coefficients.withPutOrChanged(emptyMap(), this@plus.asConstant()) { it -> this@plus + it }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns difference between the integer represented as a polynomial and the polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
|
|
||||||
*/
|
|
||||||
public override operator fun Int.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
when {
|
|
||||||
this == 0 -> -other
|
|
||||||
other.coefficients.isEmpty() -> this@minus.asPolynomial()
|
|
||||||
else -> LabeledPolynomialAsIs(
|
|
||||||
buildMap(other.coefficients.size + 1) {
|
|
||||||
put(emptyMap(), asConstant())
|
|
||||||
other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns product of the integer represented as a polynomial and the polynomial.
|
|
||||||
*
|
|
||||||
* The operation is equivalent to sum of [this] copies of [other].
|
|
||||||
*/
|
|
||||||
public override operator fun Int.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
when(this) {
|
|
||||||
0 -> zero
|
|
||||||
1 -> other
|
|
||||||
else -> LabeledPolynomialAsIs(
|
|
||||||
other.coefficients.mapValues { (_, value) -> this@times * value }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the variable represented as a monic monomial and the constant represented as a constant polynomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.plus(other: C): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
mapOf(this@plus to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to other,
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the variable represented as a monic monomial and the constant represented as a constant polynomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.minus(other: C): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
mapOf(this@minus to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to -other,
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the variable represented as a monic monomial and the constant represented as a constant polynomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.times(other: C): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
mapOf(this@times to 1U) to other,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the constant represented as a constant polynomial and the variable represented as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun C.plus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
mapOf(other to 1U) to constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to this@plus,
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the constant represented as a constant polynomial and the variable represented as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun C.minus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
mapOf(other to 1U) to -constantOne,
|
|
||||||
emptyMap<Symbol, UInt>() to this@minus,
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the constant represented as a constant polynomial and the variable represented as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun C.times(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
mapOf(other to 1U) to this@times,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun C.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
if (other.coefficients.isEmpty()) this@plus.asLabeledPolynomial()
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
other.coefficients.withPutOrChanged(emptyMap(), this@plus) { it -> this@plus + it }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun C.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
if (other.coefficients.isEmpty()) this@minus.asPolynomial()
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
buildMap(other.coefficients.size + 1) {
|
|
||||||
put(emptyMap(), this@minus)
|
|
||||||
other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun C.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
other.coefficients.mapValues { this@times * it.value }
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.plus(other: C): LabeledPolynomial<C> =
|
|
||||||
if (coefficients.isEmpty()) other.asLabeledPolynomial()
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
coefficients.withPutOrChanged(emptyMap(), other) { it -> it + other }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.minus(other: C): LabeledPolynomial<C> =
|
|
||||||
if (coefficients.isEmpty()) other.asLabeledPolynomial()
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
coefficients.withPutOrChanged(emptyMap(), -other) { it -> it - other }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.times(other: C): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
coefficients.mapValues { it.value * other }
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the constant [value] to polynomial.
|
|
||||||
*/
|
|
||||||
public override fun number(value: C): LabeledPolynomial<C> = value.asLabeledPolynomial()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the variable as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.unaryPlus(): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
mapOf(this to 1U) to constantOne,
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns negation of representation of the variable as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.unaryMinus(): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
mapOf(this to 1U) to -constantOne,
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns sum of the variables represented as monic monomials.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.plus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == other) LabeledPolynomialAsIs(
|
|
||||||
mapOf(this to 1U) to constantOne * 2
|
|
||||||
)
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
mapOf(this to 1U) to constantOne,
|
|
||||||
mapOf(other to 1U) to constantOne,
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the variables represented as monic monomials.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.minus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == other) zero
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
mapOf(this to 1U) to constantOne,
|
|
||||||
mapOf(other to 1U) to -constantOne,
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the variables represented as monic monomials.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.times(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (this == other) LabeledPolynomialAsIs(
|
|
||||||
mapOf(this to 2U) to constantOne
|
|
||||||
)
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
mapOf(this to 1U, other to 1U) to constantOne,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the variable represented as a monic monomial and the polynomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
if (other.coefficients.isEmpty()) this@plus.asPolynomial()
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
other.coefficients.withPutOrChanged(mapOf(this@plus to 1U), constantOne) { it -> constantOne + it }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the variable represented as a monic monomial and the polynomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
if (other.coefficients.isEmpty()) this@minus.asPolynomial()
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
buildMap(other.coefficients.size + 1) {
|
|
||||||
put(mapOf(this@minus to 1U), constantOne)
|
|
||||||
other.coefficients.copyMapToBy(this, { _, c -> -c }) { currentC, newC -> currentC - newC }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the variable represented as a monic monomial and the polynomial.
|
|
||||||
*/
|
|
||||||
public override operator fun Symbol.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
other.coefficients
|
|
||||||
.mapKeys { (degs, _) -> degs.withPutOrChanged(this, 1u) { it -> it + 1u } }
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns sum of the polynomial and the variable represented as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun LabeledPolynomial<C>.plus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (coefficients.isEmpty()) other.asPolynomial()
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
coefficients.withPutOrChanged(mapOf(other to 1U), constantOne) { it -> it + constantOne }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference between the polynomial and the variable represented as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun LabeledPolynomial<C>.minus(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
if (coefficients.isEmpty()) other.asPolynomial()
|
|
||||||
else LabeledPolynomialAsIs(
|
|
||||||
coefficients.withPutOrChanged(mapOf(other to 1U), -constantOne) { it -> it - constantOne }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the polynomial and the variable represented as a monic monomial.
|
|
||||||
*/
|
|
||||||
public override operator fun LabeledPolynomial<C>.times(other: Symbol): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
coefficients
|
|
||||||
.mapKeys { (degs, _) -> degs.withPutOrChanged(other, 1u) { it -> it + 1u } }
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns negation of the polynomial.
|
|
||||||
*/
|
|
||||||
override fun LabeledPolynomial<C>.unaryMinus(): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
coefficients.mapValues { -it.value }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns sum of the polynomials.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
mergeBy(coefficients, other.coefficients) { c1, c2 -> c1 + c2 }
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns difference of the polynomials.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
buildMap(coefficients.size + other.coefficients.size) {
|
|
||||||
coefficients.copyTo(this)
|
|
||||||
other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Returns product of the polynomials.
|
|
||||||
*/
|
|
||||||
override operator fun LabeledPolynomial<C>.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
|
||||||
LabeledPolynomialAsIs(
|
|
||||||
buildMap(coefficients.size * other.coefficients.size) {
|
|
||||||
for ((degs1, c1) in coefficients) for ((degs2, c2) in other.coefficients) {
|
|
||||||
val degs = mergeBy(degs1, degs2) { deg1, deg2 -> deg1 + deg2 }
|
|
||||||
val c = c1 * c2
|
|
||||||
this.putOrChange(degs, c) { it -> it + c }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of zero polynomial (zero of the polynomial ring).
|
|
||||||
*/
|
|
||||||
override val zero: LabeledPolynomial<C> = LabeledPolynomialAsIs()
|
|
||||||
/**
|
|
||||||
* Instance of unit polynomial (unit of the polynomial ring).
|
|
||||||
*/
|
|
||||||
override val one: LabeledPolynomial<C> = constantOne.asLabeledPolynomial()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
|
|
||||||
* zero, degree is -1.
|
|
||||||
*/
|
|
||||||
override val LabeledPolynomial<C>.degree: Int
|
|
||||||
get() = coefficients.entries.maxOfOrNull { (degs, _) -> degs.values.sum().toInt() } ?: -1
|
|
||||||
/**
|
|
||||||
* Map that associates variables (that appear in the polynomial in positive exponents) with their most exponents
|
|
||||||
* in which they are appeared in the polynomial.
|
|
||||||
*
|
|
||||||
* As consequence all values in the map are positive integers. Also, if the polynomial is constant, the map is empty.
|
|
||||||
* And keys of the map is the same as in [variables].
|
|
||||||
*/
|
|
||||||
public override val LabeledPolynomial<C>.degrees: Map<Symbol, UInt>
|
|
||||||
get() =
|
|
||||||
buildMap {
|
|
||||||
coefficients.keys.forEach { degs ->
|
|
||||||
degs.copyToBy(this, ::max)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Counts degree of the polynomial by the specified [variable].
|
|
||||||
*/
|
|
||||||
public override fun LabeledPolynomial<C>.degreeBy(variable: Symbol): UInt =
|
|
||||||
coefficients.entries.maxOfOrNull { (degs, _) -> degs.getOrElse(variable) { 0u } } ?: 0u
|
|
||||||
/**
|
|
||||||
* Counts degree of the polynomial by the specified [variables].
|
|
||||||
*/
|
|
||||||
public override fun LabeledPolynomial<C>.degreeBy(variables: Collection<Symbol>): UInt =
|
|
||||||
coefficients.entries.maxOfOrNull { (degs, _) -> degs.filterKeys { it in variables }.values.sum() } ?: 0u
|
|
||||||
/**
|
|
||||||
* Set of all variables that appear in the polynomial in positive exponents.
|
|
||||||
*/
|
|
||||||
public override val LabeledPolynomial<C>.variables: Set<Symbol>
|
|
||||||
get() =
|
|
||||||
buildSet {
|
|
||||||
coefficients.entries.forEach { (degs, _) -> addAll(degs.keys) }
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Count of all variables that appear in the polynomial in positive exponents.
|
|
||||||
*/
|
|
||||||
public override val LabeledPolynomial<C>.countOfVariables: Int get() = variables.size
|
|
||||||
|
|
||||||
// TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with
|
|
||||||
// [ListPolynomialSpace] as a context receiver
|
|
||||||
/**
|
|
||||||
* Substitutes provided arguments [arguments] into [this] polynomial.
|
|
||||||
*/
|
|
||||||
public inline fun LabeledPolynomial<C>.substitute(arguments: Map<Symbol, C>): LabeledPolynomial<C> = substitute(ring, arguments)
|
|
||||||
/**
|
|
||||||
* Substitutes provided arguments [arguments] into [this] polynomial.
|
|
||||||
*/
|
|
||||||
@JvmName("substitutePolynomial")
|
|
||||||
public inline fun LabeledPolynomial<C>.substitute(arguments: Map<Symbol, LabeledPolynomial<C>>) : LabeledPolynomial<C> = substitute(ring, arguments)
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user