Fix #226 #230

Merged
CommanderTvis merged 2 commits from commandertvis/issue226 into dev 2021-03-17 20:14:17 +03:00
183 changed files with 4051 additions and 3814 deletions
Showing only changes of commit c4367ac509 - Show all commits

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

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

View File

@ -12,7 +12,7 @@ jobs:
name: publish name: publish
strategy: strategy:
matrix: matrix:
os: [macOS-latest, windows-latest] os: [ macOS-latest, windows-latest ]
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- name: Checkout the repo - name: Checkout the repo

View File

@ -4,17 +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
@ -72,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)"
@ -32,7 +35,7 @@
d="m 1299.34,651.602 h 4653.75 v 3208.87 H 1299.34 Z" /><path d="m 1299.34,651.602 h 4653.75 v 3208.87 H 1299.34 Z" /><path
id="path16" id="path16"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 1651.48,1093.71 h 4076.99 v 114.609 H 1651.48 Z" /><g d="m 1651.48,1093.71 h 4076.99 v 114.609 H 1651.48 Z"/><g
id="g18"><g id="g18"><g
clip-path="url(#clipPath24)" clip-path="url(#clipPath24)"
id="g20"><g id="g20"><g

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)"
@ -28,19 +30,19 @@
d="M 318.789,2616.17 6643.05,4723.79 c 239.45,79.76 496.44,-48.75 576.29,-288.17 79.8,-239.41 -48.72,-496.4 -288.13,-576.21 L 606.949,1751.84 c -239.449,-79.81 -496.437,48.71 -576.2888,288.12 -79.8008,239.45 48.7187,496.45 288.1288,576.21 z" /><path d="M 318.789,2616.17 6643.05,4723.79 c 239.45,79.76 496.44,-48.75 576.29,-288.17 79.8,-239.41 -48.72,-496.4 -288.13,-576.21 L 606.949,1751.84 c -239.449,-79.81 -496.437,48.71 -576.2888,288.12 -79.8008,239.45 48.7187,496.45 288.1288,576.21 z" /><path
id="path16" id="path16"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 3751.56,538.199 6636.91,4443.95 c 109.22,147.89 316.21,178.98 464.11,69.76 147.89,-109.22 178.98,-316.21 69.76,-464.06 L 4285.43,143.91 C 4176.17,-3.98047 3969.18,-35.0781 3821.29,74.1406 3673.44,183.359 3642.3,390.309 3751.56,538.199 Z" /><path d="M 3751.56,538.199 6636.91,4443.95 c 109.22,147.89 316.21,178.98 464.11,69.76 147.89,-109.22 178.98,-316.21 69.76,-464.06 L 4285.43,143.91 C 4176.17,-3.98047 3969.18,-35.0781 3821.29,74.1406 3673.44,183.359 3642.3,390.309 3751.56,538.199 Z"/><path
id="path18" id="path18"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 1400.43,708.48 H 6418.24 V 4168.4 H 1400.43 Z" /><path d="M 1400.43,708.48 H 6418.24 V 4168.4 H 1400.43 Z"/><path
id="path20" id="path20"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 6145.16,1753.83 h -358.6 V 3271.02 L 5158.28,1753.83 H 5005 L 4379.69,3271.02 V 1753.83 h -358.6 v 2044.53 h 505.79 l 554.68,-1345.63 557.97,1345.63 h 505.63 z" /><path d="m 6145.16,1753.83 h -358.6 V 3271.02 L 5158.28,1753.83 H 5005 L 4379.69,3271.02 V 1753.83 h -358.6 v 2044.53 h 505.79 l 554.68,-1345.63 557.97,1345.63 h 505.63 z"/><path
id="path22" id="path22"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 1780.12,1185.24 h 4395.94 v 123.551 H 1780.12 Z" /><path d="m 1780.12,1185.24 h 4395.94 v 123.551 H 1780.12 Z"/><path
id="path24" id="path24"
style="fill:none;stroke:#000000;stroke-width:22.00740051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" style="fill:none;stroke:#000000;stroke-width:22.00740051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1780.12,3798.09 c 0,-2044.73 0,-2044.73 0,-2044.73 H 3824.84 L 2806.17,2778.24 3824.84,3798.09" /><g d="m 1780.12,3798.09 c 0,-2044.73 0,-2044.73 0,-2044.73 H 3824.84 L 2806.17,2778.24 3824.84,3798.09"/><g
id="g26"><g id="g26"><g
clip-path="url(#clipPath32)" clip-path="url(#clipPath32)"
id="g28"><g id="g28"><g

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)"
@ -40,7 +45,7 @@
d="m 791.328,957.73 h 6401.56 V 3245.31 H 791.328 Z" /><path d="m 791.328,957.73 h 6401.56 V 3245.31 H 791.328 Z" /><path
id="path16" id="path16"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 1049.45,1214.42 h 2988.24 v 86.9883 H 1049.45 Z" /><g d="m 1049.45,1214.42 h 2988.24 v 86.9883 H 1049.45 Z"/><g
id="g18"><g id="g18"><g
clip-path="url(#clipPath24)" clip-path="url(#clipPath24)"
id="g20"><g id="g20"><g

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)"
@ -104,31 +125,31 @@
d="M 265.859,2205.39 5500.51,3949.84 c 198.16,66.06 410.9,-40.31 476.99,-238.51 66.05,-198.17 -40.31,-410.86 -238.48,-476.92 L 504.379,1489.96 c -198.168,-66.05 -410.8985,40.31 -476.9571,238.48 -66.0938,198.16 40.2773,410.9 238.4371,476.95 z" /><path d="M 265.859,2205.39 5500.51,3949.84 c 198.16,66.06 410.9,-40.31 476.99,-238.51 66.05,-198.17 -40.31,-410.86 -238.48,-476.92 L 504.379,1489.96 c -198.168,-66.05 -410.8985,40.31 -476.9571,238.48 -66.0938,198.16 40.2773,410.9 238.4371,476.95 z" /><path
id="path16" id="path16"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 3096.33,444.379 2388.2,3232.811 c 90.43,122.38 261.76,148.12 384.18,57.73 122.38,-90.39 148.13,-261.72 57.74,-384.1 L 3538.2,117.969 C 3447.77,-4.41016 3276.45,-30.1602 3154.06,60.2305 3031.64,150.66 3005.9,321.949 3096.33,444.379 Z" /><path d="m 3096.33,444.379 2388.2,3232.811 c 90.43,122.38 261.76,148.12 384.18,57.73 122.38,-90.39 148.13,-261.72 57.74,-384.1 L 3538.2,117.969 C 3447.77,-4.41016 3276.45,-30.1602 3154.06,60.2305 3031.64,150.66 3005.9,321.949 3096.33,444.379 Z"/><path
id="path18" id="path18"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 734.691,901.211 h 5982.5 v 2109.69 h -5982.5 z" /><path d="m 734.691,901.211 h 5982.5 v 2109.69 h -5982.5 z"/><path
id="path20" id="path20"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 3724.45,1489.53 h -225.93 v 956.09 l -395.94,-956.09 h -96.56 l -394.07,956.09 v -956.09 h -225.93 v 1288.28 h 318.59 l 349.69,-847.97 351.4,847.97 h 318.75 z" /><path d="m 3724.45,1489.53 h -225.93 v 956.09 l -395.94,-956.09 h -96.56 l -394.07,956.09 v -956.09 h -225.93 v 1288.28 h 318.59 l 349.69,-847.97 351.4,847.97 h 318.75 z"/><path
id="path22" id="path22"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 4767.23,1489.53 H 4564.41 V 1590 c -72.11,-82.42 -172.5,-123.59 -301.25,-123.59 -85,0 -160.39,27.34 -226.09,82.03 -65.62,54.68 -98.44,130.31 -98.44,226.87 0,99.14 32.5,174.46 97.5,225.94 65.08,51.56 140.78,77.34 227.03,77.34 132.58,0 233.01,-39.92 301.25,-119.68 v 139.06 c 0,54.06 -19.96,96.48 -59.84,127.34 -39.92,30.94 -92.73,46.41 -158.44,46.41 -104.29,0 -196.33,-39.3 -276.09,-117.81 L 3986.91,2295 c 105.63,100.39 236.29,150.62 392.04,150.62 114.68,0 208.04,-27.1 280.15,-81.24 72.07,-54.07 108.13,-139.65 108.13,-256.72 z m -424.85,115.94 c 101.64,0 175.67,32.19 222.03,96.56 v 140.94 c -46.36,64.37 -120.39,96.56 -222.03,96.56 -58.04,0 -105.74,-15.47 -143.12,-46.41 -37.31,-30.85 -55.94,-71.36 -55.94,-121.56 0,-50.23 18.63,-90.47 55.94,-120.78 37.38,-30.23 85.08,-45.31 143.12,-45.31 z" /><path d="M 4767.23,1489.53 H 4564.41 V 1590 c -72.11,-82.42 -172.5,-123.59 -301.25,-123.59 -85,0 -160.39,27.34 -226.09,82.03 -65.62,54.68 -98.44,130.31 -98.44,226.87 0,99.14 32.5,174.46 97.5,225.94 65.08,51.56 140.78,77.34 227.03,77.34 132.58,0 233.01,-39.92 301.25,-119.68 v 139.06 c 0,54.06 -19.96,96.48 -59.84,127.34 -39.92,30.94 -92.73,46.41 -158.44,46.41 -104.29,0 -196.33,-39.3 -276.09,-117.81 L 3986.91,2295 c 105.63,100.39 236.29,150.62 392.04,150.62 114.68,0 208.04,-27.1 280.15,-81.24 72.07,-54.07 108.13,-139.65 108.13,-256.72 z m -424.85,115.94 c 101.64,0 175.67,32.19 222.03,96.56 v 140.94 c -46.36,64.37 -120.39,96.56 -222.03,96.56 -58.04,0 -105.74,-15.47 -143.12,-46.41 -37.31,-30.85 -55.94,-71.36 -55.94,-121.56 0,-50.23 18.63,-90.47 55.94,-120.78 37.38,-30.23 85.08,-45.31 143.12,-45.31 z"/><path
id="path24" id="path24"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 5308.32,1466.41 c -78.55,0 -138.12,20.58 -178.75,61.71 -40.55,41.26 -60.78,100.47 -60.78,177.66 v 538.91 h -154.53 v 177.65 h 154.53 v 255 h 202.81 v -255 h 189.22 V 2244.69 H 5271.6 v -488.6 c 0,-33.55 7.7,-60.31 23.13,-80.31 15.5,-19.92 37.42,-29.84 65.78,-29.84 41.13,0 71.4,10.94 90.78,32.81 l 48.28,-152.5 c -42.5,-39.88 -106.25,-59.84 -191.25,-59.84 z" /><path d="m 5308.32,1466.41 c -78.55,0 -138.12,20.58 -178.75,61.71 -40.55,41.26 -60.78,100.47 -60.78,177.66 v 538.91 h -154.53 v 177.65 h 154.53 v 255 h 202.81 v -255 h 189.22 V 2244.69 H 5271.6 v -488.6 c 0,-33.55 7.7,-60.31 23.13,-80.31 15.5,-19.92 37.42,-29.84 65.78,-29.84 41.13,0 71.4,10.94 90.78,32.81 l 48.28,-152.5 c -42.5,-39.88 -106.25,-59.84 -191.25,-59.84 z"/><path
id="path26" id="path26"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 6477.38,1489.53 h -202.65 v 587.19 c 0,68.2 -16.49,116.8 -49.38,145.78 -32.81,28.95 -79.49,43.44 -140,43.44 -47.62,0 -92.65,-12.27 -135.15,-36.72 -42.5,-24.49 -77.31,-54.06 -104.38,-88.75 v -650.94 h -202.66 v 1288.28 h 202.66 V 2295 c 34.77,41.13 81.45,76.48 140,106.09 58.63,29.69 122.07,44.53 190.31,44.53 200.82,0 301.25,-98.55 301.25,-295.62 z" /><path d="m 6477.38,1489.53 h -202.65 v 587.19 c 0,68.2 -16.49,116.8 -49.38,145.78 -32.81,28.95 -79.49,43.44 -140,43.44 -47.62,0 -92.65,-12.27 -135.15,-36.72 -42.5,-24.49 -77.31,-54.06 -104.38,-88.75 v -650.94 h -202.66 v 1288.28 h 202.66 V 2295 c 34.77,41.13 81.45,76.48 140,106.09 58.63,29.69 122.07,44.53 190.31,44.53 200.82,0 301.25,-98.55 301.25,-295.62 z"/><path
id="path28" id="path28"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 973.949,1128.48 h 2769.84 v 80.6211 H 973.949 Z" /><path d="m 973.949,1128.48 h 2769.84 v 80.6211 H 973.949 Z"/><path
id="path30" id="path30"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 978.828,2781.72 c 0,-1292.5 0,-1292.5 0,-1292.5 H 2262.3 l -639.45,647.85 639.45,644.65" /><path d="m 978.828,2781.72 c 0,-1292.5 0,-1292.5 0,-1292.5 H 2262.3 l -639.45,647.85 639.45,644.65"/><path
id="path32" id="path32"
style="fill:none;stroke:#000000;stroke-width:13.86260033;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" style="fill:none;stroke:#000000;stroke-width:13.86260033;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 978.828,2781.72 c 0,-1292.5 0,-1292.5 0,-1292.5 H 2262.3 l -639.45,647.85 639.45,644.65" /><g d="m 978.828,2781.72 c 0,-1292.5 0,-1292.5 0,-1292.5 H 2262.3 l -639.45,647.85 639.45,644.65"/><g
id="g34"><g id="g34"><g
clip-path="url(#clipPath40)" clip-path="url(#clipPath40)"
id="g36"><g id="g36"><g

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

