pre-0.0.3 #46

Merged
altavir merged 75 commits from dev into master 2019-02-20 13:05:39 +03:00
40 changed files with 665 additions and 341 deletions
Showing only changes of commit c0a43c1bd1 - Show all commits

View File

@ -5,38 +5,65 @@ import kotlin.system.measureTimeMillis
fun main(args: Array<String>) {
val dim = 1000
val n = 1000
val n = 10000
val genericField = NDField.generic(intArrayOf(dim, dim), DoubleField)
val doubleField = NDField.inline(intArrayOf(dim, dim), DoubleField)
val bufferedField = NDField.buffered(intArrayOf(dim, dim), DoubleField)
val specializedField = NDField.real(intArrayOf(dim, dim))
val genericField = NDField.generic(intArrayOf(dim, dim), DoubleField)
// val action: NDField<Double, DoubleField, NDStructure<Double>>.() -> Unit = {
// var res = one
// repeat(n) {
// res += 1.0
// }
// }
val doubleTime = measureTimeMillis {
var res = doubleField.produce { one }
bufferedField.run {
var res: NDBuffer<Double> = one
repeat(n) {
res += 1.0
}
}
}
println("Buffered addition completed in $doubleTime millis")
val elementTime = measureTimeMillis {
var res = bufferedField.produce { one }
repeat(n) {
res += 1.0
}
}
println("Inlined addition completed in $doubleTime millis")
println("Element addition completed in $elementTime millis")
val specializedTime = measureTimeMillis {
var res = specializedField.produce { one }
//specializedField.run(action)
specializedField.run {
var res: NDBuffer<Double> = one
repeat(n) {
res += 1.0
}
}
}
println("Specialized addition completed in $specializedTime millis")
val genericTime = measureTimeMillis {
var res = genericField.produce { one }
//genericField.run(action)
genericField.run {
var res = one
repeat(n) {
res += 1.0
}
}
}
println("Generic addition completed in $genericTime millis")
}

View File

