Cleanup after nd optimization phase I

This commit is contained in:
Alexander Nozik 2019-01-08 10:35:00 +03:00
parent 2bf34f2a07
commit 05d15d5b34
14 changed files with 403 additions and 326 deletions

View File

@ -39,7 +39,7 @@ fun main(args: Array<String>) {
val specializedTime = measureTimeMillis {
specializedField.run {
var res = one
var res:NDBuffer<Double> = one
repeat(n) {
res += 1.0
}

View File

@ -38,21 +38,6 @@ interface Space<T> {
fun Sequence<T>.sum(): T = fold(zero) { left, right -> left + right }
}
abstract class AbstractSpace<T> : Space<T> {
//TODO move to external extensions when they are available
final override operator fun T.unaryMinus(): T = multiply(this, -1.0)
final override operator fun T.plus(b: T): T = add(this, b)
final override operator fun T.minus(b: T): T = add(this, -b)
final override operator fun T.times(k: Number) = multiply(this, k.toDouble())
final override operator fun T.div(k: Number) = multiply(this, 1.0 / k.toDouble())
final override operator fun Number.times(b: T) = b * this
final override fun Iterable<T>.sum(): T = fold(zero) { left, right -> left + right }
final override fun Sequence<T>.sum(): T = fold(zero) { left, right -> left + right }
}
/**
* The same as {@link Space} but with additional multiplication operation
*/
@ -76,10 +61,6 @@ interface Ring<T> : Space<T> {
// operator fun Number.minus(b: T) = -b + this
}
abstract class AbstractRing<T : Any> : AbstractSpace<T>(), Ring<T> {
final override operator fun T.times(b: T): T = multiply(this, b)
}
/**
* Four operations algebra
*/
@ -89,8 +70,3 @@ interface Field<T> : Ring<T> {
operator fun T.div(b: T): T = divide(this, b)
operator fun Number.div(b: T) = this * divide(one, b)
}
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 Number.div(b: T) = this * divide(one, b)
}

View File

@ -32,7 +32,7 @@ inline class Real(val value: Double) : FieldElement<Double, Real, RealField> {
/**
* A field for double without boxing. Does not produce appropriate field element
*/
object RealField : AbstractField<Double>(), ExtendedField<Double>, Norm<Double, Double> {
object RealField : ExtendedField<Double>, Norm<Double, Double> {
override val zero: Double = 0.0
override fun add(a: Double, b: Double): Double = a + b
override fun multiply(a: Double, @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") b: Double): Double = a * b
@ -54,41 +54,51 @@ object RealField : AbstractField<Double>(), ExtendedField<Double>, Norm<Double,
/**
* A field for [Int] without boxing. Does not produce corresponding field element
*/
object IntRing : Ring<Int> {
object IntRing : Ring<Int>, Norm<Int,Int> {
override val zero: Int = 0
override fun add(a: Int, b: Int): Int = a + b
override fun multiply(a: Int, b: Int): Int = a * b
override fun multiply(a: Int, k: Double): Int = (k * a).toInt()
override val one: Int = 1
override fun norm(arg: Int): Int = arg
}
/**
* A field for [Short] without boxing. Does not produce appropriate field element
*/
object ShortRing : Ring<Short> {
object ShortRing : Ring<Short>, Norm<Short,Short>{
override val zero: Short = 0
override fun add(a: Short, b: Short): Short = (a + b).toShort()
override fun multiply(a: Short, b: Short): Short = (a * b).toShort()
override fun multiply(a: Short, k: Double): Short = (a * k).toShort()
override val one: Short = 1
override fun norm(arg: Short): Short = arg
}
//interface FieldAdapter<T, R> : Field<R> {
//
// val field: Field<T>
//
// abstract fun T.evolve(): R
// abstract fun R.devolve(): T
//
// override val zero get() = field.zero.evolve()
// override val one get() = field.zero.evolve()
//
// override fun add(a: R, b: R): R = field.add(a.devolve(), b.devolve()).evolve()
//
// override fun multiply(a: R, k: Double): R = field.multiply(a.devolve(), k).evolve()
//
//
// override fun multiply(a: R, b: R): R = field.multiply(a.devolve(), b.devolve()).evolve()
//
// override fun divide(a: R, b: R): R = field.divide(a.devolve(), b.devolve()).evolve()
//}
/**
* A field for [Byte] values
*/
object ByteRing : Ring<Byte>, Norm<Byte,Byte> {
override val zero: Byte = 0
override fun add(a: Byte, b: Byte): Byte = (a + b).toByte()
override fun multiply(a: Byte, b: Byte): Byte = (a * b).toByte()
override fun multiply(a: Byte, k: Double): Byte = (a * k).toByte()
override val one: Byte = 1
override fun norm(arg: Byte): Byte = arg
}
/**
* A field for [Long] values
*/
object LongRing : Ring<Long>, Norm<Long,Long> {
override val zero: Long = 0
override fun add(a: Long, b: Long): Long = (a + b)
override fun multiply(a: Long, b: Long): Long = (a * b)
override fun multiply(a: Long, k: Double): Long = (a * k).toLong()
override val one: Long = 1
override fun norm(arg: Long): Long = arg
}

View File

@ -1,38 +1,13 @@
package scientifik.kmath.structures
import scientifik.kmath.operations.Field
import scientifik.kmath.operations.FieldElement
abstract class StridedNDField<T, F : Field<T>>(shape: IntArray, elementField: F) :
AbstractNDField<T, F, NDBuffer<T>>(shape, elementField) {
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(): StridedNDElement<T, F> =
StridedNDElement(this@StridedNDField, buffer)
}
class BufferNDField<T, F : Field<T>>(
shape: IntArray,
elementField: F,
override val elementContext: F,
val bufferFactory: BufferFactory<T>
) : StridedNDField<T, F>(shape, elementField) {
) : StridedNDField<T, F>(shape), NDField<T, F, NDBuffer<T>> {
override fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T> = bufferFactory(size, initializer)
@ -43,27 +18,27 @@ class BufferNDField<T, F : Field<T>>(
override val zero by lazy { produce { zero } }
override val one by lazy { produce { one } }
override fun produce(initializer: F.(IntArray) -> T): StridedNDElement<T, F> =
StridedNDElement(
override fun produce(initializer: F.(IntArray) -> T): StridedNDFieldElement<T, F> =
StridedNDFieldElement(
this,
buildBuffer(strides.linearSize) { offset -> elementField.initializer(strides.index(offset)) })
buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) })
override fun map(arg: NDBuffer<T>, transform: F.(T) -> T): StridedNDElement<T, F> {
override fun map(arg: NDBuffer<T>, transform: F.(T) -> T): StridedNDFieldElement<T, F> {
check(arg)
return StridedNDElement(
return StridedNDFieldElement(
this,
buildBuffer(arg.strides.linearSize) { offset -> elementField.transform(arg.buffer[offset]) })
buildBuffer(arg.strides.linearSize) { offset -> elementContext.transform(arg.buffer[offset]) })
}
override fun mapIndexed(
arg: NDBuffer<T>,
transform: F.(index: IntArray, T) -> T
): StridedNDElement<T, F> {
): StridedNDFieldElement<T, F> {
check(arg)
return StridedNDElement(
return StridedNDFieldElement(
this,
buildBuffer(arg.strides.linearSize) { offset ->
elementField.transform(
elementContext.transform(
arg.strides.index(offset),
arg.buffer[offset]
)
@ -74,77 +49,10 @@ class BufferNDField<T, F : Field<T>>(
a: NDBuffer<T>,
b: NDBuffer<T>,
transform: F.(T, T) -> T
): StridedNDElement<T, F> {
): StridedNDFieldElement<T, F> {
check(a, b)
return StridedNDElement(
return StridedNDFieldElement(
this,
buildBuffer(strides.linearSize) { offset -> elementField.transform(a.buffer[offset], b.buffer[offset]) })
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) })
}
}
class StridedNDElement<T, F : Field<T>>(override val context: StridedNDField<T, F>, override val buffer: Buffer<T>) :
NDBuffer<T>,
FieldElement<NDBuffer<T>, StridedNDElement<T, F>, StridedNDField<T, F>>,
NDElement<T, F> {
override val elementField: F
get() = context.elementField
override fun unwrap(): NDBuffer<T> =
this
override fun NDBuffer<T>.wrap(): StridedNDElement<T, F> =
StridedNDElement(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@StridedNDElement, action) }.wrap()
override fun mapIndexed(transform: F.(index: IntArray, T) -> T) =
context.run { mapIndexed(this@StridedNDElement, 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: StridedNDElement<T, F>) =
ndElement.context.run { ndElement.map { invoke(it) } }
/* plus and minus */
/**
* Summation operation for [StridedNDElement] and single element
*/
operator fun <T : Any, F : Field<T>> StridedNDElement<T, F>.plus(arg: T) =
context.run { map { it + arg } }
/**
* Subtraction operation between [StridedNDElement] and single element
*/
operator fun <T : Any, F : Field<T>> StridedNDElement<T, F>.minus(arg: T) =
context.run { map { it - arg } }
/* prod and div */
/**
* Product operation for [StridedNDElement] and single element
*/
operator fun <T : Any, F : Field<T>> StridedNDElement<T, F>.times(arg: T) =
context.run { map { it * arg } }
/**
* Division operation between [StridedNDElement] and single element
*/
operator fun <T : Any, F : Field<T>> StridedNDElement<T, F>.div(arg: T) =
context.run { map { it / arg } }

View File

@ -33,6 +33,7 @@ interface Buffer<T> {
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>
Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) 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)
@ -40,6 +41,7 @@ interface Buffer<T> {
}
val DoubleBufferFactory: BufferFactory<Double> = { 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 LongBufferFactory: BufferFactory<Long> = { size, initializer -> LongBuffer(LongArray(size, initializer)) }
}
@ -71,6 +73,7 @@ interface MutableBuffer<T> : Buffer<T> {
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>
Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) 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)
@ -136,6 +139,20 @@ inline class DoubleBuffer(private val array: DoubleArray) : MutableBuffer<Double
override fun copy(): MutableBuffer<Double> = DoubleBuffer(array.copyOf())
}
inline class ShortBuffer(private val array: ShortArray) : MutableBuffer<Short> {
override val size: Int get() = array.size
override fun get(index: Int): Short = array[index]
override fun set(index: Int, value: Short) {
array[index] = value
}
override fun iterator(): Iterator<Short> = array.iterator()
override fun copy(): MutableBuffer<Short> = ShortBuffer(array.copyOf())
}
inline class IntBuffer(private val array: IntArray) : MutableBuffer<Int> {
override val size: Int get() = array.size

View File

@ -13,36 +13,36 @@ interface ExtendedNDField<T : Any, F : ExtendedField<T>, N : NDStructure<T>> :
ExponentialOperations<N>
/**
* NDField that supports [ExtendedField] operations on its elements
*/
class ExtendedNDFieldWrapper<T : Any, F : ExtendedField<T>, N : NDStructure<T>>(private val ndField: NDField<T, F, N>) :
ExtendedNDField<T, F, N>, NDField<T, F, N> by ndField {
override val shape: IntArray get() = ndField.shape
override val elementField: F get() = ndField.elementField
override fun produce(initializer: F.(IntArray) -> T) = ndField.produce(initializer)
override fun power(arg: N, pow: Double): N {
return produce { with(elementField) { power(arg[it], pow) } }
}
override fun exp(arg: N): N {
return produce { with(elementField) { exp(arg[it]) } }
}
override fun ln(arg: N): N {
return produce { with(elementField) { ln(arg[it]) } }
}
override fun sin(arg: N): N {
return produce { with(elementField) { sin(arg[it]) } }
}
override fun cos(arg: N): N {
return produce { with(elementField) { cos(arg[it]) } }
}
}
///**
// * NDField that supports [ExtendedField] operations on its elements
// */
//class ExtendedNDFieldWrapper<T : Any, F : ExtendedField<T>, N : NDStructure<T>>(private val ndField: NDField<T, F, N>) :
// ExtendedNDField<T, F, N>, NDField<T, F, N> by ndField {
//
// override val shape: IntArray get() = ndField.shape
// override val elementContext: F get() = ndField.elementContext
//
// override fun produce(initializer: F.(IntArray) -> T) = ndField.produce(initializer)
//
// override fun power(arg: N, pow: Double): N {
// return produce { with(elementContext) { power(arg[it], pow) } }
// }
//
// override fun exp(arg: N): N {
// return produce { with(elementContext) { exp(arg[it]) } }
// }
//
// override fun ln(arg: N): N {
// return produce { with(elementContext) { ln(arg[it]) } }
// }
//
// override fun sin(arg: N): N {
// return produce { with(elementContext) { sin(arg[it]) } }
// }
//
// override fun cos(arg: N): N {
// return produce { with(elementContext) { cos(arg[it]) } }
// }
//}

View File

@ -1,14 +1,62 @@
package scientifik.kmath.structures
import scientifik.kmath.operations.AbstractField
import scientifik.kmath.operations.Field
import scientifik.kmath.structures.Buffer.Companion.boxing
import scientifik.kmath.operations.Ring
import scientifik.kmath.operations.Space
/**
* An exception is thrown when the expected ans actual shape of NDArray differs
*/
class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : RuntimeException()
interface NDSpace<T, S : Space<T>, N : NDStructure<T>> : Space<N> {
val shape: IntArray
val elementContext: S
/**
* Produce a new [N] structure using given initializer function
*/
fun produce(initializer: S.(IntArray) -> T): N
fun map(arg: N, transform: S.(T) -> T): N
fun mapIndexed(arg: N, transform: S.(index: IntArray, T) -> T): N
fun combine(a: N, b: N, transform: S.(T, T) -> T): N
/**
* Element-by-element addition
*/
override fun add(a: N, b: N): N =
combine(a, b) { aValue, bValue -> aValue + bValue }
/**
* Multiply all elements by constant
*/
override fun multiply(a: N, k: Double): N =
map(a) { 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 -> elementContext.run { 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
}
interface NDRing<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T, R, N> {
/**
* Element-by-element multiplication
*/
override fun multiply(a: N, b: N): N =
combine(a, b) { aValue, bValue -> aValue * bValue }
operator fun N.times(arg: T) = map(this) { value -> elementContext.run { arg * value } }
operator fun T.times(arg: N) = arg * this
}
/**
* Field for n-dimensional arrays.
* @param shape - the list of dimensions of the array
@ -17,21 +65,31 @@ class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : Run
* @param F - field over structure elements
* @param R - actual nd-element type of this field
*/
interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N> {
interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F, N> {
val shape: IntArray
val elementField: F
/**
* Element-by-element division
*/
override fun divide(a: N, b: N): N =
combine(a, b) { aValue, bValue -> aValue / bValue }
fun produce(initializer: F.(IntArray) -> T): N
fun map(arg: N, transform: F.(T) -> T): N
fun mapIndexed(arg: N, transform: F.(index: IntArray, T) -> T): N
fun combine(a: N, b: N, transform: F.(T, T) -> T): N
operator fun N.div(arg: T) = map(this) { value -> elementContext.run { arg / value } }
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 {
/**
* Create a nd-field for [Double] values
*/
fun real(shape: IntArray) = RealNDField(shape)
/**
* Create a nd-field with boxing generic buffer
*/
@ -49,61 +107,3 @@ interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N> {
}
}
}
abstract class AbstractNDField<T, F : Field<T>, N : NDStructure<T>>(
override val shape: IntArray,
override val elementField: F
) : AbstractField<N>(), NDField<T, F, N> {
override val zero: N by lazy { produce { zero } }
override val one: N by lazy { produce { one } }
operator fun Function1<T, T>.invoke(structure: N) = map(structure) { value -> this@invoke(value) }
operator fun N.plus(arg: T) = map(this) { value -> elementField.run { arg + value } }
operator fun N.minus(arg: T) = map(this) { value -> elementField.run { arg - value } }
operator fun N.times(arg: T) = map(this) { value -> elementField.run { arg * value } }
operator fun N.div(arg: T) = map(this) { value -> elementField.run { arg / value } }
operator fun T.plus(arg: N) = arg + this
operator fun T.minus(arg: N) = arg - this
operator fun T.times(arg: N) = arg * this
operator fun T.div(arg: N) = arg / this
/**
* Element-by-element addition
*/
override fun add(a: N, b: N): N =
combine(a, b) { aValue, bValue -> aValue + bValue }
/**
* Multiply all elements by cinstant
*/
override fun multiply(a: N, k: Double): N =
map(a) { it * k }
/**
* Element-by-element multiplication
*/
override fun multiply(a: N, b: N): N =
combine(a, b) { aValue, bValue -> aValue * bValue }
/**
* Element-by-element division
*/
override fun divide(a: N, b: N): N =
combine(a, b) { aValue, bValue -> aValue / bValue }
/**
* Check if given objects are compatible with this context. Throw exception if they are not
*/
open fun check(vararg elements: N) {
elements.forEach {
if (!shape.contentEquals(it.shape)) {
throw ShapeMismatchException(shape, it.shape)
}
}
}
}

View File

@ -3,13 +3,14 @@ package scientifik.kmath.structures
import scientifik.kmath.operations.Field
import scientifik.kmath.operations.FieldElement
import scientifik.kmath.operations.RealField
import scientifik.kmath.operations.Space
interface NDElement<T, F : Field<T>> : NDStructure<T> {
val elementField: F
interface NDElement<T, S : Space<T>> : NDStructure<T> {
val elementField: S
fun mapIndexed(transform: F.(index: IntArray, T) -> T): NDElement<T, F>
fun map(action: F.(T) -> T) = mapIndexed { _, value -> action(value) }
fun mapIndexed(transform: S.(index: IntArray, T) -> T): NDElement<T, S>
fun map(action: S.(T) -> T) = mapIndexed { _, value -> action(value) }
companion object {
/**
@ -37,7 +38,7 @@ interface NDElement<T, F : Field<T>> : NDStructure<T> {
shape: IntArray,
field: F,
initializer: F.(IntArray) -> T
): StridedNDElement<T, F> {
): StridedNDFieldElement<T, F> {
val ndField = BufferNDField(shape, field, Buffer.Companion::boxing)
return ndField.produce(initializer)
}
@ -46,9 +47,9 @@ interface NDElement<T, F : Field<T>> : NDStructure<T> {
shape: IntArray,
field: F,
noinline initializer: F.(IntArray) -> T
): StridedNDElement<T, F> {
): StridedNDFieldElement<T, F> {
val ndField = NDField.auto(shape, field)
return StridedNDElement(ndField, ndField.produce(initializer).buffer)
return StridedNDFieldElement(ndField, ndField.produce(initializer).buffer)
}
}
}
@ -121,19 +122,19 @@ operator fun <T, F : Field<T>> NDElement<T, F>.div(arg: T): NDElement<T, F> =
/**
* Read-only [NDStructure] coupled to the context.
*/
class GenericNDElement<T, F : Field<T>>(
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>, GenericNDElement<T, F>, NDField<T, F, NDStructure<T>>> {
override val elementField: F get() = context.elementField
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() = GenericNDElement(context, this)
override fun NDStructure<T>.wrap() = GenericNDFieldElement(context, this)
override fun mapIndexed(transform: F.(index: IntArray, T) -> T) =
ndStructure(context.shape) { index: IntArray -> context.elementField.transform(index, get(index)) }.wrap()
ndStructure(context.shape) { index: IntArray -> context.elementContext.transform(index, get(index)) }.wrap()
}

View File

@ -241,18 +241,3 @@ inline fun <reified T : Any> NDStructure<T>.combine(
if (!this.shape.contentEquals(struct.shape)) error("Shape mismatch in structure combination")
return inlineNdStructure(shape) { block(this[it], struct[it]) }
}
///**
// * Create universal mutable structure
// */
//fun <T> genericNdStructure(shape: IntArray, initializer: (IntArray) -> T): MutableBufferNDStructure<T> {
// val strides = DefaultStrides(shape)
// val sequence = sequence {
// strides.indices().forEach {
// yield(initializer(it))
// }
// }
// val buffer = MutableListBuffer(sequence.toMutableList())
// return MutableBufferNDStructure(strides, buffer)
//}

View File

@ -2,12 +2,16 @@ package scientifik.kmath.structures
import scientifik.kmath.operations.RealField
typealias RealNDElement = StridedNDElement<Double, RealField>
typealias RealNDElement = StridedNDFieldElement<Double, RealField>
class RealNDField(shape: IntArray) :
StridedNDField<Double, RealField>(shape, RealField),
StridedNDField<Double, RealField>(shape),
ExtendedNDField<Double, RealField, NDBuffer<Double>> {
override val elementContext: RealField get() = RealField
override val zero by lazy { produce { zero } }
override val one by lazy { produce { one } }
@Suppress("OVERRIDE_BY_INLINE")
override inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer<Double> =
DoubleBuffer(DoubleArray(size) { initializer(it) })
@ -21,23 +25,23 @@ class RealNDField(shape: IntArray) :
): RealNDElement {
check(arg)
val array = buildBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) }
return StridedNDElement(this, array)
return StridedNDFieldElement(this, array)
}
override fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
val array = buildBuffer(strides.linearSize) { offset -> elementField.initializer(strides.index(offset)) }
return StridedNDElement(this, array)
val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
return StridedNDFieldElement(this, array)
}
override fun mapIndexed(
arg: NDBuffer<Double>,
transform: RealField.(index: IntArray, Double) -> Double
): StridedNDElement<Double, RealField> {
): StridedNDFieldElement<Double, RealField> {
check(arg)
return StridedNDElement(
return StridedNDFieldElement(
this,
buildBuffer(arg.strides.linearSize) { offset ->
elementField.transform(
elementContext.transform(
arg.strides.index(offset),
arg.buffer[offset]
)
@ -48,11 +52,11 @@ class RealNDField(shape: IntArray) :
a: NDBuffer<Double>,
b: NDBuffer<Double>,
transform: RealField.(Double, Double) -> Double
): StridedNDElement<Double, RealField> {
): StridedNDFieldElement<Double, RealField> {
check(a, b)
return StridedNDElement(
return StridedNDFieldElement(
this,
buildBuffer(strides.linearSize) { offset -> elementField.transform(a.buffer[offset], b.buffer[offset]) })
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) })
}
override fun power(arg: NDBuffer<Double>, pow: Double) = map(arg) { power(it, pow) }
@ -64,14 +68,7 @@ class RealNDField(shape: IntArray) :
override fun sin(arg: NDBuffer<Double>) = map(arg) { sin(it) }
override fun cos(arg: NDBuffer<Double>) = map(arg) { cos(it) }
//
// override fun NDBuffer<Double>.times(k: Number) = mapInline { value -> value * k.toDouble() }
//
// override fun NDBuffer<Double>.div(k: Number) = mapInline { value -> value / k.toDouble() }
//
// override fun Number.times(b: NDBuffer<Double>) = b * this
//
// override fun Number.div(b: NDBuffer<Double>) = b * (1.0 / this.toDouble())
}
@ -80,7 +77,7 @@ class RealNDField(shape: IntArray) :
*/
inline fun StridedNDField<Double, RealField>.produceInline(crossinline initializer: RealField.(Int) -> Double): RealNDElement {
val array = DoubleArray(strides.linearSize) { offset -> RealField.initializer(offset) }
return StridedNDElement(this, DoubleBuffer(array))
return StridedNDFieldElement(this, DoubleBuffer(array))
}
/**
@ -93,13 +90,13 @@ operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement) =
/* plus and minus */
/**
* Summation operation for [StridedNDElement] and single element
* Summation operation for [StridedNDFieldElement] and single element
*/
operator fun RealNDElement.plus(arg: Double) =
context.produceInline { i -> buffer[i] + arg }
/**
* Subtraction operation between [StridedNDElement] and single element
* Subtraction operation between [StridedNDFieldElement] and single element
*/
operator fun RealNDElement.minus(arg: Double) =
context.produceInline { i -> buffer[i] - arg }

View File

@ -0,0 +1,86 @@
//package scientifik.kmath.structures
//
//import scientifik.kmath.operations.ShortRing
//
//
////typealias ShortNDElement = StridedNDFieldElement<Short, ShortRing>
//
//class ShortNDRing(shape: IntArray) :
// NDRing<Short, ShortRing, NDBuffer<Short>> {
//
// inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Short): Buffer<Short> =
// ShortBuffer(ShortArray(size) { initializer(it) })
//
// /**
// * Inline transform an NDStructure to
// */
// override fun map(
// arg: NDBuffer<Short>,
// transform: ShortRing.(Short) -> Short
// ): ShortNDElement {
// check(arg)
// val array = buildBuffer(arg.strides.linearSize) { offset -> ShortRing.transform(arg.buffer[offset]) }
// return StridedNDFieldElement(this, array)
// }
//
// override fun produce(initializer: ShortRing.(IntArray) -> Short): ShortNDElement {
// val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
// return StridedNDFieldElement(this, array)
// }
//
// override fun mapIndexed(
// arg: NDBuffer<Short>,
// transform: ShortRing.(index: IntArray, Short) -> Short
// ): StridedNDFieldElement<Short, ShortRing> {
// check(arg)
// return StridedNDFieldElement(
// this,
// buildBuffer(arg.strides.linearSize) { offset ->
// elementContext.transform(
// arg.strides.index(offset),
// arg.buffer[offset]
// )
// })
// }
//
// override fun combine(
// a: NDBuffer<Short>,
// b: NDBuffer<Short>,
// transform: ShortRing.(Short, Short) -> Short
// ): StridedNDFieldElement<Short, ShortRing> {
// check(a, b)
// return StridedNDFieldElement(
// this,
// buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) })
// }
//}
//
//
///**
// * Fast element production using function inlining
// */
//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))
//}
//
///**
// * 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) =
// ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) }
//
//
///* plus and minus */
//
///**
// * Summation operation for [StridedNDFieldElement] and single element
// */
//operator fun ShortNDElement.plus(arg: Short) =
// context.produceInline { i -> buffer[i] + arg }
//
///**
// * Subtraction operation between [StridedNDFieldElement] and single element
// */
//operator fun ShortNDElement.minus(arg: Short) =
// context.produceInline { i -> buffer[i] - arg }

View File

@ -0,0 +1,97 @@
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 } }

View File

@ -4,15 +4,19 @@ import kotlinx.coroutines.*
import scientifik.kmath.operations.Field
import scientifik.kmath.operations.FieldElement
class LazyNDField<T, F : Field<T>>(shape: IntArray, field: F, val scope: CoroutineScope = GlobalScope) :
AbstractNDField<T, F, NDStructure<T>>(shape, field) {
class LazyNDField<T, F : Field<T>>(
override val shape: IntArray,
override val elementContext: F,
val scope: CoroutineScope = GlobalScope
) :
NDField<T, F, NDStructure<T>> {
override val zero by lazy { produce { zero } }
override val one by lazy { produce { one } }
override fun produce(initializer: F.(IntArray) -> T) =
LazyNDStructure(this) { elementField.initializer(it) }
LazyNDStructure(this) { elementContext.initializer(it) }
override fun mapIndexed(
arg: NDStructure<T>,
@ -22,10 +26,10 @@ class LazyNDField<T, F : Field<T>>(shape: IntArray, field: F, val scope: Corouti
return if (arg is LazyNDStructure<T, *>) {
LazyNDStructure(this) { index ->
//FIXME if value of arg is already calculated, it should be used
elementField.transform(index, arg.function(index))
elementContext.transform(index, arg.function(index))
}
} else {
LazyNDStructure(this) { elementField.transform(it, arg.await(it)) }
LazyNDStructure(this) { elementContext.transform(it, arg.await(it)) }
}
// return LazyNDStructure(this) { elementField.transform(it, arg.await(it)) }
}
@ -37,13 +41,13 @@ class LazyNDField<T, F : Field<T>>(shape: IntArray, field: F, val scope: Corouti
check(a, b)
return if (a is LazyNDStructure<T, *> && b is LazyNDStructure<T, *>) {
LazyNDStructure(this@LazyNDField) { index ->
elementField.transform(
elementContext.transform(
a.function(index),
b.function(index)
)
}
} else {
LazyNDStructure(this@LazyNDField) { elementField.transform(a.await(it), b.await(it)) }
LazyNDStructure(this@LazyNDField) { elementContext.transform(a.await(it), b.await(it)) }
}
// return LazyNDStructure(this) { elementField.transform(a.await(it), b.await(it)) }
}
@ -69,7 +73,7 @@ class LazyNDStructure<T, F : Field<T>>(
override fun NDStructure<T>.wrap(): LazyNDStructure<T, F> = LazyNDStructure(context) { await(it) }
override val shape: IntArray get() = context.shape
override val elementField: F get() = context.elementField
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) }

View File

@ -1,20 +1,16 @@
package scientifik.kmath.structures
import scientifik.kmath.operations.IntField
import kotlin.test.Test
import kotlin.test.assertEquals
class LazyNDFieldTest {
@Test
fun testLazyStructure() {
var counter = 0
val regularStructure = NDField.auto(intArrayOf(2, 2, 2), IntField).produce { it[0] + it[1] - it[2] }
val result = (regularStructure.lazy(IntField) + 2).map {
counter++
it * it
}
assertEquals(4, result[0, 0, 0])
assertEquals(1, counter)
}
// @Test
// fun testLazyStructure() {
// var counter = 0
// val regularStructure = NDField.auto(intArrayOf(2, 2, 2), IntRing).produce { it[0] + it[1] - it[2] }
// val result = (regularStructure.lazy(IntRing) + 2).map {
// counter++
// it * it
// }
// assertEquals(4, result[0, 0, 0])
// assertEquals(1, counter)
// }
}