@ -92,6 +92,14 @@ benchmark {
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
include("ExpressionsInterpretersBenchmark") include("ExpressionsInterpretersBenchmark")
} }
configurations.register("matrixInverse") {
warmups = 1 // number of warmup iterations
iterations = 3 // number of iterations
iterationTime = 500 // time in seconds per iteration
iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds
include("MatrixInverseBenchmark")
}
} }
kotlin.sourceSets.all { kotlin.sourceSets.all {
@ -105,6 +113,6 @@ tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "11" kotlinOptions.jvmTarget = "11"
} }
readme{ readme {
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
} }

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

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

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

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,17 +5,17 @@ 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 {
@Benchmark @Benchmark
fun realFieldLog(blackhole: Blackhole) { fun realFieldLog(blackhole: Blackhole) {
with(realField) { with(realNdField) {
val fortyTwo = produce { 42.0 } val fortyTwo = produce { 42.0 }
var res = one var res = one
repeat(n) { res = ln(fortyTwo) } repeat(n) { res = ln(fortyTwo) }
@ -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 realField = 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()
} }
@ -90,10 +90,10 @@ fun main() {
} }
} }
br() br()
h3{ h3 {
+"Fit result: $result" +"Fit result: $result"
} }
h3{ h3 {
+"Chi2/dof = ${result.value / (x.size - 3)}" +"Chi2/dof = ${result.value / (x.size - 3)}"
} }
} }

