Merge branch 'dev' into commandertvis/issue226

# Conflicts:
#	kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Counter.kt
This commit is contained in:
Iaroslav Postovalov 2021-03-17 23:57:50 +07:00
commit c4367ac509
No known key found for this signature in database
GPG Key ID: 46E15E4A31B3BCD7
183 changed files with 4051 additions and 3814 deletions

40
.github/workflows/pages.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Dokka publication
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Cache gradle
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ubuntu-20.04-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
ubuntu-20.04-gradle-
- name: Cache konan
uses: actions/cache@v2
with:
path: ~/.konan
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Build
run: |
./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace
mv build/dokka/htmlMultiModule/-modules.html build/dokka/htmlMultiModule/index.html
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@4.1.0
with:
branch: gh-pages
folder: build/dokka/htmlMultiModule

View File

@ -4,17 +4,22 @@
### Added
- ScaleOperations interface
- Field extends ScaleOperations
- Basic integration API
### Changed
- Exponential operations merged with hyperbolic functions
- Space is replaced by Group. Space is reserved for vector spaces.
- VectorSpace is now a vector space
- Buffer factories for primitives moved to MutableBuffer.Companion
- NDStructure and NDAlgebra to StructureND and AlgebraND respectively
- Real -> Double
### Deprecated
### Removed
- Nearest in Domain. To be implemented in geometry package.
- Number multiplication and division in main Algebra chain
- `contentEquals` from Buffer. It moved to the companion.
### Fixed
@ -72,6 +77,7 @@
- `toGrid` method.
- Public visibility of `BufferAccessor2D`
- `Real` class
- StructureND identity and equals
### Fixed
- `symbol` method in `MstExtendedField` (https://github.com/mipt-npm/kmath/pull/140)

View File

@ -3,9 +3,7 @@
![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg)
Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-core/_latestVersion)
Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion)
[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22%20AND%20a:%22kmath-core%22)
# KMath
@ -89,12 +87,12 @@ KMath is a modular library. Different modules provide different features with di
> **Maturity**: PROTOTYPE
>
> **Features:**
> - [expression-language](kmath-ast/src/jvmMain/kotlin/kscience/kmath/ast/parser.kt) : Expression language and its parser
> - [mst](kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation
> - [mst-building](kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure
> - [mst-interpreter](kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST interpreter
> - [mst-jvm-codegen](kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler
> - [mst-js-codegen](kmath-ast/src/jsMain/kotlin/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
> - [expression-language](kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser
> - [mst](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt) : MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation
> - [mst-building](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure
> - [mst-interpreter](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt) : MST interpreter
> - [mst-jvm-codegen](kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler
> - [mst-js-codegen](kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
<hr/>
@ -110,8 +108,8 @@ KMath is a modular library. Different modules provide different features with di
> **Maturity**: PROTOTYPE
>
> **Features:**
> - [complex](kmath-complex/src/commonMain/kotlin/kscience/kmath/complex/Complex.kt) : Complex Numbers
> - [quaternion](kmath-complex/src/commonMain/kotlin/kscience/kmath/complex/Quaternion.kt) : Quaternions
> - [complex](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt) : Complex Numbers
> - [quaternion](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt) : Quaternions
<hr/>
@ -121,15 +119,15 @@ KMath is a modular library. Different modules provide different features with di
> **Maturity**: DEVELOPMENT
>
> **Features:**
> - [algebras](kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : Algebraic structures like rings, spaces and fields.
> - [nd](kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Many-dimensional structures and operations on them.
> - [linear](kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition.
> - [buffers](kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt) : One-dimensional structure
> - [expressions](kmath-core/src/commonMain/kotlin/kscience/kmath/expressions) : By writing a single mathematical expression once, users will be able to apply different types of
> - [algebras](kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Algebraic structures like rings, spaces and fields.
> - [nd](kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/StructureND.kt) : Many-dimensional structures and operations on them.
> - [linear](kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition.
> - [buffers](kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffers.kt) : One-dimensional structure
> - [expressions](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions) : By writing a single mathematical expression once, users will be able to apply different types of
objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high
performance calculations to code generation.
> - [domains](kmath-core/src/commonMain/kotlin/kscience/kmath/domains) : Domains
> - [autodif](kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation
> - [domains](kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains) : Domains
> - [autodif](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation
<hr/>
@ -159,9 +157,9 @@ 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
> - [DoubleVector](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt) : Numpy-like operations for Buffers/Points
> - [DoubleMatrix](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleMatrix.kt) : Numpy-like operations for 2d real structures
> - [grids](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/structures/grids.kt) : Uniform grid generators
<hr/>
@ -171,10 +169,10 @@ One can still use generic algebras though.
> **Maturity**: PROTOTYPE
>
> **Features:**
> - [piecewise](kmath-functions/Piecewise functions.) : src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt
> - [polynomials](kmath-functions/Polynomial functions.) : src/commonMain/kotlin/kscience/kmath/functions/Polynomial.kt
> - [linear interpolation](kmath-functions/Linear XY interpolator.) : src/commonMain/kotlin/kscience/kmath/interpolation/LinearInterpolator.kt
> - [spline interpolation](kmath-functions/Cubic spline XY interpolator.) : src/commonMain/kotlin/kscience/kmath/interpolation/SplineInterpolator.kt
> - [piecewise](kmath-functions/Piecewise functions.) : src/commonMain/kotlin/space/kscience/kmath/functions/Piecewise.kt
> - [polynomials](kmath-functions/Polynomial functions.) : src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt
> - [linear interpolation](kmath-functions/Linear XY interpolator.) : src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt
> - [spline interpolation](kmath-functions/Cubic spline XY interpolator.) : src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt
<hr/>
@ -208,9 +206,9 @@ One can still use generic algebras though.
> **Maturity**: EXPERIMENTAL
>
> **Features:**
> - [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
> - [nd4jarraystructure](kmath-nd4j/#) : NDStructure wrapper for INDArray
> - [nd4jarrayrings](kmath-nd4j/#) : Rings over Nd4jArrayStructure of Int and Long
> - [nd4jarrayfields](kmath-nd4j/#) : Fields over Nd4jArrayStructure of Float and Double
<hr/>
@ -256,8 +254,8 @@ repositories {
}
dependencies {
api("kscience.kmath:kmath-core:() -> kotlin.Any")
// api("kscience.kmath:kmath-core-jvm:() -> kotlin.Any") for jvm-specific version
api("space.kscience:kmath-core:0.3.0-dev-3")
// api("kscience.kmath:kmath-core-jvm:0.3.0-dev-3") for jvm-specific version
}
```

View File

@ -20,7 +20,7 @@ allprojects {
}
group = "space.kscience"
version = "0.3.0"
version = "0.3.0-dev-3"
}
subprojects {

View File

@ -31,7 +31,7 @@ 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.
A typical implementation of `Field<T>` is the `RealField` which works on doubles, and `VectorSpace` for `Space<T>`.
A typical implementation of `Field<T>` is the `DoubleField` which works on doubles, and `VectorSpace` for `Space<T>`.
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`

View File

@ -13,16 +13,19 @@
version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata>
<defs
id="defs6"><clipPath
id="clipPath24"
clipPathUnits="userSpaceOnUse"><path
id="path22"
d="M 0,1590 H 6720 V 4400 H 0 Z" /></clipPath><clipPath
d="M 0,1590 H 6720 V 4400 H 0 Z" /></clipPath>
<clipPath
id="clipPath36"
clipPathUnits="userSpaceOnUse"><path
id="path34"
d="M 3410,0 H 6720 V 1590 H 3410 Z" /></clipPath></defs><g
d="M 3410,0 H 6720 V 1590 H 3410 Z" /></clipPath></defs>
<g
transform="matrix(1.3333333,0,0,-1.3333333,0,586.66667)"
id="g10"><g
transform="scale(0.1)"

Before

Width:  |  Height:  |  Size: 248 KiB

After

Width:  |  Height:  |  Size: 248 KiB

View File

@ -13,12 +13,14 @@
version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata>
<defs
id="defs6"><clipPath
id="clipPath32"
clipPathUnits="userSpaceOnUse"><path
id="path30"
d="M 1780,1750 H 3830 V 3800 H 1780 Z" /></clipPath></defs><g
d="M 1780,1750 H 3830 V 3800 H 1780 Z" /></clipPath></defs>
<g
transform="matrix(1.3333333,0,0,-1.3333333,0,633.33333)"
id="g10"><g
transform="scale(0.1)"

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -13,24 +13,29 @@
version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata>
<defs
id="defs6"><clipPath
id="clipPath24"
clipPathUnits="userSpaceOnUse"><path
id="path22"
d="M 0,3010 H 6470 V 4280 H 0 Z" /></clipPath><clipPath
d="M 0,3010 H 6470 V 4280 H 0 Z" /></clipPath>
<clipPath
id="clipPath36"
clipPathUnits="userSpaceOnUse"><path
id="path34"
d="M 0,1590 H 7000 V 3010 H 0 Z" /></clipPath><clipPath
d="M 0,1590 H 7000 V 3010 H 0 Z" /></clipPath>
<clipPath
id="clipPath48"
clipPathUnits="userSpaceOnUse"><path
id="path46"
d="m 0,1580 h 6470 v 10 H 0 Z" /></clipPath><clipPath
d="m 0,1580 h 6470 v 10 H 0 Z" /></clipPath>
<clipPath
id="clipPath60"
clipPathUnits="userSpaceOnUse"><path
id="path58"
d="M 3280,0 H 6460 V 1580 H 3280 Z" /></clipPath></defs><g
d="M 3280,0 H 6460 V 1580 H 3280 Z" /></clipPath></defs>
<g
transform="matrix(1.3333333,0,0,-1.3333333,0,570.66667)"
id="g10"><g
transform="scale(0.1)"

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 278 KiB

View File

@ -13,88 +13,109 @@
version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata>
<defs
id="defs6"><clipPath
id="clipPath40"
clipPathUnits="userSpaceOnUse"><path
id="path38"
d="m 2370,2780 h 1370 v 10 H 2370 Z" /></clipPath><clipPath
d="m 2370,2780 h 1370 v 10 H 2370 Z" /></clipPath>
<clipPath
id="clipPath52"
clipPathUnits="userSpaceOnUse"><path
id="path50"
d="m 5630,2780 h 860 v 10 h -860 z" /></clipPath><clipPath
d="m 5630,2780 h 860 v 10 h -860 z" /></clipPath>
<clipPath
id="clipPath64"
clipPathUnits="userSpaceOnUse"><path
id="path62"
d="m 970,2690 h 1300 v 90 H 970 Z" /></clipPath><clipPath
d="m 970,2690 h 1300 v 90 H 970 Z" /></clipPath>
<clipPath
id="clipPath76"
clipPathUnits="userSpaceOnUse"><path
id="path74"
d="m 2370,2690 h 1370 v 90 H 2370 Z" /></clipPath><clipPath
d="m 2370,2690 h 1370 v 90 H 2370 Z" /></clipPath>
<clipPath
id="clipPath88"
clipPathUnits="userSpaceOnUse"><path
id="path86"
d="m 5630,2690 h 860 v 90 h -860 z" /></clipPath><clipPath
d="m 5630,2690 h 860 v 90 h -860 z" /></clipPath>
<clipPath
id="clipPath100"
clipPathUnits="userSpaceOnUse"><path
id="path98"
d="m 970,2460 h 1300 v 230 H 970 Z" /></clipPath><clipPath
d="m 970,2460 h 1300 v 230 H 970 Z" /></clipPath>
<clipPath
id="clipPath112"
clipPathUnits="userSpaceOnUse"><path
id="path110"
d="m 2370,2460 h 1370 v 230 H 2370 Z" /></clipPath><clipPath
d="m 2370,2460 h 1370 v 230 H 2370 Z" /></clipPath>
<clipPath
id="clipPath124"
clipPathUnits="userSpaceOnUse"><path
id="path122"
d="m 4900,2460 h 620 v 230 h -620 z" /></clipPath><clipPath
d="m 4900,2460 h 620 v 230 h -620 z" /></clipPath>
<clipPath
id="clipPath136"
clipPathUnits="userSpaceOnUse"><path
id="path134"
d="m 5630,2460 h 860 v 230 h -860 z" /></clipPath><clipPath
d="m 5630,2460 h 860 v 230 h -860 z" /></clipPath>
<clipPath
id="clipPath148"
clipPathUnits="userSpaceOnUse"><path
id="path146"
d="m 970,1480 h 1300 v 980 H 970 Z" /></clipPath><clipPath
d="m 970,1480 h 1300 v 980 H 970 Z" /></clipPath>
<clipPath
id="clipPath160"
clipPathUnits="userSpaceOnUse"><path
id="path158"
d="m 2370,1480 h 1370 v 980 H 2370 Z" /></clipPath><clipPath
d="m 2370,1480 h 1370 v 980 H 2370 Z" /></clipPath>
<clipPath
id="clipPath172"
clipPathUnits="userSpaceOnUse"><path
id="path170"
d="m 3920,1480 h 860 v 980 h -860 z" /></clipPath><clipPath
d="m 3920,1480 h 860 v 980 h -860 z" /></clipPath>
<clipPath
id="clipPath184"
clipPathUnits="userSpaceOnUse"><path
id="path182"
d="m 4900,1480 h 620 v 980 h -620 z" /></clipPath><clipPath
d="m 4900,1480 h 620 v 980 h -620 z" /></clipPath>
<clipPath
id="clipPath196"
clipPathUnits="userSpaceOnUse"><path
id="path194"
d="m 5630,1480 h 860 v 980 h -860 z" /></clipPath><clipPath
d="m 5630,1480 h 860 v 980 h -860 z" /></clipPath>
<clipPath
id="clipPath208"
clipPathUnits="userSpaceOnUse"><path
id="path206"
d="m 2370,1470 h 1370 v 10 H 2370 Z" /></clipPath><clipPath
d="m 2370,1470 h 1370 v 10 H 2370 Z" /></clipPath>
<clipPath
id="clipPath220"
clipPathUnits="userSpaceOnUse"><path
id="path218"
d="m 3920,1470 h 860 v 10 h -860 z" /></clipPath><clipPath
d="m 3920,1470 h 860 v 10 h -860 z" /></clipPath>
<clipPath
id="clipPath232"
clipPathUnits="userSpaceOnUse"><path
id="path230"
d="m 4900,1470 h 620 v 10 h -620 z" /></clipPath><clipPath
d="m 4900,1470 h 620 v 10 h -620 z" /></clipPath>
<clipPath
id="clipPath244"
clipPathUnits="userSpaceOnUse"><path
id="path242"
d="m 5630,1470 h 860 v 10 h -860 z" /></clipPath><clipPath
d="m 5630,1470 h 860 v 10 h -860 z" /></clipPath>
<clipPath
id="clipPath256"
clipPathUnits="userSpaceOnUse"><path
id="path254"
d="m 3920,1450 h 860 v 20 h -860 z" /></clipPath><clipPath
d="m 3920,1450 h 860 v 20 h -860 z" /></clipPath>
<clipPath
id="clipPath268"
clipPathUnits="userSpaceOnUse"><path
id="path266"
d="m 4900,1450 h 620 v 20 h -620 z" /></clipPath></defs><g
d="m 4900,1450 h 620 v 20 h -620 z" /></clipPath></defs>
<g
transform="matrix(1.3333333,0,0,-1.3333333,0,529.33333)"
id="g10"><g
transform="scale(0.1)"

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -10,11 +10,11 @@ structures. In `kmath` performance depends on which particular context was used
Let us consider following contexts:
```kotlin
// automatically build context most suited for given type.
val autoField = NDField.auto(RealField, dim, dim)
val autoField = NDField.auto(DoubleField, dim, dim)
// specialized nd-field for Double. It works as generic Double field as well
val specializedField = NDField.real(dim, dim)
//A generic boxing field. It should be used for objects, not primitives.
val genericField = NDField.buffered(RealField, dim, dim)
val genericField = NDField.buffered(DoubleField, dim, dim)
```
Now let us perform several tests and see which implementation is best suited for each case:

View File

@ -13,9 +13,6 @@
> maven { url 'https://repo.kotlin.link' }
> maven { url 'https://dl.bintray.com/hotkeytlt/maven' }
> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap
>// Uncomment if repo.kotlin.link is unavailable
>// maven { url 'https://dl.bintray.com/mipt-npm/kscience' }
>// maven { url 'https://dl.bintray.com/mipt-npm/dev' }
> }
>
> dependencies {
@ -29,9 +26,6 @@
> maven("https://repo.kotlin.link")
> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap
> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a
>// Uncomment if repo.kotlin.link is unavailable
>// maven("https://dl.bintray.com/mipt-npm/kscience")
>// maven("https://dl.bintray.com/mipt-npm/dev")
> }
>
> dependencies {

View File

@ -3,9 +3,7 @@
![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg)
Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-core/_latestVersion)
Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion)
[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22%20AND%20a:%22kmath-core%22)
# KMath
@ -106,7 +104,7 @@ repositories {
}
dependencies {
api("kscience.kmath:kmath-core:$version")
api("${group}:kmath-core:$version")
// api("kscience.kmath:kmath-core-jvm:$version") for jvm-specific version
}
```

View File

@ -92,6 +92,14 @@ benchmark {
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
include("ExpressionsInterpretersBenchmark")
}
configurations.register("matrixInverse") {
warmups = 1 // number of warmup iterations
iterations = 3 // number of iterations
iterationTime = 500 // time in seconds per iteration
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
include("MatrixInverseBenchmark")
}
}
kotlin.sourceSets.all {

View File

@ -5,14 +5,14 @@ import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.complex
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.RealBuffer
@State(Scope.Benchmark)
internal class BufferBenchmark {
@Benchmark
fun genericRealBufferReadWrite() {
val buffer = RealBuffer(size) { it.toDouble() }
fun genericDoubleBufferReadWrite() {
val buffer = DoubleBuffer(size) { it.toDouble() }
(0 until size).forEach {
buffer[it]

View File

@ -4,14 +4,11 @@ import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import space.kscience.kmath.commons.linear.CMMatrixContext
import space.kscience.kmath.ejml.EjmlMatrixContext
import space.kscience.kmath.linear.BufferMatrixContext
import space.kscience.kmath.linear.Matrix
import space.kscience.kmath.linear.RealMatrixContext
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.ejml.EjmlLinearSpace
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.invoke
import space.kscience.kmath.operations.DoubleField
import kotlin.random.Random
@State(Scope.Benchmark)
@ -21,47 +18,47 @@ internal class DotBenchmark {
const val dim = 1000
//creating invertible matrix
val matrix1 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val matrix2 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val matrix1 = LinearSpace.real.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val matrix2 = LinearSpace.real.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val cmMatrix1 = CMMatrixContext { matrix1.toCM() }
val cmMatrix2 = CMMatrixContext { matrix2.toCM() }
val cmMatrix1 = CMLinearSpace { matrix1.toCM() }
val cmMatrix2 = CMLinearSpace { matrix2.toCM() }
val ejmlMatrix1 = EjmlMatrixContext { matrix1.toEjml() }
val ejmlMatrix2 = EjmlMatrixContext { matrix2.toEjml() }
val ejmlMatrix1 = EjmlLinearSpace { matrix1.toEjml() }
val ejmlMatrix2 = EjmlLinearSpace { matrix2.toEjml() }
}
@Benchmark
fun cmDot(blackhole: Blackhole) {
CMMatrixContext {
CMLinearSpace.run {
blackhole.consume(cmMatrix1 dot cmMatrix2)
}
}
@Benchmark
fun ejmlDot(blackhole: Blackhole) {
EjmlMatrixContext {
EjmlLinearSpace {
blackhole.consume(ejmlMatrix1 dot ejmlMatrix2)
}
}
@Benchmark
fun ejmlDotWithConversion(blackhole: Blackhole) {
EjmlMatrixContext {
EjmlLinearSpace {
blackhole.consume(matrix1 dot matrix2)
}
}
@Benchmark
fun bufferedDot(blackhole: Blackhole) {
BufferMatrixContext(RealField, Buffer.Companion::real).invoke {
LinearSpace.auto(DoubleField).invoke {
blackhole.consume(matrix1 dot matrix2)
}
}
@Benchmark
fun realDot(blackhole: Blackhole) {
RealMatrixContext {
LinearSpace.real {
blackhole.consume(matrix1 dot matrix2)
}
}

View File

@ -10,7 +10,7 @@ import space.kscience.kmath.expressions.Expression
import space.kscience.kmath.expressions.expressionInField
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import kotlin.random.Random
@ -30,7 +30,7 @@ internal class ExpressionsInterpretersBenchmark {
fun mstExpression(blackhole: Blackhole) {
val expr = algebra.mstInField {
val x = bindSymbol(x)
x * 2.0 + 2.0 / x - 16.0
x * 2.0 + number(2.0) / x - 16.0
}
invokeAndSum(expr, blackhole)
@ -40,7 +40,7 @@ internal class ExpressionsInterpretersBenchmark {
fun asmExpression(blackhole: Blackhole) {
val expr = algebra.mstInField {
val x = bindSymbol(x)
x * 2.0 + 2.0 / x - 16.0
x * 2.0 + number(2.0) / x - 16.0
}.compile()
invokeAndSum(expr, blackhole)
@ -68,7 +68,7 @@ internal class ExpressionsInterpretersBenchmark {
}
private companion object {
private val algebra = RealField
private val algebra = DoubleField
private val x by symbol
}
}

View File

@ -4,44 +4,44 @@ import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import space.kscience.kmath.commons.linear.CMMatrixContext
import space.kscience.kmath.commons.linear.CMMatrixContext.dot
import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.commons.linear.inverse
import space.kscience.kmath.ejml.EjmlMatrixContext
import space.kscience.kmath.ejml.EjmlLinearSpace
import space.kscience.kmath.ejml.inverse
import space.kscience.kmath.linear.Matrix
import space.kscience.kmath.linear.MatrixContext
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.inverseWithLup
import space.kscience.kmath.linear.real
import space.kscience.kmath.linear.invoke
import kotlin.random.Random
@State(Scope.Benchmark)
internal class LinearAlgebraBenchmark {
internal class MatrixInverseBenchmark {
companion object {
val random = Random(1224)
const val dim = 100
private val space = LinearSpace.real
//creating invertible matrix
val u = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val l = Matrix.real(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 }
val matrix = l dot u
val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 }
val matrix = space { l dot u }
}
@Benchmark
fun kmathLupInversion(blackhole: Blackhole) {
blackhole.consume(MatrixContext.real.inverseWithLup(matrix))
blackhole.consume(LinearSpace.real.inverseWithLup(matrix))
}
@Benchmark
fun cmLUPInversion(blackhole: Blackhole) {
with(CMMatrixContext) {
with(CMLinearSpace) {
blackhole.consume(inverse(matrix))
}
}
@Benchmark
fun ejmlInverse(blackhole: Blackhole) {
with(EjmlMatrixContext) {
with(EjmlLinearSpace) {
blackhole.consume(inverse(matrix))
}
}

View File

@ -5,7 +5,7 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Buffer
@State(Scope.Benchmark)
@ -13,7 +13,7 @@ internal class NDFieldBenchmark {
@Benchmark
fun autoFieldAdd(blackhole: Blackhole) {
with(autoField) {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += one }
blackhole.consume(res)
}
@ -22,7 +22,7 @@ internal class NDFieldBenchmark {
@Benchmark
fun specializedFieldAdd(blackhole: Blackhole) {
with(specializedField) {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@ -32,7 +32,7 @@ internal class NDFieldBenchmark {
@Benchmark
fun boxingFieldAdd(blackhole: Blackhole) {
with(genericField) {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@ -41,8 +41,8 @@ internal class NDFieldBenchmark {
private companion object {
private const val dim = 1000
private const val n = 100
private val autoField = NDAlgebra.auto(RealField, dim, dim)
private val specializedField = NDAlgebra.real(dim, dim)
private val genericField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim)
private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val specializedField = AlgebraND.real(dim, dim)
private val genericField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim)
}
}

View File

@ -5,11 +5,11 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.NDAlgebra
import space.kscience.kmath.nd.NDStructure
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.auto
import space.kscience.kmath.nd.real
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.viktor.ViktorNDField
@State(Scope.Benchmark)
@ -17,7 +17,7 @@ internal class ViktorBenchmark {
@Benchmark
fun automaticFieldAddition(blackhole: Blackhole) {
with(autoField) {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@ -26,7 +26,7 @@ internal class ViktorBenchmark {
@Benchmark
fun realFieldAddition(blackhole: Blackhole) {
with(realField) {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@ -54,8 +54,8 @@ internal class ViktorBenchmark {
private const val n = 100
// automatically build context most suited for given type.
private val autoField = NDAlgebra.auto(RealField, dim, dim)
private val realField = NDAlgebra.real(dim, dim)
private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val realField = AlgebraND.real(dim, dim)
private val viktorField = ViktorNDField(dim, dim)
}
}

View File

@ -5,17 +5,17 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.NDAlgebra
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.auto
import space.kscience.kmath.nd.real
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.viktor.ViktorNDField
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.viktor.ViktorFieldND
@State(Scope.Benchmark)
internal class ViktorLogBenchmark {
@Benchmark
fun realFieldLog(blackhole: Blackhole) {
with(realField) {
with(realNdField) {
val fortyTwo = produce { 42.0 }
var res = one
repeat(n) { res = ln(fortyTwo) }
@ -46,8 +46,8 @@ internal class ViktorLogBenchmark {
private const val n = 100
// automatically build context most suited for given type.
private val autoField = NDAlgebra.auto(RealField, dim, dim)
private val realField = NDAlgebra.real(dim, dim)
private val viktorField = ViktorNDField(intArrayOf(dim, dim))
private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val realNdField = AlgebraND.real(dim, dim)
private val viktorField = ViktorFieldND(intArrayOf(dim, dim))
}
}

View File

@ -1,10 +1,10 @@
package space.kscience.kmath.ast
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
fun main() {
val expr = RealField.mstInField {
val expr = DoubleField.mstInField {
val x = bindSymbol("x")
x * 2.0 + number(2.0) / x - 16.0
}

View File

@ -5,7 +5,7 @@ import space.kscience.kmath.expressions.derivative
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.kotlingrad.differentiable
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
/**
* In this example, x^2-4*x-44 function is differentiated with Kotlin, and the autodiff result is compared with
@ -14,11 +14,11 @@ import space.kscience.kmath.operations.RealField
fun main() {
val x by symbol
val actualDerivative = MstExpression(RealField, "x^2-4*x-44".parseMath())
val actualDerivative = MstExpression(DoubleField, "x^2-4*x-44".parseMath())
.differentiable()
.derivative(x)
.compile()
val expectedDerivative = MstExpression(RealField, "2*x-4".parseMath()).compile()
val expectedDerivative = MstExpression(DoubleField, "2*x-4".parseMath()).compile()
assert(actualDerivative("x" to 123.0) == expectedDerivative("x" to 123.0))
}

View File

@ -8,7 +8,7 @@ import kscience.plotly.models.TraceValues
import space.kscience.kmath.commons.optimization.chiSquared
import space.kscience.kmath.commons.optimization.minimize
import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.real.RealVector
import space.kscience.kmath.real.DoubleVector
import space.kscience.kmath.real.map
import space.kscience.kmath.real.step
import space.kscience.kmath.stat.*
@ -26,7 +26,7 @@ private val c by symbol
/**
* Shortcut to use buffers in plotly
*/
operator fun TraceValues.invoke(vector: RealVector) {
operator fun TraceValues.invoke(vector: DoubleVector) {
numbers = vector.asIterable()
}

View File

@ -0,0 +1,28 @@
package space.kscience.kmath.linear
import space.kscience.kmath.real.*
import space.kscience.kmath.structures.DoubleBuffer
fun main() {
val x0 = DoubleVector(0.0, 0.0, 0.0)
val sigma = DoubleVector(1.0, 1.0, 1.0)
val gaussian: (Point<Double>) -> Double = { x ->
require(x.size == x0.size)
kotlin.math.exp(-((x - x0) / sigma).square().sum())
}
fun ((Point<Double>) -> Double).grad(x: Point<Double>): Point<Double> {
require(x.size == x0.size)
return DoubleBuffer(x.size) { i ->
val h = sigma[i] / 5
val dVector = DoubleBuffer(x.size) { if (it == i) h else 0.0 }
val f1 = invoke(x + dVector / 2)
val f0 = invoke(x - dVector / 2)
(f1 - f0) / h
}
}
println(gaussian.grad(x0))
}

View File

@ -2,17 +2,17 @@ package space.kscience.kmath.operations
import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.complex
import space.kscience.kmath.nd.NDAlgebra
import space.kscience.kmath.nd.AlgebraND
fun main() {
// 2d element
val element = NDAlgebra.complex(2, 2).produce { (i, j) ->
val element = AlgebraND.complex(2, 2).produce { (i, j) ->
Complex(i.toDouble() - j.toDouble(), i.toDouble() + j.toDouble())
}
println(element)
// 1d element operation
val result = with(NDAlgebra.complex(8)) {
val result = with(AlgebraND.complex(8)) {
val a = produce { (it) -> i * it - it.toDouble() }
val b = 3
val c = Complex(1.0, 1.0)

View File

@ -4,8 +4,8 @@ package space.kscience.kmath.structures
import space.kscience.kmath.complex.*
import space.kscience.kmath.linear.transpose
import space.kscience.kmath.nd.NDAlgebra
import space.kscience.kmath.nd.NDStructure
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.real
import space.kscience.kmath.operations.invoke
@ -15,12 +15,12 @@ fun main() {
val dim = 1000
val n = 1000
val realField = NDAlgebra.real(dim, dim)
val complexField: ComplexNDField = NDAlgebra.complex(dim, dim)
val realField = AlgebraND.real(dim, dim)
val complexField: ComplexFieldND = AlgebraND.complex(dim, dim)
val realTime = measureTimeMillis {
realField {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) {
res += 1.0
}
@ -31,7 +31,7 @@ fun main() {
val complexTime = measureTimeMillis {
complexField {
var res: NDStructure<Complex> = one
var res: StructureND<Complex> = one
repeat(n) {
res += 1.0
}

View File

@ -4,7 +4,7 @@ import kotlinx.coroutines.GlobalScope
import org.nd4j.linalg.factory.Nd4j
import space.kscience.kmath.nd.*
import space.kscience.kmath.nd4j.Nd4jArrayField
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.viktor.ViktorNDField
import kotlin.contracts.InvocationKind
@ -24,56 +24,56 @@ fun main() {
val n = 1000
// automatically build context most suited for given type.
val autoField = NDAlgebra.auto(RealField, dim, dim)
val autoField = AlgebraND.auto(DoubleField, dim, dim)
// specialized nd-field for Double. It works as generic Double field as well
val realField = NDAlgebra.real(dim, dim)
val realField = AlgebraND.real(dim, dim)
//A generic boxing field. It should be used for objects, not primitives.
val boxingField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim)
val boxingField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim)
// Nd4j specialized field.
val nd4jField = Nd4jArrayField.real(dim, dim)
//viktor field
val viktorField = ViktorNDField(dim, dim)
//parallel processing based on Java Streams
val parallelField = NDAlgebra.realWithStream(dim,dim)
val parallelField = AlgebraND.realWithStream(dim, dim)
measureAndPrint("Boxing addition") {
boxingField {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
}
}
measureAndPrint("Specialized addition") {
realField {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
}
}
measureAndPrint("Nd4j specialized addition") {
nd4jField {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
}
}
measureAndPrint("Viktor addition") {
viktorField {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
}
}
measureAndPrint("Parallel stream addition") {
parallelField {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
}
}
measureAndPrint("Automatic field addition") {
autoField {
var res: NDStructure<Double> = one
var res: StructureND<Double> = one
repeat(n) { res += 1.0 }
}
}

View File

@ -1,107 +0,0 @@
package space.kscience.kmath.structures
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.NumbersAddOperations
import space.kscience.kmath.operations.RealField
import java.util.*
import java.util.stream.IntStream
/**
* A demonstration implementation of NDField over Real using Java [DoubleStream] for parallel execution
*/
@OptIn(UnstableKMathAPI::class)
class StreamRealNDField(
override val shape: IntArray,
) : NDField<Double, RealField>,
NumbersAddOperations<NDStructure<Double>>,
ExtendedField<NDStructure<Double>> {
private val strides = DefaultStrides(shape)
override val elementContext: RealField get() = RealField
override val zero: NDBuffer<Double> by lazy { produce { zero } }
override val one: NDBuffer<Double> by lazy { produce { one } }
override fun number(value: Number): NDBuffer<Double> {
val d = value.toDouble() // minimize conversions
return produce { d }
}
private val NDStructure<Double>.buffer: RealBuffer
get() = when {
!shape.contentEquals(this@StreamRealNDField.shape) -> throw ShapeMismatchException(
this@StreamRealNDField.shape,
shape
)
this is NDBuffer && this.strides == this@StreamRealNDField.strides -> this.buffer as RealBuffer
else -> RealBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
}
override fun produce(initializer: RealField.(IntArray) -> Double): NDBuffer<Double> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
val index = strides.index(offset)
RealField.initializer(index)
}.toArray()
return NDBuffer(strides, array.asBuffer())
}
override fun NDStructure<Double>.map(
transform: RealField.(Double) -> Double,
): NDBuffer<Double> {
val array = Arrays.stream(buffer.array).parallel().map { RealField.transform(it) }.toArray()
return NDBuffer(strides, array.asBuffer())
}
override fun NDStructure<Double>.mapIndexed(
transform: RealField.(index: IntArray, Double) -> Double,
): NDBuffer<Double> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
RealField.transform(
strides.index(offset),
buffer.array[offset]
)
}.toArray()
return NDBuffer(strides, array.asBuffer())
}
override fun combine(
a: NDStructure<Double>,
b: NDStructure<Double>,
transform: RealField.(Double, Double) -> Double,
): NDBuffer<Double> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
RealField.transform(a.buffer.array[offset], b.buffer.array[offset])
}.toArray()
return NDBuffer(strides, array.asBuffer())
}
override fun NDStructure<Double>.unaryMinus(): NDStructure<Double> = map { -it }
override fun scale(a: NDStructure<Double>, value: Double): NDStructure<Double> = a.map { it * value }
override fun power(arg: NDStructure<Double>, pow: Number): NDBuffer<Double> = arg.map { power(it, pow) }
override fun exp(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { exp(it) }
override fun ln(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { ln(it) }
override fun sin(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { sin(it) }
override fun cos(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { cos(it) }
override fun tan(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { tan(it) }
override fun asin(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { asin(it) }
override fun acos(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { acos(it) }
override fun atan(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { atan(it) }
override fun sinh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { sinh(it) }
override fun cosh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { cosh(it) }
override fun tanh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { tanh(it) }
override fun asinh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { asinh(it) }
override fun acosh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { acosh(it) }
override fun atanh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { atanh(it) }
}
fun NDAlgebra.Companion.realWithStream(vararg shape: Int): StreamRealNDField = StreamRealNDField(shape)

View File

@ -0,0 +1,107 @@
package space.kscience.kmath.structures
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.NumbersAddOperations
import java.util.*
import java.util.stream.IntStream
/**
* A demonstration implementation of NDField over Real using Java [DoubleStream] for parallel execution
*/
@OptIn(UnstableKMathAPI::class)
class StreamDoubleFieldND(
override val shape: IntArray,
) : FieldND<Double, DoubleField>,
NumbersAddOperations<StructureND<Double>>,
ExtendedField<StructureND<Double>> {
private val strides = DefaultStrides(shape)
override val elementContext: DoubleField get() = DoubleField
override val zero: BufferND<Double> by lazy { produce { zero } }
override val one: BufferND<Double> by lazy { produce { one } }
override fun number(value: Number): BufferND<Double> {
val d = value.toDouble() // minimize conversions
return produce { d }
}
private val StructureND<Double>.buffer: DoubleBuffer
get() = when {
!shape.contentEquals(this@StreamDoubleFieldND.shape) -> throw ShapeMismatchException(
this@StreamDoubleFieldND.shape,
shape
)
this is BufferND && this.strides == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer
else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
}
override fun produce(initializer: DoubleField.(IntArray) -> Double): BufferND<Double> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
val index = strides.index(offset)
DoubleField.initializer(index)
}.toArray()
return BufferND(strides, array.asBuffer())
}
override fun StructureND<Double>.map(
transform: DoubleField.(Double) -> Double,
): BufferND<Double> {
val array = Arrays.stream(buffer.array).parallel().map { DoubleField.transform(it) }.toArray()
return BufferND(strides, array.asBuffer())
}
override fun StructureND<Double>.mapIndexed(
transform: DoubleField.(index: IntArray, Double) -> Double,
): BufferND<Double> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
DoubleField.transform(
strides.index(offset),
buffer.array[offset]
)
}.toArray()
return BufferND(strides, array.asBuffer())
}
override fun combine(
a: StructureND<Double>,
b: StructureND<Double>,
transform: DoubleField.(Double, Double) -> Double,
): BufferND<Double> {
val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset ->
DoubleField.transform(a.buffer.array[offset], b.buffer.array[offset])
}.toArray()
return BufferND(strides, array.asBuffer())
}
override fun StructureND<Double>.unaryMinus(): StructureND<Double> = map { -it }
override fun scale(a: StructureND<Double>, value: Double): StructureND<Double> = a.map { it * value }
override fun power(arg: StructureND<Double>, pow: Number): BufferND<Double> = arg.map { power(it, pow) }
override fun exp(arg: StructureND<Double>): BufferND<Double> = arg.map { exp(it) }
override fun ln(arg: StructureND<Double>): BufferND<Double> = arg.map { ln(it) }
override fun sin(arg: StructureND<Double>): BufferND<Double> = arg.map { sin(it) }
override fun cos(arg: StructureND<Double>): BufferND<Double> = arg.map { cos(it) }
override fun tan(arg: StructureND<Double>): BufferND<Double> = arg.map { tan(it) }
override fun asin(arg: StructureND<Double>): BufferND<Double> = arg.map { asin(it) }
override fun acos(arg: StructureND<Double>): BufferND<Double> = arg.map { acos(it) }
override fun atan(arg: StructureND<Double>): BufferND<Double> = arg.map { atan(it) }
override fun sinh(arg: StructureND<Double>): BufferND<Double> = arg.map { sinh(it) }
override fun cosh(arg: StructureND<Double>): BufferND<Double> = arg.map { cosh(it) }
override fun tanh(arg: StructureND<Double>): BufferND<Double> = arg.map { tanh(it) }
override fun asinh(arg: StructureND<Double>): BufferND<Double> = arg.map { asinh(it) }
override fun acosh(arg: StructureND<Double>): BufferND<Double> = arg.map { acosh(it) }
override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
}
fun AlgebraND.Companion.realWithStream(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(shape)

View File

@ -1,16 +1,16 @@
package space.kscience.kmath.structures
import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.DefaultStrides
import space.kscience.kmath.nd.NDBuffer
import kotlin.system.measureTimeMillis
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
fun main() {
val n = 6000
val array = DoubleArray(n * n) { 1.0 }
val buffer = RealBuffer(array)
val buffer = DoubleBuffer(array)
val strides = DefaultStrides(intArrayOf(n, n))
val structure = NDBuffer(strides, buffer)
val structure = BufferND(strides, buffer)
measureTimeMillis {
var res = 0.0

View File

@ -1,13 +1,13 @@
package space.kscience.kmath.structures
import space.kscience.kmath.nd.NDStructure
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.mapToBuffer
import kotlin.system.measureTimeMillis
@Suppress("UNUSED_VARIABLE")
fun main() {
val n = 6000
val structure = NDStructure.build(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 }
val structure = StructureND.buffered(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 }
structure.mapToBuffer { it + 1 } // warm-up
val time1 = measureTimeMillis { val res = structure.mapToBuffer { it + 1 } }
println("Structure mapping finished in $time1 millis")
@ -20,10 +20,10 @@ fun main() {
println("Array mapping finished in $time2 millis")
val buffer = RealBuffer(DoubleArray(n * n) { 1.0 })
val buffer = DoubleBuffer(DoubleArray(n * n) { 1.0 })
val time3 = measureTimeMillis {
val target = RealBuffer(DoubleArray(n * n))
val target = DoubleBuffer(DoubleArray(n * n))
val res = array.forEachIndexed { index, value ->
target[index] = value + 1
}

View File

@ -5,7 +5,7 @@ import space.kscience.kmath.dimensions.D3
import space.kscience.kmath.dimensions.DMatrixContext
import space.kscience.kmath.dimensions.Dimension
private fun DMatrixContext<Double>.simple() {
private fun DMatrixContext<Double, *>.simple() {
val m1 = produce<D2, D3> { i, j -> (i + j).toDouble() }
val m2 = produce<D3, D2> { i, j -> (i + j).toDouble() }
@ -17,7 +17,7 @@ private object D5 : Dimension {
override val dim: UInt = 5u
}
private fun DMatrixContext<Double>.custom() {
private fun DMatrixContext<Double, *>.custom() {
val m1 = produce<D2, D5> { i, j -> (i + j).toDouble() }
val m2 = produce<D5, D2> { i, j -> (i - j).toDouble() }
val m3 = produce<D2, D2> { i, j -> (i - j).toDouble() }

View File

@ -1,10 +1,8 @@
kotlin.code.style=official
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.stability.nowarn=true
kotlin.native.enableDependencyPropagation=false
kotlin.parallel.tasks.in.project=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m
org.gradle.jvmargs=-XX:MaxMetaspaceSize=2G
org.gradle.parallel=true
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.enableDependencyPropagation=false

View File

@ -2,17 +2,17 @@
This subproject implements the following features:
- [expression-language](src/jvmMain/kotlin/kscience/kmath/ast/parser.kt) : Expression language and its parser
- [mst](src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation
- [mst-building](src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure
- [mst-interpreter](src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST interpreter
- [mst-jvm-codegen](src/jvmMain/kotlin/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler
- [mst-js-codegen](src/jsMain/kotlin/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
- [expression-language](src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser
- [mst](src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt) : MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation
- [mst-building](src/commonMain/kotlin/space/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure
- [mst-interpreter](src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt) : MST interpreter
- [mst-jvm-codegen](src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler
- [mst-js-codegen](src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
> #### Artifact:
>
> This module artifact: `space.kscience:kmath-ast:0.2.0`.
> This module artifact: `space.kscience:kmath-ast:0.3.0-dev-3`.
>
> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-ast/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-ast/_latestVersion)
>
@ -25,13 +25,10 @@ This subproject implements the following features:
> maven { url 'https://repo.kotlin.link' }
> maven { url 'https://dl.bintray.com/hotkeytlt/maven' }
> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap
>// Uncomment if repo.kotlin.link is unavailable
>// maven { url 'https://dl.bintray.com/mipt-npm/kscience' }
>// maven { url 'https://dl.bintray.com/mipt-npm/dev' }
> }
>
> dependencies {
> implementation 'space.kscience:kmath-ast:0.2.0'
> implementation 'space.kscience:kmath-ast:0.3.0-dev-3'
> }
> ```
> **Gradle Kotlin DSL:**
@ -41,13 +38,10 @@ This subproject implements the following features:
> maven("https://repo.kotlin.link")
> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap
> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a
>// Uncomment if repo.kotlin.link is unavailable
>// maven("https://dl.bintray.com/mipt-npm/kscience")
>// maven("https://dl.bintray.com/mipt-npm/dev")
> }
>
> dependencies {
> implementation("space.kscience:kmath-ast:0.2.0")
> implementation("space.kscience:kmath-ast:0.3.0-dev-3")
> }
> ```
@ -61,10 +55,10 @@ a special implementation of `Expression<T>` with implemented `invoke` function.
For example, the following builder:
```kotlin
RealField.mstInField { symbol("x") + 2 }.compile()
DoubleField.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:
```java
package space.kscience.kmath.asm.generated;
@ -94,8 +88,8 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
This API extends MST and MstExpression, so you may optimize as both of them:
```kotlin
RealField.mstInField { symbol("x") + 2 }.compile()
RealField.expression("x+2".parseMath())
DoubleField.mstInField { symbol("x") + 2 }.compile()
DoubleField.expression("x+2".parseMath())
```
#### Known issues
@ -109,7 +103,7 @@ RealField.expression("x+2".parseMath())
A similar feature is also available on JS.
```kotlin
RealField.mstInField { symbol("x") + 2 }.compile()
DoubleField.mstInField { symbol("x") + 2 }.compile()
```
The code above returns expression implemented with such a JS function:

View File

@ -58,36 +58,36 @@ readme {
feature(
id = "expression-language",
description = "Expression language and its parser",
ref = "src/jvmMain/kotlin/kscience/kmath/ast/parser.kt"
ref = "src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt"
)
feature(
id = "mst",
description = "MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation",
ref = "src/commonMain/kotlin/kscience/kmath/ast/MST.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt"
)
feature(
id = "mst-building",
description = "MST building algebraic structure",
ref = "src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/ast/MstAlgebra.kt"
)
feature(
id = "mst-interpreter",
description = "MST interpreter",
ref = "src/commonMain/kotlin/kscience/kmath/ast/MST.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt"
)
feature(
id = "mst-jvm-codegen",
description = "Dynamic MST to JVM bytecode compiler",
ref = "src/jvmMain/kotlin/kscience/kmath/asm/asm.kt"
ref = "src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt"
)
feature(
id = "mst-js-codegen",
description = "Dynamic MST to JS compiler",
ref = "src/jsMain/kotlin/kscience/kmath/estree/estree.kt"
ref = "src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt"
)
}

View File

@ -16,7 +16,7 @@ a special implementation of `Expression<T>` with implemented `invoke` function.
For example, the following builder:
```kotlin
RealField.mstInField { symbol("x") + 2 }.compile()
DoubleField.mstInField { symbol("x") + 2 }.compile()
```
… leads to generation of bytecode, which can be decompiled to the following Java class:
@ -49,8 +49,8 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
This API extends MST and MstExpression, so you may optimize as both of them:
```kotlin
RealField.mstInField { symbol("x") + 2 }.compile()
RealField.expression("x+2".parseMath())
DoubleField.mstInField { symbol("x") + 2 }.compile()
DoubleField.expression("x+2".parseMath())
```
#### Known issues
@ -64,7 +64,7 @@ RealField.expression("x+2".parseMath())
A similar feature is also available on JS.
```kotlin
RealField.mstInField { symbol("x") + 2 }.compile()
DoubleField.mstInField { symbol("x") + 2 }.compile()
```
The code above returns expression implemented with such a JS function:

View File

@ -54,7 +54,7 @@ public object MstRing : Ring<MST>, NumbersAddOperations<MST>, ScaleOperations<MS
public override val one: MST.Numeric = number(1.0)
public override fun number(value: Number): MST.Numeric = MstGroup.number(value)
public override fun bindSymbol(value: String): MST.Symbolic = MstGroup.bindSymbol(value)
public override fun bindSymbol(value: String): MST.Symbolic = MstAlgebra.bindSymbol(value)
public override fun add(a: MST, b: MST): MST.Binary = MstGroup.add(a, b)
public override fun scale(a: MST, value: Double): MST.Binary =
@ -83,7 +83,7 @@ public object MstField : Field<MST>, NumbersAddOperations<MST>, ScaleOperations<
public override val one: MST.Numeric get() = MstRing.one
public override fun bindSymbol(value: String): MST.Symbolic = MstRing.bindSymbol(value)
public override fun bindSymbol(value: String): MST.Symbolic = MstAlgebra.bindSymbol(value)
public override fun number(value: Number): MST.Numeric = MstRing.number(value)
public override fun add(a: MST, b: MST): MST.Binary = MstRing.add(a, b)
@ -112,7 +112,7 @@ public object MstExtendedField : ExtendedField<MST>, NumericAlgebra<MST> {
public override val zero: MST.Numeric get() = MstField.zero
public override val one: MST.Numeric get() = MstField.one
public override fun bindSymbol(value: String): MST.Symbolic = MstField.bindSymbol(value)
public override fun bindSymbol(value: String): MST.Symbolic = MstAlgebra.bindSymbol(value)
public override fun number(value: Number): MST.Numeric = MstRing.number(value)
public override fun sin(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.SIN_OPERATION)(arg)
public override fun cos(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.COS_OPERATION)(arg)

View File

@ -5,7 +5,7 @@ import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.toComplex
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.ByteRing
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import kotlin.test.Test
import kotlin.test.assertEquals
@ -73,7 +73,7 @@ internal class TestESTreeConsistencyWithInterpreter {
@Test
fun realField() {
val res1 = RealField.mstInField {
val res1 = DoubleField.mstInField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")(
(3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1),
@ -81,7 +81,7 @@ internal class TestESTreeConsistencyWithInterpreter {
) + zero
}("x" to 2.0)
val res2 = RealField.mstInField {
val res2 = DoubleField.mstInField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")(
(3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1),

View File

@ -4,7 +4,7 @@ import space.kscience.kmath.ast.mstInExtendedField
import space.kscience.kmath.ast.mstInField
import space.kscience.kmath.ast.mstInGroup
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
@ -12,27 +12,29 @@ import kotlin.test.assertEquals
internal class TestESTreeOperationsSupport {
@Test
fun testUnaryOperationInvocation() {
val expression = RealField.mstInGroup { -bindSymbol("x") }.compile()
val expression = DoubleField.mstInGroup { -bindSymbol("x") }.compile()
val res = expression("x" to 2.0)
assertEquals(-2.0, res)
}
@Test
fun testBinaryOperationInvocation() {
val expression = RealField.mstInGroup { -bindSymbol("x") + number(1.0) }.compile()
val expression = DoubleField.mstInGroup { -bindSymbol("x") + number(1.0) }.compile()
val res = expression("x" to 2.0)
assertEquals(-1.0, res)
}
@Test
fun testConstProductInvocation() {
val res = RealField.mstInField { bindSymbol("x") * 2 }("x" to 2.0)
val res = DoubleField.mstInField { bindSymbol("x") * 2 }("x" to 2.0)
assertEquals(4.0, res)
}
@Test
fun testMultipleCalls() {
val e = RealField.mstInExtendedField { sin(bindSymbol("x")).pow(4) - 6 * bindSymbol("x") / tanh(bindSymbol("x")) }.compile()
val e =
DoubleField.mstInExtendedField { sin(bindSymbol("x")).pow(4) - 6 * bindSymbol("x") / tanh(bindSymbol("x")) }
.compile()
val r = Random(0)
var s = 0.0
repeat(1000000) { s += e("x" to r.nextDouble()) }

View File

@ -2,50 +2,50 @@ package space.kscience.kmath.estree
import space.kscience.kmath.ast.mstInField
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import kotlin.test.Test
import kotlin.test.assertEquals
internal class TestESTreeSpecialization {
@Test
fun testUnaryPlus() {
val expr = RealField.mstInField { unaryOperationFunction("+")(bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { unaryOperationFunction("+")(bindSymbol("x")) }.compile()
assertEquals(2.0, expr("x" to 2.0))
}
@Test
fun testUnaryMinus() {
val expr = RealField.mstInField { unaryOperationFunction("-")(bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { unaryOperationFunction("-")(bindSymbol("x")) }.compile()
assertEquals(-2.0, expr("x" to 2.0))
}
@Test
fun testAdd() {
val expr = RealField.mstInField { binaryOperationFunction("+")(bindSymbol("x"), bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { binaryOperationFunction("+")(bindSymbol("x"), bindSymbol("x")) }.compile()
assertEquals(4.0, expr("x" to 2.0))
}
@Test
fun testSine() {
val expr = RealField.mstInField { unaryOperationFunction("sin")(bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { unaryOperationFunction("sin")(bindSymbol("x")) }.compile()
assertEquals(0.0, expr("x" to 0.0))
}
@Test
fun testMinus() {
val expr = RealField.mstInField { binaryOperationFunction("-")(bindSymbol("x"), bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { binaryOperationFunction("-")(bindSymbol("x"), bindSymbol("x")) }.compile()
assertEquals(0.0, expr("x" to 2.0))
}
@Test
fun testDivide() {
val expr = RealField.mstInField { binaryOperationFunction("/")(bindSymbol("x"), bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { binaryOperationFunction("/")(bindSymbol("x"), bindSymbol("x")) }.compile()
assertEquals(1.0, expr("x" to 2.0))
}
@Test
fun testPower() {
val expr = RealField
val expr = DoubleField
.mstInField { binaryOperationFunction("pow")(bindSymbol("x"), number(2)) }
.compile()

View File

@ -86,7 +86,7 @@ internal inline fun ClassWriter.visitField(
descriptor: String,
signature: String?,
value: Any?,
block: FieldVisitor.() -> Unit
block: FieldVisitor.() -> Unit,
): FieldVisitor {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return visitField(access, name, descriptor, signature, value).apply(block)

View File

@ -5,7 +5,7 @@ import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.toComplex
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.ByteRing
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import kotlin.test.Test
import kotlin.test.assertEquals
@ -73,7 +73,7 @@ internal class TestAsmConsistencyWithInterpreter {
@Test
fun realField() {
val res1 = RealField.mstInField {
val res1 = DoubleField.mstInField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")(
(3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1),
@ -81,7 +81,7 @@ internal class TestAsmConsistencyWithInterpreter {
) + zero
}("x" to 2.0)
val res2 = RealField.mstInField {
val res2 = DoubleField.mstInField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")(
(3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1),

View File

@ -4,7 +4,7 @@ import space.kscience.kmath.ast.mstInExtendedField
import space.kscience.kmath.ast.mstInField
import space.kscience.kmath.ast.mstInGroup
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
@ -12,27 +12,29 @@ import kotlin.test.assertEquals
internal class TestAsmOperationsSupport {
@Test
fun testUnaryOperationInvocation() {
val expression = RealField.mstInGroup { -bindSymbol("x") }.compile()
val expression = DoubleField.mstInGroup { -bindSymbol("x") }.compile()
val res = expression("x" to 2.0)
assertEquals(-2.0, res)
}
@Test
fun testBinaryOperationInvocation() {
val expression = RealField.mstInGroup { -bindSymbol("x") + number(1.0) }.compile()
val expression = DoubleField.mstInGroup { -bindSymbol("x") + number(1.0) }.compile()
val res = expression("x" to 2.0)
assertEquals(-1.0, res)
}
@Test
fun testConstProductInvocation() {
val res = RealField.mstInField { bindSymbol("x") * 2 }("x" to 2.0)
val res = DoubleField.mstInField { bindSymbol("x") * 2 }("x" to 2.0)
assertEquals(4.0, res)
}
@Test
fun testMultipleCalls() {
val e = RealField.mstInExtendedField { sin(bindSymbol("x")).pow(4) - 6 * bindSymbol("x") / tanh(bindSymbol("x")) }.compile()
val e =
DoubleField.mstInExtendedField { sin(bindSymbol("x")).pow(4) - 6 * bindSymbol("x") / tanh(bindSymbol("x")) }
.compile()
val r = Random(0)
var s = 0.0
repeat(1000000) { s += e("x" to r.nextDouble()) }

View File

@ -2,50 +2,50 @@ package space.kscience.kmath.asm
import space.kscience.kmath.ast.mstInField
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import kotlin.test.Test
import kotlin.test.assertEquals
internal class TestAsmSpecialization {
@Test
fun testUnaryPlus() {
val expr = RealField.mstInField { unaryOperationFunction("+")(bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { unaryOperationFunction("+")(bindSymbol("x")) }.compile()
assertEquals(2.0, expr("x" to 2.0))
}
@Test
fun testUnaryMinus() {
val expr = RealField.mstInField { unaryOperationFunction("-")(bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { unaryOperationFunction("-")(bindSymbol("x")) }.compile()
assertEquals(-2.0, expr("x" to 2.0))
}
@Test
fun testAdd() {
val expr = RealField.mstInField { binaryOperationFunction("+")(bindSymbol("x"), bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { binaryOperationFunction("+")(bindSymbol("x"), bindSymbol("x")) }.compile()
assertEquals(4.0, expr("x" to 2.0))
}
@Test
fun testSine() {
val expr = RealField.mstInField { unaryOperationFunction("sin")(bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { unaryOperationFunction("sin")(bindSymbol("x")) }.compile()
assertEquals(0.0, expr("x" to 0.0))
}
@Test
fun testMinus() {
val expr = RealField.mstInField { binaryOperationFunction("-")(bindSymbol("x"), bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { binaryOperationFunction("-")(bindSymbol("x"), bindSymbol("x")) }.compile()
assertEquals(0.0, expr("x" to 2.0))
}
@Test
fun testDivide() {
val expr = RealField.mstInField { binaryOperationFunction("/")(bindSymbol("x"), bindSymbol("x")) }.compile()
val expr = DoubleField.mstInField { binaryOperationFunction("/")(bindSymbol("x"), bindSymbol("x")) }.compile()
assertEquals(1.0, expr("x" to 2.0))
}
@Test
fun testPower() {
val expr = RealField
val expr = DoubleField
.mstInField { binaryOperationFunction("pow")(bindSymbol("x"), number(2)) }
.compile()

View File

@ -1,12 +1,12 @@
package space.kscience.kmath.ast
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Field
import space.kscience.kmath.operations.RealField
import kotlin.test.Test
import kotlin.test.assertEquals
internal class ParserPrecedenceTest {
private val f: Field<Double> = RealField
private val f: Field<Double> = DoubleField
@Test
fun test1(): Unit = assertEquals(6.0, f.evaluate("2*2+2".parseMath()))

View File

@ -4,7 +4,7 @@ import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.Algebra
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import kotlin.test.Test
import kotlin.test.assertEquals
@ -33,7 +33,7 @@ internal class ParserTest {
@Test
fun `evaluate MST with unary function`() {
val mst = "sin(0)".parseMath()
val res = RealField.evaluate(mst)
val res = DoubleField.evaluate(mst)
assertEquals(0.0, res)
}

View File

@ -0,0 +1,92 @@
package space.kscience.kmath.commons.integration
import org.apache.commons.math3.analysis.integration.IterativeLegendreGaussIntegrator
import org.apache.commons.math3.analysis.integration.SimpsonIntegrator
import space.kscience.kmath.integration.*
import space.kscience.kmath.misc.UnstableKMathAPI
/**
* Integration wrapper for Common-maths UnivariateIntegrator
*/
public class CMIntegrator(
private val defaultMaxCalls: Int = 200,
public val integratorBuilder: (Integrand) -> org.apache.commons.math3.analysis.integration.UnivariateIntegrator,
) : UnivariateIntegrator<Double> {
public class TargetRelativeAccuracy(public val value: Double) : IntegrandFeature
public class TargetAbsoluteAccuracy(public val value: Double) : IntegrandFeature
public class MinIterations(public val value: Int) : IntegrandFeature
public class MaxIterations(public val value: Int) : IntegrandFeature
override fun integrate(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
val integrator = integratorBuilder(integrand)
val maxCalls = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: defaultMaxCalls
val remainingCalls = maxCalls - integrand.calls
val range = integrand.getFeature<IntegrationRange<Double>>()?.range
?: error("Integration range is not provided")
val res = integrator.integrate(remainingCalls, integrand.function, range.start, range.endInclusive)
return integrand +
IntegrandValue(res) +
IntegrandAbsoluteAccuracy(integrator.absoluteAccuracy) +
IntegrandRelativeAccuracy(integrator.relativeAccuracy) +
IntegrandCalls(integrator.evaluations + integrand.calls)
}
public companion object {
/**
* Create a Simpson integrator based on [SimpsonIntegrator]
*/
public fun simpson(defaultMaxCalls: Int = 200): CMIntegrator = CMIntegrator(defaultMaxCalls) { integrand ->
val absoluteAccuracy = integrand.getFeature<TargetAbsoluteAccuracy>()?.value
?: SimpsonIntegrator.DEFAULT_ABSOLUTE_ACCURACY
val relativeAccuracy = integrand.getFeature<TargetRelativeAccuracy>()?.value
?: SimpsonIntegrator.DEFAULT_ABSOLUTE_ACCURACY
val minIterations = integrand.getFeature<MinIterations>()?.value
?: SimpsonIntegrator.DEFAULT_MIN_ITERATIONS_COUNT
val maxIterations = integrand.getFeature<MaxIterations>()?.value
?: SimpsonIntegrator.SIMPSON_MAX_ITERATIONS_COUNT
SimpsonIntegrator(relativeAccuracy, absoluteAccuracy, minIterations, maxIterations)
}
/**
* Create a Gauss-Legandre integrator based on [IterativeLegendreGaussIntegrator]
*/
public fun legandre(numPoints: Int, defaultMaxCalls: Int = numPoints * 5): CMIntegrator =
CMIntegrator(defaultMaxCalls) { integrand ->
val absoluteAccuracy = integrand.getFeature<TargetAbsoluteAccuracy>()?.value
?: IterativeLegendreGaussIntegrator.DEFAULT_ABSOLUTE_ACCURACY
val relativeAccuracy = integrand.getFeature<TargetRelativeAccuracy>()?.value
?: IterativeLegendreGaussIntegrator.DEFAULT_ABSOLUTE_ACCURACY
val minIterations = integrand.getFeature<MinIterations>()?.value
?: IterativeLegendreGaussIntegrator.DEFAULT_MIN_ITERATIONS_COUNT
val maxIterations = integrand.getFeature<MaxIterations>()?.value
?: IterativeLegendreGaussIntegrator.DEFAULT_MAX_ITERATIONS_COUNT
IterativeLegendreGaussIntegrator(
numPoints,
relativeAccuracy,
absoluteAccuracy,
minIterations,
maxIterations
)
}
}
}
@UnstableKMathAPI
public var MutableList<IntegrandFeature>.targetAbsoluteAccuracy: Double?
get() = filterIsInstance<CMIntegrator.TargetAbsoluteAccuracy>().lastOrNull()?.value
set(value) {
value?.let { add(CMIntegrator.TargetAbsoluteAccuracy(value)) }
}
@UnstableKMathAPI
public var MutableList<IntegrandFeature>.targetRelativeAccuracy: Double?
get() = filterIsInstance<CMIntegrator.TargetRelativeAccuracy>().lastOrNull()?.value
set(value) {
value?.let { add(CMIntegrator.TargetRelativeAccuracy(value)) }
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2015 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package space.kscience.kmath.commons.integration
import org.apache.commons.math3.analysis.integration.gauss.GaussIntegrator
import org.apache.commons.math3.analysis.integration.gauss.GaussIntegratorFactory
import space.kscience.kmath.integration.*
/**
* A simple one-pass integrator based on Gauss rule
*/
public class GaussRuleIntegrator(
private val numpoints: Int,
private var type: GaussRule = GaussRule.LEGANDRE,
) : UnivariateIntegrator<Double> {
override fun integrate(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
val range = integrand.getFeature<IntegrationRange<Double>>()?.range
?: error("Integration range is not provided")
val integrator: GaussIntegrator = getIntegrator(range)
//TODO check performance
val res: Double = integrator.integrate(integrand.function)
return integrand + IntegrandValue(res) + IntegrandCalls(integrand.calls + numpoints)
}
private fun getIntegrator(range: ClosedRange<Double>): GaussIntegrator {
return when (type) {
GaussRule.LEGANDRE -> factory.legendre(
numpoints,
range.start,
range.endInclusive
)
GaussRule.LEGANDREHP -> factory.legendreHighPrecision(
numpoints,
range.start,
range.endInclusive
)
GaussRule.UNIFORM -> GaussIntegrator(
getUniformRule(
range.start,
range.endInclusive,
numpoints
)
)
}
}
private fun getUniformRule(
min: Double,
max: Double,
numPoints: Int,
): org.apache.commons.math3.util.Pair<DoubleArray, DoubleArray> {
assert(numPoints > 2)
val points = DoubleArray(numPoints)
val weights = DoubleArray(numPoints)
val step = (max - min) / (numPoints - 1)
points[0] = min
for (i in 1 until numPoints) {
points[i] = points[i - 1] + step
weights[i] = step
}
return org.apache.commons.math3.util.Pair<DoubleArray, DoubleArray>(points, weights)
}
public enum class GaussRule {
UNIFORM, LEGANDRE, LEGANDREHP
}
public companion object {
private val factory: GaussIntegratorFactory = GaussIntegratorFactory()
public fun integrate(
range: ClosedRange<Double>,
numPoints: Int = 100,
type: GaussRule = GaussRule.LEGANDRE,
function: (Double) -> Double,
): Double = GaussRuleIntegrator(numPoints, type).integrate(
UnivariateIntegrand(function, IntegrationRange(range))
).value!!
}
}

View File

@ -3,7 +3,8 @@ package space.kscience.kmath.commons.linear
import org.apache.commons.math3.linear.*
import space.kscience.kmath.linear.*
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.RealBuffer
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.DoubleBuffer
import kotlin.reflect.KClass
import kotlin.reflect.cast
@ -11,8 +12,90 @@ public inline class CMMatrix(public val origin: RealMatrix) : Matrix<Double> {
public override val rowNum: Int get() = origin.rowDimension
public override val colNum: Int get() = origin.columnDimension
public override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j)
}
public inline class CMVector(public val origin: RealVector) : Point<Double> {
public override val size: Int get() = origin.dimension
public override operator fun get(index: Int): Double = origin.getEntry(index)
public override operator fun iterator(): Iterator<Double> = origin.toArray().iterator()
}
public fun RealVector.toPoint(): CMVector = CMVector(this)
public object CMLinearSpace : LinearSpace<Double, DoubleField> {
override val elementAlgebra: DoubleField get() = DoubleField
public override fun buildMatrix(
rows: Int,
columns: Int,
initializer: DoubleField.(i: Int, j: Int) -> Double,
): CMMatrix {
val array = Array(rows) { i -> DoubleArray(columns) { j -> DoubleField.initializer(i, j) } }
return CMMatrix(Array2DRowRealMatrix(array))
}
@OptIn(UnstableKMathAPI::class)
public fun Matrix<Double>.toCM(): CMMatrix = when (val matrix = origin) {
is CMMatrix -> matrix
else -> {
//TODO add feature analysis
val array = Array(rowNum) { i -> DoubleArray(colNum) { j -> get(i, j) } }
Array2DRowRealMatrix(array).wrap()
}
}
public fun Point<Double>.toCM(): CMVector = if (this is CMVector) this else {
val array = DoubleArray(size) { this[it] }
ArrayRealVector(array).wrap()
}
internal fun RealMatrix.wrap(): CMMatrix = CMMatrix(this)
internal fun RealVector.wrap(): CMVector = CMVector(this)
override fun buildVector(size: Int, initializer: DoubleField.(Int) -> Double): Point<Double> =
ArrayRealVector(DoubleArray(size) { DoubleField.initializer(it) }).wrap()
override fun Matrix<Double>.plus(other: Matrix<Double>): CMMatrix =
toCM().origin.add(other.toCM().origin).wrap()
override fun Point<Double>.plus(other: Point<Double>): CMVector =
toCM().origin.add(other.toCM().origin).wrap()
override fun Point<Double>.minus(other: Point<Double>): CMVector =
toCM().origin.subtract(other.toCM().origin).wrap()
public override fun Matrix<Double>.dot(other: Matrix<Double>): CMMatrix =
toCM().origin.multiply(other.toCM().origin).wrap()
public override fun Matrix<Double>.dot(vector: Point<Double>): CMVector =
toCM().origin.preMultiply(vector.toCM().origin).wrap()
public override operator fun Matrix<Double>.minus(other: Matrix<Double>): CMMatrix =
toCM().origin.subtract(other.toCM().origin).wrap()
public override operator fun Matrix<Double>.times(value: Double): CMMatrix =
toCM().origin.scalarMultiply(value).wrap()
override fun Double.times(m: Matrix<Double>): CMMatrix =
m * this
override fun Point<Double>.times(value: Double): CMVector =
toCM().origin.mapMultiply(value).wrap()
override fun Double.times(v: Point<Double>): CMVector =
v * this
@UnstableKMathAPI
override fun <T : Any> getFeature(type: KClass<T>): T? = when (type) {
override fun <F : Any> getFeature(structure: Matrix<Double>, type: KClass<F>): F? {
//Return the feature if it is intrinsic to the structure
structure.getFeature(type)?.let { return it }
val origin = structure.toCM().origin
return when (type) {
DiagonalFeature::class -> if (origin is DiagonalMatrix) DiagonalFeature else null
DeterminantFeature::class, LupDecompositionFeature::class -> object :
@ -43,79 +126,15 @@ public inline class CMMatrix(public val origin: RealMatrix) : Matrix<Double> {
override val u: Matrix<Double> by lazy { CMMatrix(sv.u) }
override val s: Matrix<Double> by lazy { CMMatrix(sv.s) }
override val v: Matrix<Double> by lazy { CMMatrix(sv.v) }
override val singularValues: Point<Double> by lazy { RealBuffer(sv.singularValues) }
override val singularValues: Point<Double> by lazy { DoubleBuffer(sv.singularValues) }
}
else -> null
}?.let(type::cast)
public override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j)
}
public fun RealMatrix.asMatrix(): CMMatrix = CMMatrix(this)
public class CMVector(public val origin: RealVector) : Point<Double> {
public override val size: Int get() = origin.dimension
public override operator fun get(index: Int): Double = origin.getEntry(index)
public override operator fun iterator(): Iterator<Double> = origin.toArray().iterator()
}
public fun Point<Double>.toCM(): CMVector = if (this is CMVector) this else {
val array = DoubleArray(size) { this[it] }
CMVector(ArrayRealVector(array))
}
public fun RealVector.toPoint(): CMVector = CMVector(this)
public object CMMatrixContext : MatrixContext<Double, CMMatrix> {
public override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): CMMatrix {
val array = Array(rows) { i -> DoubleArray(columns) { j -> initializer(i, j) } }
return CMMatrix(Array2DRowRealMatrix(array))
}
@OptIn(UnstableKMathAPI::class)
public fun Matrix<Double>.toCM(): CMMatrix = when (val matrix = origin) {
is CMMatrix -> matrix
else -> {
//TODO add feature analysis
val array = Array(rowNum) { i -> DoubleArray(colNum) { j -> get(i, j) } }
CMMatrix(Array2DRowRealMatrix(array))
}
}
override fun scale(a: Matrix<Double>, value: Double): Matrix<Double> = a.toCM().times(value)
public operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CMMatrix(origin.add(other.origin))
public operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = CMMatrix(origin.subtract(other.origin))
public override fun Matrix<Double>.dot(other: Matrix<Double>): CMMatrix =
CMMatrix(toCM().origin.multiply(other.toCM().origin))
public override fun Matrix<Double>.dot(vector: Point<Double>): CMVector =
CMVector(toCM().origin.preMultiply(vector.toCM().origin))
public override operator fun Matrix<Double>.unaryMinus(): CMMatrix =
produce(rowNum, colNum) { i, j -> -get(i, j) }
public override fun add(a: Matrix<Double>, b: Matrix<Double>): CMMatrix =
CMMatrix(a.toCM().origin.multiply(b.toCM().origin))
public override operator fun Matrix<Double>.minus(b: Matrix<Double>): CMMatrix =
CMMatrix(toCM().origin.subtract(b.toCM().origin))
// public override fun multiply(a: Matrix<Double>, k: Number): CMMatrix =
// CMMatrix(a.toCM().origin.scalarMultiply(k.toDouble()))
public override operator fun Matrix<Double>.times(value: Double): CMMatrix =
produce(rowNum, colNum) { i, j -> get(i, j) * value }
}
public operator fun CMMatrix.plus(other: CMMatrix): CMMatrix =
CMMatrix(origin.add(other.origin))
public operator fun CMMatrix.minus(other: CMMatrix): CMMatrix =
CMMatrix(origin.subtract(other.origin))
public infix fun CMMatrix.dot(other: CMMatrix): CMMatrix =
CMMatrix(origin.multiply(other.origin))
public infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = CMMatrix(origin.multiply(other.origin))

View File

@ -12,9 +12,9 @@ public enum class CMDecomposition {
CHOLESKY
}
public fun CMMatrixContext.solver(
public fun CMLinearSpace.solver(
a: Matrix<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP
decomposition: CMDecomposition = CMDecomposition.LUP,
): DecompositionSolver = when (decomposition) {
CMDecomposition.LUP -> LUDecomposition(a.toCM().origin).solver
CMDecomposition.RRQR -> RRQRDecomposition(a.toCM().origin).solver
@ -23,19 +23,19 @@ public fun CMMatrixContext.solver(
CMDecomposition.CHOLESKY -> CholeskyDecomposition(a.toCM().origin).solver
}
public fun CMMatrixContext.solve(
public fun CMLinearSpace.solve(
a: Matrix<Double>,
b: Matrix<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP
): CMMatrix = solver(a, decomposition).solve(b.toCM().origin).asMatrix()
decomposition: CMDecomposition = CMDecomposition.LUP,
): CMMatrix = solver(a, decomposition).solve(b.toCM().origin).wrap()
public fun CMMatrixContext.solve(
public fun CMLinearSpace.solve(
a: Matrix<Double>,
b: Point<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP
decomposition: CMDecomposition = CMDecomposition.LUP,
): CMVector = solver(a, decomposition).solve(b.toCM().origin).toPoint()
public fun CMMatrixContext.inverse(
public fun CMLinearSpace.inverse(
a: Matrix<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP
): CMMatrix = solver(a, decomposition).inverse.asMatrix()
decomposition: CMDecomposition = CMDecomposition.LUP,
): CMMatrix = solver(a, decomposition).inverse.wrap()

View File

@ -19,7 +19,7 @@ import kotlin.reflect.KClass
public operator fun PointValuePair.component1(): DoubleArray = point
public operator fun PointValuePair.component2(): Double = value
public class CMOptimizationProblem(override val symbols: List<Symbol>, ) :
public class CMOptimizationProblem(override val symbols: List<Symbol>) :
OptimizationProblem<Double>, SymbolIndexer, OptimizationFeature {
private val optimizationData: HashMap<KClass<out OptimizationData>, OptimizationData> = HashMap()
private var optimizatorBuilder: (() -> MultivariateOptimizer)? = null

View File

@ -10,6 +10,7 @@ import space.kscience.kmath.streaming.spread
import space.kscience.kmath.structures.*
/**
* Streaming and buffer transformations
*/
@ -17,7 +18,7 @@ public object Transformations {
private fun Buffer<Complex>.toArray(): Array<org.apache.commons.math3.complex.Complex> =
Array(size) { org.apache.commons.math3.complex.Complex(get(it).re, get(it).im) }
private fun Buffer<Double>.asArray() = if (this is RealBuffer) {
private fun Buffer<Double>.asArray() = if (this is DoubleBuffer) {
array
} else {
DoubleArray(size) { i -> get(i) }
@ -33,34 +34,34 @@ public object Transformations {
public fun fourier(
normalization: DftNormalization = DftNormalization.STANDARD,
direction: TransformType = TransformType.FORWARD
direction: TransformType = TransformType.FORWARD,
): SuspendBufferTransform<Complex, Complex> = {
FastFourierTransformer(normalization).transform(it.toArray(), direction).asBuffer()
}
public fun realFourier(
normalization: DftNormalization = DftNormalization.STANDARD,
direction: TransformType = TransformType.FORWARD
direction: TransformType = TransformType.FORWARD,
): SuspendBufferTransform<Double, Complex> = {
FastFourierTransformer(normalization).transform(it.asArray(), direction).asBuffer()
}
public fun sine(
normalization: DstNormalization = DstNormalization.STANDARD_DST_I,
direction: TransformType = TransformType.FORWARD
direction: TransformType = TransformType.FORWARD,
): SuspendBufferTransform<Double, Double> = {
FastSineTransformer(normalization).transform(it.asArray(), direction).asBuffer()
}
public fun cosine(
normalization: DctNormalization = DctNormalization.STANDARD_DCT_I,
direction: TransformType = TransformType.FORWARD
direction: TransformType = TransformType.FORWARD,
): SuspendBufferTransform<Double, Double> = {
FastCosineTransformer(normalization).transform(it.asArray(), direction).asBuffer()
}
public fun hadamard(
direction: TransformType = TransformType.FORWARD
direction: TransformType = TransformType.FORWARD,
): SuspendBufferTransform<Double, Double> = {
FastHadamardTransformer().transform(it.asArray(), direction).asBuffer()
}
@ -72,7 +73,7 @@ public object Transformations {
@FlowPreview
public fun Flow<Buffer<Complex>>.FFT(
normalization: DftNormalization = DftNormalization.STANDARD,
direction: TransformType = TransformType.FORWARD
direction: TransformType = TransformType.FORWARD,
): Flow<Buffer<Complex>> {
val transform = Transformations.fourier(normalization, direction)
return map { transform(it) }
@ -82,7 +83,7 @@ public fun Flow<Buffer<Complex>>.FFT(
@JvmName("realFFT")
public fun Flow<Buffer<Double>>.FFT(
normalization: DftNormalization = DftNormalization.STANDARD,
direction: TransformType = TransformType.FORWARD
direction: TransformType = TransformType.FORWARD,
): Flow<Buffer<Complex>> {
val transform = Transformations.realFourier(normalization, direction)
return map(transform)
@ -96,7 +97,7 @@ public fun Flow<Buffer<Double>>.FFT(
public fun Flow<Double>.FFT(
bufferSize: Int = Int.MAX_VALUE,
normalization: DftNormalization = DftNormalization.STANDARD,
direction: TransformType = TransformType.FORWARD
direction: TransformType = TransformType.FORWARD,
): Flow<Complex> = chunked(bufferSize).FFT(normalization, direction).spread()
/**

View File

@ -0,0 +1,30 @@
package space.kscience.kmath.commons.integration
import org.junit.jupiter.api.Test
import space.kscience.kmath.integration.integrate
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.DoubleField.sin
import kotlin.math.PI
import kotlin.math.abs
import kotlin.test.assertTrue
@UnstableKMathAPI
internal class IntegrationTest {
private val function: (Double) -> Double = { sin(it) }
@Test
fun simpson() {
val res = CMIntegrator.simpson().integrate(0.0..2 * PI, function)
assertTrue { abs(res) < 1e-3 }
}
@Test
fun customSimpson() {
val res = CMIntegrator.simpson().integrate(0.0..PI, function) {
targetRelativeAccuracy = 1e-4
targetAbsoluteAccuracy = 1e-4
}
assertTrue { abs(res - 2) < 1e-3 }
assertTrue { abs(res - 2) > 1e-12 }
}
}

View File

@ -2,13 +2,13 @@
Complex and hypercomplex number systems in KMath:
- [complex](src/commonMain/kotlin/kscience/kmath/complex/Complex.kt) : Complex Numbers
- [quaternion](src/commonMain/kotlin/kscience/kmath/complex/Quaternion.kt) : Quaternions
- [complex](src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt) : Complex Numbers
- [quaternion](src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt) : Quaternions
> #### Artifact:
>
> This module artifact: `space.kscience:kmath-complex:0.2.0`.
> This module artifact: `space.kscience:kmath-complex:0.3.0-dev-3`.
>
> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-complex/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-complex/_latestVersion)
>
@ -21,13 +21,10 @@ Complex and hypercomplex number systems in KMath:
> maven { url 'https://repo.kotlin.link' }
> maven { url 'https://dl.bintray.com/hotkeytlt/maven' }
> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap
>// Uncomment if repo.kotlin.link is unavailable
>// maven { url 'https://dl.bintray.com/mipt-npm/kscience' }
>// maven { url 'https://dl.bintray.com/mipt-npm/dev' }
> }
>
> dependencies {
> implementation 'space.kscience:kmath-complex:0.2.0'
> implementation 'space.kscience:kmath-complex:0.3.0-dev-3'
> }
> ```
> **Gradle Kotlin DSL:**
@ -37,12 +34,9 @@ Complex and hypercomplex number systems in KMath:
> maven("https://repo.kotlin.link")
> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap
> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a
>// Uncomment if repo.kotlin.link is unavailable
>// maven("https://dl.bintray.com/mipt-npm/kscience")
>// maven("https://dl.bintray.com/mipt-npm/dev")
> }
>
> dependencies {
> implementation("space.kscience:kmath-complex:0.2.0")
> implementation("space.kscience:kmath-complex:0.3.0-dev-3")
> }
> ```

View File

@ -25,12 +25,12 @@ readme {
feature(
id = "complex",
description = "Complex Numbers",
ref = "src/commonMain/kotlin/kscience/kmath/complex/Complex.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt"
)
feature(
id = "quaternion",
description = "Quaternions",
ref = "src/commonMain/kotlin/kscience/kmath/complex/Quaternion.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt"
)
}

View File

@ -1,10 +1,10 @@
package space.kscience.kmath.complex
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.BufferedNDField
import space.kscience.kmath.nd.NDAlgebra
import space.kscience.kmath.nd.NDBuffer
import space.kscience.kmath.nd.NDStructure
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.BufferedFieldND
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.NumbersAddOperations
import space.kscience.kmath.structures.Buffer
@ -16,16 +16,16 @@ import kotlin.contracts.contract
* An optimized nd-field for complex numbers
*/
@OptIn(UnstableKMathAPI::class)
public class ComplexNDField(
public class ComplexFieldND(
shape: IntArray,
) : BufferedNDField<Complex, ComplexField>(shape, ComplexField, Buffer.Companion::complex),
NumbersAddOperations<NDStructure<Complex>>,
ExtendedField<NDStructure<Complex>> {
) : BufferedFieldND<Complex, ComplexField>(shape, ComplexField, Buffer.Companion::complex),
NumbersAddOperations<StructureND<Complex>>,
ExtendedField<StructureND<Complex>> {
override val zero: NDBuffer<Complex> by lazy { produce { zero } }
override val one: NDBuffer<Complex> by lazy { produce { one } }
override val zero: BufferND<Complex> by lazy { produce { zero } }
override val one: BufferND<Complex> by lazy { produce { one } }
override fun number(value: Number): NDBuffer<Complex> {
override fun number(value: Number): BufferND<Complex> {
val d = value.toComplex() // minimize conversions
return produce { d }
}
@ -34,15 +34,15 @@ public class ComplexNDField(
// @Suppress("OVERRIDE_BY_INLINE")
// override inline fun map(
// arg: AbstractNDBuffer<Double>,
// transform: RealField.(Double) -> Double,
// transform: DoubleField.(Double) -> Double,
// ): RealNDElement {
// check(arg)
// val array = RealBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) }
// val array = RealBuffer(arg.strides.linearSize) { offset -> DoubleField.transform(arg.buffer[offset]) }
// return BufferedNDFieldElement(this, array)
// }
//
// @Suppress("OVERRIDE_BY_INLINE")
// override inline fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
// override inline fun produce(initializer: DoubleField.(IntArray) -> Double): RealNDElement {
// val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
// return BufferedNDFieldElement(this, array)
// }
@ -50,7 +50,7 @@ public class ComplexNDField(
// @Suppress("OVERRIDE_BY_INLINE")
// override inline fun mapIndexed(
// arg: AbstractNDBuffer<Double>,
// transform: RealField.(index: IntArray, Double) -> Double,
// transform: DoubleField.(index: IntArray, Double) -> Double,
// ): RealNDElement {
// check(arg)
// return BufferedNDFieldElement(
@ -67,7 +67,7 @@ public class ComplexNDField(
// override inline fun combine(
// a: AbstractNDBuffer<Double>,
// b: AbstractNDBuffer<Double>,
// transform: RealField.(Double, Double) -> Double,
// transform: DoubleField.(Double, Double) -> Double,
// ): RealNDElement {
// check(a, b)
// val buffer = RealBuffer(strides.linearSize) { offset ->
@ -76,44 +76,44 @@ public class ComplexNDField(
// return BufferedNDFieldElement(this, buffer)
// }
override fun power(arg: NDStructure<Complex>, pow: Number): NDBuffer<Complex> = arg.map { power(it, pow) }
override fun power(arg: StructureND<Complex>, pow: Number): BufferND<Complex> = arg.map { power(it, pow) }
override fun exp(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { exp(it) }
override fun exp(arg: StructureND<Complex>): BufferND<Complex> = arg.map { exp(it) }
override fun ln(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { ln(it) }
override fun ln(arg: StructureND<Complex>): BufferND<Complex> = arg.map { ln(it) }
override fun sin(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { sin(it) }
override fun cos(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { cos(it) }
override fun tan(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { tan(it) }
override fun asin(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { asin(it) }
override fun acos(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { acos(it) }
override fun atan(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { atan(it) }
override fun sin(arg: StructureND<Complex>): BufferND<Complex> = arg.map { sin(it) }
override fun cos(arg: StructureND<Complex>): BufferND<Complex> = arg.map { cos(it) }
override fun tan(arg: StructureND<Complex>): BufferND<Complex> = arg.map { tan(it) }
override fun asin(arg: StructureND<Complex>): BufferND<Complex> = arg.map { asin(it) }
override fun acos(arg: StructureND<Complex>): BufferND<Complex> = arg.map { acos(it) }
override fun atan(arg: StructureND<Complex>): BufferND<Complex> = arg.map { atan(it) }
override fun sinh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { sinh(it) }
override fun cosh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { cosh(it) }
override fun tanh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { tanh(it) }
override fun asinh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { asinh(it) }
override fun acosh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { acosh(it) }
override fun atanh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { atanh(it) }
override fun sinh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { sinh(it) }
override fun cosh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { cosh(it) }
override fun tanh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { tanh(it) }
override fun asinh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { asinh(it) }
override fun acosh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { acosh(it) }
override fun atanh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { atanh(it) }
}
/**
* Fast element production using function inlining
*/
public inline fun BufferedNDField<Complex, ComplexField>.produceInline(initializer: ComplexField.(Int) -> Complex): NDBuffer<Complex> {
public inline fun BufferedFieldND<Complex, ComplexField>.produceInline(initializer: ComplexField.(Int) -> Complex): BufferND<Complex> {
contract { callsInPlace(initializer, InvocationKind.EXACTLY_ONCE) }
val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.initializer(offset) }
return NDBuffer(strides, buffer)
return BufferND(strides, buffer)
}
public fun NDAlgebra.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDField(shape)
public fun AlgebraND.Companion.complex(vararg shape: Int): ComplexFieldND = ComplexFieldND(shape)
/**
* Produce a context for n-dimensional operations inside this real field
*/
public inline fun <R> ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R {
public inline fun <R> ComplexField.nd(vararg shape: Int, action: ComplexFieldND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ComplexNDField(shape).action()
return ComplexFieldND(shape).action()
}

49
kmath-core/README.md Normal file
View File

@ -0,0 +1,49 @@
# The Core Module (`kmath-core`)
The core features of KMath:
- [algebras](src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Algebraic structures like rings, spaces and fields.
- [nd](src/commonMain/kotlin/space/kscience/kmath/structures/StructureND.kt) : Many-dimensional structures and operations on them.
- [linear](src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition.
- [buffers](src/commonMain/kotlin/space/kscience/kmath/structures/Buffers.kt) : One-dimensional structure
- [expressions](src/commonMain/kotlin/space/kscience/kmath/expressions) : By writing a single mathematical expression once, users will be able to apply different types of
objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high
performance calculations to code generation.
- [domains](src/commonMain/kotlin/space/kscience/kmath/domains) : Domains
- [autodif](src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation
> #### Artifact:
>
> This module artifact: `space.kscience:kmath-core:0.3.0-dev-3`.
>
> 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)
>
> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion)
>
> **Gradle:**
>
> ```gradle
> repositories {
> maven { url 'https://repo.kotlin.link' }
> maven { url 'https://dl.bintray.com/hotkeytlt/maven' }
> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap
> }
>
> dependencies {
> implementation 'space.kscience:kmath-core:0.3.0-dev-3'
> }
> ```
> **Gradle Kotlin DSL:**
>
> ```kotlin
> repositories {
> maven("https://repo.kotlin.link")
> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap
> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a
> }
>
> dependencies {
> implementation("space.kscience:kmath-core:0.3.0-dev-3")
> }
> ```

File diff suppressed because it is too large Load Diff

View File

@ -23,13 +23,13 @@ readme {
description = """
Algebraic structures like rings, spaces and fields.
""".trimIndent(),
ref = "src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt"
)
feature(
id = "nd",
description = "Many-dimensional structures and operations on them.",
ref = "src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/structures/StructureND.kt"
)
feature(
@ -37,13 +37,13 @@ readme {
description = """
Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition.
""".trimIndent(),
ref = "src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt"
)
feature(
id = "buffers",
description = "One-dimensional structure",
ref = "src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/structures/Buffers.kt"
)
feature(
@ -53,18 +53,18 @@ readme {
objects to the expression by providing a context. Expressions can be used for a wide variety of purposes from high
performance calculations to code generation.
""".trimIndent(),
ref = "src/commonMain/kotlin/kscience/kmath/expressions"
ref = "src/commonMain/kotlin/space/kscience/kmath/expressions"
)
feature(
id = "domains",
description = "Domains",
ref = "src/commonMain/kotlin/kscience/kmath/domains"
ref = "src/commonMain/kotlin/space/kscience/kmath/domains"
)
feature(
id = "autodif",
description = "Automatic differentiation",
ref = "src/commonMain/kotlin/kscience/kmath/expressions/SimpleAutoDiff.kt"
ref = "src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt"
)
}

View File

@ -23,7 +23,7 @@ import space.kscience.kmath.misc.UnstableKMathAPI
* @author Alexander Nozik
*/
@UnstableKMathAPI
public interface RealDomain : Domain<Double> {
public interface DoubleDomain : Domain<Double> {
/**
* Global lower edge

View File

@ -27,7 +27,7 @@ import space.kscience.kmath.structures.indices
* @author Alexander Nozik
*/
@UnstableKMathAPI
public class HyperSquareDomain(private val lower: Buffer<Double>, private val upper: Buffer<Double>) : RealDomain {
public class HyperSquareDomain(private val lower: Buffer<Double>, private val upper: Buffer<Double>) : DoubleDomain {
public override val dimension: Int get() = lower.size
public override operator fun contains(point: Point<Double>): Boolean = point.indices.all { i ->

View File

@ -19,7 +19,7 @@ import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
@UnstableKMathAPI
public class UnconstrainedDomain(public override val dimension: Int) : RealDomain {
public class UnconstrainedDomain(public override val dimension: Int) : DoubleDomain {
public override operator fun contains(point: Point<Double>): Boolean = true
public override fun getLowerBound(num: Int): Double = Double.NEGATIVE_INFINITY

View File

@ -4,7 +4,7 @@ import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
@UnstableKMathAPI
public inline class UnivariateDomain(public val range: ClosedFloatingPointRange<Double>) : RealDomain {
public inline class UnivariateDomain(public val range: ClosedFloatingPointRange<Double>) : DoubleDomain {
public override val dimension: Int get() = 1
public operator fun contains(d: Double): Boolean = range.contains(d)

View File

@ -198,7 +198,7 @@ public open class SimpleAutoDiffField<T : Any, F : Field<T>>(
* Example:
* ```
* val x by symbol // define variable(s) and their values
* val y = RealField.withAutoDiff() { sqr(x) + 5 * x + 3 } // write formulate in deriv context
* val y = DoubleField.withAutoDiff() { sqr(x) + 5 * x + 3 } // write formulate in deriv context
* assertEquals(17.0, y.x) // the value of result (y)
* assertEquals(9.0, x.d) // dy/dx
* ```

View File

@ -1,144 +0,0 @@
package space.kscience.kmath.linear
import space.kscience.kmath.nd.NDStructure
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.asSequence
/**
* Alias for [Structure2D] with more familiar name.
*
* @param T the type of items.
*/
public typealias Matrix<T> = Structure2D<T>
/**
* Basic implementation of Matrix space based on [NDStructure]
*/
public class BufferMatrixContext<T : Any, A>(
public override val elementContext: A,
private val bufferFactory: BufferFactory<T>,
) : GenericMatrixContext<T, A, BufferMatrix<T>> where A : Ring<T>, A : ScaleOperations<T> {
public override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): BufferMatrix<T> {
val buffer = bufferFactory(rows * columns) { offset -> initializer(offset / columns, offset % columns) }
return BufferMatrix(rows, columns, buffer)
}
override fun scale(a: Matrix<T>, value: Double): Matrix<T> = elementContext {
produce(a.rowNum, a.colNum) { i, j ->
a[i, j] * value
}
}
public override fun point(size: Int, initializer: (Int) -> T): Point<T> = bufferFactory(size, initializer)
private fun Matrix<T>.toBufferMatrix(): BufferMatrix<T> = if (this is BufferMatrix) this else {
produce(rowNum, colNum) { i, j -> get(i, j) }
}
public fun one(rows: Int, columns: Int): Matrix<Double> = VirtualMatrix(rows, columns) { i, j ->
if (i == j) 1.0 else 0.0
} + DiagonalFeature
public override infix fun Matrix<T>.dot(other: Matrix<T>): BufferMatrix<T> {
require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" }
val bufferMatrix = toBufferMatrix()
val otherBufferMatrix = other.toBufferMatrix()
return elementContext {
produce(rowNum, other.colNum) { i, j ->
var res = one
for (l in 0 until colNum) {
res += bufferMatrix[i, l] * otherBufferMatrix[l, j]
}
res
}
}
}
public override infix fun Matrix<T>.dot(vector: Point<T>): Point<T> {
require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" }
val bufferMatrix = toBufferMatrix()
return elementContext {
bufferFactory(rowNum) { i ->
var res = one
for (j in 0 until colNum) {
res += bufferMatrix[i, j] * vector[j]
}
res
}
}
}
override fun add(a: Matrix<T>, b: Matrix<T>): BufferMatrix<T> {
require(a.rowNum == b.rowNum) { "Row number mismatch in matrix addition. Left side: ${a.rowNum}, right side: ${b.rowNum}" }
require(a.colNum == b.colNum) { "Column number mismatch in matrix addition. Left side: ${a.colNum}, right side: ${b.colNum}" }
val aBufferMatrix = a.toBufferMatrix()
val bBufferMatrix = b.toBufferMatrix()
return elementContext {
produce(a.rowNum, a.colNum) { i, j ->
aBufferMatrix[i, j] + bBufferMatrix[i, j]
}
}
}
// override fun multiply(a: Matrix<T>, k: Number): BufferMatrix<T> {
// val aBufferMatrix = a.toBufferMatrix()
// return elementContext {
// produce(a.rowNum, a.colNum) { i, j -> aBufferMatrix[i, j] * k.toDouble() }
// }
// }
public companion object
}
public class BufferMatrix<T : Any>(
public override val rowNum: Int,
public override val colNum: Int,
public val buffer: Buffer<T>,
) : Matrix<T> {
init {
require(buffer.size == rowNum * colNum) { "Dimension mismatch for matrix structure" }
}
override val shape: IntArray get() = intArrayOf(rowNum, colNum)
public override operator fun get(index: IntArray): T = get(index[0], index[1])
public override operator fun get(i: Int, j: Int): T = buffer[i * colNum + j]
public override fun elements(): Sequence<Pair<IntArray, T>> = sequence {
for (i in 0 until rowNum) for (j in 0 until colNum) yield(intArrayOf(i, j) to get(i, j))
}
public override fun equals(other: Any?): Boolean {
if (this === other) return true
return when (other) {
is NDStructure<*> -> NDStructure.contentEquals(this, other)
else -> false
}
}
override fun hashCode(): Int {
var result = rowNum
result = 31 * result + colNum
result = 31 * result + buffer.hashCode()
return result
}
public override fun toString(): String {
return if (rowNum <= 5 && colNum <= 5)
"Matrix(rowsNum = $rowNum, colNum = $colNum)\n" +
rows.asSequence().joinToString(prefix = "(", postfix = ")", separator = "\n ") { buffer ->
buffer.asSequence().joinToString(separator = "\t") { it.toString() }
}
else "Matrix(rowsNum = $rowNum, colNum = $colNum)"
}
}

View File

@ -0,0 +1,83 @@
package space.kscience.kmath.linear
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.VirtualBuffer
import space.kscience.kmath.structures.indices
public class BufferedLinearSpace<T : Any, A : Ring<T>>(
override val elementAlgebra: A,
private val bufferFactory: BufferFactory<T>,
) : LinearSpace<T, A> {
private fun ndRing(
rows: Int,
cols: Int,
): BufferedRingND<T, A> = AlgebraND.ring(elementAlgebra, bufferFactory, rows, cols)
override fun buildMatrix(rows: Int, columns: Int, initializer: A.(i: Int, j: Int) -> T): Matrix<T> =
ndRing(rows, columns).produce { (i, j) -> elementAlgebra.initializer(i, j) }.as2D()
override fun buildVector(size: Int, initializer: A.(Int) -> T): Point<T> =
bufferFactory(size) { elementAlgebra.initializer(it) }
override fun Matrix<T>.unaryMinus(): Matrix<T> = ndRing(rowNum, colNum).run {
unwrap().map { -it }.as2D()
}
override fun Matrix<T>.plus(other: Matrix<T>): Matrix<T> = ndRing(rowNum, colNum).run {
require(shape.contentEquals(other.shape)) { "Shape mismatch on Matrix::plus. Expected $shape but found ${other.shape}" }
unwrap().plus(other.unwrap()).as2D()
}
override fun Matrix<T>.minus(other: Matrix<T>): Matrix<T> = ndRing(rowNum, colNum).run {
require(shape.contentEquals(other.shape)) { "Shape mismatch on Matrix::minus. Expected $shape but found ${other.shape}" }
unwrap().minus(other.unwrap()).as2D()
}
private fun Buffer<T>.linearize() = if (this is VirtualBuffer) {
buildVector(size) { get(it) }
} else {
this
}
override fun Matrix<T>.dot(other: Matrix<T>): Matrix<T> {
require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" }
return elementAlgebra {
val rows = this@dot.rows.map { it.linearize() }
val columns = other.columns.map { it.linearize() }
buildMatrix(rowNum, other.colNum) { i, j ->
val r = rows[i]
val c = columns[j]
var res = zero
for (l in r.indices) {
res += r[l] * c[l]
}
res
}
}
}
override fun Matrix<T>.dot(vector: Point<T>): Point<T> {
require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" }
return elementAlgebra {
val rows = this@dot.rows.map { it.linearize() }
buildVector(rowNum) { i ->
val r = rows[i]
var res = zero
for (j in r.indices) {
res += r[j] * vector[j]
}
res
}
}
}
override fun Matrix<T>.times(value: T): Matrix<T> = ndRing(rowNum, colNum).run {
unwrap().map { it * value }.as2D()
}
}

View File

@ -1,25 +1,33 @@
package space.kscience.kmath.linear
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.VirtualBuffer
public typealias Point<T> = Buffer<T>
import space.kscience.kmath.nd.as1D
/**
* A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors
*/
public interface LinearSolver<T : Any> {
/**
* Solve a dot x = b matrix equation and return x
*/
public fun solve(a: Matrix<T>, b: Matrix<T>): Matrix<T>
public fun solve(a: Matrix<T>, b: Point<T>): Point<T> = solve(a, b.asMatrix()).asPoint()
public fun inverse(a: Matrix<T>): Matrix<T>
/**
* Solve a dot x = b vector equation and return b
*/
public fun solve(a: Matrix<T>, b: Point<T>): Point<T> = solve(a, b.asMatrix()).asVector()
/**
* Get inverse of a matrix
*/
public fun inverse(matrix: Matrix<T>): Matrix<T>
}
/**
* Convert matrix to vector if it is possible
*/
public fun <T : Any> Matrix<T>.asPoint(): Point<T> =
public fun <T : Any> Matrix<T>.asVector(): Point<T> =
if (this.colNum == 1)
VirtualBuffer(rowNum) { get(it, 0) }
as1D()
else
error("Can't convert matrix with more than one column to vector")

View File

@ -0,0 +1,202 @@
package space.kscience.kmath.linear
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import kotlin.reflect.KClass
/**
* Alias for [Structure2D] with more familiar name.
*
* @param T the type of items.
*/
public typealias Matrix<T> = Structure2D<T>
/**
* Alias or using [Buffer] as a point/vector in a many-dimensional space.
*/
public typealias Point<T> = Buffer<T>
/**
* Basic operations on matrices and vectors. Operates on [Matrix].
*
* @param T the type of items in the matrices.
* @param M the type of operated matrices.
*/
public interface LinearSpace<T : Any, out A : Ring<T>> {
public val elementAlgebra: A
/**
* Produces a matrix with this context and given dimensions.
*/
public fun buildMatrix(rows: Int, columns: Int, initializer: A.(i: Int, j: Int) -> T): Matrix<T>
/**
* Produces a point compatible with matrix space (and possibly optimized for it).
*/
public fun buildVector(size: Int, initializer: A.(Int) -> T): Point<T>
public operator fun Matrix<T>.unaryMinus(): Matrix<T> = buildMatrix(rowNum, colNum) { i, j ->
-get(i, j)
}
public operator fun Point<T>.unaryMinus(): Point<T> = buildVector(size) {
-get(it)
}
/**
* Matrix sum
*/
public operator fun Matrix<T>.plus(other: Matrix<T>): Matrix<T> = buildMatrix(rowNum, colNum) { i, j ->
get(i, j) + other[i, j]
}
/**
* Vector sum
*/
public operator fun Point<T>.plus(other: Point<T>): Point<T> = buildVector(size) {
get(it) + other[it]
}
/**
* Matrix subtraction
*/
public operator fun Matrix<T>.minus(other: Matrix<T>): Matrix<T> = buildMatrix(rowNum, colNum) { i, j ->
get(i, j) - other[i, j]
}
/**
* Vector subtraction
*/
public operator fun Point<T>.minus(other: Point<T>): Point<T> = buildVector(size) {
get(it) - other[it]
}
/**
* Computes the dot product of this matrix and another one.
*
* @receiver the multiplicand.
* @param other the multiplier.
* @return the dot product.
*/
public infix fun Matrix<T>.dot(other: Matrix<T>): Matrix<T> {
require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" }
return elementAlgebra {
buildMatrix(rowNum, other.colNum) { i, j ->
var res = zero
for (l in 0 until colNum) {
res += this@dot[i, l] * other[l, j]
}
res
}
}
}
/**
* Computes the dot product of this matrix and a vector.
*
* @receiver the multiplicand.
* @param vector the multiplier.
* @return the dot product.
*/
public infix fun Matrix<T>.dot(vector: Point<T>): Point<T> {
require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" }
return elementAlgebra {
buildVector(rowNum) { i ->
var res = one
for (j in 0 until colNum) {
res += this@dot[i, j] * vector[j]
}
res
}
}
}
/**
* Multiplies a matrix by its element.
*
* @receiver the multiplicand.
* @param value the multiplier.
* @receiver the product.
*/
public operator fun Matrix<T>.times(value: T): Matrix<T> =
buildMatrix(rowNum, colNum) { i, j -> get(i, j) * value }
/**
* Multiplies an element by a matrix of it.
*
* @receiver the multiplicand.
* @param m the multiplier.
* @receiver the product.
*/
public operator fun T.times(m: Matrix<T>): Matrix<T> = m * this
/**
* Multiplies a vector by its element.
*
* @receiver the multiplicand.
* @param value the multiplier.
* @receiver the product.
*/
public operator fun Point<T>.times(value: T): Point<T> =
buildVector(size) { i -> get(i) * value }
/**
* Multiplies an element by a vector of it.
*
* @receiver the multiplicand.
* @param v the multiplier.
* @receiver the product.
*/
public operator fun T.times(v: Point<T>): Point<T> = v * this
/**
* Get a feature of the structure in this scope. Structure features take precedence other context features
*
* @param F the type of feature.
* @param structure the structure.
* @param type the [KClass] instance of [F].
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI
public fun <F : Any> getFeature(structure: Matrix<T>, type: KClass<F>): F? = structure.getFeature(type)
public companion object {
/**
* A structured matrix with custom buffer
*/
public fun <T : Any, A : Ring<T>> buffered(
algebra: A,
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing,
): LinearSpace<T, A> = BufferedLinearSpace(algebra, bufferFactory)
public val real: LinearSpace<Double, DoubleField> = buffered(DoubleField, ::DoubleBuffer)
/**
* Automatic buffered matrix, unboxed if it is possible
*/
public inline fun <reified T : Any, A : Ring<T>> auto(ring: A): LinearSpace<T, A> =
buffered(ring, Buffer.Companion::auto)
}
}
/**
* Get a feature of the structure in this scope. Structure features take precedence other context features
*
* @param T the type of items in the matrices.
* @param F the type of feature.
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI
public inline fun <T : Any, reified F : Any> LinearSpace<T, *>.getFeature(structure: Matrix<T>): F? =
getFeature(structure, F::class)
public operator fun <LS : LinearSpace<*, *>, R> LS.invoke(block: LS.() -> R): R = run(block)

View File

@ -3,8 +3,8 @@ package space.kscience.kmath.linear
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.getFeature
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferAccessor2D
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.MutableBufferFactory
@ -12,7 +12,7 @@ import space.kscience.kmath.structures.MutableBufferFactory
* Common implementation of [LupDecompositionFeature].
*/
public class LupDecomposition<T : Any>(
public val context: MatrixContext<T, Matrix<T>>,
public val context: LinearSpace<T, *>,
public val elementContext: Field<T>,
public val lu: Matrix<T>,
public val pivot: IntArray,
@ -62,15 +62,14 @@ public class LupDecomposition<T : Any>(
}
@PublishedApi
internal fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, *>.abs(value: T): T =
if (value > elementContext.zero) value else elementContext { -value }
internal fun <T : Comparable<T>> LinearSpace<T, Ring<T>>.abs(value: T): T =
if (value > elementAlgebra.zero) value else elementAlgebra { -value }
/**
* Create a lup decomposition of generic matrix.
*/
public fun <T : Comparable<T>> MatrixContext<T, Matrix<T>>.lup(
public fun <T : Comparable<T>> LinearSpace<T, Field<T>>.lup(
factory: MutableBufferFactory<T>,
elementContext: Field<T>,
matrix: Matrix<T>,
checkSingular: (T) -> Boolean,
): LupDecomposition<T> {
@ -80,7 +79,7 @@ public fun <T : Comparable<T>> MatrixContext<T, Matrix<T>>.lup(
//TODO just waits for KEEP-176
BufferAccessor2D(matrix.rowNum, matrix.colNum, factory).run {
elementContext {
elementAlgebra {
val lu = create(matrix)
// Initialize permutation array and parity
@ -142,18 +141,18 @@ public fun <T : Comparable<T>> MatrixContext<T, Matrix<T>>.lup(
for (row in col + 1 until m) lu[row, col] /= luDiag
}
return LupDecomposition(this@lup, elementContext, lu.collect(), pivot, even)
return LupDecomposition(this@lup, elementAlgebra, lu.collect(), pivot, even)
}
}
}
public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, Matrix<T>>.lup(
public inline fun <reified T : Comparable<T>> LinearSpace<T, Field<T>>.lup(
matrix: Matrix<T>,
noinline checkSingular: (T) -> Boolean,
): LupDecomposition<T> = lup(MutableBuffer.Companion::auto, elementContext, matrix, checkSingular)
): LupDecomposition<T> = lup(MutableBuffer.Companion::auto, matrix, checkSingular)
public fun MatrixContext<Double, Matrix<Double>>.lup(matrix: Matrix<Double>): LupDecomposition<Double> =
lup(Buffer.Companion::real, RealField, matrix) { it < 1e-11 }
public fun LinearSpace<Double, DoubleField>.lup(matrix: Matrix<Double>): LupDecomposition<Double> =
lup(::DoubleBuffer, matrix) { it < 1e-11 }
public fun <T : Any> LupDecomposition<T>.solveWithLup(
factory: MutableBufferFactory<T>,
@ -198,7 +197,7 @@ public fun <T : Any> LupDecomposition<T>.solveWithLup(
}
}
return context.produce(pivot.size, matrix.colNum) { i, j -> bp[i, j] }
return context.buildMatrix(pivot.size, matrix.colNum) { i, j -> bp[i, j] }
}
}
}
@ -210,18 +209,18 @@ public inline fun <reified T : Any> LupDecomposition<T>.solveWithLup(matrix: Mat
* Solves a system of linear equations *ax = b** using LUP decomposition.
*/
@OptIn(UnstableKMathAPI::class)
public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, Matrix<T>>.solveWithLup(
public inline fun <reified T : Comparable<T>> LinearSpace<T, Field<T>>.solveWithLup(
a: Matrix<T>,
b: Matrix<T>,
noinline bufferFactory: MutableBufferFactory<T> = MutableBuffer.Companion::auto,
noinline checkSingular: (T) -> Boolean,
): Matrix<T> {
// Use existing decomposition if it is provided by matrix
val decomposition = a.getFeature() ?: lup(bufferFactory, elementContext, a, checkSingular)
val decomposition = a.getFeature() ?: lup(bufferFactory, a, checkSingular)
return decomposition.solveWithLup(bufferFactory, b)
}
public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, Matrix<T>>.inverseWithLup(
public inline fun <reified T : Comparable<T>> LinearSpace<T, Field<T>>.inverseWithLup(
matrix: Matrix<T>,
noinline bufferFactory: MutableBufferFactory<T> = MutableBuffer.Companion::auto,
noinline checkSingular: (T) -> Boolean,
@ -229,15 +228,15 @@ public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext
@OptIn(UnstableKMathAPI::class)
public fun RealMatrixContext.solveWithLup(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> {
public fun LinearSpace<Double, DoubleField>.solveWithLup(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> {
// Use existing decomposition if it is provided by matrix
val bufferFactory: MutableBufferFactory<Double> = MutableBuffer.Companion::real
val decomposition: LupDecomposition<Double> = a.getFeature() ?: lup(bufferFactory, RealField, a) { it < 1e-11 }
val bufferFactory: MutableBufferFactory<Double> = ::DoubleBuffer
val decomposition: LupDecomposition<Double> = a.getFeature() ?: lup(bufferFactory, a) { it < 1e-11 }
return decomposition.solveWithLup(bufferFactory, b)
}
/**
* Inverses a square matrix using LUP decomposition. Non square matrix will throw a error.
*/
public fun RealMatrixContext.inverseWithLup(matrix: Matrix<Double>): Matrix<Double> =
public fun LinearSpace<Double, DoubleField>.inverseWithLup(matrix: Matrix<Double>): Matrix<Double> =
solveWithLup(matrix, one(matrix.rowNum, matrix.colNum))

View File

@ -1,46 +1,43 @@
package space.kscience.kmath.linear
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Ring
public class MatrixBuilder(public val rows: Int, public val columns: Int) {
public operator fun <T : Any> invoke(vararg elements: T): Matrix<T> {
public class MatrixBuilder<T : Any, A : Ring<T>>(
public val linearSpace: LinearSpace<T, A>,
public val rows: Int,
public val columns: Int,
) {
public operator fun invoke(vararg elements: T): Matrix<T> {
require(rows * columns == elements.size) { "The number of elements ${elements.size} is not equal $rows * $columns" }
val buffer = elements.asBuffer()
return BufferMatrix(rows, columns, buffer)
return linearSpace.buildMatrix(rows, columns) { i, j -> elements[i * columns + j] }
}
//TODO add specific matrix builder functions like diagonal, etc
}
public fun Structure2D.Companion.build(rows: Int, columns: Int): MatrixBuilder = MatrixBuilder(rows, columns)
/**
* Create a matrix builder with given number of rows and columns
*/
@UnstableKMathAPI
public fun <T : Any, A : Ring<T>> LinearSpace<T, A>.matrix(rows: Int, columns: Int): MatrixBuilder<T, A> =
MatrixBuilder(this, rows, columns)
public fun <T : Any> Structure2D.Companion.row(vararg values: T): Matrix<T> {
val buffer = values.asBuffer()
return BufferMatrix(1, values.size, buffer)
@UnstableKMathAPI
public fun <T : Any> LinearSpace<T, Ring<T>>.vector(vararg elements: T): Point<T> {
return buildVector(elements.size) { elements[it] }
}
public inline fun <reified T : Any> Structure2D.Companion.row(
public inline fun <T : Any> LinearSpace<T, Ring<T>>.row(
size: Int,
factory: BufferFactory<T> = Buffer.Companion::auto,
noinline builder: (Int) -> T,
): Matrix<T> {
val buffer = factory(size, builder)
return BufferMatrix(1, size, buffer)
}
crossinline builder: (Int) -> T,
): Matrix<T> = buildMatrix(1, size) { _, j -> builder(j) }
public fun <T : Any> Structure2D.Companion.column(vararg values: T): Matrix<T> {
val buffer = values.asBuffer()
return BufferMatrix(values.size, 1, buffer)
}
public fun <T : Any> LinearSpace<T, Ring<T>>.row(vararg values: T): Matrix<T> = row(values.size, values::get)
public inline fun <reified T : Any> Structure2D.Companion.column(
public inline fun <T : Any> LinearSpace<T, Ring<T>>.column(
size: Int,
factory: BufferFactory<T> = Buffer.Companion::auto,
noinline builder: (Int) -> T,
): Matrix<T> {
val buffer = factory(size, builder)
return BufferMatrix(size, 1, buffer)
}
crossinline builder: (Int) -> T,
): Matrix<T> = buildMatrix(size, 1) { i, _ -> builder(i) }
public fun <T : Any> LinearSpace<T, Ring<T>>.column(vararg values: T): Matrix<T> = column(values.size, values::get)

View File

@ -1,173 +0,0 @@
package space.kscience.kmath.linear
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.asSequence
import kotlin.reflect.KClass
/**
* Basic operations on matrices. Operates on [Matrix].
*
* @param T the type of items in the matrices.
* @param M the type of operated matrices.
*/
public interface MatrixContext<T : Any, out M : Matrix<T>> : GroupOperations<Matrix<T>>, ScaleOperations<Matrix<T>> {
/**
* Produces a matrix with this context and given dimensions.
*/
public fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): M
/**
* Produces a point compatible with matrix space (and possibly optimized for it).
*/
public fun point(size: Int, initializer: (Int) -> T): Point<T> = Buffer.boxing(size, initializer)
@Suppress("UNCHECKED_CAST")
public override fun binaryOperationFunction(operation: String): (left: Matrix<T>, right: Matrix<T>) -> M =
when (operation) {
"dot" -> { left, right -> left dot right }
else -> super<GroupOperations>.binaryOperationFunction(operation) as (Matrix<T>, Matrix<T>) -> M
}
/**
* Computes the dot product of this matrix and another one.
*
* @receiver the multiplicand.
* @param other the multiplier.
* @return the dot product.
*/
public infix fun Matrix<T>.dot(other: Matrix<T>): M
/**
* Computes the dot product of this matrix and a vector.
*
* @receiver the multiplicand.
* @param vector the multiplier.
* @return the dot product.
*/
public infix fun Matrix<T>.dot(vector: Point<T>): Point<T>
/**
* Multiplies a matrix by its element.
*
* @receiver the multiplicand.
* @param value the multiplier.
* @receiver the product.
*/
public operator fun Matrix<T>.times(value: T): M
/**
* Multiplies an element by a matrix of it.
*
* @receiver the multiplicand.
* @param m the multiplier.
* @receiver the product.
*/
public operator fun T.times(m: Matrix<T>): M = m * this
/**
* Gets a feature from the matrix. This function may return some additional features to
* [kscience.kmath.nd.NDStructure.getFeature].
*
* @param F the type of feature.
* @param m the matrix.
* @param type the [KClass] instance of [F].
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI
public fun <F : Any> getFeature(m: Matrix<T>, type: KClass<F>): F? = m.getFeature(type)
public companion object {
/**
* A structured matrix with custom buffer
*/
public fun <T : Any, A> buffered(
ring: A,
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing,
): GenericMatrixContext<T, A, BufferMatrix<T>> where A : Ring<T>, A: ScaleOperations<T> = BufferMatrixContext(ring, bufferFactory)
/**
* Automatic buffered matrix, unboxed if it is possible
*/
public inline fun <reified T : Any, A> auto(ring: A): GenericMatrixContext<T, A, BufferMatrix<T>> where A : Ring<T>, A: ScaleOperations<T> =
buffered(ring, Buffer.Companion::auto)
}
}
/**
* Gets a feature from the matrix. This function may return some additional features to
* [kscience.kmath.nd.NDStructure.getFeature].
*
* @param T the type of items in the matrices.
* @param M the type of operated matrices.
* @param F the type of feature.
* @receiver the [MatrixContext] of [T].
* @param m the matrix.
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI
public inline fun <T : Any, reified F : Any> MatrixContext<T, *>.getFeature(m: Matrix<T>): F? =
getFeature(m, F::class)
/**
* Partial implementation of [MatrixContext] for matrices of [Ring].
*
* @param T the type of items in the matrices.
* @param A the type of ring of matrix elements.
* @param M the type of operated matrices.
*/
public interface GenericMatrixContext<T : Any, A, out M : Matrix<T>> : MatrixContext<T, M> where A : Ring<T>, A : ScaleOperations<T>{
/**
* The ring over matrix elements.
*/
public val elementContext: A
public override infix fun Matrix<T>.dot(other: Matrix<T>): M {
//TODO add typed error
require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" }
return produce(rowNum, other.colNum) { i, j ->
val row = rows[i]
val column = other.columns[j]
elementContext { sum(row.asSequence().zip(column.asSequence(), ::multiply)) }
}
}
public override infix fun Matrix<T>.dot(vector: Point<T>): Point<T> {
//TODO add typed error
require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" }
return point(rowNum) { i ->
val row = rows[i]
elementContext { sum(row.asSequence().zip(vector.asSequence(), ::multiply)) }
}
}
public override operator fun Matrix<T>.unaryMinus(): M =
produce(rowNum, colNum) { i, j -> elementContext { -get(i, j) } }
public override fun add(a: Matrix<T>, b: Matrix<T>): M {
require(a.rowNum == b.rowNum && a.colNum == b.colNum) {
"Matrix operation dimension mismatch. [${a.rowNum},${a.colNum}] + [${b.rowNum},${b.colNum}]"
}
return produce(a.rowNum, a.colNum) { i, j -> elementContext { a[i, j] + b[i, j] } }
}
public override operator fun Matrix<T>.minus(b: Matrix<T>): M {
require(rowNum == b.rowNum && colNum == b.colNum) {
"Matrix operation dimension mismatch. [$rowNum,$colNum] - [${b.rowNum},${b.colNum}]"
}
return produce(rowNum, colNum) { i, j -> elementContext { get(i, j) + b[i, j] } }
}
//
// public override fun multiply(a: Matrix<T>, k: Number): M =
// produce(a.rowNum, a.colNum) { i, j -> elementContext { a[i, j] * k } }
public override operator fun Matrix<T>.times(value: T): M =
produce(rowNum, colNum) { i, j -> elementContext { get(i, j) * value } }
}

View File

@ -1,14 +1,9 @@
package space.kscience.kmath.linear
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.getFeature
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.structures.asBuffer
import kotlin.math.sqrt
import kotlin.reflect.KClass
import kotlin.reflect.safeCast
/**
* A [Matrix] that holds [MatrixFeature] objects.
@ -24,11 +19,10 @@ public class MatrixWrapper<T : Any> internal constructor(
* Get the first feature matching given class. Does not guarantee that matrix has only one feature matching the criteria
*/
@UnstableKMathAPI
override fun <T : Any> getFeature(type: KClass<T>): T? = type.safeCast(features.find { type.isInstance(it) })
@Suppress("UNCHECKED_CAST")
override fun <T : Any> getFeature(type: KClass<T>): T? = features.singleOrNull { type.isInstance(it) } as? T
?: origin.getFeature(type)
override fun equals(other: Any?): Boolean = origin == other
override fun hashCode(): Int = origin.hashCode()
override fun toString(): String {
return "MatrixWrapper(matrix=$origin, features=$features)"
}
@ -61,35 +55,25 @@ public operator fun <T : Any> Matrix<T>.plus(newFeatures: Collection<MatrixFeatu
MatrixWrapper(this, newFeatures.toSet())
}
/**
* Build a square matrix from given elements.
*/
public fun <T : Any> Structure2D.Companion.square(vararg elements: T): Matrix<T> {
val size: Int = sqrt(elements.size.toDouble()).toInt()
require(size * size == elements.size) { "The number of elements ${elements.size} is not a full square" }
val buffer = elements.asBuffer()
return BufferMatrix(size, size, buffer)
}
/**
* Diagonal matrix of ones. The matrix is virtual no actual matrix is created
*/
public fun <T : Any, A> GenericMatrixContext<T, A, *>.one(
public fun <T : Any> LinearSpace<T, Ring<T>>.one(
rows: Int,
columns: Int,
): Matrix<T> where A : Ring<T>, A : ScaleOperations<T> = VirtualMatrix(rows, columns) { i, j ->
if (i == j) elementContext.one else elementContext.zero
): Matrix<T> = VirtualMatrix(rows, columns) { i, j ->
if (i == j) elementAlgebra.one else elementAlgebra.zero
} + UnitFeature
/**
* A virtual matrix of zeroes
*/
public fun <T : Any, A> GenericMatrixContext<T, A, *>.zero(
public fun <T : Any> LinearSpace<T, Ring<T>>.zero(
rows: Int,
columns: Int,
): Matrix<T> where A : Ring<T>, A : ScaleOperations<T> = VirtualMatrix(rows, columns) { _, _ ->
elementContext.zero
): Matrix<T> = VirtualMatrix(rows, columns) { _, _ ->
elementAlgebra.zero
} + ZeroFeature
public class TransposedFeature<T : Any>(public val original: Matrix<T>) : MatrixFeature

View File

@ -1,85 +0,0 @@
package space.kscience.kmath.linear
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.structures.RealBuffer
public object RealMatrixContext : MatrixContext<Double, BufferMatrix<Double>>, ScaleOperations<Matrix<Double>> {
public override fun produce(
rows: Int,
columns: Int,
initializer: (i: Int, j: Int) -> Double,
): BufferMatrix<Double> {
val buffer = RealBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) }
return BufferMatrix(rows, columns, buffer)
}
public fun Matrix<Double>.toBufferMatrix(): BufferMatrix<Double> = if (this is BufferMatrix) this else {
produce(rowNum, colNum) { i, j -> get(i, j) }
}
public fun one(rows: Int, columns: Int): Matrix<Double> = VirtualMatrix(rows, columns) { i, j ->
if (i == j) 1.0 else 0.0
} + DiagonalFeature
override fun Matrix<Double>.unaryMinus(): Matrix<Double> = produce(rowNum, colNum) { i, j -> -get(i, j) }
public override infix fun Matrix<Double>.dot(other: Matrix<Double>): BufferMatrix<Double> {
require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" }
val bufferMatrix = toBufferMatrix()
val otherBufferMatrix = other.toBufferMatrix()
return produce(rowNum, other.colNum) { i, j ->
var res = 0.0
for (l in 0 until colNum) {
res += bufferMatrix[i, l] * otherBufferMatrix[l, j]
}
res
}
}
public override infix fun Matrix<Double>.dot(vector: Point<Double>): Point<Double> {
require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" }
val bufferMatrix = toBufferMatrix()
return RealBuffer(rowNum) { i ->
var res = 0.0
for (j in 0 until colNum) {
res += bufferMatrix[i, j] * vector[j]
}
res
}
}
override fun add(a: Matrix<Double>, b: Matrix<Double>): BufferMatrix<Double> {
require(a.rowNum == b.rowNum) { "Row number mismatch in matrix addition. Left side: ${a.rowNum}, right side: ${b.rowNum}" }
require(a.colNum == b.colNum) { "Column number mismatch in matrix addition. Left side: ${a.colNum}, right side: ${b.colNum}" }
val aBufferMatrix = a.toBufferMatrix()
val bBufferMatrix = b.toBufferMatrix()
return produce(a.rowNum, a.colNum) { i, j ->
aBufferMatrix[i, j] + bBufferMatrix[i, j]
}
}
override fun scale(a: Matrix<Double>, value: Double): BufferMatrix<Double> {
val bufferMatrix = a.toBufferMatrix()
return produce(a.rowNum, a.colNum) { i, j -> bufferMatrix[i, j] * value }
}
override fun Matrix<Double>.times(value: Double): BufferMatrix<Double> = scale(this, value)
//
// override fun multiply(a: Matrix<Double>, k: Number): BufferMatrix<Double> {
// val aBufferMatrix = a.toBufferMatrix()
// return produce(a.rowNum, a.colNum) { i, j -> aBufferMatrix[i, j] * k.toDouble() }
// }
//
// override fun divide(a: Matrix<Double>, k: Number): BufferMatrix<Double> {
// val aBufferMatrix = a.toBufferMatrix()
// return produce(a.rowNum, a.colNum) { i, j -> aBufferMatrix[i, j] / k.toDouble() }
// }
}
/**
* Partially optimized real-valued matrix
*/
public val MatrixContext.Companion.real: RealMatrixContext get() = RealMatrixContext

View File

@ -1,72 +0,0 @@
package space.kscience.kmath.linear
import space.kscience.kmath.operations.Group
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
/**
* A linear space for vectors.
* Could be used on any point-like structure
*/
public interface VectorSpace<T : Any, A> : Group<Point<T>>, ScaleOperations<Point<T>>
where A : Group<T>, A : ScaleOperations<T> {
public val size: Int
public val algebra: A
override val zero: Point<T> get() = produce { algebra.zero }
public fun produce(initializer: A.(Int) -> T): Point<T>
override fun add(a: Point<T>, b: Point<T>): Point<T> = produce { algebra { a[it] + b[it] } }
override fun scale(a: Point<T>, value: Double): Point<T> = produce { algebra.scale(a[it], value) }
override fun Point<T>.unaryMinus(): Point<T> = produce { -get(it) }
//TODO add basis
public companion object {
private val realSpaceCache: MutableMap<Int, BufferVectorSpace<Double, RealField>> = hashMapOf()
/**
* Non-boxing double vector space
*/
public fun real(size: Int): BufferVectorSpace<Double, RealField> = realSpaceCache.getOrPut(size) {
BufferVectorSpace(
size,
RealField,
Buffer.Companion::auto
)
}
/**
* A structured vector space with custom buffer
*/
public fun <T : Any, A> buffered(
size: Int,
space: A,
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing,
): BufferVectorSpace<T, A> where A : Group<T>, A : ScaleOperations<T> =
BufferVectorSpace(size, space, bufferFactory)
/**
* Automatic buffered vector, unboxed if it is possible
*/
public inline fun <reified T : Any, A> auto(
size: Int,
space: A,
): VectorSpace<T, A> where A : Group<T>, A : ScaleOperations<T> =
buffered(size, space, Buffer.Companion::auto)
}
}
public class BufferVectorSpace<T : Any, A>(
override val size: Int,
override val algebra: A,
public val bufferFactory: BufferFactory<T>,
) : VectorSpace<T, A> where A : Group<T>, A : ScaleOperations<T> {
override fun produce(initializer: A.(Int) -> T): Buffer<T> = bufferFactory(size) { algebra.initializer(it) }
}

View File

@ -8,29 +8,10 @@ package space.kscience.kmath.linear
public class VirtualMatrix<T : Any>(
override val rowNum: Int,
override val colNum: Int,
public val generator: (i: Int, j: Int) -> T
public val generator: (i: Int, j: Int) -> T,
) : Matrix<T> {
override val shape: IntArray get() = intArrayOf(rowNum, colNum)
override operator fun get(i: Int, j: Int): T = generator(i, j)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Matrix<*>) return false
if (rowNum != other.rowNum) return false
if (colNum != other.colNum) return false
return elements().all { (index, value) -> value == other[index] }
}
override fun hashCode(): Int {
var result = rowNum
result = 31 * result + colNum
result = 31 * result + generator.hashCode()
return result
}
}

View File

@ -1,7 +1,9 @@
package space.kscience.kmath.nd
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.*
import kotlin.reflect.KClass
/**
* An exception is thrown when the expected ans actual shape of NDArray differs.
@ -19,7 +21,7 @@ public class ShapeMismatchException(public val expected: IntArray, public val ac
* @param C the type of the element context.
* @param N the type of the structure.
*/
public interface NDAlgebra<T, C: Algebra<T>> {
public interface AlgebraND<T, C : Algebra<T>> {
/**
* The shape of ND-structures this algebra operates on.
*/
@ -33,42 +35,66 @@ public interface NDAlgebra<T, C: Algebra<T>> {
/**
* Produces a new NDStructure using given initializer function.
*/
public fun produce(initializer: C.(IntArray) -> T): NDStructure<T>
public fun produce(initializer: C.(IntArray) -> T): StructureND<T>
/**
* Maps elements from one structure to another one by applying [transform] to them.
*/
public fun NDStructure<T>.map(transform: C.(T) -> T): NDStructure<T>
public fun StructureND<T>.map(transform: C.(T) -> T): StructureND<T>
/**
* Maps elements from one structure to another one by applying [transform] to them alongside with their indices.
*/
public fun NDStructure<T>.mapIndexed(transform: C.(index: IntArray, T) -> T): NDStructure<T>
public fun StructureND<T>.mapIndexed(transform: C.(index: IntArray, T) -> T): StructureND<T>
/**
* Combines two structures into one.
*/
public fun combine(a: NDStructure<T>, b: NDStructure<T>, transform: C.(T, T) -> T): NDStructure<T>
public fun combine(a: StructureND<T>, b: StructureND<T>, transform: C.(T, T) -> T): StructureND<T>
/**
* Element-wise invocation of function working on [T] on a [NDStructure].
* Element-wise invocation of function working on [T] on a [StructureND].
*/
public operator fun Function1<T, T>.invoke(structure: NDStructure<T>): NDStructure<T> =
public operator fun Function1<T, T>.invoke(structure: StructureND<T>): StructureND<T> =
structure.map { value -> this@invoke(value) }
/**
* Get a feature of the structure in this scope. Structure features take precedence other context features
*
* @param F the type of feature.
* @param structure the structure.
* @param type the [KClass] instance of [F].
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI
public fun <F : Any> getFeature(structure: StructureND<T>, type: KClass<F>): F? = structure.getFeature(type)
public companion object
}
/**
* Get a feature of the structure in this scope. Structure features take precedence other context features
*
* @param T the type of items in the matrices.
* @param F the type of feature.
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI
public inline fun <T : Any, reified F : Any> AlgebraND<T, *>.getFeature(structure: StructureND<T>): F? =
getFeature(structure, F::class)
/**
* Checks if given elements are consistent with this context.
*
* @param structures the structures to check.
* @return the array of valid structures.
*/
internal fun <T, C: Algebra<T>> NDAlgebra<T, C>.checkShape(vararg structures: NDStructure<T>): Array<out NDStructure<T>> = structures
.map(NDStructure<T>::shape)
internal fun <T, C : Algebra<T>> AlgebraND<T, C>.checkShape(vararg structures: StructureND<T>): Array<out StructureND<T>> =
structures
.map(StructureND<T>::shape)
.singleOrNull { !shape.contentEquals(it) }
?.let<IntArray, Array<out NDStructure<T>>> { throw ShapeMismatchException(shape, it) }
?.let<IntArray, Array<out StructureND<T>>> { throw ShapeMismatchException(shape, it) }
?: structures
/**
@ -77,19 +103,19 @@ internal fun <T, C: Algebra<T>> NDAlgebra<T, C>.checkShape(vararg structures: ND
* @param element the structure to check.
* @return the valid structure.
*/
internal fun <T, C: Algebra<T>> NDAlgebra<T, C>.checkShape(element: NDStructure<T>): NDStructure<T> {
internal fun <T, C : Algebra<T>> AlgebraND<T, C>.checkShape(element: StructureND<T>): StructureND<T> {
if (!element.shape.contentEquals(shape)) throw ShapeMismatchException(shape, element.shape)
return element
}
/**
* Space of [NDStructure].
* Space of [StructureND].
*
* @param T the type of the element contained in ND structure.
* @param N the type of ND structure.
* @param S the type of space of structure elements.
*/
public interface NDGroup<T, S : Group<T>> : Group<NDStructure<T>>, NDAlgebra<T, S> {
public interface GroupND<T, S : Group<T>> : Group<StructureND<T>>, AlgebraND<T, S> {
/**
* Element-wise addition.
*
@ -97,7 +123,7 @@ public interface NDGroup<T, S : Group<T>> : Group<NDStructure<T>>, NDAlgebra<T,
* @param b the augend.
* @return the sum.
*/
public override fun add(a: NDStructure<T>, b: NDStructure<T>): NDStructure<T> =
public override fun add(a: StructureND<T>, b: StructureND<T>): StructureND<T> =
combine(a, b) { aValue, bValue -> add(aValue, bValue) }
// /**
@ -118,7 +144,7 @@ public interface NDGroup<T, S : Group<T>> : Group<NDStructure<T>>, NDAlgebra<T,
* @param arg the augend.
* @return the sum.
*/
public operator fun NDStructure<T>.plus(arg: T): NDStructure<T> = this.map { value -> add(arg, value) }
public operator fun StructureND<T>.plus(arg: T): StructureND<T> = this.map { value -> add(arg, value) }
/**
* Subtracts an element from ND structure of it.
@ -127,7 +153,7 @@ public interface NDGroup<T, S : Group<T>> : Group<NDStructure<T>>, NDAlgebra<T,
* @param arg the divisor.
* @return the quotient.
*/
public operator fun NDStructure<T>.minus(arg: T): NDStructure<T> = this.map { value -> add(arg, -value) }
public operator fun StructureND<T>.minus(arg: T): StructureND<T> = this.map { value -> add(arg, -value) }
/**
* Adds an element to ND structure of it.
@ -136,7 +162,7 @@ public interface NDGroup<T, S : Group<T>> : Group<NDStructure<T>>, NDAlgebra<T,
* @param arg the augend.
* @return the sum.
*/
public operator fun T.plus(arg: NDStructure<T>): NDStructure<T> = arg.map { value -> add(this@plus, value) }
public operator fun T.plus(arg: StructureND<T>): StructureND<T> = arg.map { value -> add(this@plus, value) }
/**
* Subtracts an ND structure from an element of it.
@ -145,19 +171,19 @@ public interface NDGroup<T, S : Group<T>> : Group<NDStructure<T>>, NDAlgebra<T,
* @param arg the divisor.
* @return the quotient.
*/
public operator fun T.minus(arg: NDStructure<T>): NDStructure<T> = arg.map { value -> add(-this@minus, value) }
public operator fun T.minus(arg: StructureND<T>): StructureND<T> = arg.map { value -> add(-this@minus, value) }
public companion object
}
/**
* Ring of [NDStructure].
* Ring of [StructureND].
*
* @param T the type of the element contained in ND structure.
* @param N the type of ND structure.
* @param R the type of ring of structure elements.
*/
public interface NDRing<T, R : Ring<T>> : Ring<NDStructure<T>>, NDGroup<T, R> {
public interface RingND<T, R : Ring<T>> : Ring<StructureND<T>>, GroupND<T, R> {
/**
* Element-wise multiplication.
*
@ -165,7 +191,7 @@ public interface NDRing<T, R : Ring<T>> : Ring<NDStructure<T>>, NDGroup<T, R> {
* @param b the multiplier.
* @return the product.
*/
public override fun multiply(a: NDStructure<T>, b: NDStructure<T>): NDStructure<T> =
public override fun multiply(a: StructureND<T>, b: StructureND<T>): StructureND<T> =
combine(a, b) { aValue, bValue -> multiply(aValue, bValue) }
//TODO move to extensions after KEEP-176
@ -177,7 +203,7 @@ public interface NDRing<T, R : Ring<T>> : Ring<NDStructure<T>>, NDGroup<T, R> {
* @param arg the multiplier.
* @return the product.
*/
public operator fun NDStructure<T>.times(arg: T): NDStructure<T> = this.map { value -> multiply(arg, value) }
public operator fun StructureND<T>.times(arg: T): StructureND<T> = this.map { value -> multiply(arg, value) }
/**
* Multiplies an element by a ND structure of it.
@ -186,19 +212,19 @@ public interface NDRing<T, R : Ring<T>> : Ring<NDStructure<T>>, NDGroup<T, R> {
* @param arg the multiplier.
* @return the product.
*/
public operator fun T.times(arg: NDStructure<T>): NDStructure<T> = arg.map { value -> multiply(this@times, value) }
public operator fun T.times(arg: StructureND<T>): StructureND<T> = arg.map { value -> multiply(this@times, value) }
public companion object
}
/**
* Field of [NDStructure].
* Field of [StructureND].
*
* @param T the type of the element contained in ND structure.
* @param N the type of ND structure.
* @param F the type field of structure elements.
*/
public interface NDField<T, F : Field<T>> : Field<NDStructure<T>>, NDRing<T, F>, ScaleOperations<NDStructure<T>> {
public interface FieldND<T, F : Field<T>> : Field<StructureND<T>>, RingND<T, F>, ScaleOperations<StructureND<T>> {
/**
* Element-wise division.
*
@ -206,7 +232,7 @@ public interface NDField<T, F : Field<T>> : Field<NDStructure<T>>, NDRing<T, F>,
* @param b the divisor.
* @return the quotient.
*/
public override fun divide(a: NDStructure<T>, b: NDStructure<T>): NDStructure<T> =
public override fun divide(a: StructureND<T>, b: StructureND<T>): StructureND<T> =
combine(a, b) { aValue, bValue -> divide(aValue, bValue) }
//TODO move to extensions after KEEP-176
@ -217,7 +243,7 @@ public interface NDField<T, F : Field<T>> : Field<NDStructure<T>>, NDRing<T, F>,
* @param arg the divisor.
* @return the quotient.
*/
public operator fun NDStructure<T>.div(arg: T): NDStructure<T> = this.map { value -> divide(arg, value) }
public operator fun StructureND<T>.div(arg: T): StructureND<T> = this.map { value -> divide(arg, value) }
/**
* Divides an element by an ND structure of it.
@ -226,7 +252,7 @@ public interface NDField<T, F : Field<T>> : Field<NDStructure<T>>, NDRing<T, F>,
* @param arg the divisor.
* @return the quotient.
*/
public operator fun T.div(arg: NDStructure<T>): NDStructure<T> = arg.map { divide(it, this@div) }
public operator fun T.div(arg: StructureND<T>): StructureND<T> = arg.map { divide(it, this@div) }
// @ThreadLocal
// public companion object {

View File

@ -6,132 +6,132 @@ import space.kscience.kmath.structures.BufferFactory
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
public interface BufferNDAlgebra<T, A : Algebra<T>> : NDAlgebra<T, A> {
public interface BufferAlgebraND<T, A : Algebra<T>> : AlgebraND<T, A> {
public val strides: Strides
public val bufferFactory: BufferFactory<T>
override fun produce(initializer: A.(IntArray) -> T): NDBuffer<T> = NDBuffer(
override fun produce(initializer: A.(IntArray) -> T): BufferND<T> = BufferND(
strides,
bufferFactory(strides.linearSize) { offset ->
elementContext.initializer(strides.index(offset))
}
)
public val NDStructure<T>.buffer: Buffer<T>
public val StructureND<T>.buffer: Buffer<T>
get() = when {
!shape.contentEquals(this@BufferNDAlgebra.shape) -> throw ShapeMismatchException(
this@BufferNDAlgebra.shape,
!shape.contentEquals(this@BufferAlgebraND.shape) -> throw ShapeMismatchException(
this@BufferAlgebraND.shape,
shape
)
this is NDBuffer && this.strides == this@BufferNDAlgebra.strides -> this.buffer
this is BufferND && this.strides == this@BufferAlgebraND.strides -> this.buffer
else -> bufferFactory(strides.linearSize) { offset -> get(strides.index(offset)) }
}
override fun NDStructure<T>.map(transform: A.(T) -> T): NDBuffer<T> {
override fun StructureND<T>.map(transform: A.(T) -> T): BufferND<T> {
val buffer = bufferFactory(strides.linearSize) { offset ->
elementContext.transform(buffer[offset])
}
return NDBuffer(strides, buffer)
return BufferND(strides, buffer)
}
override fun NDStructure<T>.mapIndexed(transform: A.(index: IntArray, T) -> T): NDBuffer<T> {
override fun StructureND<T>.mapIndexed(transform: A.(index: IntArray, T) -> T): BufferND<T> {
val buffer = bufferFactory(strides.linearSize) { offset ->
elementContext.transform(
strides.index(offset),
buffer[offset]
)
}
return NDBuffer(strides, buffer)
return BufferND(strides, buffer)
}
override fun combine(a: NDStructure<T>, b: NDStructure<T>, transform: A.(T, T) -> T): NDBuffer<T> {
override fun combine(a: StructureND<T>, b: StructureND<T>, transform: A.(T, T) -> T): BufferND<T> {
val buffer = bufferFactory(strides.linearSize) { offset ->
elementContext.transform(a.buffer[offset], b.buffer[offset])
}
return NDBuffer(strides, buffer)
return BufferND(strides, buffer)
}
}
public open class BufferedNDGroup<T, A : Group<T>>(
public open class BufferedGroupND<T, A : Group<T>>(
final override val shape: IntArray,
final override val elementContext: A,
final override val bufferFactory: BufferFactory<T>,
) : NDGroup<T, A>, BufferNDAlgebra<T, A> {
) : GroupND<T, A>, BufferAlgebraND<T, A> {
override val strides: Strides = DefaultStrides(shape)
override val zero: NDBuffer<T> by lazy { produce { zero } }
override fun NDStructure<T>.unaryMinus(): NDStructure<T> = produce { -get(it) }
override val zero: BufferND<T> by lazy { produce { zero } }
override fun StructureND<T>.unaryMinus(): StructureND<T> = produce { -get(it) }
}
public open class BufferedNDRing<T, R : Ring<T>>(
public open class BufferedRingND<T, R : Ring<T>>(
shape: IntArray,
elementContext: R,
bufferFactory: BufferFactory<T>,
) : BufferedNDGroup<T, R>(shape, elementContext, bufferFactory), NDRing<T, R> {
override val one: NDBuffer<T> by lazy { produce { one } }
) : BufferedGroupND<T, R>(shape, elementContext, bufferFactory), RingND<T, R> {
override val one: BufferND<T> by lazy { produce { one } }
}
public open class BufferedNDField<T, R : Field<T>>(
public open class BufferedFieldND<T, R : Field<T>>(
shape: IntArray,
elementContext: R,
bufferFactory: BufferFactory<T>,
) : BufferedNDRing<T, R>(shape, elementContext, bufferFactory), NDField<T, R> {
) : BufferedRingND<T, R>(shape, elementContext, bufferFactory), FieldND<T, R> {
override fun scale(a: NDStructure<T>, value: Double): NDStructure<T> = a.map { it * value }
override fun scale(a: StructureND<T>, value: Double): StructureND<T> = a.map { it * value }
}
// space factories
public fun <T, A : Group<T>> NDAlgebra.Companion.space(
// group factories
public fun <T, A : Group<T>> AlgebraND.Companion.group(
space: A,
bufferFactory: BufferFactory<T>,
vararg shape: Int,
): BufferedNDGroup<T, A> = BufferedNDGroup(shape, space, bufferFactory)
): BufferedGroupND<T, A> = BufferedGroupND(shape, space, bufferFactory)
public inline fun <T, A : Group<T>, R> A.ndSpace(
public inline fun <T, A : Group<T>, R> A.ndGroup(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: BufferedNDGroup<T, A>.() -> R,
action: BufferedGroupND<T, A>.() -> R,
): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return NDAlgebra.space(this, bufferFactory, *shape).run(action)
return AlgebraND.group(this, bufferFactory, *shape).run(action)
}
//ring factories
public fun <T, A : Ring<T>> NDAlgebra.Companion.ring(
public fun <T, A : Ring<T>> AlgebraND.Companion.ring(
ring: A,
bufferFactory: BufferFactory<T>,
vararg shape: Int,
): BufferedNDRing<T, A> = BufferedNDRing(shape, ring, bufferFactory)
): BufferedRingND<T, A> = BufferedRingND(shape, ring, bufferFactory)
public inline fun <T, A : Ring<T>, R> A.ndRing(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: BufferedNDRing<T, A>.() -> R,
action: BufferedRingND<T, A>.() -> R,
): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return NDAlgebra.ring(this, bufferFactory, *shape).run(action)
return AlgebraND.ring(this, bufferFactory, *shape).run(action)
}
//field factories
public fun <T, A : Field<T>> NDAlgebra.Companion.field(
public fun <T, A : Field<T>> AlgebraND.Companion.field(
field: A,
bufferFactory: BufferFactory<T>,
vararg shape: Int,
): BufferedNDField<T, A> = BufferedNDField(shape, field, bufferFactory)
): BufferedFieldND<T, A> = BufferedFieldND(shape, field, bufferFactory)
@Suppress("UNCHECKED_CAST")
public inline fun <reified T : Any, A : Field<T>> NDAlgebra.Companion.auto(
public inline fun <reified T : Any, A : Field<T>> AlgebraND.Companion.auto(
field: A,
vararg shape: Int,
): NDField<T, A> = when (field) {
RealField -> RealNDField(shape) as NDField<T, A>
else -> BufferedNDField(shape, field, Buffer.Companion::auto)
): FieldND<T, A> = when (field) {
DoubleField -> DoubleFieldND(shape) as FieldND<T, A>
else -> BufferedFieldND(shape, field, Buffer.Companion::auto)
}
public inline fun <T, A : Field<T>, R> A.ndField(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: BufferedNDField<T, A>.() -> R,
action: BufferedFieldND<T, A>.() -> R,
): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return NDAlgebra.field(this, bufferFactory, *shape).run(action)
return AlgebraND.field(this, bufferFactory, *shape).run(action)
}

View File

@ -0,0 +1,48 @@
package space.kscience.kmath.nd
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
/**
* Represents [StructureND] over [Buffer].
*
* @param T the type of items.
* @param strides The strides to access elements of [Buffer] by linear indices.
* @param buffer The underlying buffer.
*/
public class BufferND<T>(
public val strides: Strides,
public val buffer: Buffer<T>,
) : StructureND<T> {
init {
if (strides.linearSize != buffer.size) {
error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}")
}
}
override operator fun get(index: IntArray): T = buffer[strides.offset(index)]
override val shape: IntArray get() = strides.shape
override fun elements(): Sequence<Pair<IntArray, T>> = strides.indices().map {
it to this[it]
}
override fun toString(): String = StructureND.toString(this)
}
/**
* Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferND]
*/
public inline fun <T, reified R : Any> StructureND<T>.mapToBuffer(
factory: BufferFactory<R> = Buffer.Companion::auto,
crossinline transform: (T) -> R,
): BufferND<R> {
return if (this is BufferND<T>)
BufferND(this.strides, factory.invoke(strides.linearSize) { transform(buffer[it]) })
else {
val strides = DefaultStrides(shape)
BufferND(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
}
}

View File

@ -0,0 +1,110 @@
package space.kscience.kmath.nd
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.NumbersAddOperations
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.structures.DoubleBuffer
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@OptIn(UnstableKMathAPI::class)
public class DoubleFieldND(
shape: IntArray,
) : BufferedFieldND<Double, DoubleField>(shape, DoubleField, ::DoubleBuffer),
NumbersAddOperations<StructureND<Double>>,
ScaleOperations<StructureND<Double>>,
ExtendedField<StructureND<Double>> {
override val zero: BufferND<Double> by lazy { produce { zero } }
override val one: BufferND<Double> by lazy { produce { one } }
override fun number(value: Number): BufferND<Double> {
val d = value.toDouble() // minimize conversions
return produce { d }
}
override val StructureND<Double>.buffer: DoubleBuffer
get() = when {
!shape.contentEquals(this@DoubleFieldND.shape) -> throw ShapeMismatchException(
this@DoubleFieldND.shape,
shape
)
this is BufferND && this.strides == this@DoubleFieldND.strides -> this.buffer as DoubleBuffer
else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun StructureND<Double>.map(
transform: DoubleField.(Double) -> Double,
): BufferND<Double> {
val buffer = DoubleBuffer(strides.linearSize) { offset -> DoubleField.transform(buffer.array[offset]) }
return BufferND(strides, buffer)
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun produce(initializer: DoubleField.(IntArray) -> Double): BufferND<Double> {
val array = DoubleArray(strides.linearSize) { offset ->
val index = strides.index(offset)
DoubleField.initializer(index)
}
return BufferND(strides, DoubleBuffer(array))
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun StructureND<Double>.mapIndexed(
transform: DoubleField.(index: IntArray, Double) -> Double,
): BufferND<Double> = BufferND(
strides,
buffer = DoubleBuffer(strides.linearSize) { offset ->
DoubleField.transform(
strides.index(offset),
buffer.array[offset]
)
})
@Suppress("OVERRIDE_BY_INLINE")
override inline fun combine(
a: StructureND<Double>,
b: StructureND<Double>,
transform: DoubleField.(Double, Double) -> Double,
): BufferND<Double> {
val buffer = DoubleBuffer(strides.linearSize) { offset ->
DoubleField.transform(a.buffer.array[offset], b.buffer.array[offset])
}
return BufferND(strides, buffer)
}
override fun scale(a: StructureND<Double>, value: Double): StructureND<Double> = a.map { it * value }
override fun power(arg: StructureND<Double>, pow: Number): BufferND<Double> = arg.map { power(it, pow) }
override fun exp(arg: StructureND<Double>): BufferND<Double> = arg.map { exp(it) }
override fun ln(arg: StructureND<Double>): BufferND<Double> = arg.map { ln(it) }
override fun sin(arg: StructureND<Double>): BufferND<Double> = arg.map { sin(it) }
override fun cos(arg: StructureND<Double>): BufferND<Double> = arg.map { cos(it) }
override fun tan(arg: StructureND<Double>): BufferND<Double> = arg.map { tan(it) }
override fun asin(arg: StructureND<Double>): BufferND<Double> = arg.map { asin(it) }
override fun acos(arg: StructureND<Double>): BufferND<Double> = arg.map { acos(it) }
override fun atan(arg: StructureND<Double>): BufferND<Double> = arg.map { atan(it) }
override fun sinh(arg: StructureND<Double>): BufferND<Double> = arg.map { sinh(it) }
override fun cosh(arg: StructureND<Double>): BufferND<Double> = arg.map { cosh(it) }
override fun tanh(arg: StructureND<Double>): BufferND<Double> = arg.map { tanh(it) }
override fun asinh(arg: StructureND<Double>): BufferND<Double> = arg.map { asinh(it) }
override fun acosh(arg: StructureND<Double>): BufferND<Double> = arg.map { acosh(it) }
override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
}
public fun AlgebraND.Companion.real(vararg shape: Int): DoubleFieldND = DoubleFieldND(shape)
/**
* Produce a context for n-dimensional operations inside this real field
*/
public inline fun <R> DoubleField.nd(vararg shape: Int, action: DoubleFieldND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return DoubleFieldND(shape).run(action)
}

View File

@ -1,111 +0,0 @@
package space.kscience.kmath.nd
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.NumbersAddOperations
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.RealBuffer
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@OptIn(UnstableKMathAPI::class)
public class RealNDField(
shape: IntArray,
) : BufferedNDField<Double, RealField>(shape, RealField, Buffer.Companion::real),
NumbersAddOperations<NDStructure<Double>>,
ScaleOperations<NDStructure<Double>>,
ExtendedField<NDStructure<Double>> {
override val zero: NDBuffer<Double> by lazy { produce { zero } }
override val one: NDBuffer<Double> by lazy { produce { one } }
override fun number(value: Number): NDBuffer<Double> {
val d = value.toDouble() // minimize conversions
return produce { d }
}
override val NDStructure<Double>.buffer: RealBuffer
get() = when {
!shape.contentEquals(this@RealNDField.shape) -> throw ShapeMismatchException(
this@RealNDField.shape,
shape
)
this is NDBuffer && this.strides == this@RealNDField.strides -> this.buffer as RealBuffer
else -> RealBuffer(strides.linearSize) { offset -> get(strides.index(offset)) }
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun NDStructure<Double>.map(
transform: RealField.(Double) -> Double,
): NDBuffer<Double> {
val buffer = RealBuffer(strides.linearSize) { offset -> RealField.transform(buffer.array[offset]) }
return NDBuffer(strides, buffer)
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun produce(initializer: RealField.(IntArray) -> Double): NDBuffer<Double> {
val array = DoubleArray(strides.linearSize) { offset ->
val index = strides.index(offset)
RealField.initializer(index)
}
return NDBuffer(strides, RealBuffer(array))
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun NDStructure<Double>.mapIndexed(
transform: RealField.(index: IntArray, Double) -> Double,
): NDBuffer<Double> = NDBuffer(
strides,
buffer = RealBuffer(strides.linearSize) { offset ->
RealField.transform(
strides.index(offset),
buffer.array[offset]
)
})
@Suppress("OVERRIDE_BY_INLINE")
override inline fun combine(
a: NDStructure<Double>,
b: NDStructure<Double>,
transform: RealField.(Double, Double) -> Double,
): NDBuffer<Double> {
val buffer = RealBuffer(strides.linearSize) { offset ->
RealField.transform(a.buffer.array[offset], b.buffer.array[offset])
}
return NDBuffer(strides, buffer)
}
override fun scale(a: NDStructure<Double>, value: Double): NDStructure<Double> = a.map { it * value }
override fun power(arg: NDStructure<Double>, pow: Number): NDBuffer<Double> = arg.map { power(it, pow) }
override fun exp(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { exp(it) }
override fun ln(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { ln(it) }
override fun sin(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { sin(it) }
override fun cos(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { cos(it) }
override fun tan(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { tan(it) }
override fun asin(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { asin(it) }
override fun acos(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { acos(it) }
override fun atan(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { atan(it) }
override fun sinh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { sinh(it) }
override fun cosh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { cosh(it) }
override fun tanh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { tanh(it) }
override fun asinh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { asinh(it) }
override fun acosh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { acosh(it) }
override fun atanh(arg: NDStructure<Double>): NDBuffer<Double> = arg.map { atanh(it) }
}
public fun NDAlgebra.Companion.real(vararg shape: Int): RealNDField = RealNDField(shape)
/**
* Produce a context for n-dimensional operations inside this real field
*/
public inline fun <R> RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return RealNDField(shape).run(action)
}

View File

@ -9,15 +9,15 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@OptIn(UnstableKMathAPI::class)
public class ShortNDRing(
public class ShortRingND(
shape: IntArray,
) : BufferedNDRing<Short, ShortRing>(shape, ShortRing, Buffer.Companion::auto),
NumbersAddOperations<NDStructure<Short>> {
) : BufferedRingND<Short, ShortRing>(shape, ShortRing, Buffer.Companion::auto),
NumbersAddOperations<StructureND<Short>> {
override val zero: NDBuffer<Short> by lazy { produce { zero } }
override val one: NDBuffer<Short> by lazy { produce { one } }
override val zero: BufferND<Short> by lazy { produce { zero } }
override val one: BufferND<Short> by lazy { produce { one } }
override fun number(value: Number): NDBuffer<Short> {
override fun number(value: Number): BufferND<Short> {
val d = value.toShort() // minimize conversions
return produce { d }
}
@ -26,11 +26,11 @@ public class ShortNDRing(
/**
* Fast element production using function inlining.
*/
public inline fun BufferedNDRing<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): NDBuffer<Short> {
return NDBuffer(strides, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) }))
public inline fun BufferedRingND<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): BufferND<Short> {
return BufferND(strides, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) }))
}
public inline fun <R> ShortRing.nd(vararg shape: Int, action: ShortNDRing.() -> R): R {
public inline fun <R> ShortRing.nd(vararg shape: Int, action: ShortRingND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ShortNDRing(shape).run(action)
return ShortRingND(shape).run(action)
}

View File

@ -6,7 +6,7 @@ import space.kscience.kmath.structures.asSequence
/**
* A structure that is guaranteed to be one-dimensional
*/
public interface Structure1D<T> : NDStructure<T>, Buffer<T> {
public interface Structure1D<T> : StructureND<T>, Buffer<T> {
public override val dimension: Int get() = 1
public override operator fun get(index: IntArray): T {
@ -20,7 +20,7 @@ public interface Structure1D<T> : NDStructure<T>, Buffer<T> {
/**
* A 1D wrapper for nd-structure
*/
private inline class Structure1DWrapper<T>(val structure: NDStructure<T>) : Structure1D<T> {
private inline class Structure1DWrapper<T>(val structure: StructureND<T>) : Structure1D<T> {
override val shape: IntArray get() = structure.shape
override val size: Int get() = structure.shape[0]
@ -43,14 +43,25 @@ private inline class Buffer1DWrapper<T>(val buffer: Buffer<T>) : Structure1D<T>
}
/**
* Represent a [NDStructure] as [Structure1D]. Throw error in case of dimension mismatch
* Represent a [StructureND] as [Structure1D]. Throw error in case of dimension mismatch
*/
public fun <T> NDStructure<T>.as1D(): Structure1D<T> = if (shape.size == 1) {
if (this is NDBuffer) Buffer1DWrapper(this.buffer) else Structure1DWrapper(this)
} else
error("Can't create 1d-structure from ${shape.size}d-structure")
public fun <T> StructureND<T>.as1D(): Structure1D<T> = this as? Structure1D<T> ?: if (shape.size == 1) {
when (this) {
is BufferND -> Buffer1DWrapper(this.buffer)
else -> Structure1DWrapper(this)
}
} else error("Can't create 1d-structure from ${shape.size}d-structure")
/**
* Represent this buffer as 1D structure
*/
public fun <T> Buffer<T>.asND(): Structure1D<T> = Buffer1DWrapper(this)
/**
* Expose inner buffer of this [Structure1D] if possible
*/
internal fun <T : Any> Structure1D<T>.unwrap(): Buffer<T> = when {
this is Buffer1DWrapper<T> -> buffer
this is Structure1DWrapper && structure is BufferND<T> -> structure.buffer
else -> this
}

View File

@ -1,16 +1,16 @@
package space.kscience.kmath.nd
import space.kscience.kmath.linear.BufferMatrix
import space.kscience.kmath.linear.RealMatrixContext
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.VirtualBuffer
import kotlin.reflect.KClass
/**
* A structure that is guaranteed to be two-dimensional.
*
* @param T the type of items.
*/
public interface Structure2D<T> : NDStructure<T> {
public interface Structure2D<T> : StructureND<T> {
/**
* The number of rows in this structure.
*/
@ -26,14 +26,14 @@ public interface Structure2D<T> : NDStructure<T> {
/**
* The buffer of rows of this structure. It gets elements from the structure dynamically.
*/
public val rows: Buffer<Buffer<T>>
get() = VirtualBuffer(rowNum) { i -> VirtualBuffer(colNum) { j -> get(i, j) } }
public val rows: List<Buffer<T>>
get() = List(rowNum) { i -> VirtualBuffer(colNum) { j -> get(i, j) } }
/**
* The buffer of columns of this structure. It gets elements from the structure dynamically.
*/
public val columns: Buffer<Buffer<T>>
get() = VirtualBuffer(colNum) { j -> VirtualBuffer(rowNum) { i -> get(i, j) } }
public val columns: List<Buffer<T>>
get() = List(colNum) { j -> VirtualBuffer(rowNum) { i -> get(i, j) } }
/**
* Retrieves an element from the structure by two indices.
@ -54,21 +54,13 @@ public interface Structure2D<T> : NDStructure<T> {
for (j in 0 until colNum) yield(intArrayOf(i, j) to get(i, j))
}
public companion object {
public inline fun real(
rows: Int,
columns: Int,
crossinline init: (i: Int, j: Int) -> Double,
): BufferMatrix<Double> = RealMatrixContext.produce(rows,columns) { i, j ->
init(i, j)
}
}
public companion object
}
/**
* A 2D wrapper for nd-structure
*/
private inline class Structure2DWrapper<T>(val structure: NDStructure<T>) : Structure2D<T> {
private inline class Structure2DWrapper<T>(val structure: StructureND<T>) : Structure2D<T> {
override val shape: IntArray get() = structure.shape
override val rowNum: Int get() = shape[0]
@ -76,20 +68,23 @@ private inline class Structure2DWrapper<T>(val structure: NDStructure<T>) : Stru
override operator fun get(i: Int, j: Int): T = structure[i, j]
@UnstableKMathAPI
override fun <F : Any> getFeature(type: KClass<F>): F? = structure.getFeature(type)
override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements()
}
/**
* Represent a [NDStructure] as [Structure1D]. Throw error in case of dimension mismatch
* Represent a [StructureND] as [Structure1D]. Throw error in case of dimension mismatch
*/
public fun <T> NDStructure<T>.as2D(): Structure2D<T> = if (shape.size == 2)
Structure2DWrapper(this)
else
error("Can't create 2d-structure from ${shape.size}d-structure")
public fun <T> StructureND<T>.as2D(): Structure2D<T> = this as? Structure2D<T> ?: when (shape.size) {
2 -> Structure2DWrapper(this)
else -> error("Can't create 2d-structure from ${shape.size}d-structure")
}
/**
* Alias for [Structure2D] with more familiar name.
*
* @param T the type of items in the matrix.
* Expose inner [StructureND] if possible
*/
public typealias Matrix<T> = Structure2D<T>
internal fun <T> Structure2D<T>.unwrap(): StructureND<T> =
if (this is Structure2DWrapper) structure
else this

View File

@ -3,8 +3,6 @@ package space.kscience.kmath.nd
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.asSequence
import kotlin.jvm.JvmName
import kotlin.native.concurrent.ThreadLocal
import kotlin.reflect.KClass
@ -14,9 +12,11 @@ import kotlin.reflect.KClass
* of dimensions and items in an array is defined by its shape, which is a sequence of non-negative integers that
* specify the sizes of each dimension.
*
* StructureND is in general identity-free. [StructureND.contentEquals] should be used in tests to compare contents.
*
* @param T the type of items.
*/
public interface NDStructure<T> {
public interface StructureND<T> {
/**
* The shape of structure, i.e. non-empty sequence of non-negative integers that specify sizes of dimensions of
* this structure.
@ -43,42 +43,57 @@ public interface NDStructure<T> {
*/
public fun elements(): Sequence<Pair<IntArray, T>>
//force override equality and hash code
public override fun equals(other: Any?): Boolean
public override fun hashCode(): Int
/**
* Feature is additional property or hint that does not directly affect the structure, but could in some cases help
* optimize operations and performance. If the feature is not present, null is defined.
* Feature is some additional strucure information which allows to access it special properties or hints.
* If the feature is not present, null is returned.
*/
@UnstableKMathAPI
public fun <T : Any> getFeature(type: KClass<T>): T? = null
public fun <F : Any> getFeature(type: KClass<F>): F? = null
public companion object {
/**
* Indicates whether some [NDStructure] is equal to another one.
* Indicates whether some [StructureND] is equal to another one.
*/
public fun contentEquals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean {
public fun <T : Any> contentEquals(st1: StructureND<T>, st2: StructureND<T>): Boolean {
if (st1 === st2) return true
// fast comparison of buffers if possible
if (st1 is NDBuffer && st2 is NDBuffer && st1.strides == st2.strides)
return st1.buffer.contentEquals(st2.buffer)
if (st1 is BufferND && st2 is BufferND && st1.strides == st2.strides)
return Buffer.contentEquals(st1.buffer, st2.buffer)
//element by element comparison if it could not be avoided
return st1.elements().all { (index, value) -> value == st2[index] }
}
/**
* Debug output to string
*/
public fun toString(structure: StructureND<*>): String {
val bufferRepr: String = when (structure.shape.size) {
1 -> (0 until structure.shape[0]).map { structure[it] }
.joinToString(prefix = "[", postfix = "]", separator = ", ")
2 -> (0 until structure.shape[0]).joinToString(prefix = "[", postfix = "]", separator = ", ") { i ->
(0 until structure.shape[1]).joinToString(prefix = "[", postfix = "]", separator = ", ") { j ->
structure[i, j].toString()
}
}
else -> "..."
}
val className = structure::class.simpleName ?: "StructureND"
return "$className(shape=${structure.shape.contentToString()}, buffer=$bufferRepr)"
}
/**
* Creates a NDStructure with explicit buffer factory.
*
* Strides should be reused if possible.
*/
public fun <T> build(
public fun <T> buffered(
strides: Strides,
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing,
initializer: (IntArray) -> T,
): NDBuffer<T> = NDBuffer(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) })
): BufferND<T> = BufferND(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) })
/**
* Inline create NDStructure with non-boxing buffer implementation if it is possible
@ -86,37 +101,37 @@ public interface NDStructure<T> {
public inline fun <reified T : Any> auto(
strides: Strides,
crossinline initializer: (IntArray) -> T,
): NDBuffer<T> = NDBuffer(strides, Buffer.auto(strides.linearSize) { i -> initializer(strides.index(i)) })
): BufferND<T> = BufferND(strides, Buffer.auto(strides.linearSize) { i -> initializer(strides.index(i)) })
public inline fun <T : Any> auto(
type: KClass<T>,
strides: Strides,
crossinline initializer: (IntArray) -> T,
): NDBuffer<T> = NDBuffer(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> build(
public fun <T> buffered(
shape: IntArray,
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing,
initializer: (IntArray) -> T,
): NDBuffer<T> = build(DefaultStrides(shape), bufferFactory, initializer)
): BufferND<T> = buffered(DefaultStrides(shape), bufferFactory, initializer)
public inline fun <reified T : Any> auto(
shape: IntArray,
crossinline initializer: (IntArray) -> T,
): NDBuffer<T> = auto(DefaultStrides(shape), initializer)
): BufferND<T> = auto(DefaultStrides(shape), initializer)
@JvmName("autoVarArg")
public inline fun <reified T : Any> auto(
vararg shape: Int,
crossinline initializer: (IntArray) -> T,
): NDBuffer<T> =
): BufferND<T> =
auto(DefaultStrides(shape), initializer)
public inline fun <T : Any> auto(
type: KClass<T>,
vararg shape: Int,
crossinline initializer: (IntArray) -> T,
): NDBuffer<T> = auto(type, DefaultStrides(shape), initializer)
): BufferND<T> = auto(type, DefaultStrides(shape), initializer)
}
}
@ -126,15 +141,15 @@ public interface NDStructure<T> {
* @param index the indices.
* @return the value.
*/
public operator fun <T> NDStructure<T>.get(vararg index: Int): T = get(index)
public operator fun <T> StructureND<T>.get(vararg index: Int): T = get(index)
@UnstableKMathAPI
public inline fun <reified T : Any> NDStructure<*>.getFeature(): T? = getFeature(T::class)
public inline fun <reified T : Any> StructureND<*>.getFeature(): T? = getFeature(T::class)
/**
* Represents mutable [NDStructure].
* Represents mutable [StructureND].
*/
public interface MutableNDStructure<T> : NDStructure<T> {
public interface MutableStructureND<T> : StructureND<T> {
/**
* Inserts an item at the specified indices.
*
@ -147,7 +162,7 @@ public interface MutableNDStructure<T> : NDStructure<T> {
/**
* Transform a structure element-by element in place.
*/
public inline fun <T> MutableNDStructure<T>.mapInPlace(action: (IntArray, T) -> T): Unit =
public inline fun <T> MutableStructureND<T>.mapInPlace(action: (IntArray, T) -> T): Unit =
elements().forEach { (index, oldValue) -> this[index] = action(index, oldValue) }
/**
@ -251,97 +266,10 @@ public class DefaultStrides private constructor(override val shape: IntArray) :
}
}
/**
* Represents [NDStructure] over [Buffer].
*
* @param T the type of items.
* @param strides The strides to access elements of [Buffer] by linear indices.
* @param buffer The underlying buffer.
*/
public open class NDBuffer<T>(
public val strides: Strides,
buffer: Buffer<T>,
) : NDStructure<T> {
init {
if (strides.linearSize != buffer.size) {
error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}")
}
}
public open val buffer: Buffer<T> = buffer
override operator fun get(index: IntArray): T = buffer[strides.offset(index)]
override val shape: IntArray get() = strides.shape
override fun elements(): Sequence<Pair<IntArray, T>> = strides.indices().map {
it to this[it]
}
override fun equals(other: Any?): Boolean {
return NDStructure.contentEquals(this, other as? NDStructure<*> ?: return false)
}
override fun hashCode(): Int {
var result = strides.hashCode()
result = 31 * result + buffer.hashCode()
return result
}
override fun toString(): String {
val bufferRepr: String = when (shape.size) {
1 -> buffer.asSequence().joinToString(prefix = "[", postfix = "]", separator = ", ")
2 -> (0 until shape[0]).joinToString(prefix = "[", postfix = "]", separator = ", ") { i ->
(0 until shape[1]).joinToString(prefix = "[", postfix = "]", separator = ", ") { j ->
val offset = strides.offset(intArrayOf(i, j))
buffer[offset].toString()
}
}
else -> "..."
}
return "NDBuffer(shape=${shape.contentToString()}, buffer=$bufferRepr)"
}
}
/**
* Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [NDBuffer]
*/
public inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer(
factory: BufferFactory<R> = Buffer.Companion::auto,
crossinline transform: (T) -> R,
): NDBuffer<R> {
return if (this is NDBuffer<T>)
NDBuffer(this.strides, factory.invoke(strides.linearSize) { transform(buffer[it]) })
else {
val strides = DefaultStrides(shape)
NDBuffer(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
}
}
/**
* Mutable ND buffer based on linear [MutableBuffer].
*/
public class MutableNDBuffer<T>(
strides: Strides,
buffer: MutableBuffer<T>,
) : NDBuffer<T>(strides, buffer), MutableNDStructure<T> {
init {
require(strides.linearSize == buffer.size) {
"Expected buffer side of ${strides.linearSize}, but found ${buffer.size}"
}
}
override val buffer: MutableBuffer<T> = super.buffer as MutableBuffer<T>
override operator fun set(index: IntArray, value: T): Unit = buffer.set(strides.offset(index), value)
}
public inline fun <reified T : Any> NDStructure<T>.combine(
struct: NDStructure<T>,
public inline fun <reified T : Any> StructureND<T>.combine(
struct: StructureND<T>,
crossinline block: (T, T) -> T,
): NDStructure<T> {
): StructureND<T> {
require(shape.contentEquals(struct.shape)) { "Shape mismatch in structure combination" }
return NDStructure.auto(shape) { block(this[it], struct[it]) }
return StructureND.auto(shape) { block(this[it], struct[it]) }
}

View File

@ -1,8 +1,8 @@
package space.kscience.kmath.operations
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.BufferedNDRing
import space.kscience.kmath.nd.NDAlgebra
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.BufferedRingND
import space.kscience.kmath.operations.BigInt.Companion.BASE
import space.kscience.kmath.operations.BigInt.Companion.BASE_SIZE
import space.kscience.kmath.structures.Buffer
@ -464,5 +464,5 @@ public inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigIn
public inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer<BigInt> =
boxing(size, initializer)
public fun NDAlgebra.Companion.bigInt(vararg shape: Int): BufferedNDRing<BigInt, BigIntField> =
BufferedNDRing(shape, BigIntField, Buffer.Companion::bigInt)
public fun AlgebraND.Companion.bigInt(vararg shape: Int): BufferedRingND<BigInt, BigIntField> =
BufferedRingND(shape, BigIntField, Buffer.Companion::bigInt)

View File

@ -55,7 +55,7 @@ public interface ExtendedField<T> : ExtendedFieldOperations<T>, Field<T>, Numeri
* A field for [Double] without boxing. Does not produce appropriate field element.
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object RealField : ExtendedField<Double>, Norm<Double, Double>, ScaleOperations<Double> {
public object DoubleField : ExtendedField<Double>, Norm<Double, Double>, ScaleOperations<Double> {
public override val zero: Double = 0.0
public override val one: Double = 1.0

View File

@ -17,7 +17,9 @@ public typealias BufferFactory<T> = (Int, (Int) -> T) -> Buffer<T>
public typealias MutableBufferFactory<T> = (Int, (Int) -> T) -> MutableBuffer<T>
/**
* A generic immutable random-access structure for both primitives and objects.
* A generic read-only random-access structure for both primitives and objects.
*
* [Buffer] is in general identity-free. [Buffer.contentEquals] should be used for content equality checks.
*
* @param T the type of elements contained in the buffer.
*/
@ -37,49 +39,46 @@ public interface Buffer<out T> {
*/
public operator fun iterator(): Iterator<T>
/**
* Checks content equality with another buffer.
*/
public fun contentEquals(other: Buffer<*>): Boolean =
asSequence().mapIndexed { index, value -> value == other[index] }.all { it }
public companion object {
/**
* Creates a [RealBuffer] with the specified [size], where each element is calculated by calling the specified
* [initializer] function.
* Check the element-by-element match of content of two buffers.
*/
public inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer =
RealBuffer(size) { initializer(it) }
public fun <T: Any> contentEquals(first: Buffer<T>, second: Buffer<T>): Boolean{
if (first.size != second.size) return false
for (i in first.indices) {
if (first[i] != second[i]) return false
}
return true
}
/**
* Creates a [ListBuffer] of given type [T] with given [size]. Each element is calculated by calling the
* specified [initializer] function.
*/
public inline fun <T> boxing(size: Int, initializer: (Int) -> T): Buffer<T> =
ListBuffer(List(size, initializer))
// TODO add resolution based on Annotation or companion resolution
List(size, initializer).asBuffer()
/**
* Creates a [Buffer] of given [type]. If the type is primitive, specialized buffers are used ([IntBuffer],
* [RealBuffer], etc.), [ListBuffer] is returned otherwise.
* [DoubleBuffer], etc.), [ListBuffer] is returned otherwise.
*
* The [size] is specified, and each element is calculated by calling the specified [initializer] function.
*/
@Suppress("UNCHECKED_CAST")
public inline fun <T : Any> auto(type: KClass<T>, size: Int, initializer: (Int) -> T): Buffer<T> =
when (type) {
Double::class -> RealBuffer(size) { initializer(it) as Double } as Buffer<T>
Short::class -> ShortBuffer(size) { initializer(it) as Short } as Buffer<T>
Int::class -> IntBuffer(size) { initializer(it) as Int } as Buffer<T>
Long::class -> LongBuffer(size) { initializer(it) as Long } as Buffer<T>
Float::class -> FloatBuffer(size) { initializer(it) as Float } as Buffer<T>
Double::class -> MutableBuffer.double(size) { initializer(it) as Double } as Buffer<T>
Short::class -> MutableBuffer.short(size) { initializer(it) as Short } as Buffer<T>
Int::class -> MutableBuffer.int(size) { initializer(it) as Int } as Buffer<T>
Long::class -> MutableBuffer.long(size) { initializer(it) as Long } as Buffer<T>
Float::class -> MutableBuffer.float(size) { initializer(it) as Float } as Buffer<T>
else -> boxing(size, initializer)
}
/**
* Creates a [Buffer] of given type [T]. If the type is primitive, specialized buffers are used ([IntBuffer],
* [RealBuffer], etc.), [ListBuffer] is returned otherwise.
* [DoubleBuffer], etc.), [ListBuffer] is returned otherwise.
*
* The [size] is specified, and each element is calculated by calling the specified [initializer] function.
*/
@ -89,21 +88,6 @@ public interface Buffer<out T> {
}
}
/**
* Creates a sequence that returns all elements from this [Buffer].
*/
public fun <T> Buffer<T>.asSequence(): Sequence<T> = Sequence(::iterator)
/**
* Creates an iterable that returns all elements from this [Buffer].
*/
public fun <T> Buffer<T>.asIterable(): Iterable<T> = Iterable(::iterator)
/**
* Converts this [Buffer] to a new [List]
*/
public fun <T> Buffer<T>.toList(): List<T> = asSequence().toList()
/**
* Returns an [IntRange] of the valid indices for this [Buffer].
*/
@ -126,6 +110,44 @@ public interface MutableBuffer<T> : Buffer<T> {
public fun copy(): MutableBuffer<T>
public companion object {
/**
* Creates a [DoubleBuffer] with the specified [size], where each element is calculated by calling the specified
* [initializer] function.
*/
public inline fun double(size: Int, initializer: (Int) -> Double): DoubleBuffer =
DoubleBuffer(size, initializer)
/**
* Creates a [ShortBuffer] with the specified [size], where each element is calculated by calling the specified
* [initializer] function.
*/
public inline fun short(size: Int, initializer: (Int) -> Short): ShortBuffer =
ShortBuffer(size, initializer)
/**
* Creates a [IntBuffer] with the specified [size], where each element is calculated by calling the specified
* [initializer] function.
*/
public inline fun int(size: Int, initializer: (Int) -> Int): IntBuffer =
IntBuffer(size, initializer)
/**
* Creates a [LongBuffer] with the specified [size], where each element is calculated by calling the specified
* [initializer] function.
*/
public inline fun long(size: Int, initializer: (Int) -> Long): LongBuffer =
LongBuffer(size, initializer)
/**
* Creates a [FloatBuffer] with the specified [size], where each element is calculated by calling the specified
* [initializer] function.
*/
public inline fun float(size: Int, initializer: (Int) -> Float): FloatBuffer =
FloatBuffer(size, initializer)
/**
* Create a boxing mutable buffer of given type
*/
@ -134,37 +156,30 @@ public interface MutableBuffer<T> : Buffer<T> {
/**
* Creates a [MutableBuffer] of given [type]. If the type is primitive, specialized buffers are used
* ([IntBuffer], [RealBuffer], etc.), [ListBuffer] is returned otherwise.
* ([IntBuffer], [DoubleBuffer], etc.), [ListBuffer] is returned otherwise.
*
* The [size] is specified, and each element is calculated by calling the specified [initializer] function.
*/
@Suppress("UNCHECKED_CAST")
public inline fun <T : Any> auto(type: KClass<out T>, size: Int, initializer: (Int) -> T): MutableBuffer<T> =
when (type) {
Double::class -> RealBuffer(size) { initializer(it) as Double } as MutableBuffer<T>
Short::class -> ShortBuffer(size) { initializer(it) as Short } as MutableBuffer<T>
Int::class -> IntBuffer(size) { initializer(it) as Int } as MutableBuffer<T>
Float::class -> FloatBuffer(size) { initializer(it) as Float } as MutableBuffer<T>
Long::class -> LongBuffer(size) { initializer(it) as Long } as MutableBuffer<T>
Double::class -> double(size) { initializer(it) as Double } as MutableBuffer<T>
Short::class -> short(size) { initializer(it) as Short } as MutableBuffer<T>
Int::class -> int(size) { initializer(it) as Int } as MutableBuffer<T>
Float::class -> float(size) { initializer(it) as Float } as MutableBuffer<T>
Long::class -> long(size) { initializer(it) as Long } as MutableBuffer<T>
else -> boxing(size, initializer)
}
/**
* Creates a [MutableBuffer] of given type [T]. If the type is primitive, specialized buffers are used
* ([IntBuffer], [RealBuffer], etc.), [ListBuffer] is returned otherwise.
* ([IntBuffer], [DoubleBuffer], etc.), [ListBuffer] is returned otherwise.
*
* The [size] is specified, and each element is calculated by calling the specified [initializer] function.
*/
@Suppress("UNCHECKED_CAST")
public inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> =
auto(T::class, size, initializer)
/**
* Creates a [RealBuffer] with the specified [size], where each element is calculated by calling the specified
* [initializer] function.
*/
public inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer =
RealBuffer(size) { initializer(it) }
}
}
@ -187,15 +202,6 @@ public inline class ListBuffer<T>(public val list: List<T>) : Buffer<T> {
*/
public fun <T> List<T>.asBuffer(): ListBuffer<T> = ListBuffer(this)
/**
* Creates a new [ListBuffer] with the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline fun <T> ListBuffer(size: Int, init: (Int) -> T): ListBuffer<T> = List(size, init).asBuffer()
/**
* [MutableBuffer] implementation over [MutableList].
*
@ -216,16 +222,20 @@ public inline class MutableListBuffer<T>(public val list: MutableList<T>) : Muta
override fun copy(): MutableBuffer<T> = MutableListBuffer(ArrayList(list))
}
/**
* Returns an [ListBuffer] that wraps the original list.
*/
public fun <T> MutableList<T>.asMutableBuffer(): MutableListBuffer<T> = MutableListBuffer(this)
/**
* [MutableBuffer] implementation over [Array].
*
* @param T the type of elements contained in the buffer.
* @property array The underlying array.
*/
public class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> {
public class ArrayBuffer<T>(internal val array: Array<T>) : MutableBuffer<T> {
// Can't inline because array is invariant
override val size: Int
get() = array.size
override val size: Int get() = array.size
override operator fun get(index: Int): T = array[index]
@ -243,16 +253,6 @@ public class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> {
*/
public fun <T> Array<T>.asBuffer(): ArrayBuffer<T> = ArrayBuffer(this)
/**
* Creates a new [ArrayBuffer] with the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an array element given its index.
*/
public inline fun <reified T> ArrayBuffer(size: Int, init: (Int) -> T): ArrayBuffer<T> =
Array(size) { i -> init(i) }.asBuffer()
/**
* Immutable wrapper for [MutableBuffer].
*
@ -280,27 +280,9 @@ public class VirtualBuffer<T>(override val size: Int, private val generator: (In
}
override operator fun iterator(): Iterator<T> = (0 until size).asSequence().map(generator).iterator()
override fun contentEquals(other: Buffer<*>): Boolean {
return if (other is VirtualBuffer) {
this.size == other.size && this.generator == other.generator
} else {
super.contentEquals(other)
}
}
}
/**
* Convert this buffer to read-only buffer.
*/
public fun <T> Buffer<T>.asReadOnly(): Buffer<T> = if (this is MutableBuffer) ReadOnlyBuffer(this) else this
/**
* Typealias for buffer transformations.
*/
public typealias BufferTransform<T, R> = (Buffer<T>) -> Buffer<R>
/**
* Typealias for buffer transformations with suspend function.
*/
public typealias SuspendBufferTransform<T, R> = suspend (Buffer<T>) -> Buffer<R>

View File

@ -1,8 +1,8 @@
package space.kscience.kmath.structures
import space.kscience.kmath.nd.DefaultStrides
import space.kscience.kmath.nd.NDStructure
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as2D
/**
@ -13,10 +13,10 @@ internal class BufferAccessor2D<T : Any>(
public val colNum: Int,
val factory: MutableBufferFactory<T>,
) {
public operator fun Buffer<T>.get(i: Int, j: Int): T = get(i + colNum * j)
public operator fun Buffer<T>.get(i: Int, j: Int): T = get(i * colNum + j)
public operator fun MutableBuffer<T>.set(i: Int, j: Int, value: T) {
set(i + colNum * j, value)
set(i * colNum + j, value)
}
public inline fun create(crossinline init: (i: Int, j: Int) -> T): MutableBuffer<T> =
@ -25,7 +25,7 @@ internal class BufferAccessor2D<T : Any>(
public fun create(mat: Structure2D<T>): MutableBuffer<T> = create { i, j -> mat[i, j] }
//TODO optimize wrapper
public fun MutableBuffer<T>.collect(): Structure2D<T> = NDStructure.build(
public fun MutableBuffer<T>.collect(): Structure2D<T> = StructureND.buffered(
DefaultStrides(intArrayOf(rowNum, colNum)),
factory
) { (i, j) ->

View File

@ -0,0 +1,56 @@
package space.kscience.kmath.structures
/**
* Specialized [MutableBuffer] implementation over [DoubleArray].
*
* @property array the underlying array.
*/
@Suppress("OVERRIDE_BY_INLINE")
public inline class DoubleBuffer(public val array: DoubleArray) : MutableBuffer<Double> {
override val size: Int get() = array.size
override operator fun get(index: Int): Double = array[index]
override operator fun set(index: Int, value: Double) {
array[index] = value
}
override operator fun iterator(): DoubleIterator = array.iterator()
override fun copy(): DoubleBuffer = DoubleBuffer(array.copyOf())
}
/**
* Creates a new [DoubleBuffer] with the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an buffer element given its index.
*/
public inline fun DoubleBuffer(size: Int, init: (Int) -> Double): DoubleBuffer = DoubleBuffer(DoubleArray(size) { init(it) })
/**
* Returns a new [DoubleBuffer] of given elements.
*/
public fun DoubleBuffer(vararg doubles: Double): DoubleBuffer = DoubleBuffer(doubles)
/**
* Simplified [DoubleBuffer] to array comparison
*/
public fun DoubleBuffer.contentEquals(vararg doubles: Double): Boolean = array.contentEquals(doubles)
/**
* Returns a new [DoubleArray] containing all of the elements of this [Buffer].
*/
public fun Buffer<Double>.toDoubleArray(): DoubleArray = when (this) {
is DoubleBuffer -> array.copyOf()
else -> DoubleArray(size, ::get)
}
/**
* Returns [DoubleBuffer] over this array.
*
* @receiver the array.
* @return the new buffer.
*/
public fun DoubleArray.asBuffer(): DoubleBuffer = DoubleBuffer(this)

View File

@ -0,0 +1,272 @@
package space.kscience.kmath.structures
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.ExtendedFieldOperations
import kotlin.math.*
/**
* [ExtendedFieldOperations] over [DoubleBuffer].
*/
public object DoubleBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> {
override fun Buffer<Double>.unaryMinus(): DoubleBuffer = if (this is DoubleBuffer) {
DoubleBuffer(size) { -array[it] }
} else {
DoubleBuffer(size) { -get(it) }
}
public override fun add(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
require(b.size == a.size) {
"The size of the first buffer ${a.size} should be the same as for second one: ${b.size} "
}
return if (a is DoubleBuffer && b is DoubleBuffer) {
val aArray = a.array
val bArray = b.array
DoubleBuffer(DoubleArray(a.size) { aArray[it] + bArray[it] })
} else DoubleBuffer(DoubleArray(a.size) { a[it] + b[it] })
}
//
// public override fun multiply(a: Buffer<Double>, k: Number): RealBuffer {
// val kValue = k.toDouble()
//
// return if (a is RealBuffer) {
// val aArray = a.array
// RealBuffer(DoubleArray(a.size) { aArray[it] * kValue })
// } else RealBuffer(DoubleArray(a.size) { a[it] * kValue })
// }
//
// public override fun divide(a: Buffer<Double>, k: Number): RealBuffer {
// val kValue = k.toDouble()
//
// return if (a is RealBuffer) {
// val aArray = a.array
// RealBuffer(DoubleArray(a.size) { aArray[it] / kValue })
// } else RealBuffer(DoubleArray(a.size) { a[it] / kValue })
// }
public override fun multiply(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
require(b.size == a.size) {
"The size of the first buffer ${a.size} should be the same as for second one: ${b.size} "
}
return if (a is DoubleBuffer && b is DoubleBuffer) {
val aArray = a.array
val bArray = b.array
DoubleBuffer(DoubleArray(a.size) { aArray[it] * bArray[it] })
} else
DoubleBuffer(DoubleArray(a.size) { a[it] * b[it] })
}
public override fun divide(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
require(b.size == a.size) {
"The size of the first buffer ${a.size} should be the same as for second one: ${b.size} "
}
return if (a is DoubleBuffer && b is DoubleBuffer) {
val aArray = a.array
val bArray = b.array
DoubleBuffer(DoubleArray(a.size) { aArray[it] / bArray[it] })
} else DoubleBuffer(DoubleArray(a.size) { a[it] / b[it] })
}
public override fun sin(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { sin(array[it]) })
} else DoubleBuffer(DoubleArray(arg.size) { sin(arg[it]) })
public override fun cos(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { cos(array[it]) })
} else DoubleBuffer(DoubleArray(arg.size) { cos(arg[it]) })
public override fun tan(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { tan(array[it]) })
} else DoubleBuffer(DoubleArray(arg.size) { tan(arg[it]) })
public override fun asin(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { asin(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { asin(arg[it]) })
public override fun acos(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { acos(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { acos(arg[it]) })
public override fun atan(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { atan(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { atan(arg[it]) })
public override fun sinh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { sinh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { sinh(arg[it]) })
public override fun cosh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { cosh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { cosh(arg[it]) })
public override fun tanh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { tanh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { tanh(arg[it]) })
public override fun asinh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { asinh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { asinh(arg[it]) })
public override fun acosh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { acosh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { acosh(arg[it]) })
public override fun atanh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { atanh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { atanh(arg[it]) })
public override fun power(arg: Buffer<Double>, pow: Number): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) })
} else
DoubleBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) })
public override fun exp(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { exp(array[it]) })
} else DoubleBuffer(DoubleArray(arg.size) { exp(arg[it]) })
public override fun ln(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { ln(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) })
}
/**
* [ExtendedField] over [DoubleBuffer].
*
* @property size the size of buffers to operate on.
*/
public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Double>> {
public override val zero: Buffer<Double> by lazy { DoubleBuffer(size) { 0.0 } }
public override val one: Buffer<Double> by lazy { DoubleBuffer(size) { 1.0 } }
override fun number(value: Number): Buffer<Double> = DoubleBuffer(size) { value.toDouble() }
override fun Buffer<Double>.unaryMinus(): Buffer<Double> = DoubleBufferFieldOperations.run {
-this@unaryMinus
}
public 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 DoubleBufferFieldOperations.add(a, b)
}
public override fun scale(a: Buffer<Double>, value: Double): DoubleBuffer {
require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
return if (a is DoubleBuffer) {
val aArray = a.array
DoubleBuffer(DoubleArray(a.size) { aArray[it] * value })
} else DoubleBuffer(DoubleArray(a.size) { a[it] * value })
}
public 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 DoubleBufferFieldOperations.multiply(a, b)
}
public 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 DoubleBufferFieldOperations.divide(a, b)
}
public override fun sin(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.sin(arg)
}
public override fun cos(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.cos(arg)
}
public override fun tan(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.tan(arg)
}
public override fun asin(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.asin(arg)
}
public override fun acos(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.acos(arg)
}
public override fun atan(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.atan(arg)
}
public override fun sinh(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.sinh(arg)
}
public override fun cosh(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.cosh(arg)
}
public override fun tanh(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.tanh(arg)
}
public override fun asinh(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.asinh(arg)
}
public override fun acosh(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.acosh(arg)
}
public override fun atanh(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.atanh(arg)
}
public 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 DoubleBufferFieldOperations.power(arg, pow)
}
public override fun exp(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.exp(arg)
}
public override fun ln(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.ln(arg)
}
}

View File

@ -48,7 +48,7 @@ public fun FlaggedBuffer<*>.isMissing(index: Int): Boolean = hasFlag(index, Valu
/**
* A real buffer which supports flags for each value like NaN or Missing
*/
public class FlaggedRealBuffer(public val values: DoubleArray, public val flags: ByteArray) : FlaggedBuffer<Double?>,
public class FlaggedDoubleBuffer(public val values: DoubleArray, public val flags: ByteArray) : FlaggedBuffer<Double?>,
Buffer<Double?> {
init {
require(values.size == flags.size) { "Values and flags must have the same dimensions" }
@ -65,7 +65,7 @@ public class FlaggedRealBuffer(public val values: DoubleArray, public val flags:
}.iterator()
}
public inline fun FlaggedRealBuffer.forEachValid(block: (Double) -> Unit) {
public inline fun FlaggedDoubleBuffer.forEachValid(block: (Double) -> Unit) {
indices
.asSequence()
.filter(::isValid)

View File

@ -36,10 +36,12 @@ public inline fun FloatBuffer(size: Int, init: (Int) -> Float): FloatBuffer = Fl
public fun FloatBuffer(vararg floats: Float): FloatBuffer = FloatBuffer(floats)
/**
* Returns a [FloatArray] containing all of the elements of this [MutableBuffer].
* Returns a new [FloatArray] containing all of the elements of this [Buffer].
*/
public val MutableBuffer<out Float>.array: FloatArray
get() = (if (this is FloatBuffer) array else FloatArray(size) { get(it) })
public fun Buffer<Float>.toFloatArray(): FloatArray = when (this) {
is FloatBuffer -> array.copyOf()
else -> FloatArray(size, ::get)
}
/**
* Returns [FloatBuffer] over this array.

View File

@ -35,10 +35,12 @@ public inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer = IntBuffe
public fun IntBuffer(vararg ints: Int): IntBuffer = IntBuffer(ints)
/**
* Returns a [IntArray] containing all of the elements of this [MutableBuffer].
* Returns a new [IntArray] containing all of the elements of this [Buffer].
*/
public val MutableBuffer<out Int>.array: IntArray
get() = (if (this is IntBuffer) array else IntArray(size) { get(it) })
public fun Buffer<Int>.toIntArray(): IntArray = when (this) {
is IntBuffer -> array.copyOf()
else -> IntArray(size, ::get)
}
/**
* Returns [IntBuffer] over this array.

View File

@ -35,10 +35,12 @@ public inline fun LongBuffer(size: Int, init: (Int) -> Long): LongBuffer = LongB
public fun LongBuffer(vararg longs: Long): LongBuffer = LongBuffer(longs)
/**
* Returns a [IntArray] containing all of the elements of this [MutableBuffer].
* Returns a new [LongArray] containing all of the elements of this [Buffer].
*/
public val MutableBuffer<out Long>.array: LongArray
get() = (if (this is LongBuffer) array else LongArray(size) { get(it) })
public fun Buffer<Long>.toLongArray(): LongArray = when (this) {
is LongBuffer -> array.copyOf()
else -> LongArray(size, ::get)
}
/**
* Returns [LongBuffer] over this array.

View File

@ -24,7 +24,7 @@ public open class MemoryBuffer<T : Any>(protected val memory: Memory, protected
public inline fun <T : Any> create(
spec: MemorySpec<T>,
size: Int,
initializer: (Int) -> T
initializer: (Int) -> T,
): MemoryBuffer<T> = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer ->
(0 until size).forEach { buffer[it] = initializer(it) }
}
@ -53,7 +53,7 @@ public class MutableMemoryBuffer<T : Any>(memory: Memory, spec: MemorySpec<T>) :
public inline fun <T : Any> create(
spec: MemorySpec<T>,
size: Int,
initializer: (Int) -> T
initializer: (Int) -> T,
): MutableMemoryBuffer<T> = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer ->
(0 until size).forEach { buffer[it] = initializer(it) }
}

View File

@ -1,54 +0,0 @@
package space.kscience.kmath.structures
/**
* Specialized [MutableBuffer] implementation over [DoubleArray].
*
* @property array the underlying array.
*/
@Suppress("OVERRIDE_BY_INLINE")
public inline class RealBuffer(public val array: DoubleArray) : MutableBuffer<Double> {
override val size: Int get() = array.size
override operator fun get(index: Int): Double = array[index]
override operator fun set(index: Int, value: Double) {
array[index] = value
}
override operator fun iterator(): DoubleIterator = array.iterator()
override fun copy(): RealBuffer = RealBuffer(array.copyOf())
}
/**
* Creates a new [RealBuffer] with the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for an buffer element given its index.
*/
public inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer = RealBuffer(DoubleArray(size) { init(it) })
/**
* Returns a new [RealBuffer] of given elements.
*/
public fun RealBuffer(vararg doubles: Double): RealBuffer = RealBuffer(doubles)
/**
* Simplified [RealBuffer] to array comparison
*/
public fun RealBuffer.contentEquals(vararg doubles: Double): Boolean = array.contentEquals(doubles)
/**
* Returns a [DoubleArray] containing all of the elements of this [MutableBuffer].
*/
public val MutableBuffer<out Double>.array: DoubleArray
get() = (if (this is RealBuffer) array else DoubleArray(size) { get(it) })
/**
* Returns [RealBuffer] over this array.
*
* @receiver the array.
* @return the new buffer.
*/
public fun DoubleArray.asBuffer(): RealBuffer = RealBuffer(this)

View File

@ -1,272 +0,0 @@
package space.kscience.kmath.structures
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.ExtendedFieldOperations
import kotlin.math.*
/**
* [ExtendedFieldOperations] over [RealBuffer].
*/
public object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> {
override fun Buffer<Double>.unaryMinus(): RealBuffer = if (this is RealBuffer) {
RealBuffer(size) { -array[it] }
} else {
RealBuffer(size) { -get(it) }
}
public override fun add(a: Buffer<Double>, b: Buffer<Double>): RealBuffer {
require(b.size == a.size) {
"The size of the first buffer ${a.size} should be the same as for second one: ${b.size} "
}
return if (a is RealBuffer && b is RealBuffer) {
val aArray = a.array
val bArray = b.array
RealBuffer(DoubleArray(a.size) { aArray[it] + bArray[it] })
} else RealBuffer(DoubleArray(a.size) { a[it] + b[it] })
}
//
// public override fun multiply(a: Buffer<Double>, k: Number): RealBuffer {
// val kValue = k.toDouble()
//
// return if (a is RealBuffer) {
// val aArray = a.array
// RealBuffer(DoubleArray(a.size) { aArray[it] * kValue })
// } else RealBuffer(DoubleArray(a.size) { a[it] * kValue })
// }
//
// public override fun divide(a: Buffer<Double>, k: Number): RealBuffer {
// val kValue = k.toDouble()
//
// return if (a is RealBuffer) {
// val aArray = a.array
// RealBuffer(DoubleArray(a.size) { aArray[it] / kValue })
// } else RealBuffer(DoubleArray(a.size) { a[it] / kValue })
// }
public override fun multiply(a: Buffer<Double>, b: Buffer<Double>): RealBuffer {
require(b.size == a.size) {
"The size of the first buffer ${a.size} should be the same as for second one: ${b.size} "
}
return if (a is RealBuffer && b is RealBuffer) {
val aArray = a.array
val bArray = b.array
RealBuffer(DoubleArray(a.size) { aArray[it] * bArray[it] })
} else
RealBuffer(DoubleArray(a.size) { a[it] * b[it] })
}
public override fun divide(a: Buffer<Double>, b: Buffer<Double>): RealBuffer {
require(b.size == a.size) {
"The size of the first buffer ${a.size} should be the same as for second one: ${b.size} "
}
return if (a is RealBuffer && b is RealBuffer) {
val aArray = a.array
val bArray = b.array
RealBuffer(DoubleArray(a.size) { aArray[it] / bArray[it] })
} else RealBuffer(DoubleArray(a.size) { a[it] / b[it] })
}
public override fun sin(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { sin(array[it]) })
} else RealBuffer(DoubleArray(arg.size) { sin(arg[it]) })
public override fun cos(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { cos(array[it]) })
} else RealBuffer(DoubleArray(arg.size) { cos(arg[it]) })
public override fun tan(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { tan(array[it]) })
} else RealBuffer(DoubleArray(arg.size) { tan(arg[it]) })
public override fun asin(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { asin(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { asin(arg[it]) })
public override fun acos(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { acos(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { acos(arg[it]) })
public override fun atan(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { atan(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { atan(arg[it]) })
public override fun sinh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { sinh(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { sinh(arg[it]) })
public override fun cosh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { cosh(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { cosh(arg[it]) })
public override fun tanh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { tanh(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { tanh(arg[it]) })
public override fun asinh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { asinh(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { asinh(arg[it]) })
public override fun acosh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { acosh(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { acosh(arg[it]) })
public override fun atanh(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { atanh(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { atanh(arg[it]) })
public override fun power(arg: Buffer<Double>, pow: Number): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) })
} else
RealBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) })
public override fun exp(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { exp(array[it]) })
} else RealBuffer(DoubleArray(arg.size) { exp(arg[it]) })
public override fun ln(arg: Buffer<Double>): RealBuffer = if (arg is RealBuffer) {
val array = arg.array
RealBuffer(DoubleArray(arg.size) { ln(array[it]) })
} else
RealBuffer(DoubleArray(arg.size) { ln(arg[it]) })
}
/**
* [ExtendedField] over [RealBuffer].
*
* @property size the size of buffers to operate on.
*/
public class RealBufferField(public val size: Int) : ExtendedField<Buffer<Double>> {
public override val zero: Buffer<Double> by lazy { RealBuffer(size) { 0.0 } }
public override val one: Buffer<Double> by lazy { RealBuffer(size) { 1.0 } }
override fun number(value: Number): Buffer<Double> = RealBuffer(size) { value.toDouble() }
override fun Buffer<Double>.unaryMinus(): Buffer<Double> = RealBufferFieldOperations.run {
-this@unaryMinus
}
public override fun add(a: Buffer<Double>, b: Buffer<Double>): RealBuffer {
require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
return RealBufferFieldOperations.add(a, b)
}
public override fun scale(a: Buffer<Double>, value: Double): RealBuffer {
require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
return if (a is RealBuffer) {
val aArray = a.array
RealBuffer(DoubleArray(a.size) { aArray[it] * value })
} else RealBuffer(DoubleArray(a.size) { a[it] * value })
}
public override fun multiply(a: Buffer<Double>, b: Buffer<Double>): RealBuffer {
require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
return RealBufferFieldOperations.multiply(a, b)
}
public override fun divide(a: Buffer<Double>, b: Buffer<Double>): RealBuffer {
require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
return RealBufferFieldOperations.divide(a, b)
}
public override fun sin(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.sin(arg)
}
public override fun cos(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.cos(arg)
}
public override fun tan(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.tan(arg)
}
public override fun asin(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.asin(arg)
}
public override fun acos(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.acos(arg)
}
public override fun atan(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.atan(arg)
}
public override fun sinh(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.sinh(arg)
}
public override fun cosh(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.cosh(arg)
}
public override fun tanh(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.tanh(arg)
}
public override fun asinh(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.asinh(arg)
}
public override fun acosh(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.acosh(arg)
}
public override fun atanh(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.atanh(arg)
}
public override fun power(arg: Buffer<Double>, pow: Number): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.power(arg, pow)
}
public override fun exp(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.exp(arg)
}
public override fun ln(arg: Buffer<Double>): RealBuffer {
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return RealBufferFieldOperations.ln(arg)
}
}

View File

@ -33,10 +33,12 @@ public inline fun ShortBuffer(size: Int, init: (Int) -> Short): ShortBuffer = Sh
public fun ShortBuffer(vararg shorts: Short): ShortBuffer = ShortBuffer(shorts)
/**
* Returns a [ShortArray] containing all of the elements of this [MutableBuffer].
* Returns a new [ShortArray] containing all of the elements of this [Buffer].
*/
public val MutableBuffer<out Short>.array: ShortArray
get() = (if (this is ShortBuffer) array else ShortArray(size) { get(it) })
public fun Buffer<Short>.toShortArray(): ShortArray = when (this) {
is ShortBuffer -> array.copyOf()
else -> ShortArray(size, ::get)
}
/**
* Returns [ShortBuffer] over this array.

View File

@ -0,0 +1,84 @@
package space.kscience.kmath.structures
import space.kscience.kmath.misc.UnstableKMathAPI
/**
* Typealias for buffer transformations.
*/
public typealias BufferTransform<T, R> = (Buffer<T>) -> Buffer<R>
/**
* Typealias for buffer transformations with suspend function.
*/
public typealias SuspendBufferTransform<T, R> = suspend (Buffer<T>) -> Buffer<R>
/**
* Creates a sequence that returns all elements from this [Buffer].
*/
public fun <T> Buffer<T>.asSequence(): Sequence<T> = Sequence(::iterator)
/**
* Creates an iterable that returns all elements from this [Buffer].
*/
public fun <T> Buffer<T>.asIterable(): Iterable<T> = Iterable(::iterator)
/**
* Returns a new [List] containing all elements of this buffer.
*/
public fun <T> Buffer<T>.toList(): List<T> = when (this) {
is ArrayBuffer<T> -> array.toList()
is ListBuffer<T> -> list.toList()
is MutableListBuffer<T> -> list.toList()
else -> asSequence().toList()
}
/**
* Returns a new [MutableList] filled with all elements of this buffer.
* **NOTE:** this method uses a protective copy, so it should not be used in performance-critical code.
*/
@UnstableKMathAPI
public fun <T> Buffer<T>.toMutableList(): MutableList<T> = when (this) {
is ArrayBuffer<T> -> array.toMutableList()
is ListBuffer<T> -> list.toMutableList()
is MutableListBuffer<T> -> list.toMutableList()
else -> MutableList(size, ::get)
}
/**
* Returns a new [Array] containing all elements of this buffer.
* **NOTE:** this method uses a protective copy, so it should not be used in performance-critical code.
*/
@UnstableKMathAPI
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.
* Provided [BufferFactory] is used to construct the new buffer.
*/
public inline fun <T : Any, reified R : Any> Buffer<T>.map(
bufferFactory: BufferFactory<R> = Buffer.Companion::auto,
crossinline block: (T) -> R,
): Buffer<R> = bufferFactory(size) { block(get(it)) }
/**
* Create a new buffer from this one with the given indexed mapping function.
* Provided [BufferFactory] is used to construct the new buffer.
*/
public inline fun <T : Any, reified R : Any> Buffer<T>.mapIndexed(
bufferFactory: BufferFactory<R> = Buffer.Companion::auto,
crossinline block: (index: Int, value: T) -> R,
): Buffer<R> = bufferFactory(size) { block(it, get(it)) }
/**
* Zip two buffers using given [transform].
*/
@UnstableKMathAPI
public inline fun <T1 : Any, T2 : Any, reified R : Any> Buffer<T1>.zip(
other: Buffer<T2>,
bufferFactory: BufferFactory<R> = Buffer.Companion::auto,
crossinline transform: (T1, T2) -> R,
): Buffer<R> {
require(size == other.size) { "Buffer size mismatch in zip: expected $size but found ${other.size}" }
return bufferFactory(size) { transform(get(it), other[it]) }
}

View File

@ -1,6 +1,6 @@
package space.kscience.kmath.expressions
import space.kscience.kmath.operations.RealField
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
import kotlin.test.Test
import kotlin.test.assertEquals
@ -11,7 +11,7 @@ class ExpressionFieldTest {
@Test
fun testExpression() {
val expression = FunctionalExpressionField(RealField).invoke {
val expression = FunctionalExpressionField(DoubleField).invoke {
val x by binding()
x * x + 2 * x + one
}
@ -27,7 +27,7 @@ class ExpressionFieldTest {
return x * x + 2 * x + one
}
val expression = FunctionalExpressionField(RealField).expression()
val expression = FunctionalExpressionField(DoubleField).expression()
assertEquals(expression(x to 1.0), 4.0)
}
@ -38,7 +38,7 @@ class ExpressionFieldTest {
x * x + 2 * x + one
}
val expression = FunctionalExpressionField(RealField).expressionBuilder()
val expression = FunctionalExpressionField(DoubleField).expressionBuilder()
assertEquals(expression(x to 1.0), 4.0)
}
}

Some files were not shown because too many files have changed in this diff Show More