Buffer factories removed from global scope
This commit is contained in:
parent
12b343e905
commit
696a916ade
@ -25,7 +25,7 @@ fun main(args: Array<String>) {
|
|||||||
bufferedField.run {
|
bufferedField.run {
|
||||||
var res: NDBuffer<Double> = one
|
var res: NDBuffer<Double> = one
|
||||||
repeat(n) {
|
repeat(n) {
|
||||||
res += 1.0
|
res += one
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,7 +34,7 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
|
|
||||||
val elementTime = measureTimeMillis {
|
val elementTime = measureTimeMillis {
|
||||||
var res = bufferedField.produce { one }
|
var res = bufferedField.run{one.toElement()}
|
||||||
repeat(n) {
|
repeat(n) {
|
||||||
res += 1.0
|
res += 1.0
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ fun main(args: Array<String>) {
|
|||||||
genericField.run {
|
genericField.run {
|
||||||
var res: NDBuffer<Double> = one
|
var res: NDBuffer<Double> = one
|
||||||
repeat(n) {
|
repeat(n) {
|
||||||
res += 1.0
|
res += one
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.kmath.structures.Buffer.Companion.DoubleBufferFactory
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
|
||||||
|
70
doc/algebra.md
Normal file
70
doc/algebra.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Algebra and algebra elements
|
||||||
|
|
||||||
|
The mathematical operations in `kmath` are generally separated from mathematical objects.
|
||||||
|
This means that in order to perform an operation, say `+`, one needs two objects of a type `T` and
|
||||||
|
and algebra context which defines appropriate operation, say `Space<T>`. Next one needs to run actual operation
|
||||||
|
in the context:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val a: T
|
||||||
|
val b: T
|
||||||
|
val space: Space<T>
|
||||||
|
|
||||||
|
val c = space.run{a + b}
|
||||||
|
```
|
||||||
|
|
||||||
|
From the first glance, this distinction seems to be a needless complication, but in fact one needs
|
||||||
|
to remember that in mathematics, one could define different operations on the same objects. For example,
|
||||||
|
one could use different types of geometry for vectors.
|
||||||
|
|
||||||
|
## Algebra hierarchy
|
||||||
|
|
||||||
|
Mathematical contexts have the following hierarchy:
|
||||||
|
|
||||||
|
**Space** <- **Ring** <- **Field**
|
||||||
|
|
||||||
|
All classes follow abstract mathematical constructs.
|
||||||
|
[Space](http://mathworld.wolfram.com/Space.html) defines `zero` element, addition operation and multiplication by constant,
|
||||||
|
[Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and unit `one` element,
|
||||||
|
[Field](http://mathworld.wolfram.com/Field.html) adds division operation.
|
||||||
|
|
||||||
|
Typical case of `Field` is the `RealField` which works on doubles. And typical case of `Space` is a `VectorSpace`.
|
||||||
|
|
||||||
|
In some cases algebra context could hold additional operation like `exp` or `sin`, in this case it inherits appropriate
|
||||||
|
interface. Also a context could have an operation which produces an element outside of its context. For example
|
||||||
|
`Matrix` `dot` operation produces a matrix with new dimensions which could not be compatible with initial matrix in
|
||||||
|
terms of linear operations.
|
||||||
|
|
||||||
|
## Algebra element
|
||||||
|
|
||||||
|
In order to achieve more familiar behavior (where you apply operations directly to mathematica objects), without involving contexts
|
||||||
|
`kmath` introduces special type objects called `MathElement`. A `MathElement` is basically some object coupled to
|
||||||
|
a mathematical context. For example `Complex` is the pair of real numbers representing real and imaginary parts,
|
||||||
|
but it also holds reference to the `ComplexField` singleton which allows to perform direct operations on `Complex`
|
||||||
|
numbers without explicit involving the context like:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val c1 = Complex(1.0, 1.0)
|
||||||
|
val c2 = Complex(1.0, -1.0)
|
||||||
|
val c3 = c1 + c2 + 3.0.toComplex()
|
||||||
|
//or with field notation:
|
||||||
|
val c4 = ComplexField.run{c1 + i - 2.0}
|
||||||
|
```
|
||||||
|
|
||||||
|
Both notations have their pros and cons.
|
||||||
|
|
||||||
|
The hierarchy for algebra elements follows the hierarchy for the corresponding algebra.
|
||||||
|
|
||||||
|
**MathElement** <- **SpaceElement** <- **RingElement** <- **FieldElement**
|
||||||
|
|
||||||
|
**MathElement** is the generic common ancestor of the class with context.
|
||||||
|
|
||||||
|
One important distinction between algebra elements and algebra contexts is that algebra element has three type parameters:
|
||||||
|
|
||||||
|
1. The type of elements, field operates on.
|
||||||
|
2. The self-type of the element returned from operation (must be algebra element).
|
||||||
|
3. The type of the algebra over first type-parameter.
|
||||||
|
|
||||||
|
The middle type is needed in case algebra members do not store context. For example, it is not possible to add
|
||||||
|
a context to regular `Double`. The element performs automatic conversions from context types and back.
|
||||||
|
One should used context operations in all important places. The performance of element operations is not guaranteed.
|
13
doc/nd-performance.md
Normal file
13
doc/nd-performance.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Performance for n-dimensional structures operations
|
||||||
|
|
||||||
|
One of the most sought after features of mathematical libraries is the high-performance operations on n-dimensional
|
||||||
|
structures. In `kmath` performance depends on which particular context was used for operation.
|
||||||
|
|
||||||
|
Let us consider following contexts:
|
||||||
|
```kotlin
|
||||||
|
// automatically build context
|
||||||
|
val bufferedField = NDField.auto(intArrayOf(dim, dim), RealField)
|
||||||
|
val specializedField = NDField.real(intArrayOf(dim, dim))
|
||||||
|
val genericField = NDField.buffered(intArrayOf(dim, dim), RealField)
|
||||||
|
val lazyNDField = NDField.lazy(intArrayOf(dim, dim), RealField)
|
||||||
|
```
|
@ -20,7 +20,7 @@ val c3 = ComplexField.run{ c1 - i*2.0}
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Note**: In theory it is possible to add behaviors directly to the context, but currently kotlin syntax does not support
|
**Note**: In theory it is possible to add behaviors directly to the context, but currently kotlin syntax does not support
|
||||||
that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) for updates.
|
that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and [KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates.
|
||||||
|
|
||||||
## Nested fields
|
## Nested fields
|
||||||
|
|
||||||
|
@ -2,7 +2,11 @@ package scientifik.kmath.linear
|
|||||||
|
|
||||||
import scientifik.kmath.operations.Field
|
import scientifik.kmath.operations.Field
|
||||||
import scientifik.kmath.operations.RealField
|
import scientifik.kmath.operations.RealField
|
||||||
import scientifik.kmath.structures.*
|
import scientifik.kmath.structures.MutableBuffer.Companion.boxing
|
||||||
|
import scientifik.kmath.structures.MutableNDStructure
|
||||||
|
import scientifik.kmath.structures.NDStructure
|
||||||
|
import scientifik.kmath.structures.get
|
||||||
|
import scientifik.kmath.structures.mutableNdStructure
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,7 +110,7 @@ abstract class LUDecomposition<T : Comparable<T>, F : Field<T>>(val matrix: Matr
|
|||||||
//TODO fix performance
|
//TODO fix performance
|
||||||
val lu: MutableNDStructure<T> = mutableNdStructure(
|
val lu: MutableNDStructure<T> = mutableNdStructure(
|
||||||
intArrayOf(matrix.numRows, matrix.numCols),
|
intArrayOf(matrix.numRows, matrix.numCols),
|
||||||
::boxingMutableBuffer
|
::boxing
|
||||||
) { index: IntArray -> matrix[index[0], index[1]] }
|
) { index: IntArray -> matrix[index[0], index[1]] }
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,8 +4,9 @@ import scientifik.kmath.operations.Field
|
|||||||
import scientifik.kmath.operations.Norm
|
import scientifik.kmath.operations.Norm
|
||||||
import scientifik.kmath.operations.RealField
|
import scientifik.kmath.operations.RealField
|
||||||
import scientifik.kmath.operations.Ring
|
import scientifik.kmath.operations.Ring
|
||||||
|
import scientifik.kmath.structures.Buffer.Companion.boxing
|
||||||
import scientifik.kmath.structures.asSequence
|
import scientifik.kmath.structures.asSequence
|
||||||
import scientifik.kmath.structures.boxingBuffer
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +53,7 @@ fun <T : Any, R : Ring<T>> Vector<T, R>.toMatrix(): Matrix<T, R> {
|
|||||||
// matrix(size, 1, context.field) { i, j -> get(i) }
|
// matrix(size, 1, context.field) { i, j -> get(i) }
|
||||||
// }
|
// }
|
||||||
//return Matrix.of(size, 1, context.space) { i, _ -> get(i) }
|
//return Matrix.of(size, 1, context.space) { i, _ -> get(i) }
|
||||||
return StructureMatrixSpace(size, 1, context.space, ::boxingBuffer).produce { i, _ -> get(i) }
|
return StructureMatrixSpace(size, 1, context.space, ::boxing).produce { i, _ -> get(i) }
|
||||||
}
|
}
|
||||||
|
|
||||||
object VectorL2Norm : Norm<Vector<out Number, *>, Double> {
|
object VectorL2Norm : Norm<Vector<out Number, *>, Double> {
|
||||||
|
@ -5,6 +5,9 @@ import scientifik.kmath.operations.Ring
|
|||||||
import scientifik.kmath.operations.Space
|
import scientifik.kmath.operations.Space
|
||||||
import scientifik.kmath.operations.SpaceElement
|
import scientifik.kmath.operations.SpaceElement
|
||||||
import scientifik.kmath.structures.*
|
import scientifik.kmath.structures.*
|
||||||
|
import scientifik.kmath.structures.Buffer.Companion.DoubleBufferFactory
|
||||||
|
import scientifik.kmath.structures.Buffer.Companion.auto
|
||||||
|
import scientifik.kmath.structures.Buffer.Companion.boxing
|
||||||
|
|
||||||
|
|
||||||
interface MatrixSpace<T : Any, R : Ring<T>> : Space<Matrix<T, R>> {
|
interface MatrixSpace<T : Any, R : Ring<T>> : Space<Matrix<T, R>> {
|
||||||
@ -52,14 +55,14 @@ interface MatrixSpace<T : Any, R : Ring<T>> : Space<Matrix<T, R>> {
|
|||||||
rows: Int,
|
rows: Int,
|
||||||
columns: Int,
|
columns: Int,
|
||||||
ring: R,
|
ring: R,
|
||||||
bufferFactory: BufferFactory<T> = ::boxingBuffer
|
bufferFactory: BufferFactory<T> = ::boxing
|
||||||
): MatrixSpace<T, R> = StructureMatrixSpace(rows, columns, ring, bufferFactory)
|
): MatrixSpace<T, R> = StructureMatrixSpace(rows, columns, ring, bufferFactory)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatic buffered matrix, unboxed if it is possible
|
* Automatic buffered matrix, unboxed if it is possible
|
||||||
*/
|
*/
|
||||||
inline fun <reified T : Any, R : Ring<T>> smart(rows: Int, columns: Int, ring: R): MatrixSpace<T, R> =
|
inline fun <reified T : Any, R : Ring<T>> smart(rows: Int, columns: Int, ring: R): MatrixSpace<T, R> =
|
||||||
buffered(rows, columns, ring, ::autoBuffer)
|
buffered(rows, columns, ring, ::auto)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,9 @@ package scientifik.kmath.linear
|
|||||||
import scientifik.kmath.operations.RealField
|
import scientifik.kmath.operations.RealField
|
||||||
import scientifik.kmath.operations.Space
|
import scientifik.kmath.operations.Space
|
||||||
import scientifik.kmath.operations.SpaceElement
|
import scientifik.kmath.operations.SpaceElement
|
||||||
import scientifik.kmath.structures.*
|
import scientifik.kmath.structures.Buffer
|
||||||
|
import scientifik.kmath.structures.BufferFactory
|
||||||
|
import scientifik.kmath.structures.asSequence
|
||||||
|
|
||||||
typealias Point<T> = Buffer<T>
|
typealias Point<T> = Buffer<T>
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> {
|
|||||||
* Non-boxing double vector space
|
* Non-boxing double vector space
|
||||||
*/
|
*/
|
||||||
fun real(size: Int): BufferVectorSpace<Double, RealField> {
|
fun real(size: Int): BufferVectorSpace<Double, RealField> {
|
||||||
return realSpaceCache.getOrPut(size) { BufferVectorSpace(size, RealField, DoubleBufferFactory) }
|
return realSpaceCache.getOrPut(size) { BufferVectorSpace(size, RealField, Buffer.DoubleBufferFactory) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,14 +51,14 @@ interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> {
|
|||||||
fun <T : Any, S : Space<T>> buffered(
|
fun <T : Any, S : Space<T>> buffered(
|
||||||
size: Int,
|
size: Int,
|
||||||
space: S,
|
space: S,
|
||||||
bufferFactory: BufferFactory<T> = ::boxingBuffer
|
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing
|
||||||
): VectorSpace<T, S> = BufferVectorSpace(size, space, bufferFactory)
|
): VectorSpace<T, S> = BufferVectorSpace(size, space, bufferFactory)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatic buffered vector, unboxed if it is possible
|
* Automatic buffered vector, unboxed if it is possible
|
||||||
*/
|
*/
|
||||||
inline fun <reified T : Any, S : Space<T>> smart(size: Int, space: S): VectorSpace<T, S> =
|
inline fun <reified T : Any, S : Space<T>> smart(size: Int, space: S): VectorSpace<T, S> =
|
||||||
buffered(size, space, ::autoBuffer)
|
buffered(size, space, Buffer.Companion::auto)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,14 +68,14 @@ interface Ring<T> : Space<T> {
|
|||||||
|
|
||||||
operator fun T.times(b: T): T = multiply(this, b)
|
operator fun T.times(b: T): T = multiply(this, b)
|
||||||
|
|
||||||
// operator fun T.plus(b: Number) = this.plus(b * one)
|
operator fun T.plus(b: Number) = this.plus(b * one)
|
||||||
// operator fun Number.plus(b: T) = b + this
|
operator fun Number.plus(b: T) = b + this
|
||||||
//
|
|
||||||
// operator fun T.minus(b: Number) = this.minus(b * one)
|
operator fun T.minus(b: Number) = this.minus(b * one)
|
||||||
// operator fun Number.minus(b: T) = -b + this
|
operator fun Number.minus(b: T) = -b + this
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractRing<T: Any> : AbstractSpace<T>(), Ring<T> {
|
abstract class AbstractRing<T : Any> : AbstractSpace<T>(), Ring<T> {
|
||||||
final override operator fun T.times(b: T): T = multiply(this, b)
|
final override operator fun T.times(b: T): T = multiply(this, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ interface Field<T> : Ring<T> {
|
|||||||
operator fun Number.div(b: T) = this * divide(one, b)
|
operator fun Number.div(b: T) = this * divide(one, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractField<T: Any> : AbstractRing<T>(), Field<T> {
|
abstract class AbstractField<T : Any> : AbstractRing<T>(), Field<T> {
|
||||||
final override operator fun T.div(b: T): T = divide(this, b)
|
final override operator fun T.div(b: T): T = divide(this, b)
|
||||||
final override operator fun Number.div(b: T) = this * divide(one, b)
|
final override operator fun Number.div(b: T) = this * divide(one, b)
|
||||||
}
|
}
|
@ -22,6 +22,9 @@ abstract class StridedNDField<T, F : Field<T>>(shape: IntArray, elementField: F)
|
|||||||
produce { index -> get(index) }
|
produce { index -> get(index) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun NDBuffer<T>.toElement(): StridedNDElement<T, F> =
|
||||||
|
StridedNDElement(this@StridedNDField, buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -40,14 +43,14 @@ class BufferNDField<T, F : Field<T>>(
|
|||||||
override val zero by lazy { produce { zero } }
|
override val zero by lazy { produce { zero } }
|
||||||
override val one by lazy { produce { one } }
|
override val one by lazy { produce { one } }
|
||||||
|
|
||||||
override fun produce(initializer: F.(IntArray) -> T): BufferNDElement<T, F> =
|
override fun produce(initializer: F.(IntArray) -> T): StridedNDElement<T, F> =
|
||||||
BufferNDElement(
|
StridedNDElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(strides.linearSize) { offset -> elementField.initializer(strides.index(offset)) })
|
buildBuffer(strides.linearSize) { offset -> elementField.initializer(strides.index(offset)) })
|
||||||
|
|
||||||
override fun map(arg: NDBuffer<T>, transform: F.(T) -> T): BufferNDElement<T, F> {
|
override fun map(arg: NDBuffer<T>, transform: F.(T) -> T): StridedNDElement<T, F> {
|
||||||
check(arg)
|
check(arg)
|
||||||
return BufferNDElement(
|
return StridedNDElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(arg.strides.linearSize) { offset -> elementField.transform(arg.buffer[offset]) })
|
buildBuffer(arg.strides.linearSize) { offset -> elementField.transform(arg.buffer[offset]) })
|
||||||
}
|
}
|
||||||
@ -55,9 +58,9 @@ class BufferNDField<T, F : Field<T>>(
|
|||||||
override fun mapIndexed(
|
override fun mapIndexed(
|
||||||
arg: NDBuffer<T>,
|
arg: NDBuffer<T>,
|
||||||
transform: F.(index: IntArray, T) -> T
|
transform: F.(index: IntArray, T) -> T
|
||||||
): BufferNDElement<T, F> {
|
): StridedNDElement<T, F> {
|
||||||
check(arg)
|
check(arg)
|
||||||
return BufferNDElement(
|
return StridedNDElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(arg.strides.linearSize) { offset ->
|
buildBuffer(arg.strides.linearSize) { offset ->
|
||||||
elementField.transform(
|
elementField.transform(
|
||||||
@ -71,17 +74,17 @@ class BufferNDField<T, F : Field<T>>(
|
|||||||
a: NDBuffer<T>,
|
a: NDBuffer<T>,
|
||||||
b: NDBuffer<T>,
|
b: NDBuffer<T>,
|
||||||
transform: F.(T, T) -> T
|
transform: F.(T, T) -> T
|
||||||
): BufferNDElement<T, F> {
|
): StridedNDElement<T, F> {
|
||||||
check(a, b)
|
check(a, b)
|
||||||
return BufferNDElement(
|
return StridedNDElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(strides.linearSize) { offset -> elementField.transform(a.buffer[offset], b.buffer[offset]) })
|
buildBuffer(strides.linearSize) { offset -> elementField.transform(a.buffer[offset], b.buffer[offset]) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BufferNDElement<T, F : Field<T>>(override val context: StridedNDField<T, F>, override val buffer: Buffer<T>) :
|
class StridedNDElement<T, F : Field<T>>(override val context: StridedNDField<T, F>, override val buffer: Buffer<T>) :
|
||||||
NDBuffer<T>,
|
NDBuffer<T>,
|
||||||
FieldElement<NDBuffer<T>, BufferNDElement<T, F>, StridedNDField<T, F>>,
|
FieldElement<NDBuffer<T>, StridedNDElement<T, F>, StridedNDField<T, F>>,
|
||||||
NDElement<T, F> {
|
NDElement<T, F> {
|
||||||
|
|
||||||
override val elementField: F
|
override val elementField: F
|
||||||
@ -90,8 +93,8 @@ class BufferNDElement<T, F : Field<T>>(override val context: StridedNDField<T, F
|
|||||||
override fun unwrap(): NDBuffer<T> =
|
override fun unwrap(): NDBuffer<T> =
|
||||||
this
|
this
|
||||||
|
|
||||||
override fun NDBuffer<T>.wrap(): BufferNDElement<T, F> =
|
override fun NDBuffer<T>.wrap(): StridedNDElement<T, F> =
|
||||||
BufferNDElement(context, this.buffer)
|
StridedNDElement(context, this.buffer)
|
||||||
|
|
||||||
override val strides
|
override val strides
|
||||||
get() = context.strides
|
get() = context.strides
|
||||||
@ -106,42 +109,42 @@ class BufferNDElement<T, F : Field<T>>(override val context: StridedNDField<T, F
|
|||||||
strides.indices().map { it to get(it) }
|
strides.indices().map { it to get(it) }
|
||||||
|
|
||||||
override fun map(action: F.(T) -> T) =
|
override fun map(action: F.(T) -> T) =
|
||||||
context.run { map(this@BufferNDElement, action) }.wrap()
|
context.run { map(this@StridedNDElement, action) }.wrap()
|
||||||
|
|
||||||
override fun mapIndexed(transform: F.(index: IntArray, T) -> T) =
|
override fun mapIndexed(transform: F.(index: IntArray, T) -> T) =
|
||||||
context.run { mapIndexed(this@BufferNDElement, transform) }.wrap()
|
context.run { mapIndexed(this@StridedNDElement, transform) }.wrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Element by element application of any operation on elements to the whole array. Just like in numpy
|
* Element by element application of any operation on elements to the whole array. Just like in numpy
|
||||||
*/
|
*/
|
||||||
operator fun <T : Any, F : Field<T>> Function1<T, T>.invoke(ndElement: BufferNDElement<T, F>) =
|
operator fun <T : Any, F : Field<T>> Function1<T, T>.invoke(ndElement: StridedNDElement<T, F>) =
|
||||||
ndElement.context.run { ndElement.map { invoke(it) } }
|
ndElement.context.run { ndElement.map { invoke(it) } }
|
||||||
|
|
||||||
/* plus and minus */
|
/* plus and minus */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summation operation for [BufferNDElement] and single element
|
* Summation operation for [StridedNDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T : Any, F : Field<T>> BufferNDElement<T, F>.plus(arg: T) =
|
operator fun <T : Any, F : Field<T>> StridedNDElement<T, F>.plus(arg: T) =
|
||||||
context.run { map { it + arg } }
|
context.run { map { it + arg } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtraction operation between [BufferNDElement] and single element
|
* Subtraction operation between [StridedNDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T : Any, F : Field<T>> BufferNDElement<T, F>.minus(arg: T) =
|
operator fun <T : Any, F : Field<T>> StridedNDElement<T, F>.minus(arg: T) =
|
||||||
context.run { map { it - arg } }
|
context.run { map { it - arg } }
|
||||||
|
|
||||||
/* prod and div */
|
/* prod and div */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Product operation for [BufferNDElement] and single element
|
* Product operation for [StridedNDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T : Any, F : Field<T>> BufferNDElement<T, F>.times(arg: T) =
|
operator fun <T : Any, F : Field<T>> StridedNDElement<T, F>.times(arg: T) =
|
||||||
context.run { map { it * arg } }
|
context.run { map { it * arg } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Division operation between [BufferNDElement] and single element
|
* Division operation between [StridedNDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T : Any, F : Field<T>> BufferNDElement<T, F>.div(arg: T) =
|
operator fun <T : Any, F : Field<T>> StridedNDElement<T, F>.div(arg: T) =
|
||||||
context.run { map { it / arg } }
|
context.run { map { it / arg } }
|
@ -1,6 +1,10 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
|
||||||
|
typealias BufferFactory<T> = (Int, (Int) -> T) -> Buffer<T>
|
||||||
|
typealias MutableBufferFactory<T> = (Int, (Int) -> T) -> MutableBuffer<T>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A generic random access structure for both primitives and objects
|
* A generic random access structure for both primitives and objects
|
||||||
*/
|
*/
|
||||||
@ -14,6 +18,31 @@ interface Buffer<T> {
|
|||||||
|
|
||||||
fun contentEquals(other: Buffer<*>): Boolean =
|
fun contentEquals(other: Buffer<*>): Boolean =
|
||||||
asSequence().mapIndexed { index, value -> value == other[index] }.all { it }
|
asSequence().mapIndexed { index, value -> value == other[index] }.all { it }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a boxing buffer of given type
|
||||||
|
*/
|
||||||
|
inline fun <T> boxing(size: Int, initializer: (Int) -> T): Buffer<T> = ListBuffer(List(size, initializer))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create most appropriate immutable buffer for given type avoiding boxing wherever possible
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): Buffer<T> {
|
||||||
|
return when (T::class) {
|
||||||
|
Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer<T>
|
||||||
|
Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as Buffer<T>
|
||||||
|
Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as Buffer<T>
|
||||||
|
else -> boxing(size, initializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val DoubleBufferFactory: BufferFactory<Double> = { size, initializer -> DoubleBuffer(DoubleArray(size, initializer)) }
|
||||||
|
val IntBufferFactory: BufferFactory<Int> = { size, initializer -> IntBuffer(IntArray(size, initializer)) }
|
||||||
|
val LongBufferFactory: BufferFactory<Long> = { size, initializer -> LongBuffer(LongArray(size, initializer)) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Buffer<T>.asSequence(): Sequence<T> = iterator().asSequence()
|
fun <T> Buffer<T>.asSequence(): Sequence<T> = iterator().asSequence()
|
||||||
@ -27,6 +56,27 @@ interface MutableBuffer<T> : Buffer<T> {
|
|||||||
* A shallow copy of the buffer
|
* A shallow copy of the buffer
|
||||||
*/
|
*/
|
||||||
fun copy(): MutableBuffer<T>
|
fun copy(): MutableBuffer<T>
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a boxing mutable buffer of given type
|
||||||
|
*/
|
||||||
|
inline fun <T : Any> boxing(size: Int, initializer: (Int) -> T): MutableBuffer<T> =
|
||||||
|
MutableListBuffer(MutableList(size, initializer))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create most appropriate mutable buffer for given type avoiding boxing wherever possible
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> {
|
||||||
|
return when (T::class) {
|
||||||
|
Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as MutableBuffer<T>
|
||||||
|
Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as MutableBuffer<T>
|
||||||
|
Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as MutableBuffer<T>
|
||||||
|
else -> boxing(size, initializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -130,48 +180,3 @@ fun <T> Buffer<T>.asReadOnly(): Buffer<T> = if (this is MutableBuffer) {
|
|||||||
} else {
|
} else {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a boxing buffer of given type
|
|
||||||
*/
|
|
||||||
inline fun <T> boxingBuffer(size: Int, initializer: (Int) -> T): Buffer<T> = ListBuffer(List(size, initializer))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create most appropriate immutable buffer for given type avoiding boxing wherever possible
|
|
||||||
*/
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
inline fun <reified T : Any> autoBuffer(size: Int, initializer: (Int) -> T): Buffer<T> {
|
|
||||||
return when (T::class) {
|
|
||||||
Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer<T>
|
|
||||||
Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as Buffer<T>
|
|
||||||
Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as Buffer<T>
|
|
||||||
else -> boxingBuffer(size, initializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a boxing mutable buffer of given type
|
|
||||||
*/
|
|
||||||
inline fun <T : Any> boxingMutableBuffer(size: Int, initializer: (Int) -> T): MutableBuffer<T> =
|
|
||||||
MutableListBuffer(MutableList(size, initializer))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create most appropriate mutable buffer for given type avoiding boxing wherever possible
|
|
||||||
*/
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
inline fun <reified T : Any> autoMutableBuffer(size: Int, initializer: (Int) -> T): MutableBuffer<T> {
|
|
||||||
return when (T::class) {
|
|
||||||
Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as MutableBuffer<T>
|
|
||||||
Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as MutableBuffer<T>
|
|
||||||
Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as MutableBuffer<T>
|
|
||||||
else -> boxingMutableBuffer(size, initializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typealias BufferFactory<T> = (Int, (Int) -> T) -> Buffer<T>
|
|
||||||
typealias MutableBufferFactory<T> = (Int, (Int) -> T) -> MutableBuffer<T>
|
|
||||||
|
|
||||||
val DoubleBufferFactory: BufferFactory<Double> = { size, initializer -> DoubleBuffer(DoubleArray(size, initializer)) }
|
|
||||||
val IntBufferFactory: BufferFactory<Int> = { size, initializer -> IntBuffer(IntArray(size, initializer)) }
|
|
||||||
val LongBufferFactory: BufferFactory<Long> = { size, initializer -> LongBuffer(LongArray(size, initializer)) }
|
|
||||||
|
|
||||||
|
@ -10,47 +10,46 @@ interface NDElement<T, F : Field<T>> : NDStructure<T> {
|
|||||||
|
|
||||||
fun mapIndexed(transform: F.(index: IntArray, T) -> T): NDElement<T, F>
|
fun mapIndexed(transform: F.(index: IntArray, T) -> T): NDElement<T, F>
|
||||||
fun map(action: F.(T) -> T) = mapIndexed { _, value -> action(value) }
|
fun map(action: F.(T) -> T) = mapIndexed { _, value -> action(value) }
|
||||||
}
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a optimized NDArray of doubles
|
||||||
|
*/
|
||||||
|
fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }) =
|
||||||
|
NDField.real(shape).produce(initializer)
|
||||||
|
|
||||||
|
|
||||||
object NDElements {
|
fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }) =
|
||||||
/**
|
real(intArrayOf(dim)) { initializer(it[0]) }
|
||||||
* Create a optimized NDArray of doubles
|
|
||||||
*/
|
|
||||||
fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }) =
|
|
||||||
NDField.real(shape).produce(initializer)
|
|
||||||
|
|
||||||
|
|
||||||
fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }) =
|
fun real2D(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }) =
|
||||||
real(intArrayOf(dim)) { initializer(it[0]) }
|
real(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) }
|
||||||
|
|
||||||
|
fun real3D(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }) =
|
||||||
|
real(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) }
|
||||||
|
|
||||||
|
|
||||||
fun real2D(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }) =
|
/**
|
||||||
real(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) }
|
* Simple boxing NDArray
|
||||||
|
*/
|
||||||
|
fun <T : Any, F : Field<T>> buffered(
|
||||||
|
shape: IntArray,
|
||||||
|
field: F,
|
||||||
|
initializer: F.(IntArray) -> T
|
||||||
|
): StridedNDElement<T, F> {
|
||||||
|
val ndField = BufferNDField(shape, field, Buffer.Companion::boxing)
|
||||||
|
return ndField.produce(initializer)
|
||||||
|
}
|
||||||
|
|
||||||
fun real3D(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }) =
|
inline fun <reified T : Any, F : Field<T>> auto(
|
||||||
real(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) }
|
shape: IntArray,
|
||||||
|
field: F,
|
||||||
|
noinline initializer: F.(IntArray) -> T
|
||||||
/**
|
): StridedNDElement<T, F> {
|
||||||
* Simple boxing NDArray
|
val ndField = NDField.auto(shape, field)
|
||||||
*/
|
return StridedNDElement(ndField, ndField.produce(initializer).buffer)
|
||||||
fun <T : Any, F : Field<T>> buffered(
|
}
|
||||||
shape: IntArray,
|
|
||||||
field: F,
|
|
||||||
initializer: F.(IntArray) -> T
|
|
||||||
): BufferNDElement<T, F> {
|
|
||||||
val ndField = BufferNDField(shape, field, ::boxingBuffer)
|
|
||||||
return ndField.produce(initializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T : Any, F : Field<T>> auto(
|
|
||||||
shape: IntArray,
|
|
||||||
field: F,
|
|
||||||
noinline initializer: F.(IntArray) -> T
|
|
||||||
): BufferNDElement<T, F> {
|
|
||||||
val ndField = NDField.auto(shape, field)
|
|
||||||
return ndField.produce(initializer)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package scientifik.kmath.structures
|
|||||||
|
|
||||||
import scientifik.kmath.operations.AbstractField
|
import scientifik.kmath.operations.AbstractField
|
||||||
import scientifik.kmath.operations.Field
|
import scientifik.kmath.operations.Field
|
||||||
|
import scientifik.kmath.structures.Buffer.Companion.boxing
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@ -36,13 +37,18 @@ interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N> {
|
|||||||
* Create a nd-field with boxing generic buffer
|
* Create a nd-field with boxing generic buffer
|
||||||
*/
|
*/
|
||||||
fun <T : Any, F : Field<T>> buffered(shape: IntArray, field: F) =
|
fun <T : Any, F : Field<T>> buffered(shape: IntArray, field: F) =
|
||||||
BufferNDField(shape, field, ::boxingBuffer)
|
BufferNDField(shape, field, Buffer.Companion::boxing)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a most suitable implementation for nd-field using reified class.
|
* Create a most suitable implementation for nd-field using reified class.
|
||||||
*/
|
*/
|
||||||
inline fun <reified T : Any, F : Field<T>> auto(shape: IntArray, field: F) =
|
inline fun <reified T : Any, F : Field<T>> auto(shape: IntArray, field: F): StridedNDField<T, F> =
|
||||||
BufferNDField(shape, field, ::autoBuffer)
|
if (T::class == Double::class) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
real(shape) as StridedNDField<T, F>
|
||||||
|
} else {
|
||||||
|
BufferNDField(shape, field, Buffer.Companion::auto)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ data class BufferNDStructure<T>(
|
|||||||
* Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferNDStructure]
|
* Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferNDStructure]
|
||||||
*/
|
*/
|
||||||
inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer(
|
inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer(
|
||||||
factory: BufferFactory<R> = ::autoBuffer,
|
factory: BufferFactory<R> = Buffer.Companion::auto,
|
||||||
crossinline transform: (T) -> R
|
crossinline transform: (T) -> R
|
||||||
): BufferNDStructure<R> {
|
): BufferNDStructure<R> {
|
||||||
return if (this is BufferNDStructure<T>) {
|
return if (this is BufferNDStructure<T>) {
|
||||||
@ -179,16 +179,16 @@ inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer(
|
|||||||
*
|
*
|
||||||
* Strides should be reused if possible
|
* Strides should be reused if possible
|
||||||
*/
|
*/
|
||||||
fun <T> ndStructure(strides: Strides, bufferFactory: BufferFactory<T> = ::boxingBuffer, initializer: (IntArray) -> T) =
|
fun <T> ndStructure(strides: Strides, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing, initializer: (IntArray) -> T) =
|
||||||
BufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) })
|
BufferNDStructure(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
|
||||||
*/
|
*/
|
||||||
inline fun <reified T : Any> inlineNDStructure(strides: Strides, crossinline initializer: (IntArray) -> T) =
|
inline fun <reified T : Any> inlineNDStructure(strides: Strides, crossinline initializer: (IntArray) -> T) =
|
||||||
BufferNDStructure(strides, autoBuffer(strides.linearSize) { i -> initializer(strides.index(i)) })
|
BufferNDStructure(strides, Buffer.Companion.auto(strides.linearSize) { i -> initializer(strides.index(i)) })
|
||||||
|
|
||||||
fun <T> ndStructure(shape: IntArray, bufferFactory: BufferFactory<T> = ::boxingBuffer, initializer: (IntArray) -> T) =
|
fun <T> ndStructure(shape: IntArray, bufferFactory: BufferFactory<T> = Buffer.Companion::boxing, initializer: (IntArray) -> T) =
|
||||||
ndStructure(DefaultStrides(shape), bufferFactory, initializer)
|
ndStructure(DefaultStrides(shape), bufferFactory, initializer)
|
||||||
|
|
||||||
inline fun <reified T : Any> inlineNdStructure(shape: IntArray, crossinline initializer: (IntArray) -> T) =
|
inline fun <reified T : Any> inlineNdStructure(shape: IntArray, crossinline initializer: (IntArray) -> T) =
|
||||||
@ -216,17 +216,17 @@ class MutableBufferNDStructure<T>(
|
|||||||
*/
|
*/
|
||||||
fun <T : Any> mutableNdStructure(
|
fun <T : Any> mutableNdStructure(
|
||||||
strides: Strides,
|
strides: Strides,
|
||||||
bufferFactory: MutableBufferFactory<T> = ::boxingMutableBuffer,
|
bufferFactory: MutableBufferFactory<T> = MutableBuffer.Companion::boxing,
|
||||||
initializer: (IntArray) -> T
|
initializer: (IntArray) -> T
|
||||||
) =
|
) =
|
||||||
MutableBufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) })
|
MutableBufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) })
|
||||||
|
|
||||||
inline fun <reified T : Any> inlineMutableNdStructure(strides: Strides, crossinline initializer: (IntArray) -> T) =
|
inline fun <reified T : Any> inlineMutableNdStructure(strides: Strides, crossinline initializer: (IntArray) -> T) =
|
||||||
MutableBufferNDStructure(strides, autoMutableBuffer(strides.linearSize) { i -> initializer(strides.index(i)) })
|
MutableBufferNDStructure(strides, MutableBuffer.auto(strides.linearSize) { i -> initializer(strides.index(i)) })
|
||||||
|
|
||||||
fun <T : Any> mutableNdStructure(
|
fun <T : Any> mutableNdStructure(
|
||||||
shape: IntArray,
|
shape: IntArray,
|
||||||
bufferFactory: MutableBufferFactory<T> = ::boxingMutableBuffer,
|
bufferFactory: MutableBufferFactory<T> = MutableBuffer.Companion::boxing,
|
||||||
initializer: (IntArray) -> T
|
initializer: (IntArray) -> T
|
||||||
) =
|
) =
|
||||||
mutableNdStructure(DefaultStrides(shape), bufferFactory, initializer)
|
mutableNdStructure(DefaultStrides(shape), bufferFactory, initializer)
|
||||||
|
@ -2,7 +2,7 @@ package scientifik.kmath.structures
|
|||||||
|
|
||||||
import scientifik.kmath.operations.RealField
|
import scientifik.kmath.operations.RealField
|
||||||
|
|
||||||
typealias RealNDElement = BufferNDElement<Double, RealField>
|
typealias RealNDElement = StridedNDElement<Double, RealField>
|
||||||
|
|
||||||
class RealNDField(shape: IntArray) :
|
class RealNDField(shape: IntArray) :
|
||||||
StridedNDField<Double, RealField>(shape, RealField),
|
StridedNDField<Double, RealField>(shape, RealField),
|
||||||
@ -21,20 +21,20 @@ class RealNDField(shape: IntArray) :
|
|||||||
): RealNDElement {
|
): RealNDElement {
|
||||||
check(arg)
|
check(arg)
|
||||||
val array = buildBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) }
|
val array = buildBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) }
|
||||||
return BufferNDElement(this, array)
|
return StridedNDElement(this, array)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
|
override fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
|
||||||
val array = buildBuffer(strides.linearSize) { offset -> elementField.initializer(strides.index(offset)) }
|
val array = buildBuffer(strides.linearSize) { offset -> elementField.initializer(strides.index(offset)) }
|
||||||
return BufferNDElement(this, array)
|
return StridedNDElement(this, array)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mapIndexed(
|
override fun mapIndexed(
|
||||||
arg: NDBuffer<Double>,
|
arg: NDBuffer<Double>,
|
||||||
transform: RealField.(index: IntArray, Double) -> Double
|
transform: RealField.(index: IntArray, Double) -> Double
|
||||||
): BufferNDElement<Double, RealField> {
|
): StridedNDElement<Double, RealField> {
|
||||||
check(arg)
|
check(arg)
|
||||||
return BufferNDElement(
|
return StridedNDElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(arg.strides.linearSize) { offset ->
|
buildBuffer(arg.strides.linearSize) { offset ->
|
||||||
elementField.transform(
|
elementField.transform(
|
||||||
@ -48,9 +48,9 @@ class RealNDField(shape: IntArray) :
|
|||||||
a: NDBuffer<Double>,
|
a: NDBuffer<Double>,
|
||||||
b: NDBuffer<Double>,
|
b: NDBuffer<Double>,
|
||||||
transform: RealField.(Double, Double) -> Double
|
transform: RealField.(Double, Double) -> Double
|
||||||
): BufferNDElement<Double, RealField> {
|
): StridedNDElement<Double, RealField> {
|
||||||
check(a, b)
|
check(a, b)
|
||||||
return BufferNDElement(
|
return StridedNDElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(strides.linearSize) { offset -> elementField.transform(a.buffer[offset], b.buffer[offset]) })
|
buildBuffer(strides.linearSize) { offset -> elementField.transform(a.buffer[offset], b.buffer[offset]) })
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ class RealNDField(shape: IntArray) :
|
|||||||
*/
|
*/
|
||||||
inline fun StridedNDField<Double, RealField>.produceInline(crossinline initializer: RealField.(Int) -> Double): RealNDElement {
|
inline fun StridedNDField<Double, RealField>.produceInline(crossinline initializer: RealField.(Int) -> Double): RealNDElement {
|
||||||
val array = DoubleArray(strides.linearSize) { offset -> elementField.initializer(offset) }
|
val array = DoubleArray(strides.linearSize) { offset -> elementField.initializer(offset) }
|
||||||
return BufferNDElement(this, DoubleBuffer(array))
|
return StridedNDElement(this, DoubleBuffer(array))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,13 +93,13 @@ operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement) =
|
|||||||
/* plus and minus */
|
/* plus and minus */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summation operation for [BufferNDElement] and single element
|
* Summation operation for [StridedNDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun RealNDElement.plus(arg: Double) =
|
operator fun RealNDElement.plus(arg: Double) =
|
||||||
context.produceInline { i -> buffer[i] + arg }
|
context.produceInline { i -> buffer[i] + arg }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtraction operation between [BufferNDElement] and single element
|
* Subtraction operation between [StridedNDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun RealNDElement.minus(arg: Double) =
|
operator fun RealNDElement.minus(arg: Double) =
|
||||||
context.produceInline { i -> buffer[i] - arg }
|
context.produceInline { i -> buffer[i] - arg }
|
@ -12,7 +12,7 @@ class ExpressionFieldTest {
|
|||||||
val context = ExpressionField(RealField)
|
val context = ExpressionField(RealField)
|
||||||
val expression = with(context) {
|
val expression = with(context) {
|
||||||
val x = variable("x", 2.0)
|
val x = variable("x", 2.0)
|
||||||
x * x + 2 * x + 1.0
|
x * x + 2 * x + one
|
||||||
}
|
}
|
||||||
assertEquals(expression("x" to 1.0), 4.0)
|
assertEquals(expression("x" to 1.0), 4.0)
|
||||||
assertEquals(expression(), 9.0)
|
assertEquals(expression(), 9.0)
|
||||||
@ -44,7 +44,7 @@ class ExpressionFieldTest {
|
|||||||
fun valueExpression() {
|
fun valueExpression() {
|
||||||
val expressionBuilder: ExpressionField<Double>.() -> Expression<Double> = {
|
val expressionBuilder: ExpressionField<Double>.() -> Expression<Double> = {
|
||||||
val x = variable("x")
|
val x = variable("x")
|
||||||
x * x + 2 * x + 1.0
|
x * x + 2 * x + one
|
||||||
}
|
}
|
||||||
|
|
||||||
val expression = ExpressionField(RealField).expressionBuilder()
|
val expression = ExpressionField(RealField).expressionBuilder()
|
||||||
|
@ -7,7 +7,7 @@ import kotlin.test.assertEquals
|
|||||||
class NDFieldTest {
|
class NDFieldTest {
|
||||||
@Test
|
@Test
|
||||||
fun testStrides() {
|
fun testStrides() {
|
||||||
val ndArray = NDElements.real(intArrayOf(10, 10)) { (it[0] + it[1]).toDouble() }
|
val ndArray = NDElement.real(intArrayOf(10, 10)) { (it[0] + it[1]).toDouble() }
|
||||||
assertEquals(ndArray[5, 5], 10.0)
|
assertEquals(ndArray[5, 5], 10.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
import scientifik.kmath.operations.Norm
|
import scientifik.kmath.operations.Norm
|
||||||
import scientifik.kmath.structures.NDElements.real2D
|
import scientifik.kmath.structures.NDElement.Companion.real2D
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
@ -23,3 +23,4 @@ object ComplexBufferSpec : FixedSizeBufferSpec<Complex> {
|
|||||||
*/
|
*/
|
||||||
fun Complex.Companion.createBuffer(size: Int) = ObjectBuffer.create(ComplexBufferSpec, size)
|
fun Complex.Companion.createBuffer(size: Int) = ObjectBuffer.create(ComplexBufferSpec, size)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user