View File

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

View File

@ -2,17 +2,17 @@ package space.kscience.kmath.operations
import space.kscience.kmath.complex.Complex import space.kscience.kmath.complex.Complex
import space.kscience.kmath.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.build(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 } val structure = StructureND.buffered(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 }
structure.mapToBuffer { it + 1 } // warm-up 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

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

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=512m 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

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

View File

@ -5,7 +5,7 @@ import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.toComplex import space.kscience.kmath.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

@ -12,6 +12,6 @@ dependencies {
api("org.apache.commons:commons-math3:3.6.1") api("org.apache.commons:commons-math3:3.6.1")
} }
readme{ readme {
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
} }

View File

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

View File

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

View File

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

View File

@ -12,9 +12,9 @@ public enum class CMDecomposition {
CHOLESKY CHOLESKY
} }
public fun CMMatrixContext.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
@ -23,19 +23,19 @@ public fun CMMatrixContext.solver(
CMDecomposition.CHOLESKY -> CholeskyDecomposition(a.toCM().origin).solver CMDecomposition.CHOLESKY -> CholeskyDecomposition(a.toCM().origin).solver
} }
public fun CMMatrixContext.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).asMatrix() ): CMMatrix = solver(a, decomposition).solve(b.toCM().origin).wrap()
public fun CMMatrixContext.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 CMMatrixContext.inverse( public fun CMLinearSpace.inverse(
a: Matrix<Double>, a: Matrix<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP decomposition: CMDecomposition = CMDecomposition.LUP,
): CMMatrix = solver(a, decomposition).inverse.asMatrix() ): 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

