Extended documentation, code refactoring, API consistency changes #125
124
doc/algebra.md
124
doc/algebra.md
@ -1,110 +1,124 @@
|
|||||||
# Algebra and algebra elements
|
# Algebraic Structures and Algebraic Elements
|
||||||
|
|
||||||
The mathematical operations in `kmath` are generally separated from mathematical objects.
|
The mathematical operations in KMath are generally separated from mathematical objects. This means that to perform an
|
||||||
This means that in order to perform an operation, say `+`, one needs two objects of a type `T` and
|
operation, say `+`, one needs two objects of a type `T` and an algebra context, which draws appropriate operation up,
|
||||||
and algebra context which defines appropriate operation, say `Space<T>`. Next one needs to run actual operation
|
say `Space<T>`. Next one needs to run the actual operation in the context:
|
||||||
in the context:
|
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val a: T
|
import scientifik.kmath.operations.*
|
||||||
val b: T
|
|
||||||
val space: Space<T>
|
|
||||||
|
|
||||||
val c = space.run{a + b}
|
val a: T = ...
|
||||||
|
val b: T = ...
|
||||||
|
val space: Space<T> = ...
|
||||||
|
|
||||||
|
val c = space { a + b }
|
||||||
```
|
```
|
||||||
|
|
||||||
From the first glance, this distinction seems to be a needless complication, but in fact one needs
|
At first glance, this distinction seems to be a needless complication, but in fact one needs to remember that in
|
||||||
to remember that in mathematics, one could define different operations on the same objects. For example,
|
mathematics, one could draw up different operations on same objects. For example, one could use different types of
|
||||||
one could use different types of geometry for vectors.
|
geometry for vectors.
|
||||||
|
|
||||||
## Algebra hierarchy
|
## Algebraic Structures
|
||||||
|
|
||||||
Mathematical contexts have the following hierarchy:
|
Mathematical contexts have the following hierarchy:
|
||||||
|
|
||||||
**Space** <- **Ring** <- **Field**
|
**Algebra** ← **Space** ← **Ring** ← **Field**
|
||||||
|
|
||||||
All classes follow abstract mathematical constructs.
|
These interfaces follow real algebraic structures:
|
||||||
[Space](http://mathworld.wolfram.com/Space.html) defines `zero` element, addition operation and multiplication by constant,
|
|
||||||
[Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and unit `one` element,
|
|
||||||
[Field](http://mathworld.wolfram.com/Field.html) adds division operation.
|
|
||||||
|
|
||||||
Typical case of `Field` is the `RealField` which works on doubles. And typical case of `Space` is a `VectorSpace`.
|
- [Space](https://mathworld.wolfram.com/VectorSpace.html) defines addition, its neutral element (i.e. 0) and scalar
|
||||||
|
multiplication;
|
||||||
|
- [Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and its neutral element (i.e. 1);
|
||||||
|
- [Field](http://mathworld.wolfram.com/Field.html) adds division operation.
|
||||||
|
|
||||||
In some cases algebra context could hold additional operation like `exp` or `sin`, in this case it inherits appropriate
|
A typical implementation of `Field<T>` is the `RealField` which works on doubles, and `VectorSpace` for `Space<T>`.
|
||||||
interface. Also a context could have an operation which produces an element outside of its context. For example
|
|
||||||
`Matrix` `dot` operation produces a matrix with new dimensions which can be incompatible with initial matrix in
|
|
||||||
terms of linear operations.
|
|
||||||
|
|
||||||
## Algebra element
|
In some cases algebra context can hold additional operations like `exp` or `sin`, and then it inherits appropriate
|
||||||
|
interface. Also, contexts may have operations, which produce elements outside of the context. For example, `Matrix.dot`
|
||||||
|
operation produces a matrix with new dimensions, which can be incompatible with initial matrix in terms of linear
|
||||||
|
operations.
|
||||||
|
|
||||||
In order to achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving contexts
|
## Algebraic Element
|
||||||
`kmath` introduces special type objects called `MathElement`. A `MathElement` is basically some object coupled to
|
|
||||||
|
To achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving
|
||||||
|
contexts KMath submits special type objects called `MathElement`. A `MathElement` is basically some object coupled to
|
||||||
a mathematical context. For example `Complex` is the pair of real numbers representing real and imaginary parts,
|
a mathematical context. For example `Complex` is the pair of real numbers representing real and imaginary parts,
|
||||||
but it also holds reference to the `ComplexField` singleton which allows to perform direct operations on `Complex`
|
but it also holds reference to the `ComplexField` singleton, which allows performing direct operations on `Complex`
|
||||||
numbers without explicit involving the context like:
|
numbers without explicit involving the context like:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val c1 = Complex(1.0, 1.0)
|
import scientifik.kmath.operations.*
|
||||||
val c2 = Complex(1.0, -1.0)
|
|
||||||
val c3 = c1 + c2 + 3.0.toComplex()
|
// Using elements
|
||||||
//or with field notation:
|
val c1 = Complex(1.0, 1.0)
|
||||||
val c4 = ComplexField.run{c1 + i - 2.0}
|
val c2 = Complex(1.0, -1.0)
|
||||||
|
val c3 = c1 + c2 + 3.0.toComplex()
|
||||||
|
|
||||||
|
// Using context
|
||||||
|
val c4 = ComplexField { c1 + i - 2.0 }
|
||||||
```
|
```
|
||||||
|
|
||||||
Both notations have their pros and cons.
|
Both notations have their pros and cons.
|
||||||
|
|
||||||
The hierarchy for algebra elements follows the hierarchy for the corresponding algebra.
|
The hierarchy for algebraic elements follows the hierarchy for the corresponding algebraic structures.
|
||||||
|
|
||||||
**MathElement** <- **SpaceElement** <- **RingElement** <- **FieldElement**
|
**MathElement** ← **SpaceElement** ← **RingElement** ← **FieldElement**
|
||||||
|
|
||||||
**MathElement** is the generic common ancestor of the class with context.
|
`MathElement<C>` is the generic common ancestor of the class with context.
|
||||||
|
|
||||||
One important distinction between algebra elements and algebra contexts is that algebra element has three type parameters:
|
One major distinction between algebraic elements and algebraic contexts is that elements have three type
|
||||||
|
parameters:
|
||||||
|
|
||||||
1. The type of elements, field operates on.
|
1. The type of elements, the field operates on.
|
||||||
2. The self-type of the element returned from operation (must be algebra element).
|
2. The self-type of the element returned from operation (which has to be an algebraic element).
|
||||||
3. The type of the algebra over first type-parameter.
|
3. The type of the algebra over first type-parameter.
|
||||||
|
|
||||||
The middle type is needed in case algebra members do not store context. For example, it is not possible to add
|
The middle type is needed for of algebra members do not store context. For example, it is impossible to add a context
|
||||||
a context to regular `Double`. The element performs automatic conversions from context types and back.
|
to regular `Double`. The element performs automatic conversions from context types and back. One should use context
|
||||||
One should used context operations in all important places. The performance of element operations is not guaranteed.
|
operations in all performance-critical places. The performance of element operations is not guaranteed.
|
||||||
|
|
||||||
## Spaces and fields
|
## Spaces and Fields
|
||||||
|
|
||||||
An obvious first choice of mathematical objects to implement in a context-oriented style are algebraic elements like spaces,
|
KMath submits both contexts and elements for builtin algebraic structures:
|
||||||
rings and fields. Those are located in the `scientifik.kmath.operations.Algebra.kt` file. Alongside common contexts, the file includes definitions for algebra elements like `FieldElement`. A `FieldElement` object
|
|
||||||
stores a reference to the `Field` which contains additive and multiplicative operations, meaning
|
|
||||||
it has one fixed context attached and does not require explicit external context. So those `MathElements` can be operated without context:
|
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
|
import scientifik.kmath.operations.*
|
||||||
|
|
||||||
val c1 = Complex(1.0, 2.0)
|
val c1 = Complex(1.0, 2.0)
|
||||||
val c2 = ComplexField.i
|
val c2 = ComplexField.i
|
||||||
|
|
||||||
val c3 = c1 + c2
|
val c3 = c1 + c2
|
||||||
|
// or
|
||||||
|
val c3 = ComplexField { c1 + c2 }
|
||||||
```
|
```
|
||||||
|
|
||||||
`ComplexField` also features special operations to mix complex and real numbers, for example:
|
Also, `ComplexField` features special operations to mix complex and real numbers, for example:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
|
import scientifik.kmath.operations.*
|
||||||
|
|
||||||
val c1 = Complex(1.0, 2.0)
|
val c1 = Complex(1.0, 2.0)
|
||||||
val c2 = ComplexField.run{ c1 - 1.0} // Returns: [re:0.0, im: 2.0]
|
val c2 = ComplexField { c1 - 1.0 } // Returns: Complex(re=0.0, im=2.0)
|
||||||
val c3 = ComplexField.run{ c1 - i*2.0}
|
val c3 = ComplexField { c1 - i * 2.0 }
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note**: In theory it is possible to add behaviors directly to the context, but currently kotlin syntax does not support
|
**Note**: In theory it is possible to add behaviors directly to the context, but as for now Kotlin does not support
|
||||||
that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and [KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates.
|
that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and
|
||||||
|
[KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates.
|
||||||
|
|
||||||
## Nested fields
|
## Nested fields
|
||||||
|
|
||||||
Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex elements like so:
|
Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex
|
||||||
|
elements like so:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val element = NDElement.complex(shape = intArrayOf(2,2)){ index: IntArray ->
|
val element = NDElement.complex(shape = intArrayOf(2, 2)) { index: IntArray ->
|
||||||
Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble())
|
Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `element` in this example is a member of the `Field` of 2-d structures, each element of which is a member of its own
|
The `element` in this example is a member of the `Field` of 2D structures, each element of which is a member of its own
|
||||||
`ComplexField`. The important thing is one does not need to create a special n-d class to hold complex
|
`ComplexField`. It is important one does not need to create a special n-d class to hold complex
|
||||||
numbers and implement operations on it, one just needs to provide a field for its elements.
|
numbers and implement operations on it, one just needs to provide a field for its elements.
|
||||||
|
|
||||||
**Note**: Fields themselves do not solve the problem of JVM boxing, but it is possible to solve with special contexts like
|
**Note**: Fields themselves do not solve the problem of JVM boxing, but it is possible to solve with special contexts like
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# Buffers
|
# Buffers
|
||||||
|
|
||||||
Buffer is one of main building blocks of kmath. It is a basic interface allowing random-access read and write (with `MutableBuffer`).
|
Buffer is one of main building blocks of kmath. It is a basic interface allowing random-access read and write (with `MutableBuffer`).
|
||||||
There are different types of buffers:
|
There are different types of buffers:
|
||||||
|
|
||||||
@ -12,4 +13,5 @@ Some kmath features require a `BufferFactory` class to operate properly. A gener
|
|||||||
buffer for given reified type (for types with custom memory buffer it still better to use their own `MemoryBuffer.create()` factory).
|
buffer for given reified type (for types with custom memory buffer it still better to use their own `MemoryBuffer.create()` factory).
|
||||||
|
|
||||||
## Buffer performance
|
## Buffer performance
|
||||||
|
|
||||||
One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead
|
One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead
|
@ -1,22 +1,34 @@
|
|||||||
# Local coding conventions
|
# Coding Conventions
|
||||||
|
|
||||||
Kmath and other `scientifik` projects use general [kotlin code conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but with a number of small changes and clarifications.
|
KMath code follows general [Kotlin conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but
|
||||||
|
with a number of small changes and clarifications.
|
||||||
|
|
||||||
## Utility class names
|
## Utility Class Naming
|
||||||
File name should coincide with a name of one of the classes contained in the file or start with small letter and describe its contents.
|
|
||||||
|
|
||||||
The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that file names should start with capital letter even if file does not contain classes. Yet starting utility classes and aggregators with a small letter seems to be a good way to clearly visually separate those files.
|
Filename should coincide with a name of one of the classes contained in the file or start with small letter and
|
||||||
|
describe its contents.
|
||||||
|
|
||||||
|
The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that
|
||||||
|
file names should start with a capital letter even if file does not contain classes. Yet starting utility classes and
|
||||||
|
aggregators with a small letter seems to be a good way to visually separate those files.
|
||||||
|
|
||||||
This convention could be changed in future in a non-breaking way.
|
This convention could be changed in future in a non-breaking way.
|
||||||
|
|
||||||
## Private variable names
|
## Private Variable Naming
|
||||||
Private variable names could start with underscore `_` in case the private mutable variable is shadowed by the public read-only value with the same meaning.
|
|
||||||
|
|
||||||
Code convention do not permit underscores in names, but is is sometimes useful to "underscore" the fact that public and private versions define the same entity. It is allowed only for private variables.
|
Private variables' names may start with underscore `_` for of the private mutable variable is shadowed by the public
|
||||||
|
read-only value with the same meaning.
|
||||||
|
|
||||||
|
This rule does not permit underscores in names, but it is sometimes useful to "underscore" the fact that public and
|
||||||
|
private versions draw up the same entity. It is allowed only for private variables.
|
||||||
|
|
||||||
This convention could be changed in future in a non-breaking way.
|
This convention could be changed in future in a non-breaking way.
|
||||||
|
|
||||||
## Functions and properties one-liners
|
## Functions and Properties One-liners
|
||||||
Use one-liners when they occupy single code window line both for functions and properties with getters like `val b: String get() = "fff"`. The same should be done with multiline expressions when they could be cleanly separated.
|
|
||||||
|
|
||||||
There is not general consensus whenever use `fun a() = {}` or `fun a(){return}`. Yet from reader perspective one-lines seem to better show that the property or function is easily calculated.
|
Use one-liners when they occupy single code window line both for functions and properties with getters like
|
||||||
|
`val b: String get() = "fff"`. The same should be performed with multiline expressions when they could be
|
||||||
|
cleanly separated.
|
||||||
|
|
||||||
|
There is no universal consensus whenever use `fun a() = ...` or `fun a() { return ... }`. Yet from reader outlook
|
||||||
|
one-lines seem to better show that the property or function is easily calculated.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
## Basic linear algebra layout
|
## Basic linear algebra layout
|
||||||
|
|
||||||
Kmath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared
|
KMath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared
|
||||||
in context classes, and are not the members of classes that store data. This allows more flexible approach to maintain multiple
|
in context classes, and are not the members of classes that store data. This allows more flexible approach to maintain multiple
|
||||||
back-ends. The new operations added as extensions to contexts instead of being member functions of data structures.
|
back-ends. The new operations added as extensions to contexts instead of being member functions of data structures.
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Nd-structure generation and operations
|
# ND-structure generation and operations
|
||||||
|
|
||||||
**TODO**
|
**TODO**
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Abstract syntax tree expression representation and operations (`kmath-ast`)
|
# Abstract Syntax Tree Expression Representation and Operations (`kmath-ast`)
|
||||||
|
|
||||||
This subproject implements the following features:
|
This subproject implements the following features:
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ This subproject implements the following features:
|
|||||||
> ```
|
> ```
|
||||||
>
|
>
|
||||||
|
|
||||||
## Dynamic expression code generation with ObjectWeb ASM
|
## Dynamic Expression Code Generation with ObjectWeb ASM
|
||||||
|
|
||||||
`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds
|
`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds
|
||||||
a special implementation of `Expression<T>` with implemented `invoke` function.
|
a special implementation of `Expression<T>` with implemented `invoke` function.
|
||||||
@ -46,7 +46,7 @@ a special implementation of `Expression<T>` with implemented `invoke` function.
|
|||||||
For example, the following builder:
|
For example, the following builder:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
RealField.mstInField { symbol("x") + 2 }.compile()
|
RealField.mstInField { symbol("x") + 2 }.compile()
|
||||||
```
|
```
|
||||||
|
|
||||||
… leads to generation of bytecode, which can be decompiled to the following Java class:
|
… leads to generation of bytecode, which can be decompiled to the following Java class:
|
||||||
@ -75,7 +75,7 @@ public final class AsmCompiledExpression_1073786867_0 implements Expression<Doub
|
|||||||
|
|
||||||
### Example Usage
|
### Example Usage
|
||||||
|
|
||||||
This API is an extension to MST and MstExpression, so you may optimize as both of them:
|
This API extends MST and MstExpression, so you may optimize as both of them:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
RealField.mstInField { symbol("x") + 2 }.compile()
|
RealField.mstInField { symbol("x") + 2 }.compile()
|
||||||
|
40
kmath-core/README.md
Normal file
40
kmath-core/README.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# The Core Module (`kmath-ast`)
|
||||||
|
|
||||||
|
The core features of KMath:
|
||||||
|
|
||||||
|
- Algebraic structures: contexts and elements.
|
||||||
|
- ND structures.
|
||||||
|
- Buffers.
|
||||||
|
- Functional Expressions.
|
||||||
|
- Domains.
|
||||||
|
- Automatic differentiation.
|
||||||
|
|
||||||
|
> #### Artifact:
|
||||||
|
> This module is distributed in the artifact `scientifik:kmath-core:0.1.4-dev-8`.
|
||||||
|
>
|
||||||
|
> **Gradle:**
|
||||||
|
>
|
||||||
|
> ```gradle
|
||||||
|
> repositories {
|
||||||
|
> maven { url 'https://dl.bintray.com/mipt-npm/scientifik' }
|
||||||
|
> maven { url 'https://dl.bintray.com/mipt-npm/dev' }
|
||||||
|
> maven { url https://dl.bintray.com/hotkeytlt/maven' }
|
||||||
|
> }
|
||||||
|
>
|
||||||
|
> dependencies {
|
||||||
|
> implementation 'scientifik:kmath-core:0.1.4-dev-8'
|
||||||
|
> }
|
||||||
|
> ```
|
||||||
|
> **Gradle Kotlin DSL:**
|
||||||
|
>
|
||||||
|
> ```kotlin
|
||||||
|
> repositories {
|
||||||
|
> maven("https://dl.bintray.com/mipt-npm/scientifik")
|
||||||
|
> maven("https://dl.bintray.com/mipt-npm/dev")
|
||||||
|
> maven("https://dl.bintray.com/hotkeytlt/maven")
|
||||||
|
> }
|
||||||
|
>
|
||||||
|
> dependencies {``
|
||||||
|
> implementation("scientifik:kmath-core:0.1.4-dev-8")
|
||||||
|
> }
|
||||||
|
> ```
|
@ -1,11 +1,7 @@
|
|||||||
plugins {
|
plugins { id("scientifik.mpp") }
|
||||||
id("scientifik.mpp")
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin.sourceSets {
|
kotlin.sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies { api(project(":kmath-memory")) }
|
||||||
api(project(":kmath-memory"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,8 @@ interface NumericAlgebra<T> : Algebra<T> {
|
|||||||
inline operator fun <A : Algebra<*>, R> A.invoke(block: A.() -> R): R = run(block)
|
inline operator fun <A : Algebra<*>, R> A.invoke(block: A.() -> R): R = run(block)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents semigroup, i.e. algebraic structure with associative binary operation called "addition".
|
* Represents semispace, i.e. algebraic structure with associative binary operation called "addition" as well as
|
||||||
|
* multiplication by scalars.
|
||||||
*
|
*
|
||||||
* In KMath groups are called spaces, and also define multiplication of element by [Number].
|
* In KMath groups are called spaces, and also define multiplication of element by [Number].
|
||||||
*
|
*
|
||||||
@ -174,10 +175,8 @@ interface SpaceOperations<T> : Algebra<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents group, i.e. algebraic structure with associative binary operation called "addition" and its neutral
|
* Represents linear space, i.e. algebraic structure with associative binary operation called "addition" and its neutral
|
||||||
* element.
|
* element as well as multiplication by scalars.
|
||||||
*
|
|
||||||
* In KMath groups are called spaces, and also define multiplication of element by [Number].
|
|
||||||
*
|
*
|
||||||
* @param T the type of element of this group.
|
* @param T the type of element of this group.
|
||||||
*/
|
*/
|
||||||
|
@ -22,12 +22,11 @@ import kotlinx.coroutines.flow.FlowCollector
|
|||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A not-necessary-Markov chain of some type
|
* A not-necessary-Markov chain of some type
|
||||||
* @param R - the chain element type
|
* @param R - the chain element type
|
||||||
*/
|
*/
|
||||||
interface Chain<out R>: Flow<R> {
|
interface Chain<out R> : Flow<R> {
|
||||||
/**
|
/**
|
||||||
* Generate next value, changing state if needed
|
* Generate next value, changing state if needed
|
||||||
*/
|
*/
|
||||||
@ -41,7 +40,7 @@ interface Chain<out R>: Flow<R> {
|
|||||||
@OptIn(InternalCoroutinesApi::class)
|
@OptIn(InternalCoroutinesApi::class)
|
||||||
override suspend fun collect(collector: FlowCollector<R>) {
|
override suspend fun collect(collector: FlowCollector<R>) {
|
||||||
kotlinx.coroutines.flow.flow {
|
kotlinx.coroutines.flow.flow {
|
||||||
while (true){
|
while (true) {
|
||||||
emit(next())
|
emit(next())
|
||||||
}
|
}
|
||||||
}.collect(collector)
|
}.collect(collector)
|
||||||
@ -71,7 +70,7 @@ class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val ge
|
|||||||
|
|
||||||
private var value: R? = null
|
private var value: R? = null
|
||||||
|
|
||||||
fun value() = value
|
fun value(): R? = value
|
||||||
|
|
||||||
override suspend fun next(): R {
|
override suspend fun next(): R {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
@ -97,12 +96,11 @@ class StatefulChain<S, out R>(
|
|||||||
private val forkState: ((S) -> S),
|
private val forkState: ((S) -> S),
|
||||||
private val gen: suspend S.(R) -> R
|
private val gen: suspend S.(R) -> R
|
||||||
) : Chain<R> {
|
) : Chain<R> {
|
||||||
|
private val mutex: Mutex = Mutex()
|
||||||
private val mutex = Mutex()
|
|
||||||
|
|
||||||
private var value: R? = null
|
private var value: R? = null
|
||||||
|
|
||||||
fun value() = value
|
fun value(): R? = value
|
||||||
|
|
||||||
override suspend fun next(): R {
|
override suspend fun next(): R {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
@ -112,9 +110,7 @@ class StatefulChain<S, out R>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fork(): Chain<R> {
|
override fun fork(): Chain<R> = StatefulChain(forkState(state), seed, forkState, gen)
|
||||||
return StatefulChain(forkState(state), seed, forkState, gen)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,7 +159,8 @@ fun <T, R> Chain<T>.collect(mapper: suspend (Chain<T>) -> R): Chain<R> = object
|
|||||||
fun <T, S, R> Chain<T>.collectWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain<T>) -> R): Chain<R> =
|
fun <T, S, R> Chain<T>.collectWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain<T>) -> R): Chain<R> =
|
||||||
object : Chain<R> {
|
object : Chain<R> {
|
||||||
override suspend fun next(): R = state.mapper(this@collectWithState)
|
override suspend fun next(): R = state.mapper(this@collectWithState)
|
||||||
override fun fork(): Chain<R> = this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper)
|
override fun fork(): Chain<R> =
|
||||||
|
this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,7 +4,8 @@ import kotlinx.coroutines.*
|
|||||||
import kotlinx.coroutines.channels.produce
|
import kotlinx.coroutines.channels.produce
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
|
||||||
val Dispatchers.Math: CoroutineDispatcher get() = Dispatchers.Default
|
val Dispatchers.Math: CoroutineDispatcher
|
||||||
|
get() = Default
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An imitator of [Deferred] which holds a suspended function block and dispatcher
|
* An imitator of [Deferred] which holds a suspended function block and dispatcher
|
||||||
@ -42,7 +43,7 @@ fun <T, R> Flow<T>.async(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@FlowPreview
|
@FlowPreview
|
||||||
fun <T, R> AsyncFlow<T>.map(action: (T) -> R) =
|
fun <T, R> AsyncFlow<T>.map(action: (T) -> R): AsyncFlow<R> =
|
||||||
AsyncFlow(deferredFlow.map { input ->
|
AsyncFlow(deferredFlow.map { input ->
|
||||||
//TODO add function composition
|
//TODO add function composition
|
||||||
LazyDeferred(input.dispatcher) {
|
LazyDeferred(input.dispatcher) {
|
||||||
@ -82,9 +83,9 @@ suspend fun <T> AsyncFlow<T>.collect(concurrency: Int, collector: FlowCollector<
|
|||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
@FlowPreview
|
@FlowPreview
|
||||||
suspend fun <T> AsyncFlow<T>.collect(concurrency: Int, action: suspend (value: T) -> Unit): Unit {
|
suspend fun <T> AsyncFlow<T>.collect(concurrency: Int, action: suspend (value: T) -> Unit) {
|
||||||
collect(concurrency, object : FlowCollector<T> {
|
collect(concurrency, object : FlowCollector<T> {
|
||||||
override suspend fun emit(value: T) = action(value)
|
override suspend fun emit(value: T): Unit = action(value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,9 +95,7 @@ fun <T, R> Flow<T>.mapParallel(
|
|||||||
dispatcher: CoroutineDispatcher = Dispatchers.Default,
|
dispatcher: CoroutineDispatcher = Dispatchers.Default,
|
||||||
transform: suspend (T) -> R
|
transform: suspend (T) -> R
|
||||||
): Flow<R> {
|
): Flow<R> {
|
||||||
return flatMapMerge{ value ->
|
return flatMapMerge { value ->
|
||||||
flow { emit(transform(value)) }
|
flow { emit(transform(value)) }
|
||||||
}.flowOn(dispatcher)
|
}.flowOn(dispatcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import scientifik.kmath.structures.asBuffer
|
|||||||
/**
|
/**
|
||||||
* Create a [Flow] from buffer
|
* Create a [Flow] from buffer
|
||||||
*/
|
*/
|
||||||
fun <T> Buffer<T>.asFlow() = iterator().asFlow()
|
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
|
||||||
|
@ -5,7 +5,6 @@ import kotlinx.coroutines.sync.withLock
|
|||||||
import scientifik.kmath.structures.Buffer
|
import scientifik.kmath.structures.Buffer
|
||||||
import scientifik.kmath.structures.MutableBuffer
|
import scientifik.kmath.structures.MutableBuffer
|
||||||
import scientifik.kmath.structures.VirtualBuffer
|
import scientifik.kmath.structures.VirtualBuffer
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thread-safe ring buffer
|
* Thread-safe ring buffer
|
||||||
@ -16,8 +15,7 @@ class RingBuffer<T>(
|
|||||||
private var startIndex: Int = 0,
|
private var startIndex: Int = 0,
|
||||||
size: Int = 0
|
size: Int = 0
|
||||||
) : Buffer<T> {
|
) : Buffer<T> {
|
||||||
|
private val mutex: Mutex = Mutex()
|
||||||
private val mutex = Mutex()
|
|
||||||
|
|
||||||
override var size: Int = size
|
override var size: Int = size
|
||||||
private set
|
private set
|
||||||
@ -28,7 +26,7 @@ class RingBuffer<T>(
|
|||||||
return buffer[startIndex.forward(index)] as T
|
return buffer[startIndex.forward(index)] as T
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isFull() = size == buffer.size
|
fun isFull(): Boolean = size == buffer.size
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterator could provide wrong results if buffer is changed in initialization (iteration is safe)
|
* Iterator could provide wrong results if buffer is changed in initialization (iteration is safe)
|
||||||
|
@ -6,7 +6,7 @@ import kotlin.sequences.Sequence
|
|||||||
/**
|
/**
|
||||||
* Represent a chain as regular iterator (uses blocking calls)
|
* Represent a chain as regular iterator (uses blocking calls)
|
||||||
*/
|
*/
|
||||||
operator fun <R> Chain<R>.iterator() = object : Iterator<R> {
|
operator fun <R> Chain<R>.iterator(): Iterator<R> = object : Iterator<R> {
|
||||||
override fun hasNext(): Boolean = true
|
override fun hasNext(): Boolean = true
|
||||||
|
|
||||||
override fun next(): R = runBlocking { next() }
|
override fun next(): R = runBlocking { next() }
|
||||||
|
@ -8,10 +8,9 @@ class LazyNDStructure<T>(
|
|||||||
override val shape: IntArray,
|
override val shape: IntArray,
|
||||||
val function: suspend (IntArray) -> T
|
val function: suspend (IntArray) -> T
|
||||||
) : NDStructure<T> {
|
) : NDStructure<T> {
|
||||||
|
private val cache: MutableMap<IntArray, Deferred<T>> = hashMapOf()
|
||||||
|
|
||||||
private val cache = HashMap<IntArray, Deferred<T>>()
|
fun deferred(index: IntArray): Deferred<T> = cache.getOrPut(index) {
|
||||||
|
|
||||||
fun deferred(index: IntArray) = cache.getOrPut(index) {
|
|
||||||
scope.async(context = Dispatchers.Math) {
|
scope.async(context = Dispatchers.Math) {
|
||||||
function(index)
|
function(index)
|
||||||
}
|
}
|
||||||
@ -42,21 +41,21 @@ class LazyNDStructure<T>(
|
|||||||
result = 31 * result + cache.hashCode()
|
result = 31 * result + cache.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> NDStructure<T>.deferred(index: IntArray) =
|
fun <T> NDStructure<T>.deferred(index: IntArray): Deferred<T> =
|
||||||
if (this is LazyNDStructure<T>) this.deferred(index) else CompletableDeferred(get(index))
|
if (this is LazyNDStructure<T>) this.deferred(index) else CompletableDeferred(get(index))
|
||||||
|
|
||||||
suspend fun <T> NDStructure<T>.await(index: IntArray) =
|
suspend fun <T> NDStructure<T>.await(index: IntArray): T =
|
||||||
if (this is LazyNDStructure<T>) this.await(index) else get(index)
|
if (this is LazyNDStructure<T>) this.await(index) else get(index)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PENDING would benifit from KEEP-176
|
* PENDING would benefit from KEEP-176
|
||||||
*/
|
*/
|
||||||
fun <T, R> NDStructure<T>.mapAsyncIndexed(scope: CoroutineScope, function: suspend (T, index: IntArray) -> R) =
|
fun <T, R> NDStructure<T>.mapAsyncIndexed(
|
||||||
LazyNDStructure(scope, shape) { index -> function(get(index), index) }
|
scope: CoroutineScope,
|
||||||
|
function: suspend (T, index: IntArray) -> R
|
||||||
|
): LazyNDStructure<R> = LazyNDStructure(scope, shape) { index -> function(get(index), index) }
|
||||||
|
|
||||||
fun <T, R> NDStructure<T>.mapAsync(scope: CoroutineScope, function: suspend (T) -> R) =
|
fun <T, R> NDStructure<T>.mapAsync(scope: CoroutineScope, function: suspend (T) -> R): LazyNDStructure<R> =
|
||||||
LazyNDStructure(scope, shape) { index -> function(get(index)) }
|
LazyNDStructure(scope, shape) { index -> function(get(index)) }
|
@ -15,14 +15,13 @@ import kotlin.test.Test
|
|||||||
@InternalCoroutinesApi
|
@InternalCoroutinesApi
|
||||||
@FlowPreview
|
@FlowPreview
|
||||||
class BufferFlowTest {
|
class BufferFlowTest {
|
||||||
|
val dispatcher: CoroutineDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
|
||||||
val dispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Timeout(2000)
|
@Timeout(2000)
|
||||||
fun map() {
|
fun map() {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
(1..20).asFlow().mapParallel( dispatcher) {
|
(1..20).asFlow().mapParallel(dispatcher) {
|
||||||
println("Started $it on ${Thread.currentThread().name}")
|
println("Started $it on ${Thread.currentThread().name}")
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
Thread.sleep(200)
|
Thread.sleep(200)
|
||||||
|
@ -19,17 +19,17 @@ class RingBufferTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun windowed(){
|
fun windowed() {
|
||||||
val flow = flow{
|
val flow = flow {
|
||||||
var i = 0
|
var i = 0
|
||||||
while(true){
|
while (true) emit(i++)
|
||||||
emit(i++)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val windowed = flow.windowed(10)
|
val windowed = flow.windowed(10)
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val first = windowed.take(1).single()
|
val first = windowed.take(1).single()
|
||||||
val res = windowed.take(15).map { it -> it.asSequence().average() }.toList()
|
val res = windowed.take(15).map { it.asSequence().average() }.toList()
|
||||||
assertEquals(0.0, res[0])
|
assertEquals(0.0, res[0])
|
||||||
assertEquals(4.5, res[9])
|
assertEquals(4.5, res[9])
|
||||||
assertEquals(9.5, res[14])
|
assertEquals(9.5, res[14])
|
||||||
|
Loading…
Reference in New Issue
Block a user