@ -3,7 +3,6 @@ package scientifik.kmath.structures
import kotlin.system.measureTimeMillis
fun main(args: Array<String>) {
val n = 6000

View File

@ -1,6 +1,6 @@
buildscript {
extra["kotlinVersion"] = "1.3.20-eap-52"
extra["ioVersion"] = "0.1.2-dev-2"
extra["ioVersion"] = "0.1.2"
extra["coroutinesVersion"] = "1.1.0"
val kotlinVersion: String by extra
@ -28,7 +28,7 @@ allprojects {
apply(plugin = "com.jfrog.artifactory")
group = "scientifik"
version = "0.0.2-dev-1"
version = "0.0.3-dev-1"
repositories {
maven("https://dl.bintray.com/kotlin/kotlin-eap")

View File

@ -26,23 +26,28 @@ internal class ConstantExpression<T>(val value: T) : Expression<T> {
override fun invoke(arguments: Map<String, T>): T = value
}
internal class SumExpression<T>(val context: Space<T>, val first: Expression<T>, val second: Expression<T>) : Expression<T> {
internal class SumExpression<T>(val context: Space<T>, val first: Expression<T>, val second: Expression<T>) :
Expression<T> {
override fun invoke(arguments: Map<String, T>): T = context.add(first.invoke(arguments), second.invoke(arguments))
}
internal class ProductExpression<T>(val context: Field<T>, val first: Expression<T>, val second: Expression<T>) : Expression<T> {
override fun invoke(arguments: Map<String, T>): T = context.multiply(first.invoke(arguments), second.invoke(arguments))
internal class ProductExpression<T>(val context: Field<T>, val first: Expression<T>, val second: Expression<T>) :
Expression<T> {
override fun invoke(arguments: Map<String, T>): T =
context.multiply(first.invoke(arguments), second.invoke(arguments))
}
internal class ConstProductExpession<T>(val context: Field<T>, val expr: Expression<T>, val const: Double) : Expression<T> {
internal class ConstProductExpession<T>(val context: Field<T>, val expr: Expression<T>, val const: Double) :
Expression<T> {
override fun invoke(arguments: Map<String, T>): T = context.multiply(expr.invoke(arguments), const)
}
internal class DivExpession<T>(val context: Field<T>, val expr: Expression<T>, val second: Expression<T>) : Expression<T> {
internal class DivExpession<T>(val context: Field<T>, val expr: Expression<T>, val second: Expression<T>) :
Expression<T> {
override fun invoke(arguments: Map<String, T>): T = context.divide(expr.invoke(arguments), second.invoke(arguments))
}
class FieldExpressionContext<T>(val field: Field<T>) : Field<Expression<T>>, ExpressionContext<T> {
class ExpressionField<T>(val field: Field<T>) : Field<Expression<T>>, ExpressionContext<T> {
override val zero: Expression<T> = ConstantExpression(field.zero)
@ -59,4 +64,15 @@ class FieldExpressionContext<T>(val field: Field<T>) : Field<Expression<T>>, Exp
override fun multiply(a: Expression<T>, b: Expression<T>): Expression<T> = ProductExpression(field, a, b)
override fun divide(a: Expression<T>, b: Expression<T>): Expression<T> = DivExpession(field, a, b)
operator fun Expression<T>.plus(arg: T) = this + const(arg)
operator fun Expression<T>.minus(arg: T) = this - const(arg)
operator fun Expression<T>.times(arg: T) = this * const(arg)
operator fun Expression<T>.div(arg: T) = this / const(arg)
operator fun T.plus(arg: Expression<T>) = arg + this
operator fun T.minus(arg: Expression<T>) = arg - this
operator fun T.times(arg: Expression<T>) = arg * this
operator fun T.div(arg: Expression<T>) = arg / this
}

View File

@ -6,7 +6,8 @@ import kotlin.math.floor
private operator fun RealPoint.minus(other: RealPoint) = ListBuffer((0 until size).map { get(it) - other[it] })
private inline fun <T> Buffer<out Double>.mapIndexed(crossinline mapper: (Int, Double) -> T): Sequence<T> = (0 until size).asSequence().map { mapper(it, get(it)) }
private inline fun <T> Buffer<out Double>.mapIndexed(crossinline mapper: (Int, Double) -> T): Sequence<T> =
(0 until size).asSequence().map { mapper(it, get(it)) }
/**
* Uniform multivariate histogram with fixed borders. Based on NDStructure implementation with complexity of m for bin search, where m is the number of dimensions.
@ -25,7 +26,8 @@ class FastHistogram(
//private val weight: NDStructure<DoubleCounter?> = ndStructure(strides){null}
//TODO optimize binSize performance if needed
private val binSize: RealPoint = ListBuffer((upper - lower).mapIndexed { index, value -> value / binNums[index] }.toList())
private val binSize: RealPoint =
ListBuffer((upper - lower).mapIndexed { index, value -> value / binNums[index] }.toList())
init {
// argument checks

View File

@ -1,10 +1,10 @@
package scientifik.kmath.histogram
import scientifik.kmath.linear.Point
import scientifik.kmath.structures.ArrayBuffer
import scientifik.kmath.structures.Buffer
import scientifik.kmath.structures.DoubleBuffer
typealias Point<T> = Buffer<T>
typealias RealPoint = Buffer<Double>
@ -52,7 +52,9 @@ interface MutableHistogram<T: Any, out B : Bin<T>>: Histogram<T,B>{
fun <T : Any> MutableHistogram<T, *>.put(vararg point: T) = put(ArrayBuffer(point))
fun MutableHistogram<Double,*>.put(vararg point: Number) = put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray()))
fun MutableHistogram<Double, *>.put(vararg point: Number) =
put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray()))
fun MutableHistogram<Double, *>.put(vararg point: Double) = put(DoubleBuffer(point))
fun <T : Any> MutableHistogram<T, *>.fill(sequence: Iterable<Point<T>>) = sequence.forEach { put(it) }
@ -60,4 +62,5 @@ fun <T: Any> MutableHistogram<T,*>.fill(sequence: Iterable<Point<T>>) = sequence
/**
* Pass a sequence builder into histogram
*/
fun <T: Any> MutableHistogram<T, *>.fill(buider: suspend SequenceScope<Point<T>>.() -> Unit) = fill(sequence(buider).asIterable())
fun <T : Any> MutableHistogram<T, *>.fill(buider: suspend SequenceScope<Point<T>>.() -> Unit) =
fill(sequence(buider).asIterable())

View File

@ -1,5 +1,6 @@
package scientifik.kmath.histogram
import scientifik.kmath.linear.Point
import scientifik.kmath.linear.Vector
import scientifik.kmath.operations.Space
import scientifik.kmath.structures.NDStructure

View File

@ -104,7 +104,10 @@ abstract class LUDecomposition<T : Comparable<T>, F : Field<T>>(val matrix: Matr
val m = matrix.numCols
val pivot = IntArray(matrix.numRows)
//TODO fix performance
val lu: MutableNDStructure<T> = mutableNdStructure(intArrayOf(matrix.numRows, matrix.numCols), ::boxingMutableBuffer) { index: IntArray -> matrix[index[0], index[1]] }
val lu: MutableNDStructure<T> = mutableNdStructure(
intArrayOf(matrix.numRows, matrix.numCols),
::boxingMutableBuffer
) { index: IntArray -> matrix[index[0], index[1]] }
with(matrix.context.ring) {
@ -180,7 +183,8 @@ abstract class LUDecomposition<T : Comparable<T>, F : Field<T>>(val matrix: Matr
}
class RealLUDecomposition(matrix: RealMatrix, private val singularityThreshold: Double = DEFAULT_TOO_SMALL) : LUDecomposition<Double, DoubleField>(matrix) {
class RealLUDecomposition(matrix: RealMatrix, private val singularityThreshold: Double = DEFAULT_TOO_SMALL) :
LUDecomposition<Double, DoubleField>(matrix) {
override fun isSingular(value: Double): Boolean {
return value.absoluteValue < singularityThreshold
}
@ -195,7 +199,8 @@ class RealLUDecomposition(matrix: RealMatrix, private val singularityThreshold:
/** Specialized solver. */
object RealLUSolver : LinearSolver<Double, DoubleField> {
fun decompose(mat: Matrix<Double, DoubleField>, threshold: Double = 1e-11): RealLUDecomposition = RealLUDecomposition(mat, threshold)
fun decompose(mat: Matrix<Double, DoubleField>, threshold: Double = 1e-11): RealLUDecomposition =
RealLUDecomposition(mat, threshold)
override fun solve(a: RealMatrix, b: RealMatrix): RealMatrix {
val decomposition = decompose(a, a.context.ring.zero)

View File

@ -1,6 +1,5 @@
package scientifik.kmath.linear
import scientifik.kmath.histogram.Point
import scientifik.kmath.operations.DoubleField
import scientifik.kmath.operations.Ring
import scientifik.kmath.operations.Space
@ -33,25 +32,34 @@ interface MatrixSpace<T : Any, R : Ring<T>> : Space<Matrix<T, R>> {
val one get() = produce { i, j -> if (i == j) ring.one else ring.zero }
override fun add(a: Matrix<T, R>, b: Matrix<T, R>): Matrix<T, R> = produce(rowNum, colNum) { i, j -> ring.run { a[i, j] + b[i, j] } }
override fun add(a: Matrix<T, R>, b: Matrix<T, R>): Matrix<T, R> =
produce(rowNum, colNum) { i, j -> ring.run { a[i, j] + b[i, j] } }
override fun multiply(a: Matrix<T, R>, k: Double): Matrix<T, R> = produce(rowNum, colNum) { i, j -> ring.run { a[i, j] * k } }
override fun multiply(a: Matrix<T, R>, k: Double): Matrix<T, R> =
produce(rowNum, colNum) { i, j -> ring.run { a[i, j] * k } }
companion object {
/**
* Non-boxing double matrix
*/
fun real(rows: Int, columns: Int): MatrixSpace<Double, DoubleField> = StructureMatrixSpace(rows, columns, DoubleField, DoubleBufferFactory)
fun real(rows: Int, columns: Int): MatrixSpace<Double, DoubleField> =
StructureMatrixSpace(rows, columns, DoubleField, DoubleBufferFactory)
/**
* A structured matrix with custom buffer
*/
fun <T : Any, R : Ring<T>> buffered(rows: Int, columns: Int, ring: R, bufferFactory: BufferFactory<T> = ::boxingBuffer): MatrixSpace<T, R> = StructureMatrixSpace(rows, columns, ring, bufferFactory)
fun <T : Any, R : Ring<T>> buffered(
rows: Int,
columns: Int,
ring: R,
bufferFactory: BufferFactory<T> = ::boxingBuffer
): MatrixSpace<T, R> = StructureMatrixSpace(rows, columns, ring, bufferFactory)
/**
* Automatic buffered matrix, unboxed if it is possible
*/
inline fun <reified T : Any, R : Ring<T>> smart(rows: Int, columns: Int, ring: R): MatrixSpace<T, R> = buffered(rows, columns, ring, ::inlineBuffer)
inline fun <reified T : Any, R : Ring<T>> smart(rows: Int, columns: Int, ring: R): MatrixSpace<T, R> =
buffered(rows, columns, ring, ::inlineBuffer)
}
}
@ -59,7 +67,7 @@ interface MatrixSpace<T : Any, R : Ring<T>> : Space<Matrix<T, R>> {
/**
* Specialized 2-d structure
*/
interface Matrix<T : Any, R : Ring<T>> : NDStructure<T>, SpaceElement<Matrix<T, R>, MatrixSpace<T, R>> {
interface Matrix<T : Any, R : Ring<T>> : NDStructure<T>, SpaceElement<Matrix<T, R>, Matrix<T, R>, MatrixSpace<T, R>> {
operator fun get(i: Int, j: Int): T
override fun get(index: IntArray): T = get(index[0], index[1])
@ -134,15 +142,21 @@ data class StructureMatrixSpace<T : Any, R : Ring<T>>(
override fun point(size: Int, initializer: (Int) -> T): Point<T> = bufferFactory(size, initializer)
}
data class StructureMatrix<T : Any, R : Ring<T>>(override val context: StructureMatrixSpace<T, R>, val structure: NDStructure<T>) : Matrix<T, R> {
data class StructureMatrix<T : Any, R : Ring<T>>(
override val context: StructureMatrixSpace<T, R>,
val structure: NDStructure<T>
) : Matrix<T, R> {
init {
if (structure.shape.size != 2 || structure.shape[0] != context.rowNum || structure.shape[1] != context.colNum) {
error("Dimension mismatch for structure, (${context.rowNum}, ${context.colNum}) expected, but ${structure.shape} found")
}
}
override fun unwrap(): Matrix<T, R> = this
override fun Matrix<T, R>.wrap(): Matrix<T, R> = this
override val shape: IntArray get() = structure.shape
override val self: Matrix<T, R> get() = this
override fun get(index: IntArray): T = structure[index]

View File

@ -1,11 +1,12 @@
package scientifik.kmath.linear
import scientifik.kmath.histogram.Point
import scientifik.kmath.operations.DoubleField
import scientifik.kmath.operations.Space
import scientifik.kmath.operations.SpaceElement
import scientifik.kmath.structures.*
typealias Point<T> = Buffer<T>
/**
* A linear space for vectors.
* Could be used on any point-like structure
@ -45,12 +46,17 @@ interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> {
/**
* A structured vector space with custom buffer
*/
fun <T : Any, S : Space<T>> buffered(size: Int, space: S, bufferFactory: BufferFactory<T> = ::boxingBuffer): VectorSpace<T, S> = BufferVectorSpace(size, space, bufferFactory)
fun <T : Any, S : Space<T>> buffered(
size: Int,
space: S,
bufferFactory: BufferFactory<T> = ::boxingBuffer
): VectorSpace<T, S> = BufferVectorSpace(size, space, bufferFactory)
/**
* Automatic buffered vector, unboxed if it is possible
*/
inline fun <reified T : Any, S : Space<T>> smart(size: Int, space: S): VectorSpace<T, S> = buffered(size, space, ::inlineBuffer)
inline fun <reified T : Any, S : Space<T>> smart(size: Int, space: S): VectorSpace<T, S> =
buffered(size, space, ::inlineBuffer)
}
}
@ -58,13 +64,13 @@ interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> {
/**
* A point coupled to the linear space
*/
interface Vector<T : Any, S : Space<T>> : SpaceElement<Vector<T,S>, VectorSpace<T, S>>, Point<T> {
interface Vector<T : Any, S : Space<T>> : SpaceElement<Point<T>, Vector<T, S>, VectorSpace<T, S>>, Point<T> {
override val size: Int get() = context.size
override operator fun plus(b: Point<T>): Vector<T, S> = context.add(self, b)
override operator fun minus(b: Point<T>): Vector<T, S> = context.add(self, context.multiply(b, -1.0))
override operator fun times(k: Number): Vector<T, S> = context.multiply(self, k.toDouble())
override operator fun div(k: Number): Vector<T, S> = context.multiply(self, 1.0 / k.toDouble())
override operator fun plus(b: Point<T>): Vector<T, S> = context.add(this, b).wrap()
override operator fun minus(b: Point<T>): Vector<T, S> = context.add(this, context.multiply(b, -1.0)).wrap()
override operator fun times(k: Number): Vector<T, S> = context.multiply(this, k.toDouble()).wrap()
override operator fun div(k: Number): Vector<T, S> = context.multiply(this, 1.0 / k.toDouble()).wrap()
companion object {
/**
@ -73,8 +79,11 @@ interface Vector<T : Any, S : Space<T>> : SpaceElement<Vector<T,S>, VectorSpace<
fun <T : Any, S : Space<T>> generic(size: Int, field: S, initializer: (Int) -> T): Vector<T, S> =
VectorSpace.buffered(size, field).produceElement(initializer)
fun real(size: Int, initializer: (Int) -> Double): Vector<Double,DoubleField> = VectorSpace.real(size).produceElement(initializer)
fun ofReal(vararg elements: Double): Vector<Double,DoubleField> = VectorSpace.real(elements.size).produceElement { elements[it] }
fun real(size: Int, initializer: (Int) -> Double): Vector<Double, DoubleField> =
VectorSpace.real(size).produceElement(initializer)
fun ofReal(vararg elements: Double): Vector<Double, DoubleField> =
VectorSpace.real(elements.size).produceElement { elements[it] }
}
}
@ -89,7 +98,8 @@ data class BufferVectorSpace<T : Any, S : Space<T>>(
}
data class BufferVector<T : Any, S : Space<T>>(override val context: VectorSpace<T, S>, val buffer: Buffer<T>) : Vector<T, S> {
data class BufferVector<T : Any, S : Space<T>>(override val context: VectorSpace<T, S>, val buffer: Buffer<T>) :
Vector<T, S> {
init {
if (context.size != buffer.size) {
@ -101,10 +111,13 @@ data class BufferVector<T : Any, S : Space<T>>(override val context: VectorSpace
return buffer[index]
}
override fun getSelf(): BufferVector<T, S
override fun unwrap(): Point<T> = this
override fun Point<T>.wrap(): Vector<T, S> = BufferVector(context, this)
override fun iterator(): Iterator<T> = (0 until size).map { buffer[it] }.iterator()
override fun toString(): String = this.asSequence().joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() }
override fun toString(): String =
this.asSequence().joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() }
}

View File

@ -27,7 +27,8 @@ fun <T, R> Sequence<T>.cumulative(initial: R, operation: (T, R) -> R): Sequence<
override fun iterator(): Iterator<R> = this@cumulative.iterator().cumulative(initial, operation)
}
fun <T, R> List<T>.cumulative(initial: R, operation: (T, R) -> R): List<R> = this.iterator().cumulative(initial, operation).asSequence().toList()
fun <T, R> List<T>.cumulative(initial: R, operation: (T, R) -> R): List<R> =
this.iterator().cumulative(initial, operation).asSequence().toList()
//Cumulative sum

View File

@ -33,13 +33,25 @@ interface Space<T> {
operator fun T.times(k: Number) = multiply(this, k.toDouble())
operator fun T.div(k: Number) = multiply(this, 1.0 / k.toDouble())
operator fun Number.times(b: T) = b * this
//TODO move to external extensions when they are available
fun Iterable<T>.sum(): T = fold(zero) { left, right -> left + right }
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
*/
@ -56,6 +68,15 @@ interface Ring<T> : Space<T> {
operator fun T.times(b: T): T = multiply(this, b)
// operator fun T.plus(b: Number) = this.plus(b * one)
// operator fun Number.plus(b: T) = b + this
//
// operator fun T.minus(b: Number) = this.minus(b * one)
// 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)
}
/**
@ -66,10 +87,9 @@ 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)
operator fun T.plus(b: Number) = this.plus(b * one)
operator fun Number.plus(b: T) = b + this
operator fun T.minus(b: Number) = this.minus(b * one)
operator fun Number.minus(b: T) = -b + this
}
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

@ -14,7 +14,8 @@ object ComplexField : Field<Complex> {
override fun multiply(a: Complex, k: Double): Complex = Complex(a.re * k, a.im * k)
override fun multiply(a: Complex, b: Complex): Complex = Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re)
override fun multiply(a: Complex, b: Complex): Complex =
Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re)
override fun divide(a: Complex, b: Complex): Complex {
val norm = b.square
@ -35,8 +36,10 @@ object ComplexField : Field<Complex> {
/**
* Complex number class
*/
data class Complex(val re: Double, val im: Double) : FieldElement<Complex, ComplexField> {
override val self: Complex get() = this
data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Complex, ComplexField> {
override fun unwrap(): Complex = this
override fun Complex.wrap(): Complex = this
override val context: ComplexField
get() = ComplexField

View File

@ -12,7 +12,6 @@ interface ExtendedField<T : Any> :
ExponentialOperations<T>
/**
* Real field element wrapping double.
*
@ -33,7 +32,7 @@ inline class Real(val value: Double) : FieldElement<Double, Real, DoubleField> {
/**
* A field for double without boxing. Does not produce appropriate field element
*/
object DoubleField : ExtendedField<Double>, Norm<Double, Double> {
object DoubleField : AbstractField<Double>(),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

View File

@ -19,10 +19,10 @@ interface TrigonometricOperations<T> : Field<T> {
fun ctg(arg: T): T = cos(arg) / sin(arg)
}
fun <T : FieldElement<T, out TrigonometricOperations<T>>> sin(arg: T): T = arg.context.sin(arg)
fun <T : FieldElement<T, out TrigonometricOperations<T>>> cos(arg: T): T = arg.context.cos(arg)
fun <T : FieldElement<T, out TrigonometricOperations<T>>> tg(arg: T): T = arg.context.tg(arg)
fun <T : FieldElement<T, out TrigonometricOperations<T>>> ctg(arg: T): T = arg.context.ctg(arg)
fun <T : MathElement<out TrigonometricOperations<T>>> sin(arg: T): T = arg.context.sin(arg)
fun <T : MathElement<out TrigonometricOperations<T>>> cos(arg: T): T = arg.context.cos(arg)
fun <T : MathElement<out TrigonometricOperations<T>>> tg(arg: T): T = arg.context.tg(arg)
fun <T : MathElement<out TrigonometricOperations<T>>> ctg(arg: T): T = arg.context.ctg(arg)
/* Power and roots */
@ -31,11 +31,12 @@ fun <T : FieldElement<T, out TrigonometricOperations<T>>> ctg(arg: T): T = arg.c
*/
interface PowerOperations<T> {
fun power(arg: T, pow: Double): T
fun sqrt(arg: T) = power(arg, 0.5)
}
infix fun <T : MathElement<T, out PowerOperations<T>>> T.pow(power: Double): T = context.power(this, power)
fun <T : MathElement<T, out PowerOperations<T>>> sqrt(arg: T): T = arg pow 0.5
fun <T : MathElement<T, out PowerOperations<T>>> sqr(arg: T): T = arg pow 2.0
infix fun <T : MathElement<out PowerOperations<T>>> T.pow(power: Double): T = context.power(this, power)
fun <T : MathElement<out PowerOperations<T>>> sqrt(arg: T): T = arg pow 0.5
fun <T : MathElement<out PowerOperations<T>>> sqr(arg: T): T = arg pow 2.0
/* Exponential */
@ -44,11 +45,11 @@ interface ExponentialOperations<T> {
fun ln(arg: T): T
}
fun <T : MathElement<T, out ExponentialOperations<T>>> exp(arg: T): T = arg.context.exp(arg)
fun <T : MathElement<T, out ExponentialOperations<T>>> ln(arg: T): T = arg.context.ln(arg)
fun <T : MathElement<out ExponentialOperations<T>>> exp(arg: T): T = arg.context.exp(arg)
fun <T : MathElement<out ExponentialOperations<T>>> ln(arg: T): T = arg.context.ln(arg)
interface Norm<in T, out R> {
interface Norm<in T: Any, out R> {
fun norm(arg: T): R
}
fun <T : MathElement<T, out Norm<T, R>>, R> norm(arg: T): R = arg.context.norm(arg)
fun <T : MathElement<out Norm<T, R>>, R> norm(arg: T): R = arg.context.norm(arg)

View File

@ -3,66 +3,115 @@ package scientifik.kmath.structures
import scientifik.kmath.operations.Field
import scientifik.kmath.operations.FieldElement
open class BufferNDField<T, F : Field<T>>(final override val shape: IntArray, final override val field: F, val bufferFactory: BufferFactory<T>) : NDField<T, F, NDBuffer<T>> {
abstract class StridedNDField<T, F : Field<T>>(shape: IntArray, elementField: F) :
AbstractNDField<T, F, NDBuffer<T>>(shape, elementField) {
abstract val bufferFactory: BufferFactory<T>
val strides = DefaultStrides(shape)
}
override fun produce(initializer: F.(IntArray) -> T) =
BufferNDElement(this, bufferFactory(strides.linearSize) { offset -> field.initializer(strides.index(offset)) })
open fun NDBuffer<T>.map(transform: F.(T) -> T) =
BufferNDElement(this@BufferNDField, bufferFactory(strides.linearSize) { offset -> field.transform(buffer[offset]) })
class BufferNDField<T, F : Field<T>>(
shape: IntArray,
elementField: F,
override val bufferFactory: BufferFactory<T>
) :
StridedNDField<T, F>(shape, elementField) {
open fun NDBuffer<T>.mapIndexed(transform: F.(index: IntArray, T) -> T) =
BufferNDElement(this@BufferNDField, bufferFactory(strides.linearSize) { offset -> field.transform(strides.index(offset), buffer[offset]) })
override fun check(vararg elements: NDBuffer<T>) {
if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides")
}
open fun combine(a: NDBuffer<T>, b: NDBuffer<T>, transform: F.(T, T) -> T) =
BufferNDElement(this, bufferFactory(strides.linearSize) { offset -> field.transform(a[offset], b[offset]) })
override val zero by lazy { produce { zero } }
override val one by lazy { produce { one } }
@Suppress("OVERRIDE_BY_INLINE")
override inline fun produce(crossinline initializer: F.(IntArray) -> T): BufferNDElement<T, F> =
BufferNDElement(
this,
bufferFactory(strides.linearSize) { offset -> elementField.initializer(strides.index(offset)) })
@Suppress("OVERRIDE_BY_INLINE")
override inline fun NDBuffer<T>.map(crossinline transform: F.(T) -> T): BufferNDElement<T, F> {
check(this)
return BufferNDElement(
this@BufferNDField,
bufferFactory(strides.linearSize) { offset -> elementField.transform(buffer[offset]) })
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun NDBuffer<T>.mapIndexed(crossinline transform: F.(index: IntArray, T) -> T): BufferNDElement<T, F> {
check(this)
return BufferNDElement(
this@BufferNDField,
bufferFactory(strides.linearSize) { offset ->
elementField.transform(
strides.index(offset),
buffer[offset]
)
})
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun combine(
a: NDBuffer<T>,
b: NDBuffer<T>,
crossinline transform: F.(T, T) -> T
): BufferNDElement<T, F> {
check(a, b)
return BufferNDElement(
this,
bufferFactory(strides.linearSize) { offset -> elementField.transform(a.buffer[offset], b.buffer[offset]) })
}
/**
* 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> =
this as? NDBuffer<T> ?: produce { index -> get(index) }
override val zero: NDBuffer<T> by lazy { produce { field.zero } }
override fun add(a: NDBuffer<T>, b: NDBuffer<T>): NDBuffer<T> = combine(a, b) { aValue, bValue -> add(aValue, bValue) }
override fun multiply(a: NDBuffer<T>, k: Double): NDBuffer<T> = a.map { it * k }
override val one: NDBuffer<T> by lazy { produce { field.one } }
override fun multiply(a: NDBuffer<T>, b: NDBuffer<T>): NDBuffer<T> = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) }
override fun divide(a: NDBuffer<T>, b: NDBuffer<T>): NDBuffer<T> = combine(a, b) { aValue, bValue -> divide(aValue, bValue) }
fun NDStructure<T>.toBuffer(): NDBuffer<T> {
return if (this is NDBuffer<T> && this.strides == this@BufferNDField.strides) {
this
} else {
produce { index -> get(index) }
}
}
}
class BufferNDElement<T, F : Field<T>>(override val context: BufferNDField<T, F>, override val buffer: Buffer<T>) :
class BufferNDElement<T, F : Field<T>>(override val context: StridedNDField<T, F>, override val buffer: Buffer<T>) :
NDBuffer<T>,
FieldElement<NDBuffer<T>, BufferNDElement<T, F>, BufferNDField<T, F>>,
FieldElement<NDBuffer<T>, BufferNDElement<T, F>, StridedNDField<T, F>>,
NDElement<T, F> {
override val elementField: F get() = context.field
override val elementField: F
get() = context.elementField
override fun unwrap(): NDBuffer<T> = this
override fun unwrap(): NDBuffer<T> =
this
override fun NDBuffer<T>.wrap(): BufferNDElement<T, F> = BufferNDElement(context, this.buffer)
override fun NDBuffer<T>.wrap(): BufferNDElement<T, F> =
BufferNDElement(context, this.buffer)
override val strides get() = context.strides
override val strides
get() = context.strides
override val shape: IntArray get() = context.shape
override val shape: IntArray
get() = context.shape
override fun get(index: IntArray): T = buffer[strides.offset(index)]
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 elements(): Sequence<Pair<IntArray, T>> =
strides.indices().map { it to get(it) }
override fun map(action: F.(T) -> T): BufferNDElement<T, F> = context.run { map(action) }
override fun map(action: F.(T) -> T): BufferNDElement<T, F> =
context.run { map(action) }
override fun mapIndexed(transform: F.(index: IntArray, T) -> T): BufferNDElement<T, F> = context.run { mapIndexed(transform) }
override fun mapIndexed(transform: F.(index: IntArray, T) -> T): BufferNDElement<T, F> =
context.run { mapIndexed(transform) }
}
/**
* Element by element application of any operation on elements to the whole array. Just like in numpy
*/

View File

@ -12,7 +12,8 @@ interface Buffer<T> {
operator fun iterator(): Iterator<T>
fun contentEquals(other: Buffer<*>): Boolean = asSequence().mapIndexed { index, value -> value == other[index] }.all { it }
fun contentEquals(other: Buffer<*>): Boolean =
asSequence().mapIndexed { index, value -> value == other[index] }.all { it }
}
fun <T> Buffer<T>.asSequence(): Sequence<T> = iterator().asSequence()
@ -151,7 +152,8 @@ inline fun <reified T : Any> inlineBuffer(size: Int, initializer: (Int) -> T): B
/**
* Create a boxing mutable buffer of given type
*/
inline 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

View File

@ -16,31 +16,32 @@ interface ExtendedNDField<T : Any, F : ExtendedField<T>, N : NDStructure<out T>>
/**
* NDField that supports [ExtendedField] operations on its elements
*/
class ExtendedNDFieldWrapper<T : Any, F : ExtendedField<T>, N : NDStructure<out T>>(private val ndField: NDField<T, F, N>) : ExtendedNDField<T, F, N>, NDField<T,F,N> by ndField {
class ExtendedNDFieldWrapper<T : Any, F : ExtendedField<T>, N : NDStructure<out 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 field: F get() = ndField.field
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(field) { power(arg[it], pow) } }
return produce { with(elementField) { power(arg[it], pow) } }
}
override fun exp(arg: N): N {
return produce { with(field) { exp(arg[it]) } }
return produce { with(elementField) { exp(arg[it]) } }
}
override fun ln(arg: N): N {
return produce { with(field) { ln(arg[it]) } }
return produce { with(elementField) { ln(arg[it]) } }
}
override fun sin(arg: N): N {
return produce { with(field) { sin(arg[it]) } }
return produce { with(elementField) { sin(arg[it]) } }
}
override fun cos(arg: N): N {
return produce { with(field) { cos(arg[it]) } }
return produce { with(elementField) { cos(arg[it]) } }
}
}

View File

@ -35,13 +35,21 @@ object NDElements {
/**
* Simple boxing NDArray
*/
fun <T : Any, F : Field<T>> generic(shape: IntArray, field: F, initializer: F.(IntArray) -> T): GenericNDElement<T, F> {
fun <T : Any, F : Field<T>> generic(
shape: IntArray,
field: F,
initializer: F.(IntArray) -> T
): GenericNDElement<T, F> {
val ndField = GenericNDField(shape, field)
val structure = ndStructure(shape) { index -> field.initializer(index) }
return GenericNDElement(ndField, structure)
}
inline fun <reified T : Any, F : Field<T>> inline(shape: IntArray, field: F, noinline initializer: F.(IntArray) -> T): GenericNDElement<T, F> {
inline fun <reified T : Any, F : Field<T>> inline(
shape: IntArray,
field: F,
noinline initializer: F.(IntArray) -> T
): GenericNDElement<T, F> {
val ndField = GenericNDField(shape, field)
val structure = ndStructure(shape, ::inlineBuffer) { index -> field.initializer(index) }
return GenericNDElement(ndField, structure)
@ -52,31 +60,36 @@ object NDElements {
/**
* Element by element application of any operation on elements to the whole array. Just like in numpy
*/
operator fun <T, F : Field<T>> Function1<T, T>.invoke(ndElement: NDElement<T, F>) = ndElement.map { value -> this@invoke(value) }
operator fun <T, F : Field<T>> Function1<T, T>.invoke(ndElement: NDElement<T, F>) =
ndElement.map { value -> this@invoke(value) }
/* plus and minus */
/**
* Summation operation for [NDElements] and single element
*/
operator fun <T, F : Field<T>> NDElement<T, F>.plus(arg: T): NDElement<T, F> = this.map { value -> elementField.run { arg + value } }
operator fun <T, F : Field<T>> NDElement<T, F>.plus(arg: T): NDElement<T, F> =
this.map { value -> elementField.run { arg + value } }
/**
* Subtraction operation between [NDElements] and single element
*/
operator fun <T, F : Field<T>> NDElement<T, F>.minus(arg: T): NDElement<T, F> = this.map { value -> elementField.run { arg - value } }
operator fun <T, F : Field<T>> NDElement<T, F>.minus(arg: T): NDElement<T, F> =
this.map { value -> elementField.run { arg - value } }
/* prod and div */
/**
* Product operation for [NDElements] and single element
*/
operator fun <T, F : Field<T>> NDElement<T, F>.times(arg: T): NDElement<T, F> = this.map { value -> elementField.run { arg * value } }
operator fun <T, F : Field<T>> NDElement<T, F>.times(arg: T): NDElement<T, F> =
this.map { value -> elementField.run { arg * value } }
/**
* Division operation between [NDElements] and single element
*/
operator fun <T, F : Field<T>> NDElement<T, F>.div(arg: T): NDElement<T, F> = this.map { value -> elementField.run { arg / value } }
operator fun <T, F : Field<T>> NDElement<T, F>.div(arg: T): NDElement<T, F> =
this.map { value -> elementField.run { arg / value } }
// /**
@ -111,16 +124,19 @@ operator fun <T, F : Field<T>> NDElement<T, F>.div(arg: T): NDElement<T, F> = th
/**
* Read-only [NDStructure] coupled to the context.
*/
class GenericNDElement<T, F : Field<T>>(override val context: NDField<T, F, NDStructure<T>>, private val structure: NDStructure<T>) :
class GenericNDElement<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.field
override val elementField: F get() = context.elementField
override fun unwrap(): NDStructure<T> = structure
override fun NDStructure<T>.wrap() = GenericNDElement(context, this)
override fun mapIndexed(transform: F.(index: IntArray, T) -> T) =
ndStructure(context.shape) { index: IntArray -> context.field.transform(index, get(index)) }.wrap()
ndStructure(context.shape) { index: IntArray -> context.elementField.transform(index, get(index)) }.wrap()
}

View File

@ -1,5 +1,6 @@
package scientifik.kmath.structures
import scientifik.kmath.operations.AbstractField
import scientifik.kmath.operations.Field
/**
@ -10,29 +11,53 @@ class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : Run
/**
* Field for n-dimensional arrays.
* @param shape - the list of dimensions of the array
* @param field - 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 F - field over structure elements
* @param R - actual nd-element type of this field
*/
interface NDField<T, F : Field<T>, N : NDStructure<out T>> : Field<N> {
interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N> {
val shape: IntArray
val field: F
/**
* Check the shape of given NDArray and throw exception if it does not coincide with shape of the field
*/
fun checkShape(vararg elements: N) {
elements.forEach {
if (!shape.contentEquals(it.shape)) {
throw ShapeMismatchException(shape, it.shape)
}
}
}
val elementField: F
fun produce(initializer: F.(IntArray) -> T): N
fun N.map(transform: F.(T) -> T): N
fun N.mapIndexed(transform: F.(index: IntArray, T) -> T): N
fun combine(a: N, b: N, transform: F.(T, T) -> T): N
/**
* Element by element application of any operation on elements to the whole array. Just like in numpy
*/
operator fun Function1<T, T>.invoke(structure: N): N
/**
* Summation operation for [NDElements] and single element
*/
operator fun N.plus(arg: T): N
/**
* Subtraction operation between [NDElements] and single element
*/
operator fun N.minus(arg: T): N
/**
* Product operation for [NDElements] and single element
*/
operator fun N.times(arg: T): N
/**
* Division operation between [NDElements] and single element
*/
operator fun N.div(arg: T): N
operator fun T.plus(arg: N): N
operator fun T.minus(arg: N): N
operator fun T.times(arg: N): N
operator fun T.div(arg: N): N
companion object {
/**
@ -48,48 +73,85 @@ interface NDField<T, F : Field<T>, N : NDStructure<out T>> : Field<N> {
/**
* Create a most suitable implementation for nd-field using reified class
*/
inline fun <reified T : Any, F : Field<T>> inline(shape: IntArray, field: F) = BufferNDField(shape, field, ::inlineBuffer)
inline fun <reified T : Any, F : Field<T>> buffered(shape: IntArray, field: F) =
BufferNDField(shape, field, ::inlineBuffer)
}
}
class GenericNDField<T : Any, F : Field<T>>(override val shape: IntArray, override val field: F, val bufferFactory: BufferFactory<T> = ::boxingBuffer) : NDField<T, F, NDStructure<T>> {
override fun produce(initializer: F.(IntArray) -> T): NDStructure<T> = ndStructure(shape, bufferFactory) { field.initializer(it) }
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 zero: NDStructure<T> by lazy { produce { zero } }
override val one: N by lazy { produce { one } }
override val one: NDStructure<T> by lazy { produce { one } }
final override operator fun Function1<T, T>.invoke(structure: N) = structure.map { value -> this@invoke(value) }
final override operator fun N.plus(arg: T) = this.map { value -> elementField.run { arg + value } }
final override operator fun N.minus(arg: T) = this.map { value -> elementField.run { arg - value } }
final override operator fun N.times(arg: T) = this.map { value -> elementField.run { arg * value } }
final override operator fun N.div(arg: T) = this.map { value -> elementField.run { arg / value } }
final override operator fun T.plus(arg: N) = arg + this
final override operator fun T.minus(arg: N) = arg - this
final override operator fun T.times(arg: N) = arg * this
final override operator fun T.div(arg: N) = arg / this
/**
* Element-by-element addition
*/
override fun add(a: NDStructure<T>, b: NDStructure<T>): NDStructure<T> {
checkShape(a, b)
return produce { field.run { a[it] + b[it] } }
}
override fun add(a: N, b: N): N =
combine(a, b) { aValue, bValue -> aValue + bValue }
/**
* Multiply all elements by cinstant
*/
override fun multiply(a: NDStructure<T>, k: Double): NDStructure<T> {
checkShape(a)
return produce { field.run { a[it] * k } }
}
override fun multiply(a: N, k: Double): N =
a.map { it * k }
/**
* Element-by-element multiplication
*/
override fun multiply(a: NDStructure<T>, b: NDStructure<T>): NDStructure<T> {
checkShape(a)
return produce { field.run { a[it] * b[it] } }
}
override fun multiply(a: N, b: N): N =
combine(a, b) { aValue, bValue -> aValue * bValue }
/**
* Element-by-element division
*/
override fun divide(a: NDStructure<T>, b: NDStructure<T>): NDStructure<T> {
checkShape(a)
return produce { field.run { a[it] / b[it] } }
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)
}
}
}
}
class GenericNDField<T : Any, F : Field<T>>(
shape: IntArray,
elementField: F,
val bufferFactory: BufferFactory<T> = ::boxingBuffer
) :
AbstractNDField<T, F, NDStructure<T>>(shape, elementField) {
override fun produce(initializer: F.(IntArray) -> T): NDStructure<T> =
ndStructure(shape, bufferFactory) { elementField.initializer(it) }
override fun NDStructure<T>.map(transform: F.(T) -> T): NDStructure<T> =
produce { index -> transform(get(index)) }
override fun NDStructure<T>.mapIndexed(transform: F.(index: IntArray, T) -> T): NDStructure<T> =
produce { index -> transform(index, get(index)) }
override fun combine(a: NDStructure<T>, b: NDStructure<T>, transform: F.(T, T) -> T): NDStructure<T> =
produce { index -> transform(a[index], b[index]) }
}

View File

@ -137,6 +137,12 @@ data class BufferNDStructure<T>(
}
}
override fun get(index: IntArray): T = buffer[strides.offset(index)]
override val shape: IntArray get() = strides.shape
override fun elements() = strides.indices().map { it to this[it] }
override fun equals(other: Any?): Boolean {
return when {
this === other -> true
@ -156,7 +162,10 @@ data class BufferNDStructure<T>(
/**
* Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferNDStructure]
*/
inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer(factory: BufferFactory<R> = ::inlineBuffer, crossinline transform: (T) -> R): BufferNDStructure<R> {
inline fun <T, reified R : Any> NDStructure<T>.mapToBuffer(
factory: BufferFactory<R> = ::inlineBuffer,
crossinline transform: (T) -> R
): BufferNDStructure<R> {
return if (this is BufferNDStructure<T>) {
BufferNDStructure(this.strides, factory.invoke(strides.linearSize) { transform(buffer[it]) })
} else {
@ -205,19 +214,30 @@ class MutableBufferNDStructure<T>(
/**
* The same as [inlineNDStructure], but mutable
*/
fun <T : Any> mutableNdStructure(strides: Strides, bufferFactory: MutableBufferFactory<T> = ::boxingMutableBuffer, initializer: (IntArray) -> T) =
fun <T : Any> mutableNdStructure(
strides: Strides,
bufferFactory: MutableBufferFactory<T> = ::boxingMutableBuffer,
initializer: (IntArray) -> T
) =
MutableBufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) })
inline fun <reified T : Any> inlineMutableNdStructure(strides: Strides, crossinline initializer: (IntArray) -> T) =
MutableBufferNDStructure(strides, inlineMutableBuffer(strides.linearSize) { i -> initializer(strides.index(i)) })
fun <T : Any> mutableNdStructure(shape: IntArray, bufferFactory: MutableBufferFactory<T> = ::boxingMutableBuffer, initializer: (IntArray) -> T) =
fun <T : Any> mutableNdStructure(
shape: IntArray,
bufferFactory: MutableBufferFactory<T> = ::boxingMutableBuffer,
initializer: (IntArray) -> T
) =
mutableNdStructure(DefaultStrides(shape), bufferFactory, initializer)
inline fun <reified T : Any> inlineMutableNdStructure(shape: IntArray, crossinline initializer: (IntArray) -> T) =
inlineMutableNdStructure(DefaultStrides(shape), initializer)
inline fun <reified T : Any> NDStructure<T>.combine(struct: NDStructure<T>, crossinline block: (T, T) -> T): NDStructure<T> {
inline fun <reified T : Any> NDStructure<T>.combine(
struct: NDStructure<T>,
crossinline block: (T, T) -> T
): NDStructure<T> {
if (!this.shape.contentEquals(struct.shape)) error("Shape mismatch in structure combination")
return inlineNdStructure(shape) { block(this[it], struct[it]) }
}

View File

@ -5,47 +5,78 @@ import scientifik.kmath.operations.DoubleField
typealias RealNDElement = BufferNDElement<Double, DoubleField>
class RealNDField(shape: IntArray) :
BufferNDField<Double, DoubleField>(shape, DoubleField, DoubleBufferFactory),
StridedNDField<Double, DoubleField>(shape, DoubleField),
ExtendedNDField<Double, DoubleField, NDBuffer<Double>> {
override val bufferFactory: BufferFactory<Double>
get() = DoubleBufferFactory
/**
* Inline map an NDStructure to
*/
private inline fun NDBuffer<Double>.mapInline(crossinline operation: DoubleField.(Double) -> Double): RealNDElement {
val array = DoubleArray(strides.linearSize) { offset -> DoubleField.operation(buffer[offset]) }
@Suppress("OVERRIDE_BY_INLINE")
override inline fun NDBuffer<Double>.map(crossinline transform: DoubleField.(Double) -> Double): RealNDElement {
check(this)
val array = DoubleArray(strides.linearSize) { offset -> DoubleField.transform(buffer[offset]) }
return BufferNDElement(this@RealNDField, DoubleBuffer(array))
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun produce(initializer: DoubleField.(IntArray) -> Double): RealNDElement {
val array = DoubleArray(strides.linearSize) { offset -> field.initializer(strides.index(offset)) }
override inline fun produce(crossinline initializer: DoubleField.(IntArray) -> Double): RealNDElement {
val array = DoubleArray(strides.linearSize) { offset -> elementField.initializer(strides.index(offset)) }
return BufferNDElement(this, DoubleBuffer(array))
}
override fun power(arg: NDBuffer<Double>, pow: Double) = arg.mapInline { power(it, pow) }
override fun exp(arg: NDBuffer<Double>) = arg.mapInline { exp(it) }
override fun ln(arg: NDBuffer<Double>) = arg.mapInline { ln(it) }
override fun sin(arg: NDBuffer<Double>) = arg.mapInline { sin(it) }
override fun cos(arg: NDBuffer<Double>) = arg.mapInline { 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())
@Suppress("OVERRIDE_BY_INLINE")
override inline fun NDBuffer<Double>.mapIndexed(crossinline transform: DoubleField.(index: IntArray, Double) -> Double): BufferNDElement<Double, DoubleField> {
check(this)
return BufferNDElement(
this@RealNDField,
bufferFactory(strides.linearSize) { offset ->
elementField.transform(
strides.index(offset),
buffer[offset]
)
})
}
@Suppress("OVERRIDE_BY_INLINE")
override inline fun combine(
a: NDBuffer<Double>,
b: NDBuffer<Double>,
crossinline transform: DoubleField.(Double, Double) -> Double
): BufferNDElement<Double, DoubleField> {
check(a, b)
return BufferNDElement(
this,
bufferFactory(strides.linearSize) { offset -> elementField.transform(a.buffer[offset], b.buffer[offset]) })
}
override fun power(arg: NDBuffer<Double>, pow: Double) = arg.map { power(it, pow) }
override fun exp(arg: NDBuffer<Double>) = arg.map { exp(it) }
override fun ln(arg: NDBuffer<Double>) = arg.map { ln(it) }
override fun sin(arg: NDBuffer<Double>) = arg.map { sin(it) }
override fun cos(arg: NDBuffer<Double>) = arg.map { 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())
}
/**
* 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) }
inline fun StridedNDField<Double, DoubleField>.produceInline(crossinline initializer: DoubleField.(Int) -> Double): RealNDElement {
val array = DoubleArray(strides.linearSize) { offset -> elementField.initializer(offset) }
return BufferNDElement(this, DoubleBuffer(array))
}
@ -55,6 +86,7 @@ inline fun BufferNDField<Double, DoubleField>.produceInline(crossinline initiali
operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement) =
ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) }
/* plus and minus */
/**

View File

@ -6,10 +6,10 @@ import scientifik.kmath.operations.DoubleField
import kotlin.test.Test
import kotlin.test.assertEquals
class FieldExpressionContextTest {
class ExpressionFieldTest {
@Test
fun testExpression() {
val context = FieldExpressionContext(DoubleField)
val context = ExpressionField(DoubleField)
val expression = with(context) {
val x = variable("x", 2.0)
x * x + 2 * x + 1.0
@ -20,10 +20,10 @@ class FieldExpressionContextTest {
@Test
fun testComplex() {
val context = FieldExpressionContext(ComplexField)
val context = ExpressionField(ComplexField)
val expression = with(context) {
val x = variable("x", Complex(2.0, 0.0))
x * x + 2 * x + 1.0
x * x + 2 * x + one
}
assertEquals(expression("x" to Complex(1.0, 0.0)), Complex(4.0, 0.0))
assertEquals(expression(), Complex(9.0, 0.0))
@ -31,23 +31,23 @@ class FieldExpressionContextTest {
@Test
fun separateContext() {
fun <T> FieldExpressionContext<T>.expression(): Expression<T>{
fun <T> ExpressionField<T>.expression(): Expression<T> {
val x = variable("x")
return x * x + 2 * x + 1.0
return x * x + 2 * x + one
}
val expression = FieldExpressionContext(DoubleField).expression()
val expression = ExpressionField(DoubleField).expression()
assertEquals(expression("x" to 1.0), 4.0)
}
@Test
fun valueExpression() {
val expressionBuilder: FieldExpressionContext<Double>.()->Expression<Double> = {
val expressionBuilder: ExpressionField<Double>.() -> Expression<Double> = {
val x = variable("x")
x * x + 2 * x + 1.0
}
val expression = FieldExpressionContext(DoubleField).expressionBuilder()
val expression = ExpressionField(DoubleField).expressionBuilder()
assertEquals(expression("x" to 1.0), 4.0)
}
}

View File

@ -6,10 +6,9 @@ import kotlin.test.assertEquals
class RealFieldTest {
@Test
fun testSqrt() {
//fails because KT-27586
val sqrt = with(RealField) {
val sqrt = with(DoubleField) {
sqrt(25 * one)
}
assertEquals(5.0, sqrt.value)
assertEquals(5.0, sqrt)
}
}

View File

@ -1,6 +1,7 @@
package scientifik.kmath.structures
import scientifik.kmath.operations.Norm
import scientifik.kmath.structures.NDElements.real2D
import kotlin.math.abs
import kotlin.math.pow
import kotlin.test.Test

View File

@ -2,15 +2,32 @@ package scientifik.kmath.histogram
actual class LongCounter {
private var sum: Long = 0
actual fun decrement() {sum--}
actual fun increment() {sum++}
actual fun reset() {sum = 0}
actual fun sum(): Long = sum
actual fun add(l: Long) {sum+=l}
actual fun decrement() {
sum--
}
actual fun increment() {
sum++
}
actual fun reset() {
sum = 0
}
actual fun sum(): Long = sum
actual fun add(l: Long) {
sum += l
}
}
actual class DoubleCounter {
private var sum: Double = 0.0
actual fun reset() {sum = 0.0}
actual fun sum(): Double = sum
actual fun add(d: Double) {sum+=d}
actual fun reset() {
sum = 0.0
}
actual fun sum(): Double = sum
actual fun add(d: Double) {
sum += d
}
}

View File

@ -26,7 +26,8 @@ class UnivariateBin(val position: Double, val size: Double, val counter: LongCou
/**
* Univariate histogram with log(n) bin search speed
*/
class UnivariateHistogram private constructor(private val factory: (Double) -> UnivariateBin) : MutableHistogram<Double,UnivariateBin> {
class UnivariateHistogram private constructor(private val factory: (Double) -> UnivariateBin) :
MutableHistogram<Double, UnivariateBin> {
private val bins: TreeMap<Double, UnivariateBin> = TreeMap()

View File

@ -2,7 +2,8 @@ package scientifik.kmath.structures
import java.nio.ByteBuffer
class ObjectBuffer<T : Any>(private val buffer: ByteBuffer, private val spec: FixedSizeBufferSpec<T>) : MutableBuffer<T> {
class ObjectBuffer<T : Any>(private val buffer: ByteBuffer, private val spec: FixedSizeBufferSpec<T>) :
MutableBuffer<T> {
override val size: Int
get() = buffer.limit() / spec.unitSize

View File

@ -6,6 +6,9 @@ import kotlinx.coroutines.Dispatchers
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
expect fun <R> runBlocking(context: CoroutineContext = EmptyCoroutineContext, function: suspend CoroutineScope.()->R): R
expect fun <R> runBlocking(
context: CoroutineContext = EmptyCoroutineContext,
function: suspend CoroutineScope.() -> R
): R
val Dispatchers.Math: CoroutineDispatcher get() = Dispatchers.Default

View File

@ -3,7 +3,8 @@ 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) : BufferNDField<T, F>(shape,field, ::boxingBuffer) {
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>): NDElements<T, F> {
return LazyNDStructure(this) { index ->
@ -34,13 +35,23 @@ class LazyNDField<T, F : Field<T>>(shape: IntArray, field: F, val scope: Corouti
}
}
class LazyNDStructure<T, F : Field<T>>(override val context: LazyNDField<T, F>, val function: suspend F.(IntArray) -> T) : NDElements<T, F>, NDStructure<T> {
class LazyNDStructure<T, F : Field<T>>(
override val context: LazyNDField<T, F>,
val function: suspend F.(IntArray) -> T
) : NDElements<T, F>, NDStructure<T> {
override val self: NDElements<T, F> get() = this
override val shape: IntArray get() = context.shape
private val cache = HashMap<IntArray, Deferred<T>>()
fun deferred(index: IntArray) = cache.getOrPut(index) { context.scope.async(context = Dispatchers.Math) { function.invoke(context.field, index) } }
fun deferred(index: IntArray) = cache.getOrPut(index) {
context.scope.async(context = Dispatchers.Math) {
function.invoke(
context.elementField,
index
)
}
}
suspend fun await(index: IntArray): T = deferred(index).await()
@ -54,9 +65,11 @@ class LazyNDStructure<T, F : Field<T>>(override val context: LazyNDField<T, F>,
}
}
fun <T> NDStructure<T>.deferred(index: IntArray) = if (this is LazyNDStructure<T, *>) this.deferred(index) else CompletableDeferred(get(index))
fun <T> NDStructure<T>.deferred(index: IntArray) =
if (this is LazyNDStructure<T, *>) this.deferred(index) else CompletableDeferred(get(index))
suspend fun <T> NDStructure<T>.await(index: IntArray) = if (this is LazyNDStructure<T, *>) this.await(index) else get(index)
suspend fun <T> NDStructure<T>.await(index: IntArray) =
if (this is LazyNDStructure<T, *>) this.await(index) else get(index)
fun <T, F : Field<T>> NDElements<T, F>.lazy(scope: CoroutineScope = GlobalScope): LazyNDStructure<T, F> {
return if (this is LazyNDStructure<T, F>) {
@ -67,10 +80,12 @@ fun <T, F : Field<T>> NDElements<T, F>.lazy(scope: CoroutineScope = GlobalScope)
}
}
inline fun <T, F : Field<T>> LazyNDStructure<T, F>.mapIndexed(crossinline action: suspend F.(IntArray, T) -> T) = LazyNDStructure(context) { index ->
inline fun <T, F : Field<T>> LazyNDStructure<T, F>.mapIndexed(crossinline action: suspend F.(IntArray, T) -> T) =
LazyNDStructure(context) { index ->
action.invoke(this, index, await(index))
}
inline fun <T, F : Field<T>> LazyNDStructure<T, F>.map(crossinline action: suspend F.(T) -> T) = LazyNDStructure(context) { index ->
inline fun <T, F : Field<T>> LazyNDStructure<T, F>.map(crossinline action: suspend F.(T) -> T) =
LazyNDStructure(context) { index ->
action.invoke(this, await(index))
}

View File

@ -3,4 +3,5 @@ package scientifik.kmath.structures
import kotlinx.coroutines.CoroutineScope
import kotlin.coroutines.CoroutineContext
actual fun <R> runBlocking(context: CoroutineContext, function: suspend CoroutineScope.() -> R): R = kotlinx.coroutines.runBlocking(context, function)
actual fun <R> runBlocking(context: CoroutineContext, function: suspend CoroutineScope.() -> R): R =
kotlinx.coroutines.runBlocking(context, function)