Merge dev, API check failed

This commit is contained in:
Roland Grinis 2021-03-21 07:41:48 +00:00
commit 510c855a65
180 changed files with 3277 additions and 2813 deletions

View File

@ -22,8 +22,17 @@ jobs:
key: ubuntu-20.04-gradle-${{ hashFiles('*.gradle.kts') }} key: ubuntu-20.04-gradle-${{ hashFiles('*.gradle.kts') }}
restore-keys: | restore-keys: |
ubuntu-20.04-gradle- 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 - name: Build
run: ./gradlew build --no-daemon --stacktrace run: |
./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace
mv build/dokka/htmlMultiModule/-modules.html build/dokka/htmlMultiModule/index.html
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@4.1.0 uses: JamesIves/github-pages-deploy-action@4.1.0
with: with:

View File

@ -4,18 +4,22 @@
### Added ### Added
- ScaleOperations interface - ScaleOperations interface
- Field extends ScaleOperations - Field extends ScaleOperations
- Basic integration API
### Changed ### Changed
- Exponential operations merged with hyperbolic functions - Exponential operations merged with hyperbolic functions
- Space is replaced by Group. Space is reserved for vector spaces. - Space is replaced by Group. Space is reserved for vector spaces.
- VectorSpace is now a vector space - 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 ### Deprecated
### Removed ### Removed
- Nearest in Domain. To be implemented in geometry package. - Nearest in Domain. To be implemented in geometry package.
- Number multiplication and division in main Algebra chain - Number multiplication and division in main Algebra chain
- `contentEquals` from Buffer. It moved to the companion.
### Fixed ### Fixed
@ -73,6 +77,7 @@
- `toGrid` method. - `toGrid` method.
- Public visibility of `BufferAccessor2D` - Public visibility of `BufferAccessor2D`
- `Real` class - `Real` class
- StructureND identity and equals
### Fixed ### Fixed
- `symbol` method in `MstExtendedField` (https://github.com/mipt-npm/kmath/pull/140) - `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) ![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) [![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)
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)
# KMath # KMath
@ -89,12 +87,12 @@ KMath is a modular library. Different modules provide different features with di
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
> >
> **Features:** > **Features:**
> - [expression-language](kmath-ast/src/jvmMain/kotlin/kscience/kmath/ast/parser.kt) : Expression language and its parser > - [expression-language](kmath-ast/src/jvmMain/kotlin/space/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](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/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure > - [mst-building](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure
> - [mst-interpreter](kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST interpreter > - [mst-interpreter](kmath-ast/src/commonMain/kotlin/space/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-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/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler > - [mst-js-codegen](kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
<hr/> <hr/>
@ -110,8 +108,8 @@ KMath is a modular library. Different modules provide different features with di
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
> >
> **Features:** > **Features:**
> - [complex](kmath-complex/src/commonMain/kotlin/kscience/kmath/complex/Complex.kt) : Complex Numbers > - [complex](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt) : Complex Numbers
> - [quaternion](kmath-complex/src/commonMain/kotlin/kscience/kmath/complex/Quaternion.kt) : Quaternions > - [quaternion](kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt) : Quaternions
<hr/> <hr/>
@ -121,15 +119,15 @@ KMath is a modular library. Different modules provide different features with di
> **Maturity**: DEVELOPMENT > **Maturity**: DEVELOPMENT
> >
> **Features:** > **Features:**
> - [algebras](kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : Algebraic structures like rings, spaces and fields. > - [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/kscience/kmath/structures/NDStructure.kt) : Many-dimensional structures and operations on them. > - [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/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. > - [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/kscience/kmath/structures/Buffers.kt) : One-dimensional structure > - [buffers](kmath-core/src/commonMain/kotlin/space/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 > - [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 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. performance calculations to code generation.
> - [domains](kmath-core/src/commonMain/kotlin/kscience/kmath/domains) : Domains > - [domains](kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains) : Domains
> - [autodif](kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation > - [autodif](kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation
<hr/> <hr/>
@ -159,9 +157,9 @@ One can still use generic algebras though.
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
> >
> **Features:** > **Features:**
> - [RealVector](kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealVector.kt) : Numpy-like operations for Buffers/Points > - [DoubleVector](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.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 > - [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/kscience/kmath/structures/grids.kt) : Uniform grid generators > - [grids](kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/structures/grids.kt) : Uniform grid generators
<hr/> <hr/>
@ -171,10 +169,10 @@ One can still use generic algebras though.
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
> >
> **Features:** > **Features:**
> - [piecewise](kmath-functions/Piecewise functions.) : src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt > - [piecewise](kmath-functions/Piecewise functions.) : src/commonMain/kotlin/space/kscience/kmath/functions/Piecewise.kt
> - [polynomials](kmath-functions/Polynomial functions.) : src/commonMain/kotlin/kscience/kmath/functions/Polynomial.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/kscience/kmath/interpolation/LinearInterpolator.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/kscience/kmath/interpolation/SplineInterpolator.kt > - [spline interpolation](kmath-functions/Cubic spline XY interpolator.) : src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt
<hr/> <hr/>
@ -208,9 +206,9 @@ One can still use generic algebras though.
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
> >
> **Features:** > **Features:**
> - [nd4jarraystructure](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt) : NDStructure wrapper for INDArray > - [nd4jarraystructure](kmath-nd4j/#) : NDStructure wrapper for INDArray
> - [nd4jarrayrings](kmath-nd4j/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt) : Rings over Nd4jArrayStructure of Int and Long > - [nd4jarrayrings](kmath-nd4j/#) : 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 > - [nd4jarrayfields](kmath-nd4j/#) : Fields over Nd4jArrayStructure of Float and Double
<hr/> <hr/>
@ -256,8 +254,8 @@ repositories {
} }
dependencies { dependencies {
api("kscience.kmath:kmath-core:() -> kotlin.Any") api("space.kscience:kmath-core:0.3.0-dev-3")
// api("kscience.kmath:kmath-core-jvm:() -> kotlin.Any") for jvm-specific version // 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" group = "space.kscience"
version = "0.3.0" version = "0.3.0-dev-3"
} }
subprojects { subprojects {

View File

@ -31,7 +31,7 @@ multiplication;
- [Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and its neutral element (i.e. 1); - [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. - [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 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` 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 version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type 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="defs6"><clipPath
id="clipPath24" id="clipPath24"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path22" 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" id="clipPath36"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path34" 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)" transform="matrix(1.3333333,0,0,-1.3333333,0,586.66667)"
id="g10"><g id="g10"><g
transform="scale(0.1)" 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 version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type 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="defs6"><clipPath
id="clipPath32" id="clipPath32"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path30" 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)" transform="matrix(1.3333333,0,0,-1.3333333,0,633.33333)"
id="g10"><g id="g10"><g
transform="scale(0.1)" 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 version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type 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="defs6"><clipPath
id="clipPath24" id="clipPath24"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path22" 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" id="clipPath36"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path34" 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" id="clipPath48"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path46" 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" id="clipPath60"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path58" 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)" transform="matrix(1.3333333,0,0,-1.3333333,0,570.66667)"
id="g10"><g id="g10"><g
transform="scale(0.1)" 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 version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type 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="defs6"><clipPath
id="clipPath40" id="clipPath40"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path38" 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" id="clipPath52"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path50" 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" id="clipPath64"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path62" 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" id="clipPath76"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path74" 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" id="clipPath88"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path86" 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" id="clipPath100"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path98" 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" id="clipPath112"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path110" 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" id="clipPath124"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path122" 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" id="clipPath136"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path134" 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" id="clipPath148"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path146" 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" id="clipPath160"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path158" 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" id="clipPath172"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path170" 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" id="clipPath184"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path182" 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" id="clipPath196"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path194" 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" id="clipPath208"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path206" 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" id="clipPath220"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path218" 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" id="clipPath232"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path230" 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" id="clipPath244"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path242" 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" id="clipPath256"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path254" 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" id="clipPath268"
clipPathUnits="userSpaceOnUse"><path clipPathUnits="userSpaceOnUse"><path
id="path266" 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)" transform="matrix(1.3333333,0,0,-1.3333333,0,529.33333)"
id="g10"><g id="g10"><g
transform="scale(0.1)" 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: Let us consider following contexts:
```kotlin ```kotlin
// automatically build context most suited for given type. // 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 // specialized nd-field for Double. It works as generic Double field as well
val specializedField = NDField.real(dim, dim) val specializedField = NDField.real(dim, dim)
//A generic boxing field. It should be used for objects, not primitives. //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: 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://repo.kotlin.link' }
> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } > maven { url 'https://dl.bintray.com/hotkeytlt/maven' }
> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap > 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 { > dependencies {
@ -29,9 +26,6 @@
> maven("https://repo.kotlin.link") > 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/kotlin/kotlin-eap") // include for builds based on kotlin-eap
> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a > 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 { > dependencies {

View File

@ -3,9 +3,7 @@
![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg) ![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) [![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)
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)
# KMath # KMath
@ -106,7 +104,7 @@ repositories {
} }
dependencies { dependencies {
api("kscience.kmath:kmath-core:$version") api("${group}:kmath-core:$version")
// api("kscience.kmath:kmath-core-jvm:$version") for jvm-specific version // api("kscience.kmath:kmath-core-jvm:$version") for jvm-specific version
} }
``` ```

View File

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

View File

@ -8,7 +8,7 @@ import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.ejml.EjmlLinearSpace import space.kscience.kmath.ejml.EjmlLinearSpace
import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.invoke import space.kscience.kmath.linear.invoke
import space.kscience.kmath.operations.RealField import space.kscience.kmath.operations.DoubleField
import kotlin.random.Random import kotlin.random.Random
@State(Scope.Benchmark) @State(Scope.Benchmark)
@ -51,7 +51,7 @@ internal class DotBenchmark {
@Benchmark @Benchmark
fun bufferedDot(blackhole: Blackhole) { fun bufferedDot(blackhole: Blackhole) {
LinearSpace.auto(RealField).invoke { LinearSpace.auto(DoubleField).invoke {
blackhole.consume(matrix1 dot matrix2) 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.expressionInField
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.expressions.symbol 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 space.kscience.kmath.operations.bindSymbol
import kotlin.random.Random import kotlin.random.Random
@ -68,7 +68,7 @@ internal class ExpressionsInterpretersBenchmark {
} }
private companion object { private companion object {
private val algebra = RealField private val algebra = DoubleField
private val x by symbol private val x by symbol
} }
} }

View File

@ -5,7 +5,7 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import space.kscience.kmath.nd.* import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.RealField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
@State(Scope.Benchmark) @State(Scope.Benchmark)
@ -13,7 +13,7 @@ internal class NDFieldBenchmark {
@Benchmark @Benchmark
fun autoFieldAdd(blackhole: Blackhole) { fun autoFieldAdd(blackhole: Blackhole) {
with(autoField) { with(autoField) {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += one } repeat(n) { res += one }
blackhole.consume(res) blackhole.consume(res)
} }
@ -22,7 +22,7 @@ internal class NDFieldBenchmark {
@Benchmark @Benchmark
fun specializedFieldAdd(blackhole: Blackhole) { fun specializedFieldAdd(blackhole: Blackhole) {
with(specializedField) { with(specializedField) {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
blackhole.consume(res) blackhole.consume(res)
} }
@ -32,7 +32,7 @@ internal class NDFieldBenchmark {
@Benchmark @Benchmark
fun boxingFieldAdd(blackhole: Blackhole) { fun boxingFieldAdd(blackhole: Blackhole) {
with(genericField) { with(genericField) {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
blackhole.consume(res) blackhole.consume(res)
} }
@ -41,8 +41,8 @@ internal class NDFieldBenchmark {
private companion object { private companion object {
private const val dim = 1000 private const val dim = 1000
private const val n = 100 private const val n = 100
private val autoField = NDAlgebra.auto(RealField, dim, dim) private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val specializedField = NDAlgebra.real(dim, dim) private val specializedField = AlgebraND.real(dim, dim)
private val genericField = NDAlgebra.field(RealField, Buffer.Companion::boxing, 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.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.NDAlgebra import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.NDStructure import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.auto import space.kscience.kmath.nd.auto
import space.kscience.kmath.nd.real import space.kscience.kmath.nd.real
import space.kscience.kmath.operations.RealField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.viktor.ViktorNDField import space.kscience.kmath.viktor.ViktorNDField
@State(Scope.Benchmark) @State(Scope.Benchmark)
@ -17,7 +17,7 @@ internal class ViktorBenchmark {
@Benchmark @Benchmark
fun automaticFieldAddition(blackhole: Blackhole) { fun automaticFieldAddition(blackhole: Blackhole) {
with(autoField) { with(autoField) {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
blackhole.consume(res) blackhole.consume(res)
} }
@ -26,7 +26,7 @@ internal class ViktorBenchmark {
@Benchmark @Benchmark
fun realFieldAddition(blackhole: Blackhole) { fun realFieldAddition(blackhole: Blackhole) {
with(realField) { with(realField) {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
blackhole.consume(res) blackhole.consume(res)
} }
@ -54,8 +54,8 @@ internal class ViktorBenchmark {
private const val n = 100 private const val n = 100
// automatically build context most suited for given type. // automatically build context most suited for given type.
private val autoField = NDAlgebra.auto(RealField, dim, dim) private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val realField = NDAlgebra.real(dim, dim) private val realField = AlgebraND.real(dim, dim)
private val viktorField = ViktorNDField(dim, dim) private val viktorField = ViktorNDField(dim, dim)
} }
} }

View File

@ -5,11 +5,11 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.NDAlgebra import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.auto import space.kscience.kmath.nd.auto
import space.kscience.kmath.nd.real import space.kscience.kmath.nd.real
import space.kscience.kmath.operations.RealField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.viktor.ViktorNDField import space.kscience.kmath.viktor.ViktorFieldND
@State(Scope.Benchmark) @State(Scope.Benchmark)
internal class ViktorLogBenchmark { internal class ViktorLogBenchmark {
@ -46,8 +46,8 @@ internal class ViktorLogBenchmark {
private const val n = 100 private const val n = 100
// automatically build context most suited for given type. // automatically build context most suited for given type.
private val autoField = NDAlgebra.auto(RealField, dim, dim) private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val realNdField = NDAlgebra.real(dim, dim) private val realNdField = AlgebraND.real(dim, dim)
private val viktorField = ViktorNDField(intArrayOf(dim, dim)) private val viktorField = ViktorFieldND(intArrayOf(dim, dim))
} }
} }

View File

@ -1,10 +1,10 @@
package space.kscience.kmath.ast package space.kscience.kmath.ast
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.RealField import space.kscience.kmath.operations.DoubleField
fun main() { fun main() {
val expr = RealField.mstInField { val expr = DoubleField.mstInField {
val x = bindSymbol("x") val x = bindSymbol("x")
x * 2.0 + number(2.0) / x - 16.0 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.invoke
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.kotlingrad.differentiable 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 * 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() { fun main() {
val x by symbol 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() .differentiable()
.derivative(x) .derivative(x)
.compile() .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)) 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.chiSquared
import space.kscience.kmath.commons.optimization.minimize import space.kscience.kmath.commons.optimization.minimize
import space.kscience.kmath.expressions.symbol 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.map
import space.kscience.kmath.real.step import space.kscience.kmath.real.step
import space.kscience.kmath.stat.* import space.kscience.kmath.stat.*
@ -26,7 +26,7 @@ private val c by symbol
/** /**
* Shortcut to use buffers in plotly * Shortcut to use buffers in plotly
*/ */
operator fun TraceValues.invoke(vector: RealVector) { operator fun TraceValues.invoke(vector: DoubleVector) {
numbers = vector.asIterable() numbers = vector.asIterable()
} }

View File

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

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.complex.complex import space.kscience.kmath.complex.complex
import space.kscience.kmath.nd.NDAlgebra import space.kscience.kmath.nd.AlgebraND
fun main() { fun main() {
// 2d element // 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()) Complex(i.toDouble() - j.toDouble(), i.toDouble() + j.toDouble())
} }
println(element) println(element)
// 1d element operation // 1d element operation
val result = with(NDAlgebra.complex(8)) { val result = with(AlgebraND.complex(8)) {
val a = produce { (it) -> i * it - it.toDouble() } val a = produce { (it) -> i * it - it.toDouble() }
val b = 3 val b = 3
val c = Complex(1.0, 1.0) 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.complex.*
import space.kscience.kmath.linear.transpose import space.kscience.kmath.linear.transpose
import space.kscience.kmath.nd.NDAlgebra import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.NDStructure import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.real import space.kscience.kmath.nd.real
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
@ -15,12 +15,12 @@ fun main() {
val dim = 1000 val dim = 1000
val n = 1000 val n = 1000
val realField = NDAlgebra.real(dim, dim) val realField = AlgebraND.real(dim, dim)
val complexField: ComplexNDField = NDAlgebra.complex(dim, dim) val complexField: ComplexFieldND = AlgebraND.complex(dim, dim)
val realTime = measureTimeMillis { val realTime = measureTimeMillis {
realField { realField {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { repeat(n) {
res += 1.0 res += 1.0
} }
@ -31,7 +31,7 @@ fun main() {
val complexTime = measureTimeMillis { val complexTime = measureTimeMillis {
complexField { complexField {
var res: NDStructure<Complex> = one var res: StructureND<Complex> = one
repeat(n) { repeat(n) {
res += 1.0 res += 1.0
} }

View File

@ -4,7 +4,7 @@ import kotlinx.coroutines.GlobalScope
import org.nd4j.linalg.factory.Nd4j import org.nd4j.linalg.factory.Nd4j
import space.kscience.kmath.nd.* import space.kscience.kmath.nd.*
import space.kscience.kmath.nd4j.Nd4jArrayField 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.operations.invoke
import space.kscience.kmath.viktor.ViktorNDField import space.kscience.kmath.viktor.ViktorNDField
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
@ -24,56 +24,56 @@ fun main() {
val n = 1000 val n = 1000
// automatically build context most suited for given type. // 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 // 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. //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. // Nd4j specialized field.
val nd4jField = Nd4jArrayField.real(dim, dim) val nd4jField = Nd4jArrayField.real(dim, dim)
//viktor field //viktor field
val viktorField = ViktorNDField(dim, dim) val viktorField = ViktorNDField(dim, dim)
//parallel processing based on Java Streams //parallel processing based on Java Streams
val parallelField = NDAlgebra.realWithStream(dim,dim) val parallelField = AlgebraND.realWithStream(dim, dim)
measureAndPrint("Boxing addition") { measureAndPrint("Boxing addition") {
boxingField { boxingField {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Specialized addition") { measureAndPrint("Specialized addition") {
realField { realField {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Nd4j specialized addition") { measureAndPrint("Nd4j specialized addition") {
nd4jField { nd4jField {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Viktor addition") { measureAndPrint("Viktor addition") {
viktorField { viktorField {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Parallel stream addition") { measureAndPrint("Parallel stream addition") {
parallelField { parallelField {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
measureAndPrint("Automatic field addition") { measureAndPrint("Automatic field addition") {
autoField { autoField {
var res: NDStructure<Double> = one var res: StructureND<Double> = one
repeat(n) { res += 1.0 } 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 package space.kscience.kmath.structures
import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.DefaultStrides import space.kscience.kmath.nd.DefaultStrides
import space.kscience.kmath.nd.NDBuffer
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
fun main() { fun main() {
val n = 6000 val n = 6000
val array = DoubleArray(n * n) { 1.0 } val array = DoubleArray(n * n) { 1.0 }
val buffer = RealBuffer(array) val buffer = DoubleBuffer(array)
val strides = DefaultStrides(intArrayOf(n, n)) val strides = DefaultStrides(intArrayOf(n, n))
val structure = NDBuffer(strides, buffer) val structure = BufferND(strides, buffer)
measureTimeMillis { measureTimeMillis {
var res = 0.0 var res = 0.0

View File

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

View File

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

View File

@ -2,17 +2,17 @@
This subproject implements the following features: This subproject implements the following features:
- [expression-language](src/jvmMain/kotlin/kscience/kmath/ast/parser.kt) : Expression language and its parser - [expression-language](src/jvmMain/kotlin/space/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](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/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure - [mst-building](src/commonMain/kotlin/space/kscience/kmath/ast/MstAlgebra.kt) : MST building algebraic structure
- [mst-interpreter](src/commonMain/kotlin/kscience/kmath/ast/MST.kt) : MST interpreter - [mst-interpreter](src/commonMain/kotlin/space/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-jvm-codegen](src/jvmMain/kotlin/space/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 - [mst-js-codegen](src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler
> #### Artifact: > #### 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) > 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://repo.kotlin.link' }
> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } > maven { url 'https://dl.bintray.com/hotkeytlt/maven' }
> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap > 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 { > dependencies {
> implementation 'space.kscience:kmath-ast:0.2.0' > implementation 'space.kscience:kmath-ast:0.3.0-dev-3'
> } > }
> ``` > ```
> **Gradle Kotlin DSL:** > **Gradle Kotlin DSL:**
@ -41,13 +38,10 @@ This subproject implements the following features:
> maven("https://repo.kotlin.link") > 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/kotlin/kotlin-eap") // include for builds based on kotlin-eap
> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a > 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 { > 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: For example, the following builder:
```kotlin ```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 ```java
package space.kscience.kmath.asm.generated; 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: This API extends MST and MstExpression, so you may optimize as both of them:
```kotlin ```kotlin
RealField.mstInField { symbol("x") + 2 }.compile() DoubleField.mstInField { symbol("x") + 2 }.compile()
RealField.expression("x+2".parseMath()) DoubleField.expression("x+2".parseMath())
``` ```
#### Known issues #### Known issues
@ -109,7 +103,7 @@ RealField.expression("x+2".parseMath())
A similar feature is also available on JS. A similar feature is also available on JS.
```kotlin ```kotlin
RealField.mstInField { symbol("x") + 2 }.compile() DoubleField.mstInField { symbol("x") + 2 }.compile()
``` ```
The code above returns expression implemented with such a JS function: The code above returns expression implemented with such a JS function:

View File

@ -58,36 +58,36 @@ readme {
feature( feature(
id = "expression-language", id = "expression-language",
description = "Expression language and its parser", 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( feature(
id = "mst", id = "mst",
description = "MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation", 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( feature(
id = "mst-building", id = "mst-building",
description = "MST building algebraic structure", 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( feature(
id = "mst-interpreter", id = "mst-interpreter",
description = "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( feature(
id = "mst-jvm-codegen", id = "mst-jvm-codegen",
description = "Dynamic MST to JVM bytecode compiler", 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( feature(
id = "mst-js-codegen", id = "mst-js-codegen",
description = "Dynamic MST to JS compiler", 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: For example, the following builder:
```kotlin ```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:
@ -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: This API extends MST and MstExpression, so you may optimize as both of them:
```kotlin ```kotlin
RealField.mstInField { symbol("x") + 2 }.compile() DoubleField.mstInField { symbol("x") + 2 }.compile()
RealField.expression("x+2".parseMath()) DoubleField.expression("x+2".parseMath())
``` ```
#### Known issues #### Known issues
@ -64,7 +64,7 @@ RealField.expression("x+2".parseMath())
A similar feature is also available on JS. A similar feature is also available on JS.
```kotlin ```kotlin
RealField.mstInField { symbol("x") + 2 }.compile() DoubleField.mstInField { symbol("x") + 2 }.compile()
``` ```
The code above returns expression implemented with such a JS function: The code above returns expression implemented with such a JS function:

View File

@ -5,7 +5,7 @@ import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.toComplex import space.kscience.kmath.complex.toComplex
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.ByteRing 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.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -73,7 +73,7 @@ internal class TestESTreeConsistencyWithInterpreter {
@Test @Test
fun realField() { fun realField() {
val res1 = RealField.mstInField { val res1 = DoubleField.mstInField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( +(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 (3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1), + number(1),
@ -81,7 +81,7 @@ internal class TestESTreeConsistencyWithInterpreter {
) + zero ) + zero
}("x" to 2.0) }("x" to 2.0)
val res2 = RealField.mstInField { val res2 = DoubleField.mstInField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( +(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 (3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1), + 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.mstInField
import space.kscience.kmath.ast.mstInGroup import space.kscience.kmath.ast.mstInGroup
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.RealField import space.kscience.kmath.operations.DoubleField
import kotlin.random.Random import kotlin.random.Random
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -12,27 +12,29 @@ import kotlin.test.assertEquals
internal class TestESTreeOperationsSupport { internal class TestESTreeOperationsSupport {
@Test @Test
fun testUnaryOperationInvocation() { fun testUnaryOperationInvocation() {
val expression = RealField.mstInGroup { -bindSymbol("x") }.compile() val expression = DoubleField.mstInGroup { -bindSymbol("x") }.compile()
val res = expression("x" to 2.0) val res = expression("x" to 2.0)
assertEquals(-2.0, res) assertEquals(-2.0, res)
} }
@Test @Test
fun testBinaryOperationInvocation() { 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) val res = expression("x" to 2.0)
assertEquals(-1.0, res) assertEquals(-1.0, res)
} }
@Test @Test
fun testConstProductInvocation() { 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) assertEquals(4.0, res)
} }
@Test @Test
fun testMultipleCalls() { 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) val r = Random(0)
var s = 0.0 var s = 0.0
repeat(1000000) { s += e("x" to r.nextDouble()) } 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.ast.mstInField
import space.kscience.kmath.expressions.invoke 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.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class TestESTreeSpecialization { internal class TestESTreeSpecialization {
@Test @Test
fun testUnaryPlus() { 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)) assertEquals(2.0, expr("x" to 2.0))
} }
@Test @Test
fun testUnaryMinus() { 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)) assertEquals(-2.0, expr("x" to 2.0))
} }
@Test @Test
fun testAdd() { 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)) assertEquals(4.0, expr("x" to 2.0))
} }
@Test @Test
fun testSine() { 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)) assertEquals(0.0, expr("x" to 0.0))
} }
@Test @Test
fun testMinus() { 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)) assertEquals(0.0, expr("x" to 2.0))
} }
@Test @Test
fun testDivide() { 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)) assertEquals(1.0, expr("x" to 2.0))
} }
@Test @Test
fun testPower() { fun testPower() {
val expr = RealField val expr = DoubleField
.mstInField { binaryOperationFunction("pow")(bindSymbol("x"), number(2)) } .mstInField { binaryOperationFunction("pow")(bindSymbol("x"), number(2)) }
.compile() .compile()

View File

@ -86,7 +86,7 @@ internal inline fun ClassWriter.visitField(
descriptor: String, descriptor: String,
signature: String?, signature: String?,
value: Any?, value: Any?,
block: FieldVisitor.() -> Unit block: FieldVisitor.() -> Unit,
): FieldVisitor { ): FieldVisitor {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return visitField(access, name, descriptor, signature, value).apply(block) 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.complex.toComplex
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.ByteRing 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.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -73,7 +73,7 @@ internal class TestAsmConsistencyWithInterpreter {
@Test @Test
fun realField() { fun realField() {
val res1 = RealField.mstInField { val res1 = DoubleField.mstInField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( +(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 (3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1), + number(1),
@ -81,7 +81,7 @@ internal class TestAsmConsistencyWithInterpreter {
) + zero ) + zero
}("x" to 2.0) }("x" to 2.0)
val res2 = RealField.mstInField { val res2 = DoubleField.mstInField {
+(3 - 2 + 2 * number(1) + 1.0) + binaryOperationFunction("+")( +(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 (3.0 - (bindSymbol("x") + (scale(add(number(1.0), number(1.0)), 2.0) + 1.0))) * 3 - 1.0
+ number(1), + 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.mstInField
import space.kscience.kmath.ast.mstInGroup import space.kscience.kmath.ast.mstInGroup
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.RealField import space.kscience.kmath.operations.DoubleField
import kotlin.random.Random import kotlin.random.Random
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -12,27 +12,29 @@ import kotlin.test.assertEquals
internal class TestAsmOperationsSupport { internal class TestAsmOperationsSupport {
@Test @Test
fun testUnaryOperationInvocation() { fun testUnaryOperationInvocation() {
val expression = RealField.mstInGroup { -bindSymbol("x") }.compile() val expression = DoubleField.mstInGroup { -bindSymbol("x") }.compile()
val res = expression("x" to 2.0) val res = expression("x" to 2.0)
assertEquals(-2.0, res) assertEquals(-2.0, res)
} }
@Test @Test
fun testBinaryOperationInvocation() { 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) val res = expression("x" to 2.0)
assertEquals(-1.0, res) assertEquals(-1.0, res)
} }
@Test @Test
fun testConstProductInvocation() { 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) assertEquals(4.0, res)
} }
@Test @Test
fun testMultipleCalls() { 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) val r = Random(0)
var s = 0.0 var s = 0.0
repeat(1000000) { s += e("x" to r.nextDouble()) } 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.ast.mstInField
import space.kscience.kmath.expressions.invoke 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.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class TestAsmSpecialization { internal class TestAsmSpecialization {
@Test @Test
fun testUnaryPlus() { 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)) assertEquals(2.0, expr("x" to 2.0))
} }
@Test @Test
fun testUnaryMinus() { 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)) assertEquals(-2.0, expr("x" to 2.0))
} }
@Test @Test
fun testAdd() { 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)) assertEquals(4.0, expr("x" to 2.0))
} }
@Test @Test
fun testSine() { 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)) assertEquals(0.0, expr("x" to 0.0))
} }
@Test @Test
fun testMinus() { 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)) assertEquals(0.0, expr("x" to 2.0))
} }
@Test @Test
fun testDivide() { 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)) assertEquals(1.0, expr("x" to 2.0))
} }
@Test @Test
fun testPower() { fun testPower() {
val expr = RealField val expr = DoubleField
.mstInField { binaryOperationFunction("pow")(bindSymbol("x"), number(2)) } .mstInField { binaryOperationFunction("pow")(bindSymbol("x"), number(2)) }
.compile() .compile()

View File

@ -1,12 +1,12 @@
package space.kscience.kmath.ast package space.kscience.kmath.ast
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Field
import space.kscience.kmath.operations.RealField
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class ParserPrecedenceTest { internal class ParserPrecedenceTest {
private val f: Field<Double> = RealField private val f: Field<Double> = DoubleField
@Test @Test
fun test1(): Unit = assertEquals(6.0, f.evaluate("2*2+2".parseMath())) 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.complex.ComplexField
import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.invoke
import space.kscience.kmath.operations.Algebra 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.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -33,7 +33,7 @@ internal class ParserTest {
@Test @Test
fun `evaluate MST with unary function`() { fun `evaluate MST with unary function`() {
val mst = "sin(0)".parseMath() val mst = "sin(0)".parseMath()
val res = RealField.evaluate(mst) val res = DoubleField.evaluate(mst)
assertEquals(0.0, res) 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,63 +3,16 @@ package space.kscience.kmath.commons.linear
import org.apache.commons.math3.linear.* import org.apache.commons.math3.linear.*
import space.kscience.kmath.linear.* import space.kscience.kmath.linear.*
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.NDStructure import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.RealField import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.RealBuffer
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.cast import kotlin.reflect.cast
public class CMMatrix(public val origin: RealMatrix) : Matrix<Double> { public inline class CMMatrix(public val origin: RealMatrix) : Matrix<Double> {
public override val rowNum: Int get() = origin.rowDimension public override val rowNum: Int get() = origin.rowDimension
public override val colNum: Int get() = origin.columnDimension public override val colNum: Int get() = origin.columnDimension
@UnstableKMathAPI
override fun <T : Any> getFeature(type: KClass<T>): T? = when (type) {
DiagonalFeature::class -> if (origin is DiagonalMatrix) DiagonalFeature else null
DeterminantFeature::class, LupDecompositionFeature::class -> object :
DeterminantFeature<Double>,
LupDecompositionFeature<Double> {
private val lup by lazy { LUDecomposition(origin) }
override val determinant: Double by lazy { lup.determinant }
override val l: Matrix<Double> by lazy { CMMatrix(lup.l) + LFeature }
override val u: Matrix<Double> by lazy { CMMatrix(lup.u) + UFeature }
override val p: Matrix<Double> by lazy { CMMatrix(lup.p) }
}
CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature<Double> {
override val l: Matrix<Double> by lazy {
val cholesky = CholeskyDecomposition(origin)
CMMatrix(cholesky.l) + LFeature
}
}
QRDecompositionFeature::class -> object : QRDecompositionFeature<Double> {
private val qr by lazy { QRDecomposition(origin) }
override val q: Matrix<Double> by lazy { CMMatrix(qr.q) + OrthogonalFeature }
override val r: Matrix<Double> by lazy { CMMatrix(qr.r) + UFeature }
}
SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature<Double> {
private val sv by lazy { SingularValueDecomposition(origin) }
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) }
}
else -> null
}?.let(type::cast)
public override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j) public override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is NDStructure<*>) return false
return NDStructure.contentEquals(this, other)
}
override fun hashCode(): Int = origin.hashCode()
} }
public inline class CMVector(public val origin: RealVector) : Point<Double> { public inline class CMVector(public val origin: RealVector) : Point<Double> {
@ -72,15 +25,15 @@ public inline class CMVector(public val origin: RealVector) : Point<Double> {
public fun RealVector.toPoint(): CMVector = CMVector(this) public fun RealVector.toPoint(): CMVector = CMVector(this)
public object CMLinearSpace : LinearSpace<Double, RealField> { public object CMLinearSpace : LinearSpace<Double, DoubleField> {
override val elementAlgebra: RealField get() = RealField override val elementAlgebra: DoubleField get() = DoubleField
public override fun buildMatrix( public override fun buildMatrix(
rows: Int, rows: Int,
columns: Int, columns: Int,
initializer: RealField.(i: Int, j: Int) -> Double, initializer: DoubleField.(i: Int, j: Int) -> Double,
): CMMatrix { ): CMMatrix {
val array = Array(rows) { i -> DoubleArray(columns) { j -> RealField.initializer(i, j) } } val array = Array(rows) { i -> DoubleArray(columns) { j -> DoubleField.initializer(i, j) } }
return CMMatrix(Array2DRowRealMatrix(array)) return CMMatrix(Array2DRowRealMatrix(array))
} }
@ -102,8 +55,8 @@ public object CMLinearSpace : LinearSpace<Double, RealField> {
internal fun RealMatrix.wrap(): CMMatrix = CMMatrix(this) internal fun RealMatrix.wrap(): CMMatrix = CMMatrix(this)
internal fun RealVector.wrap(): CMVector = CMVector(this) internal fun RealVector.wrap(): CMVector = CMVector(this)
override fun buildVector(size: Int, initializer: RealField.(Int) -> Double): Point<Double> = override fun buildVector(size: Int, initializer: DoubleField.(Int) -> Double): Point<Double> =
ArrayRealVector(DoubleArray(size) { RealField.initializer(it) }).wrap() ArrayRealVector(DoubleArray(size) { DoubleField.initializer(it) }).wrap()
override fun Matrix<Double>.plus(other: Matrix<Double>): CMMatrix = override fun Matrix<Double>.plus(other: Matrix<Double>): CMMatrix =
toCM().origin.add(other.toCM().origin).wrap() toCM().origin.add(other.toCM().origin).wrap()
@ -134,13 +87,54 @@ public object CMLinearSpace : LinearSpace<Double, RealField> {
override fun Double.times(v: Point<Double>): CMVector = override fun Double.times(v: Point<Double>): CMVector =
v * this v * this
@UnstableKMathAPI
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 :
DeterminantFeature<Double>,
LupDecompositionFeature<Double> {
private val lup by lazy { LUDecomposition(origin) }
override val determinant: Double by lazy { lup.determinant }
override val l: Matrix<Double> by lazy { CMMatrix(lup.l) + LFeature }
override val u: Matrix<Double> by lazy { CMMatrix(lup.u) + UFeature }
override val p: Matrix<Double> by lazy { CMMatrix(lup.p) }
} }
public operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature<Double> {
CMMatrix(origin.add(other.origin)) override val l: Matrix<Double> by lazy {
val cholesky = CholeskyDecomposition(origin)
CMMatrix(cholesky.l) + LFeature
}
}
public operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = QRDecompositionFeature::class -> object : QRDecompositionFeature<Double> {
CMMatrix(origin.subtract(other.origin)) private val qr by lazy { QRDecomposition(origin) }
override val q: Matrix<Double> by lazy { CMMatrix(qr.q) + OrthogonalFeature }
override val r: Matrix<Double> by lazy { CMMatrix(qr.r) + UFeature }
}
public infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature<Double> {
CMMatrix(origin.multiply(other.origin)) private val sv by lazy { SingularValueDecomposition(origin) }
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 { DoubleBuffer(sv.singularValues) }
}
else -> null
}?.let(type::cast)
}
}
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))

View File

@ -14,7 +14,7 @@ public enum class CMDecomposition {
public fun CMLinearSpace.solver( public fun CMLinearSpace.solver(
a: Matrix<Double>, a: Matrix<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP decomposition: CMDecomposition = CMDecomposition.LUP,
): DecompositionSolver = when (decomposition) { ): DecompositionSolver = when (decomposition) {
CMDecomposition.LUP -> LUDecomposition(a.toCM().origin).solver CMDecomposition.LUP -> LUDecomposition(a.toCM().origin).solver
CMDecomposition.RRQR -> RRQRDecomposition(a.toCM().origin).solver CMDecomposition.RRQR -> RRQRDecomposition(a.toCM().origin).solver
@ -26,16 +26,16 @@ public fun CMLinearSpace.solver(
public fun CMLinearSpace.solve( public fun CMLinearSpace.solve(
a: Matrix<Double>, a: Matrix<Double>,
b: Matrix<Double>, b: Matrix<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP decomposition: CMDecomposition = CMDecomposition.LUP,
): CMMatrix = solver(a, decomposition).solve(b.toCM().origin).wrap() ): CMMatrix = solver(a, decomposition).solve(b.toCM().origin).wrap()
public fun CMLinearSpace.solve( public fun CMLinearSpace.solve(
a: Matrix<Double>, a: Matrix<Double>,
b: Point<Double>, b: Point<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP decomposition: CMDecomposition = CMDecomposition.LUP,
): CMVector = solver(a, decomposition).solve(b.toCM().origin).toPoint() ): CMVector = solver(a, decomposition).solve(b.toCM().origin).toPoint()
public fun CMLinearSpace.inverse( public fun CMLinearSpace.inverse(
a: Matrix<Double>, a: Matrix<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP decomposition: CMDecomposition = CMDecomposition.LUP,
): CMMatrix = solver(a, decomposition).inverse.wrap() ): 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.component1(): DoubleArray = point
public operator fun PointValuePair.component2(): Double = value 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 { OptimizationProblem<Double>, SymbolIndexer, OptimizationFeature {
private val optimizationData: HashMap<KClass<out OptimizationData>, OptimizationData> = HashMap() private val optimizationData: HashMap<KClass<out OptimizationData>, OptimizationData> = HashMap()
private var optimizatorBuilder: (() -> MultivariateOptimizer)? = null private var optimizatorBuilder: (() -> MultivariateOptimizer)? = null

View File

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

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 and hypercomplex number systems in KMath:
- [complex](src/commonMain/kotlin/kscience/kmath/complex/Complex.kt) : Complex Numbers - [complex](src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt) : Complex Numbers
- [quaternion](src/commonMain/kotlin/kscience/kmath/complex/Quaternion.kt) : Quaternions - [quaternion](src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt) : Quaternions
> #### Artifact: > #### 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) > 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://repo.kotlin.link' }
> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } > maven { url 'https://dl.bintray.com/hotkeytlt/maven' }
> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap > 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 { > dependencies {
> implementation 'space.kscience:kmath-complex:0.2.0' > implementation 'space.kscience:kmath-complex:0.3.0-dev-3'
> } > }
> ``` > ```
> **Gradle Kotlin DSL:** > **Gradle Kotlin DSL:**
@ -37,12 +34,9 @@ Complex and hypercomplex number systems in KMath:
> maven("https://repo.kotlin.link") > 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/kotlin/kotlin-eap") // include for builds based on kotlin-eap
> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a > 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 { > 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( feature(
id = "complex", id = "complex",
description = "Complex Numbers", description = "Complex Numbers",
ref = "src/commonMain/kotlin/kscience/kmath/complex/Complex.kt" ref = "src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt"
) )
feature( feature(
id = "quaternion", id = "quaternion",
description = "Quaternions", 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 package space.kscience.kmath.complex
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.BufferedNDField import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.NDAlgebra import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.NDBuffer import space.kscience.kmath.nd.BufferedFieldND
import space.kscience.kmath.nd.NDStructure import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.NumbersAddOperations import space.kscience.kmath.operations.NumbersAddOperations
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
@ -16,16 +16,16 @@ import kotlin.contracts.contract
* An optimized nd-field for complex numbers * An optimized nd-field for complex numbers
*/ */
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public class ComplexNDField( public class ComplexFieldND(
shape: IntArray, shape: IntArray,
) : BufferedNDField<Complex, ComplexField>(shape, ComplexField, Buffer.Companion::complex), ) : BufferedFieldND<Complex, ComplexField>(shape, ComplexField, Buffer.Companion::complex),
NumbersAddOperations<NDStructure<Complex>>, NumbersAddOperations<StructureND<Complex>>,
ExtendedField<NDStructure<Complex>> { ExtendedField<StructureND<Complex>> {
override val zero: NDBuffer<Complex> by lazy { produce { zero } } override val zero: BufferND<Complex> by lazy { produce { zero } }
override val one: NDBuffer<Complex> by lazy { produce { one } } 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 val d = value.toComplex() // minimize conversions
return produce { d } return produce { d }
} }
@ -34,15 +34,15 @@ public class ComplexNDField(
// @Suppress("OVERRIDE_BY_INLINE") // @Suppress("OVERRIDE_BY_INLINE")
// override inline fun map( // override inline fun map(
// arg: AbstractNDBuffer<Double>, // arg: AbstractNDBuffer<Double>,
// transform: RealField.(Double) -> Double, // transform: DoubleField.(Double) -> Double,
// ): RealNDElement { // ): RealNDElement {
// check(arg) // 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) // return BufferedNDFieldElement(this, array)
// } // }
// //
// @Suppress("OVERRIDE_BY_INLINE") // @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)) } // val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
// return BufferedNDFieldElement(this, array) // return BufferedNDFieldElement(this, array)
// } // }
@ -50,7 +50,7 @@ public class ComplexNDField(
// @Suppress("OVERRIDE_BY_INLINE") // @Suppress("OVERRIDE_BY_INLINE")
// override inline fun mapIndexed( // override inline fun mapIndexed(
// arg: AbstractNDBuffer<Double>, // arg: AbstractNDBuffer<Double>,
// transform: RealField.(index: IntArray, Double) -> Double, // transform: DoubleField.(index: IntArray, Double) -> Double,
// ): RealNDElement { // ): RealNDElement {
// check(arg) // check(arg)
// return BufferedNDFieldElement( // return BufferedNDFieldElement(
@ -67,7 +67,7 @@ public class ComplexNDField(
// override inline fun combine( // override inline fun combine(
// a: AbstractNDBuffer<Double>, // a: AbstractNDBuffer<Double>,
// b: AbstractNDBuffer<Double>, // b: AbstractNDBuffer<Double>,
// transform: RealField.(Double, Double) -> Double, // transform: DoubleField.(Double, Double) -> Double,
// ): RealNDElement { // ): RealNDElement {
// check(a, b) // check(a, b)
// val buffer = RealBuffer(strides.linearSize) { offset -> // val buffer = RealBuffer(strides.linearSize) { offset ->
@ -76,44 +76,44 @@ public class ComplexNDField(
// return BufferedNDFieldElement(this, buffer) // 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 sin(arg: StructureND<Complex>): BufferND<Complex> = arg.map { sin(it) }
override fun cos(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { cos(it) } override fun cos(arg: StructureND<Complex>): BufferND<Complex> = arg.map { cos(it) }
override fun tan(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { tan(it) } override fun tan(arg: StructureND<Complex>): BufferND<Complex> = arg.map { tan(it) }
override fun asin(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { asin(it) } override fun asin(arg: StructureND<Complex>): BufferND<Complex> = arg.map { asin(it) }
override fun acos(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { acos(it) } override fun acos(arg: StructureND<Complex>): BufferND<Complex> = arg.map { acos(it) }
override fun atan(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { atan(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 sinh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { sinh(it) }
override fun cosh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { cosh(it) } override fun cosh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { cosh(it) }
override fun tanh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { tanh(it) } override fun tanh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { tanh(it) }
override fun asinh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { asinh(it) } override fun asinh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { asinh(it) }
override fun acosh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { acosh(it) } override fun acosh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { acosh(it) }
override fun atanh(arg: NDStructure<Complex>): NDBuffer<Complex> = arg.map { atanh(it) } override fun atanh(arg: StructureND<Complex>): BufferND<Complex> = arg.map { atanh(it) }
} }
/** /**
* Fast element production using function inlining * 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) } contract { callsInPlace(initializer, InvocationKind.EXACTLY_ONCE) }
val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.initializer(offset) } 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 * 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) } 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 = """ description = """
Algebraic structures like rings, spaces and fields. Algebraic structures like rings, spaces and fields.
""".trimIndent(), """.trimIndent(),
ref = "src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt" ref = "src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt"
) )
feature( feature(
id = "nd", id = "nd",
description = "Many-dimensional structures and operations on them.", 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( feature(
@ -37,13 +37,13 @@ readme {
description = """ description = """
Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition. Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. Advanced linear algebra operations like matrix inversion and LU decomposition.
""".trimIndent(), """.trimIndent(),
ref = "src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt" ref = "src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt"
) )
feature( feature(
id = "buffers", id = "buffers",
description = "One-dimensional structure", description = "One-dimensional structure",
ref = "src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt" ref = "src/commonMain/kotlin/space/kscience/kmath/structures/Buffers.kt"
) )
feature( 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 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. performance calculations to code generation.
""".trimIndent(), """.trimIndent(),
ref = "src/commonMain/kotlin/kscience/kmath/expressions" ref = "src/commonMain/kotlin/space/kscience/kmath/expressions"
) )
feature( feature(
id = "domains", id = "domains",
description = "Domains", description = "Domains",
ref = "src/commonMain/kotlin/kscience/kmath/domains" ref = "src/commonMain/kotlin/space/kscience/kmath/domains"
) )
feature( feature(
id = "autodif", id = "autodif",
description = "Automatic differentiation", 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 * @author Alexander Nozik
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public interface RealDomain : Domain<Double> { public interface DoubleDomain : Domain<Double> {
/** /**
* Global lower edge * Global lower edge

View File

@ -27,7 +27,7 @@ import space.kscience.kmath.structures.indices
* @author Alexander Nozik * @author Alexander Nozik
*/ */
@UnstableKMathAPI @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 val dimension: Int get() = lower.size
public override operator fun contains(point: Point<Double>): Boolean = point.indices.all { i -> 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 import space.kscience.kmath.misc.UnstableKMathAPI
@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 operator fun contains(point: Point<Double>): Boolean = true
public override fun getLowerBound(num: Int): Double = Double.NEGATIVE_INFINITY 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 import space.kscience.kmath.misc.UnstableKMathAPI
@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 override val dimension: Int get() = 1
public operator fun contains(d: Double): Boolean = range.contains(d) 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: * Example:
* ``` * ```
* val x by symbol // define variable(s) and their values * 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(17.0, y.x) // the value of result (y)
* assertEquals(9.0, x.d) // dy/dx * assertEquals(9.0, x.d) // dy/dx
* ``` * ```

View File

@ -9,7 +9,7 @@ import space.kscience.kmath.structures.VirtualBuffer
import space.kscience.kmath.structures.indices import space.kscience.kmath.structures.indices
public class BufferLinearSpace<T : Any, A : Ring<T>>( public class BufferedLinearSpace<T : Any, A : Ring<T>>(
override val elementAlgebra: A, override val elementAlgebra: A,
private val bufferFactory: BufferFactory<T>, private val bufferFactory: BufferFactory<T>,
) : LinearSpace<T, A> { ) : LinearSpace<T, A> {
@ -17,7 +17,7 @@ public class BufferLinearSpace<T : Any, A : Ring<T>>(
private fun ndRing( private fun ndRing(
rows: Int, rows: Int,
cols: Int, cols: Int,
): BufferedNDRing<T, A> = NDAlgebra.ring(elementAlgebra, bufferFactory, rows, cols) ): 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> = 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() ndRing(rows, columns).produce { (i, j) -> elementAlgebra.initializer(i, j) }.as2D()

View File

@ -6,9 +6,20 @@ 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 * A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors
*/ */
public interface LinearSolver<T : Any> { 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: 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() public fun solve(a: Matrix<T>, b: Point<T>): Point<T> = solve(a, b.asMatrix()).asVector()
public fun inverse(a: Matrix<T>): Matrix<T>
/**
* Get inverse of a matrix
*/
public fun inverse(matrix: Matrix<T>): Matrix<T>
} }
/** /**

View File

@ -5,6 +5,7 @@ import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -155,16 +156,15 @@ public interface LinearSpace<T : Any, out A : Ring<T>> {
public operator fun T.times(v: Point<T>): Point<T> = v * this public operator fun T.times(v: Point<T>): Point<T> = v * this
/** /**
* Gets a feature from the matrix. This function may return some additional features to * Get a feature of the structure in this scope. Structure features take precedence other context features
* [space.kscience.kmath.nd.NDStructure.getFeature].
* *
* @param F the type of feature. * @param F the type of feature.
* @param m the matrix. * @param structure the structure.
* @param type the [KClass] instance of [F]. * @param type the [KClass] instance of [F].
* @return a feature object or `null` if it isn't present. * @return a feature object or `null` if it isn't present.
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public fun <F : Any> getFeature(m: Matrix<T>, type: KClass<F>): F? = m.getFeature(type) public fun <F : Any> getFeature(structure: Matrix<T>, type: KClass<F>): F? = structure.getFeature(type)
public companion object { public companion object {
@ -174,9 +174,9 @@ public interface LinearSpace<T : Any, out A : Ring<T>> {
public fun <T : Any, A : Ring<T>> buffered( public fun <T : Any, A : Ring<T>> buffered(
algebra: A, algebra: A,
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing,
): LinearSpace<T, A> = BufferLinearSpace(algebra,bufferFactory) ): LinearSpace<T, A> = BufferedLinearSpace(algebra, bufferFactory)
public val real: LinearSpace<Double, RealField> = buffered(RealField, Buffer.Companion::real) public val real: LinearSpace<Double, DoubleField> = buffered(DoubleField, ::DoubleBuffer)
/** /**
* Automatic buffered matrix, unboxed if it is possible * Automatic buffered matrix, unboxed if it is possible
@ -186,19 +186,17 @@ public interface LinearSpace<T : Any, out A : Ring<T>> {
} }
} }
public operator fun <LS : LinearSpace<*, *>, R> LS.invoke(block: LS.() -> R): R = run(block)
/** /**
* Gets a feature from the matrix. This function may return some additional features to * Get a feature of the structure in this scope. Structure features take precedence other context features
* [space.kscience.kmath.nd.NDStructure.getFeature].
* *
* @param T the type of items in the matrices. * @param T the type of items in the matrices.
* @param M the type of operated matrices.
* @param F the type of feature. * @param F the type of feature.
* @receiver the [LinearSpace] of [T].
* @param m the matrix.
* @return a feature object or `null` if it isn't present. * @return a feature object or `null` if it isn't present.
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public inline fun <T : Any, reified F : Any> LinearSpace<T, *>.getFeature(m: Matrix<T>): F? = getFeature(m, F::class) 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.misc.UnstableKMathAPI
import space.kscience.kmath.nd.getFeature import space.kscience.kmath.nd.getFeature
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferAccessor2D import space.kscience.kmath.structures.BufferAccessor2D
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.MutableBufferFactory import space.kscience.kmath.structures.MutableBufferFactory
@ -151,8 +151,8 @@ public inline fun <reified T : Comparable<T>> LinearSpace<T, Field<T>>.lup(
noinline checkSingular: (T) -> Boolean, noinline checkSingular: (T) -> Boolean,
): LupDecomposition<T> = lup(MutableBuffer.Companion::auto, matrix, checkSingular) ): LupDecomposition<T> = lup(MutableBuffer.Companion::auto, matrix, checkSingular)
public fun LinearSpace<Double, RealField>.lup(matrix: Matrix<Double>): LupDecomposition<Double> = public fun LinearSpace<Double, DoubleField>.lup(matrix: Matrix<Double>): LupDecomposition<Double> =
lup(Buffer.Companion::real, matrix) { it < 1e-11 } lup(::DoubleBuffer, matrix) { it < 1e-11 }
public fun <T : Any> LupDecomposition<T>.solveWithLup( public fun <T : Any> LupDecomposition<T>.solveWithLup(
factory: MutableBufferFactory<T>, factory: MutableBufferFactory<T>,
@ -228,9 +228,9 @@ public inline fun <reified T : Comparable<T>> LinearSpace<T, Field<T>>.inverseWi
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public fun LinearSpace<Double, RealField>.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 // Use existing decomposition if it is provided by matrix
val bufferFactory: MutableBufferFactory<Double> = MutableBuffer.Companion::real val bufferFactory: MutableBufferFactory<Double> = ::DoubleBuffer
val decomposition: LupDecomposition<Double> = a.getFeature() ?: lup(bufferFactory, a) { it < 1e-11 } val decomposition: LupDecomposition<Double> = a.getFeature() ?: lup(bufferFactory, a) { it < 1e-11 }
return decomposition.solveWithLup(bufferFactory, b) return decomposition.solveWithLup(bufferFactory, b)
} }
@ -238,5 +238,5 @@ public fun LinearSpace<Double, RealField>.solveWithLup(a: Matrix<Double>, b: Mat
/** /**
* Inverses a square matrix using LUP decomposition. Non square matrix will throw a error. * Inverses a square matrix using LUP decomposition. Non square matrix will throw a error.
*/ */
public fun LinearSpace<Double, RealField>.inverseWithLup(matrix: Matrix<Double>): Matrix<Double> = public fun LinearSpace<Double, DoubleField>.inverseWithLup(matrix: Matrix<Double>): Matrix<Double> =
solveWithLup(matrix, one(matrix.rowNum, matrix.colNum)) solveWithLup(matrix, one(matrix.rowNum, matrix.colNum))

View File

@ -23,8 +23,6 @@ public class MatrixWrapper<T : Any> internal constructor(
override fun <T : Any> getFeature(type: KClass<T>): T? = features.singleOrNull { type.isInstance(it) } as? T override fun <T : Any> getFeature(type: KClass<T>): T? = features.singleOrNull { type.isInstance(it) } as? T
?: origin.getFeature(type) ?: origin.getFeature(type)
override fun equals(other: Any?): Boolean = origin == other
override fun hashCode(): Int = origin.hashCode()
override fun toString(): String { override fun toString(): String {
return "MatrixWrapper(matrix=$origin, features=$features)" return "MatrixWrapper(matrix=$origin, features=$features)"
} }

View File

@ -1,7 +1,5 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.kmath.nd.NDStructure
/** /**
* The matrix where each element is evaluated each time when is being accessed. * The matrix where each element is evaluated each time when is being accessed.
* *
@ -10,23 +8,10 @@ import space.kscience.kmath.nd.NDStructure
public class VirtualMatrix<T : Any>( public class VirtualMatrix<T : Any>(
override val rowNum: Int, override val rowNum: Int,
override val colNum: Int, override val colNum: Int,
public val generator: (i: Int, j: Int) -> T public val generator: (i: Int, j: Int) -> T,
) : Matrix<T> { ) : Matrix<T> {
override val shape: IntArray get() = intArrayOf(rowNum, colNum) override val shape: IntArray get() = intArrayOf(rowNum, colNum)
override operator fun get(i: Int, j: Int): T = generator(i, j) override operator fun get(i: Int, j: Int): T = generator(i, j)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is NDStructure<*>) return false
return NDStructure.contentEquals(this, other)
}
override fun hashCode(): Int {
var result = rowNum
result = 31 * result + colNum
result = 31 * result + generator.hashCode()
return result
}
} }

View File

@ -37,8 +37,8 @@ public fun <T, R> List<T>.cumulative(initial: R, operation: (R, T) -> R): List<R
/** /**
* Cumulative sum with custom space * Cumulative sum with custom space
*/ */
public fun <T> Iterable<T>.cumulativeSum(space: Group<T>): Iterable<T> = public fun <T> Iterable<T>.cumulativeSum(group: Group<T>): Iterable<T> =
space { cumulative(zero) { element: T, sum: T -> sum + element } } group { cumulative(zero) { element: T, sum: T -> sum + element } }
@JvmName("cumulativeSumOfDouble") @JvmName("cumulativeSumOfDouble")
public fun Iterable<Double>.cumulativeSum(): Iterable<Double> = cumulative(0.0) { element, sum -> sum + element } public fun Iterable<Double>.cumulativeSum(): Iterable<Double> = cumulative(0.0) { element, sum -> sum + element }
@ -49,8 +49,8 @@ public fun Iterable<Int>.cumulativeSum(): Iterable<Int> = cumulative(0) { elemen
@JvmName("cumulativeSumOfLong") @JvmName("cumulativeSumOfLong")
public fun Iterable<Long>.cumulativeSum(): Iterable<Long> = cumulative(0L) { element, sum -> sum + element } public fun Iterable<Long>.cumulativeSum(): Iterable<Long> = cumulative(0L) { element, sum -> sum + element }
public fun <T> Sequence<T>.cumulativeSum(space: Group<T>): Sequence<T> = public fun <T> Sequence<T>.cumulativeSum(group: Group<T>): Sequence<T> =
space { cumulative(zero) { element: T, sum: T -> sum + element } } group { cumulative(zero) { element: T, sum: T -> sum + element } }
@JvmName("cumulativeSumOfDouble") @JvmName("cumulativeSumOfDouble")
public fun Sequence<Double>.cumulativeSum(): Sequence<Double> = cumulative(0.0) { element, sum -> sum + element } public fun Sequence<Double>.cumulativeSum(): Sequence<Double> = cumulative(0.0) { element, sum -> sum + element }
@ -61,8 +61,8 @@ public fun Sequence<Int>.cumulativeSum(): Sequence<Int> = cumulative(0) { elemen
@JvmName("cumulativeSumOfLong") @JvmName("cumulativeSumOfLong")
public fun Sequence<Long>.cumulativeSum(): Sequence<Long> = cumulative(0L) { element, sum -> sum + element } public fun Sequence<Long>.cumulativeSum(): Sequence<Long> = cumulative(0L) { element, sum -> sum + element }
public fun <T> List<T>.cumulativeSum(space: Group<T>): List<T> = public fun <T> List<T>.cumulativeSum(group: Group<T>): List<T> =
space { cumulative(zero) { element: T, sum: T -> sum + element } } group { cumulative(zero) { element: T, sum: T -> sum + element } }
@JvmName("cumulativeSumOfDouble") @JvmName("cumulativeSumOfDouble")
public fun List<Double>.cumulativeSum(): List<Double> = cumulative(0.0) { element, sum -> sum + element } public fun List<Double>.cumulativeSum(): List<Double> = cumulative(0.0) { element, sum -> sum + element }

View File

@ -1,7 +1,9 @@
package space.kscience.kmath.nd package space.kscience.kmath.nd
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.* import space.kscience.kmath.structures.*
import kotlin.reflect.KClass
/** /**
* An exception is thrown when the expected ans actual shape of NDArray differs. * 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 C the type of the element context.
* @param N the type of the structure. * @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. * 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. * 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. * 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. * 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. * 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) } 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 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. * Checks if given elements are consistent with this context.
* *
* @param structures the structures to check. * @param structures the structures to check.
* @return the array of valid structures. * @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 internal fun <T, C : Algebra<T>> AlgebraND<T, C>.checkShape(vararg structures: StructureND<T>): Array<out StructureND<T>> =
.map(NDStructure<T>::shape) structures
.map(StructureND<T>::shape)
.singleOrNull { !shape.contentEquals(it) } .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 ?: structures
/** /**
@ -77,19 +103,19 @@ internal fun <T, C: Algebra<T>> NDAlgebra<T, C>.checkShape(vararg structures: ND
* @param element the structure to check. * @param element the structure to check.
* @return the valid structure. * @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) if (!element.shape.contentEquals(shape)) throw ShapeMismatchException(shape, element.shape)
return element return element
} }
/** /**
* Space of [NDStructure]. * Space of [StructureND].
* *
* @param T the type of the element contained in ND structure. * @param T the type of the element contained in ND structure.
* @param N the type of ND structure. * @param N the type of ND structure.
* @param S the type of space of structure elements. * @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. * Element-wise addition.
* *
@ -97,7 +123,7 @@ public interface NDGroup<T, S : Group<T>> : Group<NDStructure<T>>, NDAlgebra<T,
* @param b the augend. * @param b the augend.
* @return the sum. * @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) } 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. * @param arg the augend.
* @return the sum. * @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. * 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. * @param arg the divisor.
* @return the quotient. * @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. * 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. * @param arg the augend.
* @return the sum. * @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. * 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. * @param arg the divisor.
* @return the quotient. * @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 public companion object
} }
/** /**
* Ring of [NDStructure]. * Ring of [StructureND].
* *
* @param T the type of the element contained in ND structure. * @param T the type of the element contained in ND structure.
* @param N the type of ND structure. * @param N the type of ND structure.
* @param R the type of ring of structure elements. * @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. * Element-wise multiplication.
* *
@ -165,7 +191,7 @@ public interface NDRing<T, R : Ring<T>> : Ring<NDStructure<T>>, NDGroup<T, R> {
* @param b the multiplier. * @param b the multiplier.
* @return the product. * @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) } combine(a, b) { aValue, bValue -> multiply(aValue, bValue) }
//TODO move to extensions after KEEP-176 //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. * @param arg the multiplier.
* @return the product. * @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. * 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. * @param arg the multiplier.
* @return the product. * @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 public companion object
} }
/** /**
* Field of [NDStructure]. * Field of [StructureND].
* *
* @param T the type of the element contained in ND structure. * @param T the type of the element contained in ND structure.
* @param N the type of ND structure. * @param N the type of ND structure.
* @param F the type field of structure elements. * @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. * Element-wise division.
* *
@ -206,7 +232,7 @@ public interface NDField<T, F : Field<T>> : Field<NDStructure<T>>, NDRing<T, F>,
* @param b the divisor. * @param b the divisor.
* @return the quotient. * @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) } combine(a, b) { aValue, bValue -> divide(aValue, bValue) }
//TODO move to extensions after KEEP-176 //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. * @param arg the divisor.
* @return the quotient. * @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. * 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. * @param arg the divisor.
* @return the quotient. * @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 // @ThreadLocal
// public companion object { // public companion object {

View File

@ -6,132 +6,132 @@ import space.kscience.kmath.structures.BufferFactory
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract 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 strides: Strides
public val bufferFactory: BufferFactory<T> 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, strides,
bufferFactory(strides.linearSize) { offset -> bufferFactory(strides.linearSize) { offset ->
elementContext.initializer(strides.index(offset)) elementContext.initializer(strides.index(offset))
} }
) )
public val NDStructure<T>.buffer: Buffer<T> public val StructureND<T>.buffer: Buffer<T>
get() = when { get() = when {
!shape.contentEquals(this@BufferNDAlgebra.shape) -> throw ShapeMismatchException( !shape.contentEquals(this@BufferAlgebraND.shape) -> throw ShapeMismatchException(
this@BufferNDAlgebra.shape, this@BufferAlgebraND.shape,
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)) } 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 -> val buffer = bufferFactory(strides.linearSize) { offset ->
elementContext.transform(buffer[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 -> val buffer = bufferFactory(strides.linearSize) { offset ->
elementContext.transform( elementContext.transform(
strides.index(offset), strides.index(offset),
buffer[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 -> val buffer = bufferFactory(strides.linearSize) { offset ->
elementContext.transform(a.buffer[offset], b.buffer[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 shape: IntArray,
final override val elementContext: A, final override val elementContext: A,
final override val bufferFactory: BufferFactory<T>, 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 strides: Strides = DefaultStrides(shape)
override val zero: NDBuffer<T> by lazy { produce { zero } } override val zero: BufferND<T> by lazy { produce { zero } }
override fun NDStructure<T>.unaryMinus(): NDStructure<T> = produce { -get(it) } 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, shape: IntArray,
elementContext: R, elementContext: R,
bufferFactory: BufferFactory<T>, bufferFactory: BufferFactory<T>,
) : BufferedNDGroup<T, R>(shape, elementContext, bufferFactory), NDRing<T, R> { ) : BufferedGroupND<T, R>(shape, elementContext, bufferFactory), RingND<T, R> {
override val one: NDBuffer<T> by lazy { produce { one } } 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, shape: IntArray,
elementContext: R, elementContext: R,
bufferFactory: BufferFactory<T>, 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 }
} }
// group factories // group factories
public fun <T, A : Group<T>> NDAlgebra.Companion.group( public fun <T, A : Group<T>> AlgebraND.Companion.group(
space: A, space: A,
bufferFactory: BufferFactory<T>, bufferFactory: BufferFactory<T>,
vararg shape: Int, 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.ndGroup( public inline fun <T, A : Group<T>, R> A.ndGroup(
noinline bufferFactory: BufferFactory<T>, noinline bufferFactory: BufferFactory<T>,
vararg shape: Int, vararg shape: Int,
action: BufferedNDGroup<T, A>.() -> R, action: BufferedGroupND<T, A>.() -> R,
): R { ): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return NDAlgebra.group(this, bufferFactory, *shape).run(action) return AlgebraND.group(this, bufferFactory, *shape).run(action)
} }
//ring factories //ring factories
public fun <T, A : Ring<T>> NDAlgebra.Companion.ring( public fun <T, A : Ring<T>> AlgebraND.Companion.ring(
ring: A, ring: A,
bufferFactory: BufferFactory<T>, bufferFactory: BufferFactory<T>,
vararg shape: Int, 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( public inline fun <T, A : Ring<T>, R> A.ndRing(
noinline bufferFactory: BufferFactory<T>, noinline bufferFactory: BufferFactory<T>,
vararg shape: Int, vararg shape: Int,
action: BufferedNDRing<T, A>.() -> R, action: BufferedRingND<T, A>.() -> R,
): R { ): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return NDAlgebra.ring(this, bufferFactory, *shape).run(action) return AlgebraND.ring(this, bufferFactory, *shape).run(action)
} }
//field factories //field factories
public fun <T, A : Field<T>> NDAlgebra.Companion.field( public fun <T, A : Field<T>> AlgebraND.Companion.field(
field: A, field: A,
bufferFactory: BufferFactory<T>, bufferFactory: BufferFactory<T>,
vararg shape: Int, vararg shape: Int,
): BufferedNDField<T, A> = BufferedNDField(shape, field, bufferFactory) ): BufferedFieldND<T, A> = BufferedFieldND(shape, field, bufferFactory)
@Suppress("UNCHECKED_CAST") @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, field: A,
vararg shape: Int, vararg shape: Int,
): NDField<T, A> = when (field) { ): FieldND<T, A> = when (field) {
RealField -> RealNDField(shape) as NDField<T, A> DoubleField -> DoubleFieldND(shape) as FieldND<T, A>
else -> BufferedNDField(shape, field, Buffer.Companion::auto) else -> BufferedFieldND(shape, field, Buffer.Companion::auto)
} }
public inline fun <T, A : Field<T>, R> A.ndField( public inline fun <T, A : Field<T>, R> A.ndField(
noinline bufferFactory: BufferFactory<T>, noinline bufferFactory: BufferFactory<T>,
vararg shape: Int, vararg shape: Int,
action: BufferedNDField<T, A>.() -> R, action: BufferedFieldND<T, A>.() -> R,
): R { ): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } 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 import kotlin.contracts.contract
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public class ShortNDRing( public class ShortRingND(
shape: IntArray, shape: IntArray,
) : BufferedNDRing<Short, ShortRing>(shape, ShortRing, Buffer.Companion::auto), ) : BufferedRingND<Short, ShortRing>(shape, ShortRing, Buffer.Companion::auto),
NumbersAddOperations<NDStructure<Short>> { NumbersAddOperations<StructureND<Short>> {
override val zero: NDBuffer<Short> by lazy { produce { zero } } override val zero: BufferND<Short> by lazy { produce { zero } }
override val one: NDBuffer<Short> by lazy { produce { one } } 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 val d = value.toShort() // minimize conversions
return produce { d } return produce { d }
} }
@ -26,11 +26,11 @@ public class ShortNDRing(
/** /**
* Fast element production using function inlining. * Fast element production using function inlining.
*/ */
public inline fun BufferedNDRing<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): NDBuffer<Short> { public inline fun BufferedRingND<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): BufferND<Short> {
return NDBuffer(strides, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) })) 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) } contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ShortNDRing(shape).run(action) return ShortRingND(shape).run(action)
} }

View File

@ -8,7 +8,7 @@ import space.kscience.kmath.structures.asSequence
/** /**
* A structure that is guaranteed to be one-dimensional * 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 val dimension: Int get() = 1
public override operator fun get(index: IntArray): T { public override operator fun get(index: IntArray): T {
@ -22,7 +22,7 @@ public interface Structure1D<T> : NDStructure<T>, Buffer<T> {
/** /**
* A mutable structure that is guaranteed to be one-dimensional * A mutable structure that is guaranteed to be one-dimensional
*/ */
public interface MutableStructure1D<T> : Structure1D<T>, MutableNDStructure<T>, MutableBuffer<T> { public interface MutableStructure1D<T> : Structure1D<T>, MutableStructureND<T>, MutableBuffer<T> {
public override operator fun set(index: IntArray, value: T) { public override operator fun set(index: IntArray, value: T) {
require(index.size == 1) { "Index dimension mismatch. Expected 1 but found ${index.size}" } require(index.size == 1) { "Index dimension mismatch. Expected 1 but found ${index.size}" }
set(index[0], value) set(index[0], value)
@ -32,7 +32,7 @@ public interface MutableStructure1D<T> : Structure1D<T>, MutableNDStructure<T>,
/** /**
* A 1D wrapper for nd-structure * 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 shape: IntArray get() = structure.shape
override val size: Int get() = structure.shape[0] override val size: Int get() = structure.shape[0]
@ -43,12 +43,10 @@ private inline class Structure1DWrapper<T>(val structure: NDStructure<T>) : Stru
/** /**
* A 1D wrapper for a mutable nd-structure * A 1D wrapper for a mutable nd-structure
*/ */
private inline class MutableStructure1DWrapper<T>(val structure: MutableNDStructure<T>) : MutableStructure1D<T> { private inline class MutableStructure1DWrapper<T>(val structure: MutableStructureND<T>) : MutableStructure1D<T> {
override val shape: IntArray get() = structure.shape override val shape: IntArray get() = structure.shape
override val size: Int get() = structure.shape[0] override val size: Int get() = structure.shape[0]
override fun elements(): Sequence<Pair<IntArray, T>> { override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements()
TODO("Not yet implemented")
}
override fun get(index: Int): T = structure[index] override fun get(index: Int): T = structure[index]
override fun set(index: Int, value: T) { override fun set(index: Int, value: T) {
@ -73,37 +71,19 @@ private inline class Buffer1DWrapper<T>(val buffer: Buffer<T>) : Structure1D<T>
override operator fun get(index: Int): T = buffer[index] override operator fun get(index: Int): T = buffer[index]
} }
private inline class MutableBuffer1DWrapper<T>(val buffer: MutableBuffer<T>) : MutableStructure1D<T> {
override val shape: IntArray get() = intArrayOf(buffer.size)
override val size: Int get() = buffer.size
override fun elements(): Sequence<Pair<IntArray, T>> =
buffer.asSequence().mapIndexed { index, value -> intArrayOf(index) to value }
override operator fun get(index: Int): T = buffer[index]
override fun set(index: Int, value: T) {
buffer[index] = value
}
override fun copy(): MutableBuffer<T> = buffer.copy()
}
/** /**
* 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> = this as? Structure1D<T> ?: if (shape.size == 1) { public fun <T> StructureND<T>.as1D(): Structure1D<T> = this as? Structure1D<T> ?: if (shape.size == 1) {
when (this) { when (this) {
is NDBuffer -> Buffer1DWrapper(this.buffer) is BufferND -> Buffer1DWrapper(this.buffer)
else -> Structure1DWrapper(this) else -> Structure1DWrapper(this)
} }
} else error("Can't create 1d-structure from ${shape.size}d-structure") } else error("Can't create 1d-structure from ${shape.size}d-structure")
public fun <T> MutableNDStructure<T>.as1D(): MutableStructure1D<T> = public fun <T> MutableStructureND<T>.as1D(): MutableStructure1D<T> =
this as? MutableStructure1D<T> ?: if (shape.size == 1) { this as? MutableStructure1D<T> ?: if (shape.size == 1) {
when (this) { MutableStructure1DWrapper(this)
is MutableNDBuffer -> MutableBuffer1DWrapper(this.buffer)
else -> MutableStructure1DWrapper(this)
}
} else error("Can't create 1d-structure from ${shape.size}d-structure") } else error("Can't create 1d-structure from ${shape.size}d-structure")
/** /**
@ -111,4 +91,12 @@ public fun <T> MutableNDStructure<T>.as1D(): MutableStructure1D<T> =
*/ */
public fun <T> Buffer<T>.asND(): Structure1D<T> = Buffer1DWrapper(this) public fun <T> Buffer<T>.asND(): Structure1D<T> = Buffer1DWrapper(this)
public fun <T> MutableBuffer<T>.asND(): MutableStructure1D<T> = MutableBuffer1DWrapper(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

@ -10,7 +10,7 @@ import kotlin.reflect.KClass
* *
* @param T the type of items. * @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. * The number of rows in this structure.
*/ */
@ -60,7 +60,7 @@ public interface Structure2D<T> : NDStructure<T> {
/** /**
* Represents mutable [Structure2D]. * Represents mutable [Structure2D].
*/ */
public interface MutableStructure2D<T> : Structure2D<T>, MutableNDStructure<T> { public interface MutableStructure2D<T> : Structure2D<T>, MutableStructureND<T> {
/** /**
* Inserts an item at the specified indices. * Inserts an item at the specified indices.
* *
@ -74,7 +74,7 @@ public interface MutableStructure2D<T> : Structure2D<T>, MutableNDStructure<T> {
/** /**
* A 2D wrapper for nd-structure * A 2D wrapper for nd-structure
*/ */
private 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 shape: IntArray get() = structure.shape
override val rowNum: Int get() = shape[0] override val rowNum: Int get() = shape[0]
@ -86,16 +86,12 @@ private class Structure2DWrapper<T>(val structure: NDStructure<T>) : Structure2D
override fun <F : Any> getFeature(type: KClass<F>): F? = structure.getFeature(type) override fun <F : Any> getFeature(type: KClass<F>): F? = structure.getFeature(type)
override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements() override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements()
override fun equals(other: Any?): Boolean = structure == other
override fun hashCode(): Int = structure.hashCode()
} }
/** /**
* A 2D wrapper for a mutable nd-structure * A 2D wrapper for a mutable nd-structure
*/ */
private class MutableStructure2DWrapper<T>(val structure: MutableNDStructure<T>): MutableStructure2D<T> private class MutableStructure2DWrapper<T>(val structure: MutableStructureND<T>): MutableStructure2D<T>
{ {
override val shape: IntArray get() = structure.shape override val shape: IntArray get() = structure.shape
@ -120,19 +116,25 @@ private class MutableStructure2DWrapper<T>(val structure: MutableNDStructure<T>)
} }
/** /**
* Represent a [NDStructure] as [Structure2D]. 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> = this as? Structure2D<T> ?: when (shape.size) { public fun <T> StructureND<T>.as2D(): Structure2D<T> = this as? Structure2D<T> ?: when (shape.size) {
2 -> Structure2DWrapper(this) 2 -> Structure2DWrapper(this)
else -> error("Can't create 2d-structure from ${shape.size}d-structure") else -> error("Can't create 2d-structure from ${shape.size}d-structure")
} }
internal fun <T> Structure2D<T>.unwrap(): NDStructure<T> = if (this is Structure2DWrapper) structure else this public fun <T> MutableStructureND<T>.as2D(): MutableStructure2D<T> = this as? MutableStructure2D<T> ?: when (shape.size) {
public fun <T> MutableNDStructure<T>.as2D(): MutableStructure2D<T> = this as? MutableStructure2D<T> ?: when (shape.size) {
2 -> MutableStructure2DWrapper(this) 2 -> MutableStructure2DWrapper(this)
else -> error("Can't create 2d-structure from ${shape.size}d-structure") else -> error("Can't create 2d-structure from ${shape.size}d-structure")
} }
internal fun <T> MutableStructure2D<T>.unwrap(): MutableNDStructure<T> = /**
* Expose inner [StructureND] if possible
*/
internal fun <T> Structure2D<T>.unwrap(): StructureND<T> =
if (this is Structure2DWrapper) structure
else this
internal fun <T> MutableStructure2D<T>.unwrap(): MutableStructureND<T> =
if (this is MutableStructure2DWrapper) structure else this if (this is MutableStructure2DWrapper) structure else this

View File

@ -3,8 +3,6 @@ package space.kscience.kmath.nd
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.asSequence
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.native.concurrent.ThreadLocal import kotlin.native.concurrent.ThreadLocal
import kotlin.reflect.KClass 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 * 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. * 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. * @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 * The shape of structure, i.e. non-empty sequence of non-negative integers that specify sizes of dimensions of
* this structure. * this structure.
@ -43,32 +43,47 @@ public interface NDStructure<T> {
*/ */
public fun elements(): Sequence<Pair<IntArray, 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 * Feature is some additional strucure information which allows to access it special properties or hints.
* optimize operations and performance. If the feature is not present, null is defined. * If the feature is not present, null is returned.
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public fun <F : Any> getFeature(type: KClass<F>): F? = null public fun <F : Any> getFeature(type: KClass<F>): F? = null
public companion object { 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 if (st1 === st2) return true
// fast comparison of buffers if possible // fast comparison of buffers if possible
if (st1 is NDBuffer && st2 is NDBuffer && st1.strides == st2.strides) if (st1 is BufferND && st2 is BufferND && st1.strides == st2.strides)
return st1.buffer.contentEquals(st2.buffer) return Buffer.contentEquals(st1.buffer, st2.buffer)
//element by element comparison if it could not be avoided //element by element comparison if it could not be avoided
return st1.elements().all { (index, value) -> value == st2[index] } 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. * Creates a NDStructure with explicit buffer factory.
* *
@ -78,7 +93,7 @@ public interface NDStructure<T> {
strides: Strides, strides: Strides,
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing,
initializer: (IntArray) -> T, 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 * 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( public inline fun <reified T : Any> auto(
strides: Strides, strides: Strides,
crossinline initializer: (IntArray) -> T, 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( public inline fun <T : Any> auto(
type: KClass<T>, type: KClass<T>,
strides: Strides, strides: Strides,
crossinline initializer: (IntArray) -> T, 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> buffered( public fun <T> buffered(
shape: IntArray, shape: IntArray,
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing,
initializer: (IntArray) -> T, initializer: (IntArray) -> T,
): NDBuffer<T> = buffered(DefaultStrides(shape), bufferFactory, initializer) ): BufferND<T> = buffered(DefaultStrides(shape), bufferFactory, initializer)
public inline fun <reified T : Any> auto( public inline fun <reified T : Any> auto(
shape: IntArray, shape: IntArray,
crossinline initializer: (IntArray) -> T, crossinline initializer: (IntArray) -> T,
): NDBuffer<T> = auto(DefaultStrides(shape), initializer) ): BufferND<T> = auto(DefaultStrides(shape), initializer)
@JvmName("autoVarArg") @JvmName("autoVarArg")
public inline fun <reified T : Any> auto( public inline fun <reified T : Any> auto(
vararg shape: Int, vararg shape: Int,
crossinline initializer: (IntArray) -> T, crossinline initializer: (IntArray) -> T,
): NDBuffer<T> = ): BufferND<T> =
auto(DefaultStrides(shape), initializer) auto(DefaultStrides(shape), initializer)
public inline fun <T : Any> auto( public inline fun <T : Any> auto(
type: KClass<T>, type: KClass<T>,
vararg shape: Int, vararg shape: Int,
crossinline initializer: (IntArray) -> T, crossinline initializer: (IntArray) -> T,
): 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. * @param index the indices.
* @return the value. * @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 @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. * 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. * 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) } elements().forEach { (index, oldValue) -> this[index] = action(index, oldValue) }
/** /**
@ -194,7 +209,8 @@ public interface Strides {
} }
} }
internal inline fun offsetFromIndex(index: IntArray, shape: IntArray, strides: IntArray): Int = index.mapIndexed { i, value -> internal inline fun offsetFromIndex(index: IntArray, shape: IntArray, strides: IntArray): Int =
index.mapIndexed { i, value ->
if (value < 0 || value >= shape[i]) throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${shape[i]})") if (value < 0 || value >= shape[i]) throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${shape[i]})")
value * strides[i] value * strides[i]
}.sum() }.sum()
@ -262,97 +278,11 @@ 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 { public inline fun <reified T : Any> StructureND<T>.combine(
if (strides.linearSize != buffer.size) { struct: StructureND<T>,
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 open 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>,
crossinline block: (T, T) -> T, crossinline block: (T, T) -> T,
): NDStructure<T> { ): StructureND<T> {
require(shape.contentEquals(struct.shape)) { "Shape mismatch in structure combination" } 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 package space.kscience.kmath.operations
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.BufferedNDRing import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.NDAlgebra import space.kscience.kmath.nd.BufferedRingND
import space.kscience.kmath.operations.BigInt.Companion.BASE import space.kscience.kmath.operations.BigInt.Companion.BASE
import space.kscience.kmath.operations.BigInt.Companion.BASE_SIZE import space.kscience.kmath.operations.BigInt.Companion.BASE_SIZE
import space.kscience.kmath.structures.Buffer 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> = public inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer<BigInt> =
boxing(size, initializer) boxing(size, initializer)
public fun NDAlgebra.Companion.bigInt(vararg shape: Int): BufferedNDRing<BigInt, BigIntField> = public fun AlgebraND.Companion.bigInt(vararg shape: Int): BufferedRingND<BigInt, BigIntField> =
BufferedNDRing(shape, BigIntField, Buffer.Companion::bigInt) BufferedRingND(shape, BigIntField, Buffer.Companion::bigInt)

View File

@ -49,19 +49,19 @@ public fun <T : Comparable<T>> Group<T>.abs(value: T): T = if (value > zero) val
* Returns the sum of all elements in the iterable in provided space. * Returns the sum of all elements in the iterable in provided space.
* *
* @receiver the collection to sum up. * @receiver the collection to sum up.
* @param space the algebra that provides addition. * @param group the algebra that provides addition.
* @return the sum. * @return the sum.
*/ */
public fun <T> Iterable<T>.sumWith(space: Group<T>): T = space.sum(this) public fun <T> Iterable<T>.sumWith(group: Group<T>): T = group.sum(this)
/** /**
* Returns the sum of all elements in the sequence in provided space. * Returns the sum of all elements in the sequence in provided space.
* *
* @receiver the collection to sum up. * @receiver the collection to sum up.
* @param space the algebra that provides addition. * @param group the algebra that provides addition.
* @return the sum. * @return the sum.
*/ */
public fun <T> Sequence<T>.sumWith(space: Group<T>): T = space.sum(this) public fun <T> Sequence<T>.sumWith(group: Group<T>): T = group.sum(this)
/** /**
* Returns an average value of elements in the iterable in this [Group]. * Returns an average value of elements in the iterable in this [Group].

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. * A field for [Double] without boxing. Does not produce appropriate field element.
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @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 zero: Double = 0.0
public override val one: Double = 1.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> 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. * @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> 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 { public companion object {
/** /**
* Creates a [RealBuffer] with the specified [size], where each element is calculated by calling the specified * Check the element-by-element match of content of two buffers.
* [initializer] function.
*/ */
public inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer = public fun <T: Any> contentEquals(first: Buffer<T>, second: Buffer<T>): Boolean{
RealBuffer(size) { initializer(it) } 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 * Creates a [ListBuffer] of given type [T] with given [size]. Each element is calculated by calling the
* specified [initializer] function. * specified [initializer] function.
*/ */
public inline fun <T> boxing(size: Int, initializer: (Int) -> T): Buffer<T> = public inline fun <T> boxing(size: Int, initializer: (Int) -> T): Buffer<T> =
ListBuffer(List(size, initializer)) List(size, initializer).asBuffer()
// TODO add resolution based on Annotation or companion resolution
/** /**
* Creates a [Buffer] of given [type]. If the type is primitive, specialized buffers are used ([IntBuffer], * 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. * The [size] is specified, and each element is calculated by calling the specified [initializer] function.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public inline fun <T : Any> auto(type: KClass<T>, size: Int, initializer: (Int) -> T): Buffer<T> = public inline fun <T : Any> auto(type: KClass<T>, size: Int, initializer: (Int) -> T): Buffer<T> =
when (type) { when (type) {
Double::class -> RealBuffer(size) { initializer(it) as Double } as Buffer<T> Double::class -> MutableBuffer.double(size) { initializer(it) as Double } as Buffer<T>
Short::class -> ShortBuffer(size) { initializer(it) as Short } as Buffer<T> Short::class -> MutableBuffer.short(size) { initializer(it) as Short } as Buffer<T>
Int::class -> IntBuffer(size) { initializer(it) as Int } as Buffer<T> Int::class -> MutableBuffer.int(size) { initializer(it) as Int } as Buffer<T>
Long::class -> LongBuffer(size) { initializer(it) as Long } as Buffer<T> Long::class -> MutableBuffer.long(size) { initializer(it) as Long } as Buffer<T>
Float::class -> FloatBuffer(size) { initializer(it) as Float } as Buffer<T> Float::class -> MutableBuffer.float(size) { initializer(it) as Float } as Buffer<T>
else -> boxing(size, initializer) else -> boxing(size, initializer)
} }
/** /**
* Creates a [Buffer] of given type [T]. If the type is primitive, specialized buffers are used ([IntBuffer], * 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. * The [size] is specified, and each element is calculated by calling the specified [initializer] function.
*/ */
@ -89,41 +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)
/**
* 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.
*/
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 -> asSequence().toMutableList()
}
/**
* Returns a new [Array] containing all elements of this buffer.
*/
public inline fun <reified T> Buffer<T>.toTypedArray(): Array<T> = asSequence().toList().toTypedArray()
/** /**
* Returns an [IntRange] of the valid indices for this [Buffer]. * Returns an [IntRange] of the valid indices for this [Buffer].
*/ */
@ -146,6 +110,44 @@ public interface MutableBuffer<T> : Buffer<T> {
public fun copy(): MutableBuffer<T> public fun copy(): MutableBuffer<T>
public companion object { 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 * Create a boxing mutable buffer of given type
*/ */
@ -154,37 +156,30 @@ public interface MutableBuffer<T> : Buffer<T> {
/** /**
* Creates a [MutableBuffer] of given [type]. If the type is primitive, specialized buffers are used * 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. * The [size] is specified, and each element is calculated by calling the specified [initializer] function.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public inline fun <T : Any> auto(type: KClass<out T>, size: Int, initializer: (Int) -> T): MutableBuffer<T> = public inline fun <T : Any> auto(type: KClass<out T>, size: Int, initializer: (Int) -> T): MutableBuffer<T> =
when (type) { when (type) {
Double::class -> RealBuffer(size) { initializer(it) as Double } as MutableBuffer<T> Double::class -> double(size) { initializer(it) as Double } as MutableBuffer<T>
Short::class -> ShortBuffer(size) { initializer(it) as Short } as MutableBuffer<T> Short::class -> short(size) { initializer(it) as Short } as MutableBuffer<T>
Int::class -> IntBuffer(size) { initializer(it) as Int } as MutableBuffer<T> Int::class -> int(size) { initializer(it) as Int } as MutableBuffer<T>
Float::class -> FloatBuffer(size) { initializer(it) as Float } as MutableBuffer<T> Float::class -> float(size) { initializer(it) as Float } as MutableBuffer<T>
Long::class -> LongBuffer(size) { initializer(it) as Long } as MutableBuffer<T> Long::class -> long(size) { initializer(it) as Long } as MutableBuffer<T>
else -> boxing(size, initializer) else -> boxing(size, initializer)
} }
/** /**
* Creates a [MutableBuffer] of given type [T]. If the type is primitive, specialized buffers are used * 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. * The [size] is specified, and each element is calculated by calling the specified [initializer] function.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> = public inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> =
auto(T::class, size, initializer) auto(T::class, size, initializer)
/**
* 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) }
} }
} }
@ -207,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) 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]. * [MutableBuffer] implementation over [MutableList].
* *
@ -237,7 +223,11 @@ public inline class MutableListBuffer<T>(public val list: MutableList<T>) : Muta
} }
/** /**
<<<<<<< HEAD
* Returns an [MutableListBuffer] that wraps the original list. * Returns an [MutableListBuffer] that wraps the original list.
=======
* Returns an [ListBuffer] that wraps the original list.
>>>>>>> dev
*/ */
public fun <T> MutableList<T>.asMutableBuffer(): MutableListBuffer<T> = MutableListBuffer(this) public fun <T> MutableList<T>.asMutableBuffer(): MutableListBuffer<T> = MutableListBuffer(this)
@ -249,8 +239,7 @@ public fun <T> MutableList<T>.asMutableBuffer(): MutableListBuffer<T> = MutableL
*/ */
public class ArrayBuffer<T>(internal val array: Array<T>) : MutableBuffer<T> { public class ArrayBuffer<T>(internal val array: Array<T>) : MutableBuffer<T> {
// Can't inline because array is invariant // Can't inline because array is invariant
override val size: Int override val size: Int get() = array.size
get() = array.size
override operator fun get(index: Int): T = array[index] override operator fun get(index: Int): T = array[index]
@ -268,16 +257,6 @@ public class ArrayBuffer<T>(internal val array: Array<T>) : MutableBuffer<T> {
*/ */
public fun <T> Array<T>.asBuffer(): ArrayBuffer<T> = ArrayBuffer(this) 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]. * Immutable wrapper for [MutableBuffer].
* *
@ -305,27 +284,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 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. * Convert this buffer to read-only buffer.
*/ */
public fun <T> Buffer<T>.asReadOnly(): Buffer<T> = if (this is MutableBuffer) ReadOnlyBuffer(this) else this 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 package space.kscience.kmath.structures
import space.kscience.kmath.nd.DefaultStrides import space.kscience.kmath.nd.DefaultStrides
import space.kscience.kmath.nd.NDStructure
import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.as2D
/** /**
@ -25,7 +25,7 @@ internal class BufferAccessor2D<T : Any>(
public fun create(mat: Structure2D<T>): MutableBuffer<T> = create { i, j -> mat[i, j] } public fun create(mat: Structure2D<T>): MutableBuffer<T> = create { i, j -> mat[i, j] }
//TODO optimize wrapper //TODO optimize wrapper
public fun MutableBuffer<T>.collect(): Structure2D<T> = NDStructure.buffered( public fun MutableBuffer<T>.collect(): Structure2D<T> = StructureND.buffered(
DefaultStrides(intArrayOf(rowNum, colNum)), DefaultStrides(intArrayOf(rowNum, colNum)),
factory factory
) { (i, j) -> ) { (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 * 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?> { Buffer<Double?> {
init { init {
require(values.size == flags.size) { "Values and flags must have the same dimensions" } 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() }.iterator()
} }
public inline fun FlaggedRealBuffer.forEachValid(block: (Double) -> Unit) { public inline fun FlaggedDoubleBuffer.forEachValid(block: (Double) -> Unit) {
indices indices
.asSequence() .asSequence()
.filter(::isValid) .filter(::isValid)

View File

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

View File

@ -1,56 +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 new [DoubleArray] containing all of the elements of this [Buffer].
*/
public fun Buffer<Double>.toDoubleArray(): DoubleArray = when(this) {
is RealBuffer -> array.copyOf()
else -> DoubleArray(size, ::get)
}
/**
* 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

@ -1,7 +1,84 @@
package space.kscience.kmath.structures 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( public inline fun <T : Any, reified R : Any> Buffer<T>.map(
bufferFactory: BufferFactory<R> = Buffer.Companion::auto, bufferFactory: BufferFactory<R> = Buffer.Companion::auto,
crossinline block: (T) -> R, crossinline block: (T) -> R,
): Buffer<R> = bufferFactory(size) { block(get(it)) } ): 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,5 +1,5 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.nd.MutableNDStructure import space.kscience.kmath.nd.MutableStructureND
public typealias TensorStructure<T> = MutableNDStructure<T> public typealias TensorStructure<T> = MutableStructureND<T>

View File

@ -90,4 +90,4 @@ public class DoubleTensor internal constructor(
shape: IntArray, shape: IntArray,
buffer: DoubleArray, buffer: DoubleArray,
offset: Int = 0 offset: Int = 0
) : BufferedTensor<Double>(shape, RealBuffer(buffer), offset) ) : BufferedTensor<Double>(shape, DoubleBuffer(buffer), offset)

View File

@ -31,6 +31,6 @@ internal fun Buffer<Float>.array(): FloatArray = when (this) {
* Returns a reference to [DoubleArray] containing all of the elements of this [Buffer]. * Returns a reference to [DoubleArray] containing all of the elements of this [Buffer].
*/ */
internal fun Buffer<Double>.array(): DoubleArray = when (this) { internal fun Buffer<Double>.array(): DoubleArray = when (this) {
is RealBuffer -> array is DoubleBuffer -> array
else -> throw RuntimeException("Failed to cast Buffer to DoubleArray") else -> throw RuntimeException("Failed to cast Buffer to DoubleArray")
} }

View File

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

View File

@ -1,6 +1,7 @@
package space.kscience.kmath.expressions package space.kscience.kmath.expressions
import space.kscience.kmath.operations.RealField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.asBuffer
import kotlin.math.E import kotlin.math.E
import kotlin.math.PI import kotlin.math.PI
@ -14,19 +15,19 @@ class SimpleAutoDiffTest {
fun dx( fun dx(
xBinding: Pair<Symbol, Double>, xBinding: Pair<Symbol, Double>,
body: SimpleAutoDiffField<Double, RealField>.(x: AutoDiffValue<Double>) -> AutoDiffValue<Double>, body: SimpleAutoDiffField<Double, DoubleField>.(x: AutoDiffValue<Double>) -> AutoDiffValue<Double>,
): DerivationResult<Double> = RealField.simpleAutoDiff(xBinding) { body(bindSymbol(xBinding.first)) } ): DerivationResult<Double> = DoubleField.simpleAutoDiff(xBinding) { body(bindSymbol(xBinding.first)) }
fun dxy( fun dxy(
xBinding: Pair<Symbol, Double>, xBinding: Pair<Symbol, Double>,
yBinding: Pair<Symbol, Double>, yBinding: Pair<Symbol, Double>,
body: SimpleAutoDiffField<Double, RealField>.(x: AutoDiffValue<Double>, y: AutoDiffValue<Double>) -> AutoDiffValue<Double>, body: SimpleAutoDiffField<Double, DoubleField>.(x: AutoDiffValue<Double>, y: AutoDiffValue<Double>) -> AutoDiffValue<Double>,
): DerivationResult<Double> = RealField.simpleAutoDiff(xBinding, yBinding) { ): DerivationResult<Double> = DoubleField.simpleAutoDiff(xBinding, yBinding) {
body(bindSymbol(xBinding.first), bindSymbol(yBinding.first)) body(bindSymbol(xBinding.first), bindSymbol(yBinding.first))
} }
fun diff(block: SimpleAutoDiffField<Double, RealField>.() -> AutoDiffValue<Double>): SimpleAutoDiffExpression<Double, RealField> { fun diff(block: SimpleAutoDiffField<Double, DoubleField>.() -> AutoDiffValue<Double>): SimpleAutoDiffExpression<Double, DoubleField> {
return SimpleAutoDiffExpression(RealField, block) return SimpleAutoDiffExpression(DoubleField, block)
} }
val x by symbol val x by symbol
@ -35,7 +36,7 @@ class SimpleAutoDiffTest {
@Test @Test
fun testPlusX2() { fun testPlusX2() {
val y = RealField.simpleAutoDiff(x to 3.0) { val y = DoubleField.simpleAutoDiff(x to 3.0) {
// diff w.r.t this x at 3 // diff w.r.t this x at 3
val x = bindSymbol(x) val x = bindSymbol(x)
x + x x + x
@ -58,7 +59,7 @@ class SimpleAutoDiffTest {
@Test @Test
fun testPlus() { fun testPlus() {
// two variables // two variables
val z = RealField.simpleAutoDiff(x to 2.0, y to 3.0) { val z = DoubleField.simpleAutoDiff(x to 2.0, y to 3.0) {
val x = bindSymbol(x) val x = bindSymbol(x)
val y = bindSymbol(y) val y = bindSymbol(y)
x + y x + y
@ -71,7 +72,7 @@ class SimpleAutoDiffTest {
@Test @Test
fun testMinus() { fun testMinus() {
// two variables // two variables
val z = RealField.simpleAutoDiff(x to 7.0, y to 3.0) { val z = DoubleField.simpleAutoDiff(x to 7.0, y to 3.0) {
val x = bindSymbol(x) val x = bindSymbol(x)
val y = bindSymbol(y) val y = bindSymbol(y)
@ -276,7 +277,7 @@ class SimpleAutoDiffTest {
fun testDivGrad() { fun testDivGrad() {
val res = dxy(x to 1.0, y to 2.0) { x, y -> x * x + y * y } val res = dxy(x to 1.0, y to 2.0) { x, y -> x * x + y * y }
assertEquals(6.0, res.div()) assertEquals(6.0, res.div())
assertTrue(res.grad(x, y).contentEquals(doubleArrayOf(2.0, 4.0).asBuffer())) assertTrue(Buffer.contentEquals(res.grad(x, y), doubleArrayOf(2.0, 4.0).asBuffer()))
} }
private fun assertApprox(a: Double, b: Double) { private fun assertApprox(a: Double, b: Double) {

View File

@ -1,17 +1,23 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.StructureND
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue
fun <T : Any> assertMatrixEquals(expected: StructureND<T>, actual: StructureND<T>) {
assertTrue { StructureND.contentEquals(expected, actual) }
}
@UnstableKMathAPI @UnstableKMathAPI
class RealLUSolverTest { class DoubleLUSolverTest {
@Test @Test
fun testInvertOne() { fun testInvertOne() {
val matrix = LinearSpace.real.one(2, 2) val matrix = LinearSpace.real.one(2, 2)
val inverted = LinearSpace.real.inverseWithLup(matrix) val inverted = LinearSpace.real.inverseWithLup(matrix)
assertEquals(matrix, inverted) assertMatrixEquals(matrix, inverted)
} }
@Test @Test
@ -27,7 +33,7 @@ class RealLUSolverTest {
//Check determinant //Check determinant
assertEquals(7.0, lup.determinant) assertEquals(7.0, lup.determinant)
assertEquals(lup.p dot matrix, lup.l dot lup.u) assertMatrixEquals(lup.p dot matrix, lup.l dot lup.u)
} }
} }
@ -45,6 +51,6 @@ class RealLUSolverTest {
-0.125, 0.375 -0.125, 0.375
) )
assertEquals(expected, inverted) assertMatrixEquals(expected, inverted)
} }
} }

View File

@ -1,10 +1,11 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.NDStructure import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.as2D
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue
@UnstableKMathAPI @UnstableKMathAPI
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
@ -13,7 +14,7 @@ class MatrixTest {
fun testTranspose() { fun testTranspose() {
val matrix = LinearSpace.real.one(3, 3) val matrix = LinearSpace.real.one(3, 3)
val transposed = matrix.transpose() val transposed = matrix.transpose()
assertEquals(matrix, transposed) assertTrue { StructureND.contentEquals(matrix, transposed) }
} }
@Test @Test
@ -50,8 +51,8 @@ class MatrixTest {
@Test @Test
fun test2DDot() { fun test2DDot() {
val firstMatrix = NDStructure.auto(2, 3) { (i, j) -> (i + j).toDouble() }.as2D() val firstMatrix = StructureND.auto(2, 3) { (i, j) -> (i + j).toDouble() }.as2D()
val secondMatrix = NDStructure.auto(3, 2) { (i, j) -> (i + j).toDouble() }.as2D() val secondMatrix = StructureND.auto(3, 2) { (i, j) -> (i + j).toDouble() }.as2D()
LinearSpace.real.run { LinearSpace.real.run {
// val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() } // val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() }

View File

@ -4,13 +4,13 @@ import space.kscience.kmath.testutils.FieldVerifier
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class RealFieldTest { internal class DoubleFieldTest {
@Test @Test
fun verify() = FieldVerifier(RealField, 42.0, 66.0, 2.0, 5).verify() fun verify() = FieldVerifier(DoubleField, 42.0, 66.0, 2.0, 5).verify()
@Test @Test
fun testSqrt() { fun testSqrt() {
val sqrt = RealField { sqrt(25 * one) } val sqrt = DoubleField { sqrt(25 * one) }
assertEquals(5.0, sqrt) assertEquals(5.0, sqrt)
} }
} }

View File

@ -1,6 +1,6 @@
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.kmath.nd.NDAlgebra import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.get import space.kscience.kmath.nd.get
import space.kscience.kmath.nd.real import space.kscience.kmath.nd.real
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
@ -11,12 +11,12 @@ import kotlin.test.assertEquals
internal class NDFieldTest { internal class NDFieldTest {
@Test @Test
fun verify() { fun verify() {
(NDAlgebra.real(12, 32)) { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) } (AlgebraND.real(12, 32)) { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) }
} }
@Test @Test
fun testStrides() { fun testStrides() {
val ndArray = NDAlgebra.real(10, 10).produce { (it[0] + it[1]).toDouble() } val ndArray = AlgebraND.real(10, 10).produce { (it[0] + it[1]).toDouble() }
assertEquals(ndArray[5, 5], 10.0) assertEquals(ndArray[5, 5], 10.0)
} }
} }

View File

@ -11,7 +11,7 @@ import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
class NumberNDFieldTest { class NumberNDFieldTest {
val algebra = NDAlgebra.real(3,3) val algebra = AlgebraND.real(3, 3)
val array1 = algebra.produce { (i, j) -> (i + j).toDouble() } val array1 = algebra.produce { (i, j) -> (i + j).toDouble() }
val array2 = algebra.produce { (i, j) -> (i - j).toDouble() } val array2 = algebra.produce { (i, j) -> (i - j).toDouble() }
@ -69,15 +69,15 @@ class NumberNDFieldTest {
val division = array1.combine(array2, Double::div) val division = array1.combine(array2, Double::div)
} }
object L2Norm : Norm<NDStructure<out Number>, Double> { object L2Norm : Norm<StructureND<out Number>, Double> {
override fun norm(arg: NDStructure<out Number>): Double = override fun norm(arg: StructureND<out Number>): Double =
kotlin.math.sqrt(arg.elements().sumByDouble { it.second.toDouble() }) kotlin.math.sqrt(arg.elements().sumByDouble { it.second.toDouble() })
} }
@Test @Test
fun testInternalContext() { fun testInternalContext() {
algebra { algebra {
(NDAlgebra.real(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } } (AlgebraND.real(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } }
} }
} }
} }

View File

@ -3,7 +3,7 @@ package space.kscience.kmath.chains
/** /**
* Performance optimized chain for real values * Performance optimized chain for real values
*/ */
public abstract class BlockingRealChain : Chain<Double> { public abstract class BlockingDoubleChain : Chain<Double> {
public abstract fun nextDouble(): Double public abstract fun nextDouble(): Double
override suspend fun next(): Double = nextDouble() override suspend fun next(): Double = nextDouble()

View File

@ -83,7 +83,7 @@ public class StatefulChain<S, out R>(
private val state: S, private val state: S,
private val seed: S.() -> R, private val seed: S.() -> R,
private val forkState: ((S) -> S), private val forkState: ((S) -> S),
private val gen: suspend S.(R) -> R private val gen: suspend S.(R) -> R,
) : Chain<R> { ) : Chain<R> {
private val mutex: Mutex = Mutex() private val mutex: Mutex = Mutex()
private var value: R? = null private var value: R? = null
@ -145,7 +145,7 @@ public fun <T, R> Chain<T>.collect(mapper: suspend (Chain<T>) -> R): Chain<R> =
public fun <T, S, R> Chain<T>.collectWithState( public fun <T, S, R> Chain<T>.collectWithState(
state: S, state: S,
stateFork: (S) -> S, stateFork: (S) -> S,
mapper: suspend S.(Chain<T>) -> R mapper: suspend S.(Chain<T>) -> R,
): Chain<R> = object : Chain<R> { ): Chain<R> = object : Chain<R> {
override suspend fun next(): R = state.mapper(this@collectWithState) override suspend fun next(): R = state.mapper(this@collectWithState)

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