pre-0.0.3 #46
@ -17,7 +17,7 @@ open class NDFieldBenchmark {
|
|||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
fun autoElementAdd() {
|
fun autoElementAdd() {
|
||||||
var res = bufferedField.run { one.toElement() }
|
var res = genericField.one
|
||||||
repeat(n) {
|
repeat(n) {
|
||||||
res += 1.0
|
res += 1.0
|
||||||
}
|
}
|
||||||
|
@ -16,54 +16,54 @@ fun main(args: Array<String>) {
|
|||||||
//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(intArrayOf(dim, dim), RealField)
|
val genericField = NDField.buffered(intArrayOf(dim, dim), RealField)
|
||||||
|
|
||||||
|
//
|
||||||
val autoTime = measureTimeMillis {
|
// val autoTime = measureTimeMillis {
|
||||||
autoField.run {
|
// autoField.run {
|
||||||
var res = one
|
// var res = one
|
||||||
repeat(n) {
|
// repeat(n) {
|
||||||
res += 1.0
|
// res += 1.0
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
println("Buffered addition completed in $autoTime millis")
|
// println("Buffered addition completed in $autoTime millis")
|
||||||
|
//
|
||||||
val elementTime = measureTimeMillis {
|
// val elementTime = measureTimeMillis {
|
||||||
var res = genericField.one
|
// var res = genericField.one
|
||||||
repeat(n) {
|
// repeat(n) {
|
||||||
res += 1.0
|
// res += 1.0
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
println("Element addition completed in $elementTime millis")
|
// println("Element addition completed in $elementTime millis")
|
||||||
|
//
|
||||||
val specializedTime = measureTimeMillis {
|
// val specializedTime = measureTimeMillis {
|
||||||
specializedField.run {
|
// specializedField.run {
|
||||||
var res:NDBuffer<Double> = one
|
// var res:NDBuffer<Double> = one
|
||||||
repeat(n) {
|
// repeat(n) {
|
||||||
res += 1.0
|
// res += 1.0
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
println("Specialized addition completed in $specializedTime millis")
|
// println("Specialized addition completed in $specializedTime millis")
|
||||||
|
//
|
||||||
|
//
|
||||||
val lazyTime = measureTimeMillis {
|
// val lazyTime = measureTimeMillis {
|
||||||
lazyField.run {
|
// lazyField.run {
|
||||||
val res = one.map {
|
// val res = one.map {
|
||||||
var c = 0.0
|
// var c = 0.0
|
||||||
repeat(n) {
|
// repeat(n) {
|
||||||
c += 1.0
|
// c += 1.0
|
||||||
}
|
// }
|
||||||
c
|
// c
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
res.elements().forEach { it.second }
|
// res.elements().forEach { it.second }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
println("Lazy addition completed in $lazyTime millis")
|
// println("Lazy addition completed in $lazyTime millis")
|
||||||
|
|
||||||
val genericTime = measureTimeMillis {
|
val genericTime = measureTimeMillis {
|
||||||
//genericField.run(action)
|
//genericField.run(action)
|
||||||
|
@ -33,9 +33,9 @@ interface Space<T> {
|
|||||||
operator fun T.times(k: Number) = multiply(this, k.toDouble())
|
operator fun T.times(k: Number) = multiply(this, k.toDouble())
|
||||||
operator fun T.div(k: Number) = multiply(this, 1.0 / k.toDouble())
|
operator fun T.div(k: Number) = multiply(this, 1.0 / k.toDouble())
|
||||||
operator fun Number.times(b: T) = b * this
|
operator fun Number.times(b: T) = b * this
|
||||||
fun Iterable<T>.sum(): T = fold(zero) { left, right -> left + right }
|
fun Iterable<T>.sum(): T = fold(zero) { left, right -> add(left,right) }
|
||||||
|
|
||||||
fun Sequence<T>.sum(): T = fold(zero) { left, right -> left + right }
|
fun Sequence<T>.sum(): T = fold(zero) { left, right -> add(left, right) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,13 +2,20 @@ package scientifik.kmath.operations
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The generic mathematics elements which is able to store its context
|
* The generic mathematics elements which is able to store its context
|
||||||
* @param S the type of mathematical context for this element
|
* @param T the type of space operation results
|
||||||
|
* @param I self type of the element. Needed for static type checking
|
||||||
|
* @param C the type of mathematical context for this element
|
||||||
*/
|
*/
|
||||||
interface MathElement<S> {
|
interface MathElement<C> {
|
||||||
/**
|
/**
|
||||||
* The context this element belongs to
|
* The context this element belongs to
|
||||||
*/
|
*/
|
||||||
val context: S
|
val context: C
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MathWrapper<T, I> {
|
||||||
|
fun unwrap(): T
|
||||||
|
fun T.wrap(): I
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,13 +24,7 @@ interface MathElement<S> {
|
|||||||
* @param I self type of the element. Needed for static type checking
|
* @param I self type of the element. Needed for static type checking
|
||||||
* @param S the type of space
|
* @param S the type of space
|
||||||
*/
|
*/
|
||||||
interface SpaceElement<T, I : SpaceElement<T, I, S>, S : Space<T>> : MathElement<S> {
|
interface SpaceElement<T, I : SpaceElement<T, I, S>, S : Space<T>> : MathElement<S>, MathWrapper<T, I> {
|
||||||
/**
|
|
||||||
* Self value. Needed for static type checking.
|
|
||||||
*/
|
|
||||||
fun unwrap(): T
|
|
||||||
|
|
||||||
fun T.wrap(): I
|
|
||||||
|
|
||||||
operator fun plus(b: T) = context.add(unwrap(), b).wrap()
|
operator fun plus(b: T) = context.add(unwrap(), b).wrap()
|
||||||
operator fun minus(b: T) = context.add(unwrap(), context.multiply(b, -1.0)).wrap()
|
operator fun minus(b: T) = context.add(unwrap(), context.multiply(b, -1.0)).wrap()
|
||||||
@ -35,7 +36,6 @@ interface SpaceElement<T, I : SpaceElement<T, I, S>, S : Space<T>> : MathElement
|
|||||||
* Ring element
|
* Ring element
|
||||||
*/
|
*/
|
||||||
interface RingElement<T, I : RingElement<T, I, R>, R : Ring<T>> : SpaceElement<T, I, R> {
|
interface RingElement<T, I : RingElement<T, I, R>, R : Ring<T>> : SpaceElement<T, I, R> {
|
||||||
override val context: R
|
|
||||||
operator fun times(b: T) = context.multiply(unwrap(), b).wrap()
|
operator fun times(b: T) = context.multiply(unwrap(), b).wrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +44,5 @@ interface RingElement<T, I : RingElement<T, I, R>, R : Ring<T>> : SpaceElement<T
|
|||||||
*/
|
*/
|
||||||
interface FieldElement<T, I : FieldElement<T, I, F>, F : Field<T>> : RingElement<T, I, F> {
|
interface FieldElement<T, I : FieldElement<T, I, F>, F : Field<T>> : RingElement<T, I, F> {
|
||||||
override val context: F
|
override val context: F
|
||||||
|
|
||||||
operator fun div(b: T) = context.divide(unwrap(), b).wrap()
|
operator fun div(b: T) = context.divide(unwrap(), b).wrap()
|
||||||
}
|
}
|
@ -32,6 +32,7 @@ inline class Real(val value: Double) : FieldElement<Double, Real, RealField> {
|
|||||||
/**
|
/**
|
||||||
* 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")
|
||||||
object RealField : ExtendedField<Double>, Norm<Double, Double> {
|
object RealField : ExtendedField<Double>, Norm<Double, Double> {
|
||||||
override val zero: Double = 0.0
|
override val zero: Double = 0.0
|
||||||
override fun add(a: Double, b: Double): Double = a + b
|
override fun add(a: Double, b: Double): Double = a + b
|
||||||
@ -45,10 +46,13 @@ object RealField : ExtendedField<Double>, Norm<Double, Double> {
|
|||||||
override fun power(arg: Double, pow: Double): Double = arg.pow(pow)
|
override fun power(arg: Double, pow: Double): Double = arg.pow(pow)
|
||||||
|
|
||||||
override fun exp(arg: Double): Double = kotlin.math.exp(arg)
|
override fun exp(arg: Double): Double = kotlin.math.exp(arg)
|
||||||
|
|
||||||
override fun ln(arg: Double): Double = kotlin.math.ln(arg)
|
override fun ln(arg: Double): Double = kotlin.math.ln(arg)
|
||||||
|
|
||||||
override fun norm(arg: Double): Double = kotlin.math.abs(arg)
|
override fun norm(arg: Double): Double = kotlin.math.abs(arg)
|
||||||
|
|
||||||
|
override fun Double.unaryMinus(): Double = -this
|
||||||
|
|
||||||
|
override fun Double.minus(b: Double): Double = this - b
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
import scientifik.kmath.operations.Field
|
import scientifik.kmath.operations.Field
|
||||||
|
import scientifik.kmath.operations.FieldElement
|
||||||
|
|
||||||
|
|
||||||
class BufferNDField<T, F : Field<T>>(
|
class BoxingNDField<T, F : Field<T>>(
|
||||||
shape: IntArray,
|
override val shape: IntArray,
|
||||||
override val elementContext: F,
|
override val elementContext: F,
|
||||||
val bufferFactory: BufferFactory<T>
|
val bufferFactory: BufferFactory<T>
|
||||||
) : StridedNDField<T, F>(shape), NDField<T, F, NDBuffer<T>> {
|
) : BufferedNDField<T, F> {
|
||||||
|
|
||||||
override fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T> = bufferFactory(size, initializer)
|
override val strides: Strides = DefaultStrides(shape)
|
||||||
|
|
||||||
|
override fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T> =
|
||||||
|
bufferFactory(size, initializer)
|
||||||
|
|
||||||
override fun check(vararg elements: NDBuffer<T>) {
|
override fun check(vararg elements: NDBuffer<T>) {
|
||||||
if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides")
|
if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides")
|
||||||
@ -18,14 +22,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): StridedNDFieldElement<T, F> =
|
override fun produce(initializer: F.(IntArray) -> T) =
|
||||||
StridedNDFieldElement(
|
BufferedNDFieldElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) })
|
buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) })
|
||||||
|
|
||||||
override fun map(arg: NDBuffer<T>, transform: F.(T) -> T): StridedNDFieldElement<T, F> {
|
override fun map(arg: NDBuffer<T>, transform: F.(T) -> T): BufferedNDFieldElement<T, F> {
|
||||||
check(arg)
|
check(arg)
|
||||||
return StridedNDFieldElement(
|
return BufferedNDFieldElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(arg.strides.linearSize) { offset -> elementContext.transform(arg.buffer[offset]) })
|
buildBuffer(arg.strides.linearSize) { offset -> elementContext.transform(arg.buffer[offset]) })
|
||||||
}
|
}
|
||||||
@ -33,9 +37,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
|
||||||
): StridedNDFieldElement<T, F> {
|
): BufferedNDFieldElement<T, F> {
|
||||||
check(arg)
|
check(arg)
|
||||||
return StridedNDFieldElement(
|
return BufferedNDFieldElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(arg.strides.linearSize) { offset ->
|
buildBuffer(arg.strides.linearSize) { offset ->
|
||||||
elementContext.transform(
|
elementContext.transform(
|
||||||
@ -49,10 +53,13 @@ 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
|
||||||
): StridedNDFieldElement<T, F> {
|
): BufferedNDFieldElement<T, F> {
|
||||||
check(a, b)
|
check(a, b)
|
||||||
return StridedNDFieldElement(
|
return BufferedNDFieldElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) })
|
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun NDBuffer<T>.toElement(): FieldElement<NDBuffer<T>, *, out BufferedNDField<T, F>> =
|
||||||
|
BufferedNDFieldElement(this@BoxingNDField, buffer)
|
||||||
}
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.*
|
||||||
|
|
||||||
|
interface BufferedNDAlgebra<T, C>: NDAlgebra<T, C, NDBuffer<T>>{
|
||||||
|
val strides: Strides
|
||||||
|
|
||||||
|
fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T>
|
||||||
|
|
||||||
|
override fun check(vararg elements: NDBuffer<T>) {
|
||||||
|
if (!elements.all { it.strides == this.strides }) error("Strides mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert any [NDStructure] to buffered structure using strides from this context.
|
||||||
|
* If the structure is already [NDBuffer], conversion is free. If not, it could be expensive because iteration over indexes
|
||||||
|
*
|
||||||
|
* If the argument is [NDBuffer] with different strides structure, the new element will be produced.
|
||||||
|
*/
|
||||||
|
fun NDStructure<T>.toBuffer(): NDBuffer<T> {
|
||||||
|
return if (this is NDBuffer<T> && this.strides == this@BufferedNDAlgebra.strides) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
produce { index -> get(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a buffer to element of this algebra
|
||||||
|
*/
|
||||||
|
fun NDBuffer<T>.toElement(): MathElement<out BufferedNDAlgebra<T, C>>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface BufferedNDSpace<T, S : Space<T>> : NDSpace<T, S, NDBuffer<T>>, BufferedNDAlgebra<T,S> {
|
||||||
|
override fun NDBuffer<T>.toElement(): SpaceElement<NDBuffer<T>, *, out BufferedNDSpace<T, S>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BufferedNDRing<T, R : Ring<T>> : NDRing<T, R, NDBuffer<T>>, BufferedNDSpace<T, R> {
|
||||||
|
override fun NDBuffer<T>.toElement(): RingElement<NDBuffer<T>, *, out BufferedNDRing<T, R>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BufferedNDField<T, F : Field<T>> : NDField<T, F, NDBuffer<T>>, BufferedNDRing<T, F> {
|
||||||
|
override fun NDBuffer<T>.toElement(): FieldElement<NDBuffer<T>, *, out BufferedNDField<T, F>>
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base interface for an element with context, containing strides
|
||||||
|
*/
|
||||||
|
interface BufferedNDElement<T, C> : NDBuffer<T>, NDElement<T, C, NDBuffer<T>> {
|
||||||
|
override val context: BufferedNDAlgebra<T, C>
|
||||||
|
|
||||||
|
override val strides get() = context.strides
|
||||||
|
|
||||||
|
override val shape: IntArray get() = context.shape
|
||||||
|
}
|
||||||
|
|
||||||
|
class BufferedNDSpaceElement<T, S : Space<T>>(
|
||||||
|
override val context: BufferedNDSpace<T, S>,
|
||||||
|
override val buffer: Buffer<T>
|
||||||
|
) : BufferedNDElement<T, S>, SpaceElement<NDBuffer<T>, BufferedNDSpaceElement<T, S>, BufferedNDSpace<T, S>> {
|
||||||
|
|
||||||
|
override fun unwrap(): NDBuffer<T> = this
|
||||||
|
|
||||||
|
override fun NDBuffer<T>.wrap(): BufferedNDSpaceElement<T, S> {
|
||||||
|
context.check(this)
|
||||||
|
return BufferedNDSpaceElement(context, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BufferedNDRingElement<T, R : Ring<T>>(
|
||||||
|
override val context: BufferedNDRing<T, R>,
|
||||||
|
override val buffer: Buffer<T>
|
||||||
|
) : BufferedNDElement<T, R>, RingElement<NDBuffer<T>, BufferedNDRingElement<T, R>, BufferedNDRing<T, R>> {
|
||||||
|
|
||||||
|
override fun unwrap(): NDBuffer<T> = this
|
||||||
|
|
||||||
|
override fun NDBuffer<T>.wrap(): BufferedNDRingElement<T, R> {
|
||||||
|
context.check(this)
|
||||||
|
return BufferedNDRingElement(context, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BufferedNDFieldElement<T, F : Field<T>>(
|
||||||
|
override val context: BufferedNDField<T, F>,
|
||||||
|
override val buffer: Buffer<T>
|
||||||
|
) : BufferedNDElement<T, F>, FieldElement<NDBuffer<T>, BufferedNDFieldElement<T, F>, BufferedNDField<T, F>> {
|
||||||
|
|
||||||
|
override fun unwrap(): NDBuffer<T> = this
|
||||||
|
|
||||||
|
override fun NDBuffer<T>.wrap(): BufferedNDFieldElement<T, F> {
|
||||||
|
context.check(this)
|
||||||
|
return BufferedNDFieldElement(context, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: BufferedNDElement<T, F>) =
|
||||||
|
ndElement.context.run { map(ndElement) { invoke(it) }.toElement() }
|
||||||
|
|
||||||
|
/* plus and minus */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Summation operation for [BufferedNDElement] and single element
|
||||||
|
*/
|
||||||
|
operator fun <T : Any, F : Space<T>> BufferedNDElement<T, F>.plus(arg: T) =
|
||||||
|
context.map(this) { it + arg }.wrap()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtraction operation between [BufferedNDElement] and single element
|
||||||
|
*/
|
||||||
|
operator fun <T : Any, F : Space<T>> BufferedNDElement<T, F>.minus(arg: T) =
|
||||||
|
context.map(this) { it - arg }.wrap()
|
||||||
|
|
||||||
|
/* prod and div */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product operation for [BufferedNDElement] and single element
|
||||||
|
*/
|
||||||
|
operator fun <T : Any, F : Ring<T>> BufferedNDElement<T, F>.times(arg: T) =
|
||||||
|
context.map(this) { it * arg }.wrap()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Division operation between [BufferedNDElement] and single element
|
||||||
|
*/
|
||||||
|
operator fun <T : Any, F : Field<T>> BufferedNDElement<T, F>.div(arg: T) =
|
||||||
|
context.map(this) { it / arg }.wrap()
|
@ -30,7 +30,7 @@ interface Buffer<T> {
|
|||||||
* Create most appropriate immutable buffer for given type avoiding boxing wherever possible
|
* Create most appropriate immutable buffer for given type avoiding boxing wherever possible
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): Buffer<T> {
|
inline fun <reified T : Any> auto(size: Int, crossinline initializer: (Int) -> T): Buffer<T> {
|
||||||
return when (T::class) {
|
return when (T::class) {
|
||||||
Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer<T>
|
Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer<T>
|
||||||
Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as Buffer<T>
|
Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as Buffer<T>
|
||||||
@ -40,8 +40,10 @@ interface Buffer<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val DoubleBufferFactory: BufferFactory<Double> = { size, initializer -> DoubleBuffer(DoubleArray(size, initializer)) }
|
val DoubleBufferFactory: BufferFactory<Double> =
|
||||||
val ShortBufferFactory: BufferFactory<Short> = { size, initializer -> ShortBuffer(ShortArray(size, initializer)) }
|
{ size, initializer -> DoubleBuffer(DoubleArray(size, initializer)) }
|
||||||
|
val ShortBufferFactory: BufferFactory<Short> =
|
||||||
|
{ size, initializer -> ShortBuffer(ShortArray(size, initializer)) }
|
||||||
val IntBufferFactory: BufferFactory<Int> = { size, initializer -> IntBuffer(IntArray(size, initializer)) }
|
val IntBufferFactory: BufferFactory<Int> = { size, initializer -> IntBuffer(IntArray(size, initializer)) }
|
||||||
val LongBufferFactory: BufferFactory<Long> = { size, initializer -> LongBuffer(LongArray(size, initializer)) }
|
val LongBufferFactory: BufferFactory<Long> = { size, initializer -> LongBuffer(LongArray(size, initializer)) }
|
||||||
}
|
}
|
||||||
|
@ -11,40 +11,79 @@ import scientifik.kmath.operations.Space
|
|||||||
class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : RuntimeException()
|
class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : RuntimeException()
|
||||||
|
|
||||||
|
|
||||||
interface NDSpace<T, S : Space<T>, N : NDStructure<T>> : Space<N> {
|
/**
|
||||||
|
* The base interface for all nd-algebra implementations
|
||||||
|
* @param T the type of nd-structure element
|
||||||
|
* @param C the type of the context
|
||||||
|
* @param N the type of the structure
|
||||||
|
*/
|
||||||
|
interface NDAlgebra<T, C, N : NDStructure<T>> {
|
||||||
val shape: IntArray
|
val shape: IntArray
|
||||||
val elementContext: S
|
val elementContext: C
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a new [N] structure using given initializer function
|
* Produce a new [N] structure using given initializer function
|
||||||
*/
|
*/
|
||||||
fun produce(initializer: S.(IntArray) -> T): N
|
fun produce(initializer: C.(IntArray) -> T): N
|
||||||
|
|
||||||
fun map(arg: N, transform: S.(T) -> T): N
|
/**
|
||||||
fun mapIndexed(arg: N, transform: S.(index: IntArray, T) -> T): N
|
* Map elements from one structure to another one
|
||||||
fun combine(a: N, b: N, transform: S.(T, T) -> T): N
|
*/
|
||||||
|
fun map(arg: N, transform: C.(T) -> T): N
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map indexed elements
|
||||||
|
*/
|
||||||
|
fun mapIndexed(arg: N, transform: C.(index: IntArray, T) -> T): N
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combine two structures into one
|
||||||
|
*/
|
||||||
|
fun combine(a: N, b: N, transform: C.(T, T) -> T): N
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if given elements are consistent with this context
|
||||||
|
*/
|
||||||
|
fun check(vararg elements: N) {
|
||||||
|
elements.forEach {
|
||||||
|
if (!shape.contentEquals(it.shape)) {
|
||||||
|
throw ShapeMismatchException(shape, it.shape)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* element-by-element invoke a function working on [T] on a [NDStructure]
|
||||||
|
*/
|
||||||
|
operator fun Function1<T, T>.invoke(structure: N) = map(structure) { value -> this@invoke(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An nd-space over element space
|
||||||
|
*/
|
||||||
|
interface NDSpace<T, S : Space<T>, N : NDStructure<T>> : Space<N>, NDAlgebra<T, S, N> {
|
||||||
/**
|
/**
|
||||||
* Element-by-element addition
|
* Element-by-element addition
|
||||||
*/
|
*/
|
||||||
override fun add(a: N, b: N): N =
|
override fun add(a: N, b: N): N =
|
||||||
combine(a, b) { aValue, bValue -> aValue + bValue }
|
combine(a, b) { aValue, bValue -> add(aValue, bValue) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiply all elements by constant
|
* Multiply all elements by constant
|
||||||
*/
|
*/
|
||||||
override fun multiply(a: N, k: Double): N =
|
override fun multiply(a: N, k: Double): N =
|
||||||
map(a) { it * k }
|
map(a) { multiply(it, k) }
|
||||||
|
|
||||||
operator fun Function1<T, T>.invoke(structure: N) = map(structure) { value -> this@invoke(value) }
|
operator fun N.plus(arg: T) = map(this) { value -> add(arg, value) }
|
||||||
operator fun N.plus(arg: T) = map(this) { value -> elementContext.run { arg + value } }
|
operator fun N.minus(arg: T) = map(this) { value -> add(arg, -value) }
|
||||||
operator fun N.minus(arg: T) = map(this) { value -> elementContext.run { arg - value } }
|
|
||||||
|
|
||||||
operator fun T.plus(arg: N) = arg + this
|
|
||||||
operator fun T.minus(arg: N) = arg - this
|
|
||||||
|
|
||||||
|
operator fun T.plus(arg: N) = map(arg) { value -> add(this@plus, value) }
|
||||||
|
operator fun T.minus(arg: N) = map(arg) { value -> add(-this@minus, value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An nd-ring over element ring
|
||||||
|
*/
|
||||||
interface NDRing<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T, R, N> {
|
interface NDRing<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T, R, N> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,16 +92,16 @@ interface NDRing<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T, R, N>
|
|||||||
override fun multiply(a: N, b: N): N =
|
override fun multiply(a: N, b: N): N =
|
||||||
combine(a, b) { aValue, bValue -> aValue * bValue }
|
combine(a, b) { aValue, bValue -> aValue * bValue }
|
||||||
|
|
||||||
operator fun N.times(arg: T) = map(this) { value -> elementContext.run { arg * value } }
|
operator fun N.times(arg: T) = map(this) { value -> multiply(arg, value) }
|
||||||
operator fun T.times(arg: N) = arg * this
|
operator fun T.times(arg: N) = map(arg) { value -> multiply(this@times, value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field for n-dimensional arrays.
|
* Field for n-dimensional structures.
|
||||||
* @param shape - the list of dimensions of the array
|
* @param shape - the list of dimensions of the array
|
||||||
* @param elementField - operations field defined on individual array element
|
* @param elementField - operations field defined on individual array element
|
||||||
* @param T - the type of the element contained in ND structure
|
* @param T - the type of the element contained in ND structure
|
||||||
* @param F - field over structure elements
|
* @param F - field of structure elements
|
||||||
* @param R - actual nd-element type of this field
|
* @param R - actual nd-element type of this field
|
||||||
*/
|
*/
|
||||||
interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F, N> {
|
interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F, N> {
|
||||||
@ -73,17 +112,9 @@ interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F,
|
|||||||
override fun divide(a: N, b: N): N =
|
override fun divide(a: N, b: N): N =
|
||||||
combine(a, b) { aValue, bValue -> aValue / bValue }
|
combine(a, b) { aValue, bValue -> aValue / bValue }
|
||||||
|
|
||||||
operator fun N.div(arg: T) = map(this) { value -> elementContext.run { arg / value } }
|
operator fun N.div(arg: T) = map(this) { value -> arg / value }
|
||||||
operator fun T.div(arg: N) = arg / this
|
operator fun T.div(arg: N) = arg / this
|
||||||
|
|
||||||
fun check(vararg elements: N) {
|
|
||||||
elements.forEach {
|
|
||||||
if (!shape.contentEquals(it.shape)) {
|
|
||||||
throw ShapeMismatchException(shape, it.shape)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Create a nd-field for [Double] values
|
* Create a nd-field for [Double] values
|
||||||
@ -94,16 +125,16 @@ interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F,
|
|||||||
* 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, Buffer.Companion::boxing)
|
BoxingNDField(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.
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
inline fun <reified T : Any, F : Field<T>> auto(shape: IntArray, field: F): StridedNDField<T, F> =
|
inline fun <reified T : Any, F : Field<T>> auto(shape: IntArray, field: F): BufferedNDField<T, F> =
|
||||||
when {
|
when {
|
||||||
T::class == Double::class -> real(shape) as StridedNDField<T, F>
|
T::class == Double::class -> real(shape) as BufferedNDField<T, F>
|
||||||
else -> BufferNDField(shape, field, Buffer.Companion::auto)
|
else -> BoxingNDField(shape, field, Buffer.Companion::auto)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
import scientifik.kmath.operations.Field
|
import scientifik.kmath.operations.Field
|
||||||
import scientifik.kmath.operations.FieldElement
|
|
||||||
import scientifik.kmath.operations.RealField
|
import scientifik.kmath.operations.RealField
|
||||||
|
import scientifik.kmath.operations.Ring
|
||||||
import scientifik.kmath.operations.Space
|
import scientifik.kmath.operations.Space
|
||||||
|
|
||||||
|
|
||||||
interface NDElement<T, S : Space<T>> : NDStructure<T> {
|
interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> {
|
||||||
val elementField: S
|
|
||||||
|
|
||||||
fun mapIndexed(transform: S.(index: IntArray, T) -> T): NDElement<T, S>
|
val context: NDAlgebra<T, C, N>
|
||||||
fun map(action: S.(T) -> T) = mapIndexed { _, value -> action(value) }
|
|
||||||
|
fun unwrap(): N
|
||||||
|
|
||||||
|
fun N.wrap(): NDElement<T, C, N>
|
||||||
|
|
||||||
|
fun mapIndexed(transform: C.(index: IntArray, T) -> T) = context.mapIndexed(unwrap(), transform).wrap()
|
||||||
|
fun map(transform: C.(T) -> T) = context.map(unwrap(), transform).wrap()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
@ -38,8 +43,8 @@ interface NDElement<T, S : Space<T>> : NDStructure<T> {
|
|||||||
shape: IntArray,
|
shape: IntArray,
|
||||||
field: F,
|
field: F,
|
||||||
initializer: F.(IntArray) -> T
|
initializer: F.(IntArray) -> T
|
||||||
): StridedNDFieldElement<T, F> {
|
): BufferedNDElement<T, F> {
|
||||||
val ndField = BufferNDField(shape, field, Buffer.Companion::boxing)
|
val ndField = BoxingNDField(shape, field, Buffer.Companion::boxing)
|
||||||
return ndField.produce(initializer)
|
return ndField.produce(initializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,47 +52,46 @@ interface NDElement<T, S : Space<T>> : NDStructure<T> {
|
|||||||
shape: IntArray,
|
shape: IntArray,
|
||||||
field: F,
|
field: F,
|
||||||
noinline initializer: F.(IntArray) -> T
|
noinline initializer: F.(IntArray) -> T
|
||||||
): StridedNDFieldElement<T, F> {
|
): BufferedNDFieldElement<T, F> {
|
||||||
val ndField = NDField.auto(shape, field)
|
val ndField = NDField.auto(shape, field)
|
||||||
return StridedNDFieldElement(ndField, ndField.produce(initializer).buffer)
|
return BufferedNDFieldElement(ndField, ndField.produce(initializer).buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 [NDElement]
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> Function1<T, T>.invoke(ndElement: NDElement<T, F>) =
|
operator fun <T, C> Function1<T, T>.invoke(ndElement: NDElement<T, C, *>) =
|
||||||
ndElement.map { value -> this@invoke(value) }
|
ndElement.map { value -> this@invoke(value) }
|
||||||
|
|
||||||
/* plus and minus */
|
/* plus and minus */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summation operation for [NDElements] and single element
|
* Summation operation for [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> NDElement<T, F>.plus(arg: T): NDElement<T, F> =
|
operator fun <T, S : Space<T>> NDElement<T, S, *>.plus(arg: T) =
|
||||||
this.map { value -> elementField.run { arg + value } }
|
map { value -> arg + value }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtraction operation between [NDElements] and single element
|
* Subtraction operation between [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> NDElement<T, F>.minus(arg: T): NDElement<T, F> =
|
operator fun <T, S : Space<T>> NDElement<T, S, *>.minus(arg: T) =
|
||||||
this.map { value -> elementField.run { arg - value } }
|
map { value -> arg - value }
|
||||||
|
|
||||||
/* prod and div */
|
/* prod and div */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Product operation for [NDElements] and single element
|
* Product operation for [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> NDElement<T, F>.times(arg: T): NDElement<T, F> =
|
operator fun <T, R : Ring<T>> NDElement<T, R, *>.times(arg: T) =
|
||||||
this.map { value -> elementField.run { arg * value } }
|
map { value -> arg * value }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Division operation between [NDElements] and single element
|
* Division operation between [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> NDElement<T, F>.div(arg: T): NDElement<T, F> =
|
operator fun <T, F : Field<T>> NDElement<T, F, *>.div(arg: T) =
|
||||||
this.map { value -> elementField.run { arg / value } }
|
map { value -> arg / value }
|
||||||
|
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
@ -117,24 +121,3 @@ operator fun <T, F : Field<T>> NDElement<T, F>.div(arg: T): NDElement<T, F> =
|
|||||||
// operator fun T.div(arg: NDStructure<T>): NDElement<T, F> = produce { index ->
|
// operator fun T.div(arg: NDStructure<T>): NDElement<T, F> = produce { index ->
|
||||||
// field.run { this@div / arg[index] }
|
// field.run { this@div / arg[index] }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read-only [NDStructure] coupled to the context.
|
|
||||||
*/
|
|
||||||
class GenericNDFieldElement<T, F : Field<T>>(
|
|
||||||
override val context: NDField<T, F, NDStructure<T>>,
|
|
||||||
private val structure: NDStructure<T>
|
|
||||||
) :
|
|
||||||
NDStructure<T> by structure,
|
|
||||||
NDElement<T, F>,
|
|
||||||
FieldElement<NDStructure<T>, GenericNDFieldElement<T, F>, NDField<T, F, NDStructure<T>>> {
|
|
||||||
override val elementField: F get() = context.elementContext
|
|
||||||
|
|
||||||
override fun unwrap(): NDStructure<T> = structure
|
|
||||||
|
|
||||||
override fun NDStructure<T>.wrap() = GenericNDFieldElement(context, this)
|
|
||||||
|
|
||||||
override fun mapIndexed(transform: F.(index: IntArray, T) -> T) =
|
|
||||||
ndStructure(context.shape) { index: IntArray -> context.elementContext.transform(index, get(index)) }.wrap()
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.FieldElement
|
||||||
import scientifik.kmath.operations.RealField
|
import scientifik.kmath.operations.RealField
|
||||||
|
|
||||||
typealias RealNDElement = StridedNDFieldElement<Double, RealField>
|
typealias RealNDElement = BufferedNDFieldElement<Double, RealField>
|
||||||
|
|
||||||
class RealNDField(shape: IntArray) :
|
class RealNDField(override val shape: IntArray) :
|
||||||
StridedNDField<Double, RealField>(shape),
|
BufferedNDField<Double, RealField>,
|
||||||
ExtendedNDField<Double, RealField, NDBuffer<Double>> {
|
ExtendedNDField<Double, RealField, NDBuffer<Double>> {
|
||||||
|
|
||||||
|
override val strides: Strides = DefaultStrides(shape)
|
||||||
|
|
||||||
override val elementContext: RealField get() = RealField
|
override val elementContext: RealField get() = RealField
|
||||||
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 } }
|
||||||
@ -25,20 +28,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 StridedNDFieldElement(this, array)
|
return BufferedNDFieldElement(this, array)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
|
override fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
|
||||||
val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
|
val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
|
||||||
return StridedNDFieldElement(this, array)
|
return BufferedNDFieldElement(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
|
||||||
): StridedNDFieldElement<Double, RealField> {
|
): RealNDElement {
|
||||||
check(arg)
|
check(arg)
|
||||||
return StridedNDFieldElement(
|
return BufferedNDFieldElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(arg.strides.linearSize) { offset ->
|
buildBuffer(arg.strides.linearSize) { offset ->
|
||||||
elementContext.transform(
|
elementContext.transform(
|
||||||
@ -52,13 +55,16 @@ 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
|
||||||
): StridedNDFieldElement<Double, RealField> {
|
): RealNDElement {
|
||||||
check(a, b)
|
check(a, b)
|
||||||
return StridedNDFieldElement(
|
return BufferedNDFieldElement(
|
||||||
this,
|
this,
|
||||||
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) })
|
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun NDBuffer<Double>.toElement(): FieldElement<NDBuffer<Double>, *, out BufferedNDField<Double, RealField>> =
|
||||||
|
BufferedNDFieldElement(this@RealNDField, buffer)
|
||||||
|
|
||||||
override fun power(arg: NDBuffer<Double>, pow: Double) = map(arg) { power(it, pow) }
|
override fun power(arg: NDBuffer<Double>, pow: Double) = map(arg) { power(it, pow) }
|
||||||
|
|
||||||
override fun exp(arg: NDBuffer<Double>) = map(arg) { exp(it) }
|
override fun exp(arg: NDBuffer<Double>) = map(arg) { exp(it) }
|
||||||
@ -75,9 +81,9 @@ class RealNDField(shape: IntArray) :
|
|||||||
/**
|
/**
|
||||||
* Fast element production using function inlining
|
* Fast element production using function inlining
|
||||||
*/
|
*/
|
||||||
inline fun StridedNDField<Double, RealField>.produceInline(crossinline initializer: RealField.(Int) -> Double): RealNDElement {
|
inline fun BufferedNDField<Double, RealField>.produceInline(crossinline initializer: RealField.(Int) -> Double): RealNDElement {
|
||||||
val array = DoubleArray(strides.linearSize) { offset -> RealField.initializer(offset) }
|
val array = DoubleArray(strides.linearSize) { offset -> RealField.initializer(offset) }
|
||||||
return StridedNDFieldElement(this, DoubleBuffer(array))
|
return BufferedNDFieldElement(this, DoubleBuffer(array))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,13 +96,13 @@ operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement) =
|
|||||||
/* plus and minus */
|
/* plus and minus */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summation operation for [StridedNDFieldElement] and single element
|
* Summation operation for [BufferedNDElement] 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 [StridedNDFieldElement] and single element
|
* Subtraction operation between [BufferedNDElement] 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 }
|
||||||
|
@ -1,86 +1,96 @@
|
|||||||
//package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
//
|
|
||||||
//import scientifik.kmath.operations.ShortRing
|
import scientifik.kmath.operations.RingElement
|
||||||
//
|
import scientifik.kmath.operations.ShortRing
|
||||||
//
|
|
||||||
////typealias ShortNDElement = StridedNDFieldElement<Short, ShortRing>
|
|
||||||
//
|
typealias ShortNDElement = BufferedNDRingElement<Short, ShortRing>
|
||||||
//class ShortNDRing(shape: IntArray) :
|
|
||||||
// NDRing<Short, ShortRing, NDBuffer<Short>> {
|
class ShortNDRing(override val shape: IntArray) :
|
||||||
//
|
BufferedNDRing<Short, ShortRing> {
|
||||||
// inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Short): Buffer<Short> =
|
|
||||||
// ShortBuffer(ShortArray(size) { initializer(it) })
|
override val strides: Strides = DefaultStrides(shape)
|
||||||
//
|
|
||||||
// /**
|
override val elementContext: ShortRing get() = ShortRing
|
||||||
// * Inline transform an NDStructure to
|
override val zero by lazy { produce { ShortRing.zero } }
|
||||||
// */
|
override val one by lazy { produce { ShortRing.one } }
|
||||||
// override fun map(
|
|
||||||
// arg: NDBuffer<Short>,
|
override inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Short): Buffer<Short> =
|
||||||
// transform: ShortRing.(Short) -> Short
|
ShortBuffer(ShortArray(size) { initializer(it) })
|
||||||
// ): ShortNDElement {
|
|
||||||
// check(arg)
|
/**
|
||||||
// val array = buildBuffer(arg.strides.linearSize) { offset -> ShortRing.transform(arg.buffer[offset]) }
|
* Inline transform an NDStructure to
|
||||||
// return StridedNDFieldElement(this, array)
|
*/
|
||||||
// }
|
override fun map(
|
||||||
//
|
arg: NDBuffer<Short>,
|
||||||
// override fun produce(initializer: ShortRing.(IntArray) -> Short): ShortNDElement {
|
transform: ShortRing.(Short) -> Short
|
||||||
// val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
|
): ShortNDElement {
|
||||||
// return StridedNDFieldElement(this, array)
|
check(arg)
|
||||||
// }
|
val array = buildBuffer(arg.strides.linearSize) { offset -> ShortRing.transform(arg.buffer[offset]) }
|
||||||
//
|
return BufferedNDRingElement(this, array)
|
||||||
// override fun mapIndexed(
|
}
|
||||||
// arg: NDBuffer<Short>,
|
|
||||||
// transform: ShortRing.(index: IntArray, Short) -> Short
|
override fun produce(initializer: ShortRing.(IntArray) -> Short): ShortNDElement {
|
||||||
// ): StridedNDFieldElement<Short, ShortRing> {
|
val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
|
||||||
// check(arg)
|
return BufferedNDRingElement(this, array)
|
||||||
// return StridedNDFieldElement(
|
}
|
||||||
// this,
|
|
||||||
// buildBuffer(arg.strides.linearSize) { offset ->
|
override fun mapIndexed(
|
||||||
// elementContext.transform(
|
arg: NDBuffer<Short>,
|
||||||
// arg.strides.index(offset),
|
transform: ShortRing.(index: IntArray, Short) -> Short
|
||||||
// arg.buffer[offset]
|
): ShortNDElement {
|
||||||
// )
|
check(arg)
|
||||||
// })
|
return BufferedNDRingElement(
|
||||||
// }
|
this,
|
||||||
//
|
buildBuffer(arg.strides.linearSize) { offset ->
|
||||||
// override fun combine(
|
elementContext.transform(
|
||||||
// a: NDBuffer<Short>,
|
arg.strides.index(offset),
|
||||||
// b: NDBuffer<Short>,
|
arg.buffer[offset]
|
||||||
// transform: ShortRing.(Short, Short) -> Short
|
)
|
||||||
// ): StridedNDFieldElement<Short, ShortRing> {
|
})
|
||||||
// check(a, b)
|
}
|
||||||
// return StridedNDFieldElement(
|
|
||||||
// this,
|
override fun combine(
|
||||||
// buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) })
|
a: NDBuffer<Short>,
|
||||||
// }
|
b: NDBuffer<Short>,
|
||||||
//}
|
transform: ShortRing.(Short, Short) -> Short
|
||||||
//
|
): ShortNDElement {
|
||||||
//
|
check(a, b)
|
||||||
///**
|
return BufferedNDRingElement(
|
||||||
// * Fast element production using function inlining
|
this,
|
||||||
// */
|
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) })
|
||||||
//inline fun StridedNDField<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): ShortNDElement {
|
}
|
||||||
// val array = ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) }
|
|
||||||
// return StridedNDFieldElement(this, ShortBuffer(array))
|
override fun NDBuffer<Short>.toElement(): RingElement<NDBuffer<Short>, *, out BufferedNDRing<Short, ShortRing>> =
|
||||||
//}
|
BufferedNDRingElement(this@ShortNDRing, buffer)
|
||||||
//
|
}
|
||||||
///**
|
|
||||||
// * Element by element application of any operation on elements to the whole array. Just like in numpy
|
|
||||||
// */
|
/**
|
||||||
//operator fun Function1<Short, Short>.invoke(ndElement: ShortNDElement) =
|
* Fast element production using function inlining
|
||||||
// ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) }
|
*/
|
||||||
//
|
inline fun BufferedNDRing<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): ShortNDElement {
|
||||||
//
|
val array = ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) }
|
||||||
///* plus and minus */
|
return BufferedNDRingElement(this, ShortBuffer(array))
|
||||||
//
|
}
|
||||||
///**
|
|
||||||
// * Summation operation for [StridedNDFieldElement] and single element
|
/**
|
||||||
// */
|
* Element by element application of any operation on elements to the whole array. Just like in numpy
|
||||||
//operator fun ShortNDElement.plus(arg: Short) =
|
*/
|
||||||
// context.produceInline { i -> buffer[i] + arg }
|
operator fun Function1<Short, Short>.invoke(ndElement: ShortNDElement) =
|
||||||
//
|
ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) }
|
||||||
///**
|
|
||||||
// * Subtraction operation between [StridedNDFieldElement] and single element
|
|
||||||
// */
|
/* plus and minus */
|
||||||
//operator fun ShortNDElement.minus(arg: Short) =
|
|
||||||
// context.produceInline { i -> buffer[i] - arg }
|
/**
|
||||||
|
* Summation operation for [StridedNDFieldElement] and single element
|
||||||
|
*/
|
||||||
|
operator fun ShortNDElement.plus(arg: Short) =
|
||||||
|
context.produceInline { i -> (buffer[i] + arg).toShort() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtraction operation between [StridedNDFieldElement] and single element
|
||||||
|
*/
|
||||||
|
operator fun ShortNDElement.minus(arg: Short) =
|
||||||
|
context.produceInline { i -> (buffer[i] - arg).toShort() }
|
@ -1,97 +0,0 @@
|
|||||||
package scientifik.kmath.structures
|
|
||||||
|
|
||||||
import scientifik.kmath.operations.Field
|
|
||||||
import scientifik.kmath.operations.FieldElement
|
|
||||||
|
|
||||||
abstract class StridedNDField<T, F : Field<T>>(final override val shape: IntArray) : NDField<T, F, NDBuffer<T>> {
|
|
||||||
val strides = DefaultStrides(shape)
|
|
||||||
|
|
||||||
abstract fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert any [NDStructure] to buffered structure using strides from this context.
|
|
||||||
* If the structure is already [NDBuffer], conversion is free. If not, it could be expensive because iteration over indexes
|
|
||||||
*
|
|
||||||
* If the argument is [NDBuffer] with different strides structure, the new element will be produced.
|
|
||||||
*/
|
|
||||||
fun NDStructure<T>.toBuffer(): NDBuffer<T> {
|
|
||||||
return if (this is NDBuffer<T> && this.strides == this@StridedNDField.strides) {
|
|
||||||
this
|
|
||||||
} else {
|
|
||||||
produce { index -> get(index) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun NDBuffer<T>.toElement(): StridedNDFieldElement<T, F> =
|
|
||||||
StridedNDFieldElement(this@StridedNDField, buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
class StridedNDFieldElement<T, F : Field<T>>(
|
|
||||||
override val context: StridedNDField<T, F>,
|
|
||||||
override val buffer: Buffer<T>
|
|
||||||
) :
|
|
||||||
NDBuffer<T>,
|
|
||||||
FieldElement<NDBuffer<T>, StridedNDFieldElement<T, F>, StridedNDField<T, F>>,
|
|
||||||
NDElement<T, F> {
|
|
||||||
|
|
||||||
override val elementField: F
|
|
||||||
get() = context.elementContext
|
|
||||||
|
|
||||||
override fun unwrap(): NDBuffer<T> =
|
|
||||||
this
|
|
||||||
|
|
||||||
override fun NDBuffer<T>.wrap(): StridedNDFieldElement<T, F> =
|
|
||||||
StridedNDFieldElement(context, this.buffer)
|
|
||||||
|
|
||||||
override val strides
|
|
||||||
get() = context.strides
|
|
||||||
|
|
||||||
override val shape: IntArray
|
|
||||||
get() = context.shape
|
|
||||||
|
|
||||||
override fun get(index: IntArray): T =
|
|
||||||
buffer[strides.offset(index)]
|
|
||||||
|
|
||||||
override fun elements(): Sequence<Pair<IntArray, T>> =
|
|
||||||
strides.indices().map { it to get(it) }
|
|
||||||
|
|
||||||
override fun map(action: F.(T) -> T) =
|
|
||||||
context.run { map(this@StridedNDFieldElement, action) }.wrap()
|
|
||||||
|
|
||||||
override fun mapIndexed(transform: F.(index: IntArray, T) -> T) =
|
|
||||||
context.run { mapIndexed(this@StridedNDFieldElement, transform) }.wrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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: StridedNDFieldElement<T, F>) =
|
|
||||||
ndElement.context.run { ndElement.map { invoke(it) } }
|
|
||||||
|
|
||||||
/* plus and minus */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Summation operation for [StridedNDFieldElement] and single element
|
|
||||||
*/
|
|
||||||
operator fun <T : Any, F : Field<T>> StridedNDFieldElement<T, F>.plus(arg: T) =
|
|
||||||
context.run { map { it + arg } }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subtraction operation between [StridedNDFieldElement] and single element
|
|
||||||
*/
|
|
||||||
operator fun <T : Any, F : Field<T>> StridedNDFieldElement<T, F>.minus(arg: T) =
|
|
||||||
context.run { map { it - arg } }
|
|
||||||
|
|
||||||
/* prod and div */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Product operation for [StridedNDFieldElement] and single element
|
|
||||||
*/
|
|
||||||
operator fun <T : Any, F : Field<T>> StridedNDFieldElement<T, F>.times(arg: T) =
|
|
||||||
context.run { map { it * arg } }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Division operation between [StridedNDFieldElement] and single element
|
|
||||||
*/
|
|
||||||
operator fun <T : Any, F : Field<T>> StridedNDFieldElement<T, F>.div(arg: T) =
|
|
||||||
context.run { map { it / arg } }
|
|
@ -29,7 +29,7 @@ fun MutableBuffer.Companion.complex(size: Int) =
|
|||||||
ObjectBuffer.create(ComplexBufferSpec, size)
|
ObjectBuffer.create(ComplexBufferSpec, size)
|
||||||
|
|
||||||
fun NDField.Companion.complex(shape: IntArray) =
|
fun NDField.Companion.complex(shape: IntArray) =
|
||||||
BufferNDField(shape, ComplexField) { size, init -> ObjectBuffer.create(ComplexBufferSpec, size, init) }
|
BoxingNDField(shape, ComplexField) { size, init -> ObjectBuffer.create(ComplexBufferSpec, size, init) }
|
||||||
|
|
||||||
fun NDElement.Companion.complex(shape: IntArray, initializer: ComplexField.(IntArray) -> Complex) =
|
fun NDElement.Companion.complex(shape: IntArray, initializer: ComplexField.(IntArray) -> Complex) =
|
||||||
NDField.complex(shape).produce(initializer)
|
NDField.complex(shape).produce(initializer)
|
||||||
|
@ -8,8 +8,7 @@ class LazyNDField<T, F : Field<T>>(
|
|||||||
override val shape: IntArray,
|
override val shape: IntArray,
|
||||||
override val elementContext: F,
|
override val elementContext: F,
|
||||||
val scope: CoroutineScope = GlobalScope
|
val scope: CoroutineScope = GlobalScope
|
||||||
) :
|
) : NDField<T, F, NDStructure<T>> {
|
||||||
NDField<T, F, NDStructure<T>> {
|
|
||||||
|
|
||||||
override val zero by lazy { produce { zero } }
|
override val zero by lazy { produce { zero } }
|
||||||
|
|
||||||
@ -65,7 +64,8 @@ class LazyNDField<T, F : Field<T>>(
|
|||||||
class LazyNDStructure<T, F : Field<T>>(
|
class LazyNDStructure<T, F : Field<T>>(
|
||||||
override val context: LazyNDField<T, F>,
|
override val context: LazyNDField<T, F>,
|
||||||
val function: suspend (IntArray) -> T
|
val function: suspend (IntArray) -> T
|
||||||
) : FieldElement<NDStructure<T>, LazyNDStructure<T, F>, LazyNDField<T, F>>, NDElement<T, F> {
|
) : FieldElement<NDStructure<T>, LazyNDStructure<T, F>, LazyNDField<T, F>>,
|
||||||
|
NDElement<T, F, NDStructure<T>> {
|
||||||
|
|
||||||
|
|
||||||
override fun unwrap(): NDStructure<T> = this
|
override fun unwrap(): NDStructure<T> = this
|
||||||
@ -73,10 +73,6 @@ class LazyNDStructure<T, F : Field<T>>(
|
|||||||
override fun NDStructure<T>.wrap(): LazyNDStructure<T, F> = LazyNDStructure(context) { await(it) }
|
override fun NDStructure<T>.wrap(): LazyNDStructure<T, F> = LazyNDStructure(context) { await(it) }
|
||||||
|
|
||||||
override val shape: IntArray get() = context.shape
|
override val shape: IntArray get() = context.shape
|
||||||
override val elementField: F get() = context.elementContext
|
|
||||||
|
|
||||||
override fun mapIndexed(transform: F.(index: IntArray, T) -> T): NDElement<T, F> =
|
|
||||||
context.run { mapIndexed(this@LazyNDStructure, transform) }
|
|
||||||
|
|
||||||
private val cache = HashMap<IntArray, Deferred<T>>()
|
private val cache = HashMap<IntArray, Deferred<T>>()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user