@ -27,10 +27,10 @@ internal class AutoDiffTest {
val y = bindSymbol("y") val y = bindSymbol("y")
val z = x * (-sin(x * y) + y) + 2.0 val z = x * (-sin(x * y) + y) + 2.0
println(z.derivative(x)) println(z.derivative(x))
println(z.derivative(y,x)) println(z.derivative(y, x))
assertEquals(z.derivative(x, y), z.derivative(y, x)) assertEquals(z.derivative(x, y), z.derivative(y, x))
//check that improper order cause failure //check that improper order cause failure
assertFails { z.derivative(x,x,y) } assertFails { z.derivative(x, x, y) }
} }
} }

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()
} }

View File

@ -172,7 +172,7 @@ public object QuaternionField : Field<Quaternion>, Norm<Quaternion, Quaternion>,
else -> super<Field>.bindSymbol(value) else -> super<Field>.bindSymbol(value)
} }
override fun number(value: Number): Quaternion =value.toQuaternion() override fun number(value: Number): Quaternion = value.toQuaternion()
public override fun sinh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / 2.0 public override fun sinh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / 2.0
public override fun cosh(arg: Quaternion): Quaternion = (exp(arg) + exp(-arg)) / 2.0 public override fun cosh(arg: Quaternion): Quaternion = (exp(arg) + exp(-arg)) / 2.0

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

