From 5b653f10d7298e361b8d8f69f11b63f0902dfb82 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 29 Nov 2020 13:32:20 +0300 Subject: [PATCH] kmath-for-real refactoring --- CHANGELOG.md | 1 + README.md | 38 ++++--- examples/build.gradle.kts | 9 +- .../kmath/commons/fit/fitWithAutoDiff.kt | 101 ++++++++++++++++++ .../prob => stat}/DistributionBenchmark.kt | 0 .../prob => stat}/DistributionDemo.kt | 11 +- kmath-core/README.md | 6 +- .../kscience/kmath/linear/VectorSpace.kt | 8 +- .../kotlin/kscience/kmath/misc/annotations.kt | 4 + .../kscience/kmath/structures/Buffers.kt | 5 + .../kotlin/kscience/kmath/chains/Chain.kt | 2 +- kmath-for-real/README.md | 44 ++++++++ kmath-for-real/build.gradle.kts | 28 +++++ kmath-for-real/docs/README-TEMPLATE.md | 5 + .../kotlin/kscience/kmath/real/RealMatrix.kt | 52 ++++----- .../kotlin/kscience/kmath/real/RealVector.kt | 74 ++++++++----- .../kotlin/kscience/kmath/real/grids.kt | 3 +- kmath-nd4j/README.md | 8 +- .../kscience/kmath/stat/distributions.kt | 5 +- 19 files changed, 310 insertions(+), 94 deletions(-) create mode 100644 examples/src/main/kotlin/kscience/kmath/commons/fit/fitWithAutoDiff.kt rename examples/src/main/kotlin/kscience/kmath/{commons/prob => stat}/DistributionBenchmark.kt (100%) rename examples/src/main/kotlin/kscience/kmath/{commons/prob => stat}/DistributionDemo.kt (83%) create mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/misc/annotations.kt create mode 100644 kmath-for-real/README.md create mode 100644 kmath-for-real/docs/README-TEMPLATE.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 259c56c34..99511a161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - Full autodiff refactoring based on `Symbol` - `kmath-prob` renamed to `kmath-stat` - Grid generators moved to `kmath-for-real` +- Use `Point` instead of specialized type in `kmath-for-real` ### Deprecated diff --git a/README.md b/README.md index c4e3e5374..50a916d2c 100644 --- a/README.md +++ b/README.md @@ -132,9 +132,17 @@ submit a feature request if you want something to be implemented first.
* ### [kmath-for-real](kmath-for-real) -> +> Extension module that should be used to achieve numpy-like behavior. +All operations are specialized to work with `Double` numbers without declaring algebraic contexts. +One can still use generic algebras though. > > **Maturity**: EXPERIMENTAL +> +> **Features:** +> - [RealVector](kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt) : Numpy-like operations for Buffers/Points +> - [RealMatrix](kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt) : Numpy-like operations for 2d real structures +> - [grids](kmath-for-real/src/commonMain/kotlin/kscience/kmath/structures/grids.kt) : Uniform grid generators +
* ### [kmath-functions](kmath-functions) @@ -155,6 +163,12 @@ submit a feature request if you want something to be implemented first. > **Maturity**: EXPERIMENTAL
+* ### [kmath-kotlingrad](kmath-kotlingrad) +> +> +> **Maturity**: EXPERIMENTAL +
+ * ### [kmath-memory](kmath-memory) > > @@ -167,7 +181,7 @@ submit a feature request if you want something to be implemented first. > **Maturity**: EXPERIMENTAL > > **Features:** -> - [nd4jarraystrucure](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : NDStructure wrapper for INDArray +> - [nd4jarraystructure](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : NDStructure wrapper for INDArray > - [nd4jarrayrings](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Rings over Nd4jArrayStructure of Int and Long > - [nd4jarrayfields](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt) : Fields over Nd4jArrayStructure of Float and Double @@ -211,20 +225,12 @@ Release artifacts are accessible from bintray with following configuration (see ```kotlin repositories { - jcenter() - maven("https://clojars.org/repo") - maven("https://dl.bintray.com/egor-bogomolov/astminer/") - maven("https://dl.bintray.com/hotkeytlt/maven") - maven("https://dl.bintray.com/kotlin/kotlin-eap") - maven("https://dl.bintray.com/kotlin/kotlinx") maven("https://dl.bintray.com/mipt-npm/kscience") - maven("https://jitpack.io") - mavenCentral() } dependencies { - api("kscience.kmath:kmath-core:0.2.0-dev-3") - // api("kscience.kmath:kmath-core-jvm:0.2.0-dev-3") for jvm-specific version + api("kscience.kmath:kmath-core:0.2.0-dev-4") + // api("kscience.kmath:kmath-core-jvm:0.2.0-dev-4") for jvm-specific version } ``` @@ -236,15 +242,7 @@ Development builds are uploaded to the separate repository: ```kotlin repositories { - jcenter() - maven("https://clojars.org/repo") - maven("https://dl.bintray.com/egor-bogomolov/astminer/") - maven("https://dl.bintray.com/hotkeytlt/maven") - maven("https://dl.bintray.com/kotlin/kotlin-eap") - maven("https://dl.bintray.com/kotlin/kotlinx") maven("https://dl.bintray.com/mipt-npm/dev") - maven("https://jitpack.io") - mavenCentral() } ``` diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index d42627ff0..eead68a9d 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -35,6 +35,9 @@ dependencies { implementation(project(":kmath-dimensions")) implementation(project(":kmath-ejml")) implementation(project(":kmath-nd4j")) + + implementation(project(":kmath-for-real")) + implementation("org.deeplearning4j:deeplearning4j-core:1.0.0-beta7") implementation("org.nd4j:nd4j-native:1.0.0-beta7") @@ -51,7 +54,11 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-io:0.2.0-npm-dev-11") implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20") implementation("org.slf4j:slf4j-simple:1.7.30") - "benchmarksImplementation"("org.jetbrains.kotlinx:kotlinx.benchmark.runtime-jvm:0.2.0-dev-8") + + // plotting + implementation("kscience.plotlykt:plotlykt-server:0.3.1-dev") + + "benchmarksImplementation"("org.jetbrains.kotlinx:kotlinx.benchmark.runtime-jvm:0.2.0-dev-20") "benchmarksImplementation"(sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath) } diff --git a/examples/src/main/kotlin/kscience/kmath/commons/fit/fitWithAutoDiff.kt b/examples/src/main/kotlin/kscience/kmath/commons/fit/fitWithAutoDiff.kt new file mode 100644 index 000000000..6cfa1961b --- /dev/null +++ b/examples/src/main/kotlin/kscience/kmath/commons/fit/fitWithAutoDiff.kt @@ -0,0 +1,101 @@ +package kscience.kmath.commons.fit + +import kotlinx.html.br +import kotlinx.html.h3 +import kscience.kmath.commons.optimization.chiSquared +import kscience.kmath.commons.optimization.minimize +import kscience.kmath.expressions.symbol +import kscience.kmath.real.RealVector +import kscience.kmath.real.map +import kscience.kmath.real.step +import kscience.kmath.stat.* +import kscience.kmath.structures.asIterable +import kscience.kmath.structures.toList +import kscience.plotly.* +import kscience.plotly.models.ScatterMode +import kscience.plotly.models.TraceValues +import kotlin.math.pow +import kotlin.math.sqrt + +//Forward declaration of symbols that will be used in expressions. +// This declaration is required for +private val a by symbol +private val b by symbol +private val c by symbol + +/** + * Shortcut to use buffers in plotly + */ +operator fun TraceValues.invoke(vector: RealVector) { + numbers = vector.asIterable() +} + +/** + * Least squares fie with auto-differentiation. Uses `kmath-commons` and `kmath-for-real` modules. + */ +fun main() { + + //A generator for a normally distributed values + val generator = Distribution.normal() + + //A chain/flow of random values with the given seed + val chain = generator.sample(RandomGenerator.default(112667)) + + + //Create a uniformly distributed x values like numpy.arrange + val x = 1.0..100.0 step 1.0 + + + //Perform an operation on each x value (much more effective, than numpy) + val y = x.map { + val value = it.pow(2) + it + 1 + value + chain.nextDouble() * sqrt(value) + } + // this will also work, but less effective: + // val y = x.pow(2)+ x + 1 + chain.nextDouble() + + // create same errors for all xs + val yErr = y.map { sqrt(it) }//RealVector.same(x.size, sigma) + + // compute differentiable chi^2 sum for given model ax^2 + bx + c + val chi2 = Fitting.chiSquared(x, y, yErr) { x1 -> + //bind variables to autodiff context + val a = bind(a) + val b = bind(b) + //Include default value for c if it is not provided as a parameter + val c = bindOrNull(c) ?: one + a * x1.pow(2) + b * x1 + c + } + + //minimize the chi^2 in given starting point. Derivatives are not required, they are already included. + val result: OptimizationResult = chi2.minimize(a to 1.5, b to 0.9, c to 1.0) + + val page = Plotly.page { + plot { + scatter { + mode = ScatterMode.markers + x(x) + y(y) + error_y { + array = yErr.toList() + } + name = "data" + } + scatter { + mode = ScatterMode.lines + x(x) + y(x.map { result.point[a]!! * it.pow(2) + result.point[b]!! * it + 1 }) + name = "fit" + } + } + br() + h3{ + +"Fit result: $result" + } + h3{ + +"Chi2/dof = ${result.value / (x.size - 3)}" + } + } + + page.makeFile() +} \ No newline at end of file diff --git a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt similarity index 100% rename from examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt rename to examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt diff --git a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt b/examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt similarity index 83% rename from examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt rename to examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt index 6146e17af..24a4cb1a7 100644 --- a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt @@ -1,14 +1,17 @@ -package kscience.kmath.commons.prob +package kscience.kmath.stat import kotlinx.coroutines.runBlocking import kscience.kmath.chains.Chain import kscience.kmath.chains.collectWithState -import kscience.kmath.stat.Distribution -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.normal +/** + * The state of distribution averager + */ private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) +/** + * Averaging + */ private fun Chain.mean(): Chain = collectWithState(AveragingChainState(), { it.copy() }) { chain -> val next = chain.next() num++ diff --git a/kmath-core/README.md b/kmath-core/README.md index 42a513a10..3a919e85a 100644 --- a/kmath-core/README.md +++ b/kmath-core/README.md @@ -12,7 +12,7 @@ The core features of KMath: > #### Artifact: > -> This module artifact: `kscience.kmath:kmath-core:0.2.0-dev-3`. +> This module artifact: `kscience.kmath:kmath-core:0.2.0-dev-4`. > > Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-core/_latestVersion) > @@ -30,7 +30,7 @@ The core features of KMath: > } > > dependencies { -> implementation 'kscience.kmath:kmath-core:0.2.0-dev-3' +> implementation 'kscience.kmath:kmath-core:0.2.0-dev-4' > } > ``` > **Gradle Kotlin DSL:** @@ -44,6 +44,6 @@ The core features of KMath: > } > > dependencies { -> implementation("kscience.kmath:kmath-core:0.2.0-dev-3") +> implementation("kscience.kmath:kmath-core:0.2.0-dev-4") > } > ``` diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VectorSpace.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VectorSpace.kt index 67056c6b2..2a3b8f5d1 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VectorSpace.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/VectorSpace.kt @@ -15,7 +15,7 @@ public interface VectorSpace> : Space> { public val space: S override val zero: Point get() = produce { space.zero } - public fun produce(initializer: (Int) -> T): Point + public fun produce(initializer: S.(Int) -> T): Point /** * Produce a space-element of this vector space for expressions @@ -48,7 +48,7 @@ public interface VectorSpace> : Space> { public fun > buffered( size: Int, space: S, - bufferFactory: BufferFactory = Buffer.Companion::boxing + bufferFactory: BufferFactory = Buffer.Companion::boxing, ): BufferVectorSpace = BufferVectorSpace(size, space, bufferFactory) /** @@ -63,8 +63,8 @@ public interface VectorSpace> : Space> { public class BufferVectorSpace>( override val size: Int, override val space: S, - public val bufferFactory: BufferFactory + public val bufferFactory: BufferFactory, ) : VectorSpace { - override fun produce(initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) + override fun produce(initializer: S.(Int) -> T): Buffer = bufferFactory(size) { space.initializer(it) } //override fun produceElement(initializer: (Int) -> T): Vector = BufferVector(this, produce(initializer)) } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/annotations.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/annotations.kt new file mode 100644 index 000000000..d70ac7b39 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/annotations.kt @@ -0,0 +1,4 @@ +package kscience.kmath.misc + +@RequiresOptIn("This API is unstable and could change in future", RequiresOptIn.Level.WARNING) +public annotation class UnstableKMathAPI \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt index 5174eb314..bfec6f871 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt @@ -102,6 +102,11 @@ public fun Buffer.asSequence(): Sequence = Sequence(::iterator) */ public fun Buffer.asIterable(): Iterable = Iterable(::iterator) +/** + * Converts this [Buffer] to a new [List] + */ +public fun Buffer.toList(): List = asSequence().toList() + /** * Returns an [IntRange] of the valid indices for this [Buffer]. */ diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt index 8c15e52c7..7ff7b7aae 100644 --- a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt @@ -47,7 +47,7 @@ public fun Iterator.asChain(): Chain = SimpleChain { next() } public fun Sequence.asChain(): Chain = iterator().asChain() /** - * A simple chain of independent tokens + * A simple chain of independent tokens. [fork] returns the same chain. */ public class SimpleChain(private val gen: suspend () -> R) : Chain { public override suspend fun next(): R = gen() diff --git a/kmath-for-real/README.md b/kmath-for-real/README.md new file mode 100644 index 000000000..2ddf78e57 --- /dev/null +++ b/kmath-for-real/README.md @@ -0,0 +1,44 @@ +# Real number specialization module (`kmath-for-real`) + + - [RealVector](src/commonMain/kotlin/kscience/kmath/real/RealVector.kt) : Numpy-like operations for Buffers/Points + - [RealMatrix](src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt) : Numpy-like operations for 2d real structures + - [grids](src/commonMain/kotlin/kscience/kmath/structures/grids.kt) : Uniform grid generators + + +> #### Artifact: +> +> This module artifact: `kscience.kmath:kmath-for-real:0.2.0-dev-4`. +> +> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-for-real/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-for-real/_latestVersion) +> +> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-for-real/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-for-real/_latestVersion) +> +> **Gradle:** +> +> ```gradle +> repositories { +> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } +> maven { url 'https://dl.bintray.com/mipt-npm/kscience' } +> maven { url 'https://dl.bintray.com/mipt-npm/dev' } +> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + +> } +> +> dependencies { +> implementation 'kscience.kmath:kmath-for-real:0.2.0-dev-4' +> } +> ``` +> **Gradle Kotlin DSL:** +> +> ```kotlin +> repositories { +> maven("https://dl.bintray.com/kotlin/kotlin-eap") +> maven("https://dl.bintray.com/mipt-npm/kscience") +> maven("https://dl.bintray.com/mipt-npm/dev") +> maven("https://dl.bintray.com/hotkeytlt/maven") +> } +> +> dependencies { +> implementation("kscience.kmath:kmath-for-real:0.2.0-dev-4") +> } +> ``` diff --git a/kmath-for-real/build.gradle.kts b/kmath-for-real/build.gradle.kts index 2a4539c10..f26f98c2c 100644 --- a/kmath-for-real/build.gradle.kts +++ b/kmath-for-real/build.gradle.kts @@ -7,3 +7,31 @@ kotlin.sourceSets.commonMain { api(project(":kmath-core")) } } + +readme { + description = """ + Extension module that should be used to achieve numpy-like behavior. + All operations are specialized to work with `Double` numbers without declaring algebraic contexts. + One can still use generic algebras though. + """.trimIndent() + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL + propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) + + feature( + id = "RealVector", + description = "Numpy-like operations for Buffers/Points", + ref = "src/commonMain/kotlin/kscience/kmath/real/RealVector.kt" + ) + + feature( + id = "RealMatrix", + description = "Numpy-like operations for 2d real structures", + ref = "src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt" + ) + + feature( + id = "grids", + description = "Uniform grid generators", + ref = "src/commonMain/kotlin/kscience/kmath/structures/grids.kt" + ) +} diff --git a/kmath-for-real/docs/README-TEMPLATE.md b/kmath-for-real/docs/README-TEMPLATE.md new file mode 100644 index 000000000..670844bd0 --- /dev/null +++ b/kmath-for-real/docs/README-TEMPLATE.md @@ -0,0 +1,5 @@ +# Real number specialization module (`kmath-for-real`) + +${features} + +${artifact} diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt index 1860b5870..784b27f9d 100644 --- a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt @@ -3,6 +3,7 @@ package kscience.kmath.real import kscience.kmath.linear.MatrixContext import kscience.kmath.linear.RealMatrixContext.elementContext import kscience.kmath.linear.VirtualMatrix +import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.operations.invoke import kscience.kmath.operations.sum import kscience.kmath.structures.Buffer @@ -36,7 +37,7 @@ public fun Sequence.toMatrix(): RealMatrix = toList().let { MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] } } -public fun Matrix.repeatStackVertical(n: Int): RealMatrix = +public fun RealMatrix.repeatStackVertical(n: Int): RealMatrix = VirtualMatrix(rowNum * n, colNum) { row, col -> get(if (row == 0) 0 else row % rowNum, col) } @@ -45,43 +46,43 @@ public fun Matrix.repeatStackVertical(n: Int): RealMatrix = * Operations for matrix and real number */ -public operator fun Matrix.times(double: Double): RealMatrix = +public operator fun RealMatrix.times(double: Double): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> this[row, col] * double } -public operator fun Matrix.plus(double: Double): RealMatrix = +public operator fun RealMatrix.plus(double: Double): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> this[row, col] + double } -public operator fun Matrix.minus(double: Double): RealMatrix = +public operator fun RealMatrix.minus(double: Double): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> this[row, col] - double } -public operator fun Matrix.div(double: Double): RealMatrix = +public operator fun RealMatrix.div(double: Double): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> this[row, col] / double } -public operator fun Double.times(matrix: Matrix): RealMatrix = +public operator fun Double.times(matrix: RealMatrix): RealMatrix = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> this * matrix[row, col] } -public operator fun Double.plus(matrix: Matrix): RealMatrix = +public operator fun Double.plus(matrix: RealMatrix): RealMatrix = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> this + matrix[row, col] } -public operator fun Double.minus(matrix: Matrix): RealMatrix = +public operator fun Double.minus(matrix: RealMatrix): RealMatrix = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> this - matrix[row, col] } // TODO: does this operation make sense? Should it be 'this/matrix[row, col]'? -//operator fun Double.div(matrix: Matrix) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { +//operator fun Double.div(matrix: RealMatrix) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { // row, col -> matrix[row, col] / this //} @@ -89,11 +90,11 @@ public operator fun Double.minus(matrix: Matrix): RealMatrix = * Per-element (!) square and power operations */ -public fun Matrix.square(): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> +public fun RealMatrix.square(): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> this[row, col].pow(2) } -public fun Matrix.pow(n: Int): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { i, j -> +public fun RealMatrix.pow(n: Int): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { i, j -> this[i, j].pow(n) } @@ -101,20 +102,21 @@ public fun Matrix.pow(n: Int): RealMatrix = MatrixContext.real.produce(r * Operations on two matrices (per-element!) */ -public operator fun Matrix.times(other: Matrix): RealMatrix = +@UnstableKMathAPI +public operator fun RealMatrix.times(other: RealMatrix): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> this[row, col] * other[row, col] } -public operator fun Matrix.plus(other: Matrix): RealMatrix = +public operator fun RealMatrix.plus(other: RealMatrix): RealMatrix = MatrixContext.real.add(this, other) -public operator fun Matrix.minus(other: Matrix): RealMatrix = +public operator fun RealMatrix.minus(other: RealMatrix): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> this[row, col] - other[row, col] } /* * Operations on columns */ -public inline fun Matrix.appendColumn(crossinline mapper: (Buffer) -> Double): Matrix = +public inline fun RealMatrix.appendColumn(crossinline mapper: (Buffer) -> Double): RealMatrix = MatrixContext.real.produce(rowNum, colNum + 1) { row, col -> if (col < colNum) this[row, col] @@ -122,28 +124,28 @@ public inline fun Matrix.appendColumn(crossinline mapper: (Buffer.extractColumns(columnRange: IntRange): RealMatrix = +public fun RealMatrix.extractColumns(columnRange: IntRange): RealMatrix = MatrixContext.real.produce(rowNum, columnRange.count()) { row, col -> this[row, columnRange.first + col] } -public fun Matrix.extractColumn(columnIndex: Int): RealMatrix = +public fun RealMatrix.extractColumn(columnIndex: Int): RealMatrix = extractColumns(columnIndex..columnIndex) -public fun Matrix.sumByColumn(): RealBuffer = RealBuffer(colNum) { j -> +public fun RealMatrix.sumByColumn(): RealBuffer = RealBuffer(colNum) { j -> val column = columns[j] elementContext { sum(column.asIterable()) } } -public fun Matrix.minByColumn(): RealBuffer = RealBuffer(colNum) { j -> +public fun RealMatrix.minByColumn(): RealBuffer = RealBuffer(colNum) { j -> columns[j].asIterable().minOrNull() ?: error("Cannot produce min on empty column") } -public fun Matrix.maxByColumn(): RealBuffer = RealBuffer(colNum) { j -> +public fun RealMatrix.maxByColumn(): RealBuffer = RealBuffer(colNum) { j -> columns[j].asIterable().maxOrNull() ?: error("Cannot produce min on empty column") } -public fun Matrix.averageByColumn(): RealBuffer = RealBuffer(colNum) { j -> +public fun RealMatrix.averageByColumn(): RealBuffer = RealBuffer(colNum) { j -> columns[j].asIterable().average() } @@ -151,7 +153,7 @@ public fun Matrix.averageByColumn(): RealBuffer = RealBuffer(colNum) { j * Operations processing all elements */ -public fun Matrix.sum(): Double = elements().map { (_, value) -> value }.sum() -public fun Matrix.min(): Double? = elements().map { (_, value) -> value }.minOrNull() -public fun Matrix.max(): Double? = elements().map { (_, value) -> value }.maxOrNull() -public fun Matrix.average(): Double = elements().map { (_, value) -> value }.average() +public fun RealMatrix.sum(): Double = elements().map { (_, value) -> value }.sum() +public fun RealMatrix.min(): Double? = elements().map { (_, value) -> value }.minOrNull() +public fun RealMatrix.max(): Double? = elements().map { (_, value) -> value }.maxOrNull() +public fun RealMatrix.average(): Double = elements().map { (_, value) -> value }.average() diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt index 1ee33ee32..33684fc8c 100644 --- a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt @@ -1,47 +1,63 @@ package kscience.kmath.real -import kscience.kmath.linear.BufferVectorSpace import kscience.kmath.linear.Point -import kscience.kmath.linear.VectorSpace import kscience.kmath.operations.Norm -import kscience.kmath.operations.RealField -import kscience.kmath.operations.SpaceElement import kscience.kmath.structures.Buffer -import kscience.kmath.structures.RealBuffer -import kscience.kmath.structures.asBuffer import kscience.kmath.structures.asIterable +import kotlin.math.pow import kotlin.math.sqrt -public typealias RealPoint = Point - -public fun RealPoint.asVector(): RealVector = RealVector(this) -public fun DoubleArray.asVector(): RealVector = asBuffer().asVector() -public fun List.asVector(): RealVector = asBuffer().asVector() +public typealias RealVector = Point public object VectorL2Norm : Norm, Double> { override fun norm(arg: Point): Double = sqrt(arg.asIterable().sumByDouble(Number::toDouble)) } -public inline class RealVector(private val point: Point) : - SpaceElement>, RealPoint { - public override val size: Int get() = point.size - public override val context: VectorSpace get() = space(point.size) +/** + * Fill the vector of given [size] with given [value] + */ +public fun Buffer.Companion.same(size: Int, value: Number): RealVector = real(size) { value.toDouble() } - public override fun unwrap(): RealPoint = point - public override fun RealPoint.wrap(): RealVector = RealVector(this) - public override operator fun get(index: Int): Double = point[index] - public override operator fun iterator(): Iterator = point.iterator() +// Transformation methods - public companion object { - private val spaceCache: MutableMap> = hashMapOf() +public inline fun RealVector.map(transform: (Double) -> Double): RealVector = + Buffer.real(size) { transform(get(it)) } - public inline operator fun invoke(dim: Int, initializer: (Int) -> Double): RealVector = - RealVector(RealBuffer(dim, initializer)) +public inline fun RealVector.mapIndexed(transform: (index: Int, value: Double) -> Double): RealVector = + Buffer.real(size) { transform(it, get(it)) } - public operator fun invoke(vararg values: Double): RealVector = values.asVector() +public fun RealVector.pow(p: Double): RealVector = map { it.pow(p) } - public fun space(dim: Int): BufferVectorSpace = spaceCache.getOrPut(dim) { - BufferVectorSpace(dim, RealField) { size, init -> Buffer.real(size, init) } - } - } -} +public fun RealVector.pow(p: Int): RealVector = map { it.pow(p) } + +public fun exp(vector: RealVector): RealVector = vector.map { kotlin.math.exp(it) } + +public operator fun RealVector.plus(other: RealVector): RealVector = + mapIndexed { index, value -> value + other[index] } + +public operator fun RealVector.plus(number: Number): RealVector = map { it + number.toDouble() } + +public operator fun Number.plus(vector: RealVector): RealVector = vector + this + +public operator fun RealVector.unaryMinus(): Buffer = map { -it } + +public operator fun RealVector.minus(other: RealVector): RealVector = + mapIndexed { index, value -> value - other[index] } + +public operator fun RealVector.minus(number: Number): RealVector = map { it - number.toDouble() } + +public operator fun Number.minus(vector: RealVector): RealVector = vector.map { toDouble() - it } + +public operator fun RealVector.times(other: RealVector): RealVector = + mapIndexed { index, value -> value * other[index] } + +public operator fun RealVector.times(number: Number): RealVector = map { it * number.toDouble() } + +public operator fun Number.times(vector: RealVector): RealVector = vector * this + +public operator fun RealVector.div(other: RealVector): RealVector = + mapIndexed { index, value -> value / other[index] } + +public operator fun RealVector.div(number: Number): RealVector = map { it / number.toDouble() } + +public operator fun Number.div(vector: RealVector): RealVector = vector.map { toDouble() / it } diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/grids.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/grids.kt index bd0e092e0..69a149fb8 100644 --- a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/grids.kt +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/grids.kt @@ -1,6 +1,5 @@ package kscience.kmath.real -import kscience.kmath.linear.Point import kscience.kmath.structures.asBuffer import kotlin.math.abs @@ -34,7 +33,7 @@ public fun ClosedFloatingPointRange.toSequenceWithStep(step: Double): Se } } -public infix fun ClosedFloatingPointRange.step(step: Double): Point = +public infix fun ClosedFloatingPointRange.step(step: Double): RealVector = toSequenceWithStep(step).toList().asBuffer() /** diff --git a/kmath-nd4j/README.md b/kmath-nd4j/README.md index b071df499..176dfc09d 100644 --- a/kmath-nd4j/README.md +++ b/kmath-nd4j/README.md @@ -2,14 +2,14 @@ This subproject implements the following features: - - [nd4jarraystrucure](src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : NDStructure wrapper for INDArray + - [nd4jarraystructure](src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : NDStructure wrapper for INDArray - [nd4jarrayrings](src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Rings over Nd4jArrayStructure of Int and Long - [nd4jarrayfields](src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt) : Fields over Nd4jArrayStructure of Float and Double > #### Artifact: > -> This module artifact: `kscience.kmath:kmath-nd4j:0.2.0-dev-3`. +> This module artifact: `kscience.kmath:kmath-nd4j:0.2.0-dev-4`. > > Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-nd4j/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-nd4j/_latestVersion) > @@ -27,7 +27,7 @@ This subproject implements the following features: > } > > dependencies { -> implementation 'kscience.kmath:kmath-nd4j:0.2.0-dev-3' +> implementation 'kscience.kmath:kmath-nd4j:0.2.0-dev-4' > } > ``` > **Gradle Kotlin DSL:** @@ -41,7 +41,7 @@ This subproject implements the following features: > } > > dependencies { -> implementation("kscience.kmath:kmath-nd4j:0.2.0-dev-3") +> implementation("kscience.kmath:kmath-nd4j:0.2.0-dev-4") > } > ``` diff --git a/kmath-stat/src/jvmMain/kotlin/kscience/kmath/stat/distributions.kt b/kmath-stat/src/jvmMain/kotlin/kscience/kmath/stat/distributions.kt index 9a77b0bd2..6cc18a37c 100644 --- a/kmath-stat/src/jvmMain/kotlin/kscience/kmath/stat/distributions.kt +++ b/kmath-stat/src/jvmMain/kotlin/kscience/kmath/stat/distributions.kt @@ -51,7 +51,7 @@ private fun normalSampler(method: NormalSamplerMethod, provider: UniformRandomPr public fun Distribution.Companion.normal( method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat -): Distribution = object : ContinuousSamplerDistribution() { +): ContinuousSamplerDistribution = object : ContinuousSamplerDistribution() { override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { val provider = generator.asUniformRandomProvider() return normalSampler(method, provider) @@ -60,6 +60,9 @@ public fun Distribution.Companion.normal( override fun probability(arg: Double): Double = exp(-arg.pow(2) / 2) / sqrt(PI * 2) } +/** + * A univariate normal distribution with given [mean] and [sigma]. [method] defines commons-rng generation method + */ public fun Distribution.Companion.normal( mean: Double, sigma: Double,