Adjustments to RealNDField
This commit is contained in:
parent
334dadc6bf
commit
b47cdd12c2
@ -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")
|
||||
}
|
@ -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 }
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
@ -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 ->
|
||||
|
Loading…
Reference in New Issue
Block a user