Adjustments to RealNDField

This commit is contained in:
Alexander Nozik 2018-12-31 13:48:27 +03:00
parent 334dadc6bf
commit b47cdd12c2
7 changed files with 192 additions and 21 deletions

View File

@ -0,0 +1,42 @@
package scientifik.kmath.structures
import scientifik.kmath.operations.DoubleField
import kotlin.system.measureTimeMillis
fun main(args: Array<String>) {
val dim = 1000
val n = 1000
val genericField = NDField.generic(intArrayOf(dim, dim), DoubleField)
val doubleField = NDField.inline(intArrayOf(dim, dim), DoubleField)
val specializedField = NDField.real(intArrayOf(dim, dim))
val doubleTime = measureTimeMillis {
var res = doubleField.produce { one }
repeat(n) {
res += 1.0
}
}
println("Inlined addition completed in $doubleTime millis")
val specializedTime = measureTimeMillis {
var res = specializedField.produce { one }
repeat(n) {
res += 1.0
}
}
println("Specialized addition completed in $specializedTime millis")
val genericTime = measureTimeMillis {
var res = genericField.produce { one }
repeat(n) {
res += 1.0
}
}
println("Generic addition completed in $genericTime millis")
}

View File

@ -2,15 +2,36 @@ package scientifik.kmath.structures
import scientifik.kmath.operations.Field
class BufferNDField<T : Any, F : Field<T>>(override val shape: IntArray, override val field: F, val bufferFactory: BufferFactory<T>) : NDField<T, F> {
open class BufferNDField<T, F : Field<T>>(final override val shape: IntArray, final override val field: F, val bufferFactory: BufferFactory<T>) : NDField<T, F> {
val strides = DefaultStrides(shape)
override inline fun produce(crossinline initializer: F.(IntArray) -> T): NDElement<T, F> {
override fun produce(initializer: F.(IntArray) -> T): BufferNDElement<T, F> {
return BufferNDElement(this, bufferFactory(strides.linearSize) { offset -> field.initializer(strides.index(offset)) })
}
open fun produceBuffered(initializer: F.(Int) -> T) =
BufferNDElement(this, bufferFactory(strides.linearSize) { offset -> field.initializer(offset) })
// override fun add(a: NDStructure<T>, b: NDStructure<T>): NDElement<T, F> {
// checkShape(a, b)
// return if (a is BufferNDElement<T, *> && b is BufferNDElement<T, *>) {
// BufferNDElement(this,bufferFactory(strides.linearSize){i-> field.run { a.buffer[i] + b.buffer[i]}})
// } else {
// produce { field.run { a[it] + b[it] } }
// }
// }
//
// override fun NDStructure<T>.plus(b: Number): NDElement<T,F> {
// checkShape(this)
// return if (this is BufferNDElement<T, *>) {
// BufferNDElement(this@BufferNDField,bufferFactory(strides.linearSize){i-> field.run { this@plus.buffer[i] + b}})
// } else {
// produce {index -> field.run { this@plus[index] + b } }
// }
// }
}
class BufferNDElement<T : Any, F : Field<T>>(override val context: BufferNDField<T, F>, private val buffer: Buffer<T>) : NDElement<T, F> {
class BufferNDElement<T, F : Field<T>>(override val context: BufferNDField<T, F>, val buffer: Buffer<T>) : NDElement<T, F> {
override val self: NDStructure<T> get() = this
override val shape: IntArray get() = context.shape
@ -19,4 +40,39 @@ class BufferNDElement<T : Any, F : Field<T>>(override val context: BufferNDField
override fun elements(): Sequence<Pair<IntArray, T>> = context.strides.indices().map { it to get(it) }
}
}
/**
* 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>) =
ndElement.context.produceBuffered { i -> invoke(ndElement.buffer[i]) }
/* plus and minus */
/**
* Summation operation for [BufferNDElement] and single element
*/
operator fun <T : Any, F : Field<T>> BufferNDElement<T, F>.plus(arg: T) =
context.produceBuffered { i -> buffer[i] + arg }
/**
* Subtraction operation between [BufferNDElement] and single element
*/
operator fun <T: Any, F : Field<T>> BufferNDElement<T, F>.minus(arg: T) =
context.produceBuffered { i -> buffer[i] - arg }
/* prod and div */
/**
* Product operation for [BufferNDElement] and single element
*/
operator fun <T: Any, F : Field<T>> BufferNDElement<T, F>.times(arg: T) =
context.produceBuffered { i -> buffer[i] * arg }
/**
* Division operation between [BufferNDElement] and single element
*/
operator fun <T: Any, F : Field<T>> BufferNDElement<T, F>.div(arg: T) =
context.produceBuffered { i -> buffer[i] / arg }

View File

@ -133,13 +133,13 @@ fun <T> Buffer<T>.asReadOnly(): Buffer<T> = if (this is MutableBuffer) {
/**
* Create a boxing buffer of given type
*/
fun <T : Any> boxingBuffer(size: Int, initializer: (Int) -> T): Buffer<T> = ListBuffer(List(size, initializer))
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> inlineBuffer(size: Int, noinline initializer: (Int) -> T): Buffer<T> {
inline fun <reified T : Any> inlineBuffer(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>
@ -151,13 +151,13 @@ inline fun <reified T : Any> inlineBuffer(size: Int, noinline initializer: (Int)
/**
* Create a boxing mutable buffer of given type
*/
fun <T : Any> boxingMutableBuffer(size: Int, initializer: (Int) -> T): MutableBuffer<T> = MutableListBuffer(MutableList(size, initializer))
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> inlineMutableBuffer(size: Int, noinline initializer: (Int) -> T): MutableBuffer<T> {
inline fun <reified T : Any> inlineMutableBuffer(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>

View File

@ -6,13 +6,17 @@ import scientifik.kmath.operations.PowerOperations
import scientifik.kmath.operations.TrigonometricOperations
interface ExtendedNDField<T : Any, F : ExtendedField<T>> :
NDField<T, F>,
TrigonometricOperations<NDStructure<T>>,
PowerOperations<NDStructure<T>>,
ExponentialOperations<NDStructure<T>>
/**
* NDField that supports [ExtendedField] operations on its elements
*/
inline class ExtendedNDField<T : Any, F : ExtendedField<T>>(private val ndField: NDField<T, F>) : NDField<T, F>,
TrigonometricOperations<NDStructure<T>>,
PowerOperations<NDStructure<T>>,
ExponentialOperations<NDStructure<T>> {
inline class ExtendedNDFieldWrapper<T : Any, F : ExtendedField<T>>(private val ndField: NDField<T, F>) : ExtendedNDField<T, F> {
override val shape: IntArray get() = ndField.shape
override val field: F get() = ndField.field

View File

@ -73,12 +73,11 @@ interface NDField<T, F : Field<T>> : Field<NDStructure<T>> {
return produce { field.run { a[it] / b[it] } }
}
companion object {
/**
* Create a nd-field for [Double] values
*/
fun real(shape: IntArray) = ExtendedNDField(BufferNDField(shape, DoubleField, DoubleBufferFactory))
fun real(shape: IntArray) = RealNDField(shape)
/**
* Create a nd-field with boxing generic buffer
@ -123,11 +122,11 @@ interface NDElement<T, F : Field<T>> : FieldElement<NDStructure<T>, NDField<T, F
* Simple boxing NDArray
*/
fun <T : Any, F : Field<T>> generic(shape: IntArray, field: F, initializer: F.(IntArray) -> T): NDElement<T, F> {
return NDField.generic(shape,field).produce(initializer)
return NDField.generic(shape, field).produce(initializer)
}
inline fun <reified T : Any, F : Field<T>> inline(shape: IntArray, field: F, crossinline initializer: F.(IntArray) -> T): NDElement<T, F> {
return NDField.inline(shape,field).produce(initializer)
inline fun <reified T : Any, F : Field<T>> inline(shape: IntArray, field: F, noinline initializer: F.(IntArray) -> T): NDElement<T, F> {
return NDField.inline(shape, field).produce(initializer)
}
}
}

View File

@ -0,0 +1,73 @@
package scientifik.kmath.structures
import scientifik.kmath.operations.DoubleField
typealias RealNDElement = BufferNDElement<Double, DoubleField>
class RealNDField(shape: IntArray) : BufferNDField<Double, DoubleField>(shape, DoubleField, DoubleBufferFactory), ExtendedNDField<Double, DoubleField> {
/**
* Inline map an NDStructure to
*/
private inline fun NDStructure<Double>.mapInline(crossinline operation: DoubleField.(Double) -> Double): RealNDElement {
return if (this is BufferNDElement<Double, *>) {
val array = DoubleArray(strides.linearSize) { offset -> DoubleField.operation(buffer[offset]) }
BufferNDElement(this@RealNDField, DoubleBuffer(array))
} else {
produce { index -> DoubleField.operation(get(index)) }
}
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun produce(initializer: DoubleField.(IntArray) -> Double): RealNDElement {
val array = DoubleArray(strides.linearSize) { offset -> field.initializer(strides.index(offset)) }
return BufferNDElement(this, DoubleBuffer(array))
}
override fun power(arg: NDStructure<Double>, pow: Double) = arg.mapInline { power(it, pow) }
override fun exp(arg: NDStructure<Double>) = arg.mapInline { exp(it) }
override fun ln(arg: NDStructure<Double>) = arg.mapInline { ln(it) }
override fun sin(arg: NDStructure<Double>) = arg.mapInline { sin(it) }
override fun cos(arg: NDStructure<Double>) = arg.mapInline { cos(it) }
override fun NDStructure<Double>.times(k: Number) = mapInline { value -> value * k.toDouble() }
override fun NDStructure<Double>.div(k: Number) = mapInline { value -> value / k.toDouble() }
override fun Number.times(b: NDStructure<Double>) = b * this
override fun Number.div(b: NDStructure<Double>) = b * (1.0 / this.toDouble())
}
/**
* Fast element production using function inlining
*/
inline fun BufferNDField<Double, DoubleField>.produceInline(crossinline initializer: DoubleField.(Int) -> Double): RealNDElement {
val array = DoubleArray(strides.linearSize) { offset -> field.initializer(offset) }
return BufferNDElement(this, DoubleBuffer(array))
}
/**
* Element by element application of any operation on elements to the whole array. Just like in numpy
*/
operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement) =
ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) }
/* plus and minus */
/**
* Summation operation for [BufferNDElement] and single element
*/
operator fun RealNDElement.plus(arg: Double) =
context.produceInline { i -> buffer[i] + arg }
/**
* Subtraction operation between [BufferNDElement] and single element
*/
operator fun RealNDElement.minus(arg: Double) =
context.produceInline { i -> buffer[i] - arg }

View File

@ -3,10 +3,7 @@ package scientifik.kmath.structures
import kotlinx.coroutines.*
import scientifik.kmath.operations.Field
class LazyNDField<T, F : Field<T>>(shape: IntArray, field: F, val scope: CoroutineScope = GlobalScope) : NDField<T, F>(shape, field) {
override fun produceStructure(initializer: F.(IntArray) -> T): NDStructure<T> = LazyNDStructure(this) { initializer(field, it) }
class LazyNDField<T, F : Field<T>>(shape: IntArray, field: F, val scope: CoroutineScope = GlobalScope) : BufferNDField<T, F>(shape,field, ::boxingBuffer) {
override fun add(a: NDStructure<T>, b: NDStructure<T>): NDElement<T, F> {
return LazyNDStructure(this) { index ->