@ -28,7 +28,7 @@ public fun <T, R : Expression<T>> DifferentiableExpression<T, R>.derivative(name
/** /**
* A [DifferentiableExpression] that defines only first derivatives * A [DifferentiableExpression] that defines only first derivatives
*/ */
public abstract class FirstDerivativeExpression<T, R : Expression<T>> : DifferentiableExpression<T,R> { public abstract class FirstDerivativeExpression<T, R : Expression<T>> : DifferentiableExpression<T, R> {
/** /**
* Returns first derivative of this expression by given [symbol]. * Returns first derivative of this expression by given [symbol].
*/ */

View File

@ -98,7 +98,7 @@ public open class FunctionalExpressionRing<T, A : Ring<T>>(
super<FunctionalExpressionGroup>.binaryOperationFunction(operation) super<FunctionalExpressionGroup>.binaryOperationFunction(operation)
} }
public open class FunctionalExpressionField<T, A: Field<T>>( public open class FunctionalExpressionField<T, A : Field<T>>(
algebra: A, algebra: A,
) : FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>>, ) : FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>>,
ScaleOperations<Expression<T>> { ScaleOperations<Expression<T>> {

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,29 +8,10 @@ package space.kscience.kmath.linear
public class VirtualMatrix<T : Any>( 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 Matrix<*>) return false
if (rowNum != other.rowNum) return false
if (colNum != other.colNum) return false
return elements().all { (index, value) -> value == other[index] }
}
override fun hashCode(): Int {
var result = rowNum
result = 31 * result + colNum
result = 31 * result + generator.hashCode()
return result
}
} }

View File

@ -1,7 +1,9 @@
package space.kscience.kmath.nd 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 }
} }
// space factories // group factories
public fun <T, A : Group<T>> NDAlgebra.Companion.space( 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.ndSpace( 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.space(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

@ -6,7 +6,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 {
@ -20,7 +20,7 @@ public interface Structure1D<T> : NDStructure<T>, Buffer<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,14 +43,25 @@ private inline class Buffer1DWrapper<T>(val buffer: Buffer<T>) : Structure1D<T>
} }
/** /**
* Represent a [NDStructure] as [Structure1D]. Throw error in case of dimension mismatch * Represent a [StructureND] as [Structure1D]. Throw error in case of dimension mismatch
*/ */
public fun <T> NDStructure<T>.as1D(): Structure1D<T> = if (shape.size == 1) { public fun <T> StructureND<T>.as1D(): Structure1D<T> = this as? Structure1D<T> ?: if (shape.size == 1) {
if (this is NDBuffer) Buffer1DWrapper(this.buffer) else Structure1DWrapper(this) when (this) {
} else is BufferND -> Buffer1DWrapper(this.buffer)
error("Can't create 1d-structure from ${shape.size}d-structure") else -> Structure1DWrapper(this)
}
} else error("Can't create 1d-structure from ${shape.size}d-structure")
/** /**
* Represent this buffer as 1D structure * Represent this buffer as 1D structure
*/ */
public fun <T> Buffer<T>.asND(): Structure1D<T> = Buffer1DWrapper(this) public fun <T> Buffer<T>.asND(): Structure1D<T> = Buffer1DWrapper(this)
/**
* Expose inner buffer of this [Structure1D] if possible
*/
internal fun <T : Any> Structure1D<T>.unwrap(): Buffer<T> = when {
this is Buffer1DWrapper<T> -> buffer
this is Structure1DWrapper && structure is BufferND<T> -> structure.buffer
else -> this
}

View File

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

View File

@ -3,8 +3,6 @@ package space.kscience.kmath.nd
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.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,42 +43,57 @@ 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 <T : Any> getFeature(type: KClass<T>): T? = 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.
* *
* Strides should be reused if possible. * Strides should be reused if possible.
*/ */
public fun <T> build( public fun <T> buffered(
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> build( 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> = build(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) }
/** /**
@ -251,97 +266,10 @@ public class DefaultStrides private constructor(override val shape: IntArray) :
} }
} }
/** public inline fun <reified T : Any> StructureND<T>.combine(
* Represents [NDStructure] over [Buffer]. struct: StructureND<T>,
*
* @param T the type of items.
* @param strides The strides to access elements of [Buffer] by linear indices.
* @param buffer The underlying buffer.
*/
public open class NDBuffer<T>(
public val strides: Strides,
buffer: Buffer<T>,
) : NDStructure<T> {
init {
if (strides.linearSize != buffer.size) {
error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}")
}
}
public open val buffer: Buffer<T> = buffer
override operator fun get(index: IntArray): T = buffer[strides.offset(index)]
override val shape: IntArray get() = strides.shape
override fun elements(): Sequence<Pair<IntArray, T>> = strides.indices().map {
it to this[it]
}
override fun equals(other: Any?): Boolean {
return NDStructure.contentEquals(this, other as? NDStructure<*> ?: return false)
}
override fun hashCode(): Int {
var result = strides.hashCode()
result = 31 * result + buffer.hashCode()
return result
}
override fun toString(): String {
val bufferRepr: String = when (shape.size) {
1 -> buffer.asSequence().joinToString(prefix = "[", postfix = "]", separator = ", ")
2 -> (0 until shape[0]).joinToString(prefix = "[", postfix = "]", separator = ", ") { i ->
(0 until shape[1]).joinToString(prefix = "[", postfix = "]", separator = ", ") { j ->
val offset = strides.offset(intArrayOf(i, j))
buffer[offset].toString()
}
}
else -> "..."
}
return "NDBuffer(shape=${shape.contentToString()}, buffer=$bufferRepr)"
}
}
/**
* Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [NDBuffer]
*/
public inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer(
factory: BufferFactory<R> = Buffer.Companion::auto,
crossinline transform: (T) -> R,
): NDBuffer<R> {
return if (this is NDBuffer<T>)
NDBuffer(this.strides, factory.invoke(strides.linearSize) { transform(buffer[it]) })
else {
val strides = DefaultStrides(shape)
NDBuffer(strides, factory.invoke(strides.linearSize) { transform(get(strides.index(it))) })
}
}
/**
* Mutable ND buffer based on linear [MutableBuffer].
*/
public class MutableNDBuffer<T>(
strides: Strides,
buffer: MutableBuffer<T>,
) : NDBuffer<T>(strides, buffer), MutableNDStructure<T> {
init {
require(strides.linearSize == buffer.size) {
"Expected buffer side of ${strides.linearSize}, but found ${buffer.size}"
}
}
override val buffer: MutableBuffer<T> = super.buffer as MutableBuffer<T>
override operator fun set(index: IntArray, value: T): Unit = buffer.set(strides.offset(index), value)
}
public inline fun <reified T : Any> NDStructure<T>.combine(
struct: NDStructure<T>,
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

@ -41,7 +41,7 @@ public interface AlgebraElement<T, C : Algebra<T>> {
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public operator fun <T : AlgebraElement<T, S>, S : NumbersAddOperations<T>> T.minus(b: T): T = public operator fun <T : AlgebraElement<T, S>, S : NumbersAddOperations<T>> T.minus(b: T): T =
context.add(this, context.run { -b}) context.add(this, context.run { -b })
/** /**
* Adds element to this one. * Adds element to this one.

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

@ -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,21 +88,6 @@ public interface Buffer<out T> {
} }
} }
/**
* Creates a sequence that returns all elements from this [Buffer].
*/
public fun <T> Buffer<T>.asSequence(): Sequence<T> = Sequence(::iterator)
/**
* Creates an iterable that returns all elements from this [Buffer].
*/
public fun <T> Buffer<T>.asIterable(): Iterable<T> = Iterable(::iterator)
/**
* Converts this [Buffer] to a new [List]
*/
public fun <T> Buffer<T>.toList(): List<T> = asSequence().toList()
/** /**
* Returns an [IntRange] of the valid indices for this [Buffer]. * Returns an [IntRange] of the valid indices for this [Buffer].
*/ */
@ -126,6 +110,44 @@ public interface MutableBuffer<T> : Buffer<T> {
public fun copy(): MutableBuffer<T> public 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
*/ */
@ -134,37 +156,30 @@ public interface MutableBuffer<T> : Buffer<T> {
/** /**
* Creates a [MutableBuffer] of given [type]. If the type is primitive, specialized buffers are used * 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) }
} }
} }
@ -187,15 +202,6 @@ public inline class ListBuffer<T>(public val list: List<T>) : Buffer<T> {
*/ */
public fun <T> List<T>.asBuffer(): ListBuffer<T> = ListBuffer(this) 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].
* *
@ -216,16 +222,20 @@ public inline class MutableListBuffer<T>(public val list: MutableList<T>) : Muta
override fun copy(): MutableBuffer<T> = MutableListBuffer(ArrayList(list)) override fun copy(): MutableBuffer<T> = MutableListBuffer(ArrayList(list))
} }
/**
* Returns an [ListBuffer] that wraps the original list.
*/
public fun <T> MutableList<T>.asMutableBuffer(): MutableListBuffer<T> = MutableListBuffer(this)
/** /**
* [MutableBuffer] implementation over [Array]. * [MutableBuffer] implementation over [Array].
* *
* @param T the type of elements contained in the buffer. * @param T the type of elements contained in the buffer.
* @property array The underlying array. * @property array The underlying array.
*/ */
public class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> { public class ArrayBuffer<T>(internal val array: Array<T>) : MutableBuffer<T> {
// Can't inline because array is invariant // 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]
@ -243,16 +253,6 @@ public class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> {
*/ */
public fun <T> Array<T>.asBuffer(): ArrayBuffer<T> = ArrayBuffer(this) 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].
* *
@ -280,27 +280,9 @@ public class VirtualBuffer<T>(override val size: Int, private val generator: (In
} }
override operator fun iterator(): Iterator<T> = (0 until size).asSequence().map(generator).iterator() override 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
/** /**
@ -13,10 +13,10 @@ internal class BufferAccessor2D<T : Any>(
public val colNum: Int, public val colNum: Int,
val factory: MutableBufferFactory<T>, val factory: MutableBufferFactory<T>,
) { ) {
public operator fun Buffer<T>.get(i: Int, j: Int): T = get(i + colNum * j) public operator fun Buffer<T>.get(i: Int, j: Int): T = get(i * colNum + j)
public operator fun MutableBuffer<T>.set(i: Int, j: Int, value: T) { public operator fun MutableBuffer<T>.set(i: Int, j: Int, value: T) {
set(i + colNum * j, value) set(i * colNum + j, value)
} }
public inline fun create(crossinline init: (i: Int, j: Int) -> T): MutableBuffer<T> = public inline fun create(crossinline init: (i: Int, j: Int) -> T): MutableBuffer<T> =
@ -25,7 +25,7 @@ internal class BufferAccessor2D<T : Any>(
public fun create(mat: Structure2D<T>): MutableBuffer<T> = create { i, j -> mat[i, j] } 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.build( 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

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

View File

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

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