diff --git a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt b/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt index fe10fbd75..098c61cf6 100644 --- a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt @@ -19,7 +19,7 @@ open class ArrayBenchmark { arrayBuffer = IntBuffer.wrap(array) nativeBuffer = IntBuffer.allocate(10000) for (i in 0 until 10000) { - nativeBuffer.put(i,i) + nativeBuffer.put(i, i) } } diff --git a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt b/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt index fd27ae3e0..90f9bc372 100644 --- a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt @@ -22,12 +22,12 @@ open class BufferBenchmark { @Benchmark fun complexBufferReadWrite() { - val buffer = Complex.createBuffer(size/2) - (0 until size/2).forEach { + val buffer = Complex.createBuffer(size / 2) + (0 until size / 2).forEach { buffer[it] = Complex(it.toDouble(), -it.toDouble()) } - (0 until size/2).forEach { + (0 until size / 2).forEach { buffer[it] } } diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/structures/BufferNDStructureBenchmark.kt b/benchmarks/src/main/kotlin/scientifik/kmath/structures/BufferNDStructureBenchmark.kt index de2e6844e..99111f6b3 100644 --- a/benchmarks/src/main/kotlin/scientifik/kmath/structures/BufferNDStructureBenchmark.kt +++ b/benchmarks/src/main/kotlin/scientifik/kmath/structures/BufferNDStructureBenchmark.kt @@ -5,26 +5,50 @@ import kotlin.system.measureTimeMillis fun main(args: Array) { 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>.() -> Unit = { +// var res = one +// repeat(n) { +// res += 1.0 +// } +// } + val doubleTime = measureTimeMillis { - var res = doubleField.produce { one } + + bufferedField.run { + var res: NDBuffer = 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 } - repeat(n) { - res += 1.0 + //specializedField.run(action) + specializedField.run { + var res: NDBuffer = one + repeat(n) { + res += 1.0 + } } } @@ -32,9 +56,12 @@ fun main(args: Array) { val genericTime = measureTimeMillis { - var res = genericField.produce { one } - repeat(n) { - res += 1.0 + //genericField.run(action) + genericField.run { + var res = one + repeat(n) { + res += 1.0 + } } } diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt b/benchmarks/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt index 4402264f8..7bb4c8267 100644 --- a/benchmarks/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt +++ b/benchmarks/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt @@ -3,7 +3,6 @@ package scientifik.kmath.structures import kotlin.system.measureTimeMillis - fun main(args: Array) { val n = 6000 @@ -17,21 +16,21 @@ fun main(args: Array) { } println("Structure mapping finished in $time1 millis") - val array = DoubleArray(n*n){1.0} + val array = DoubleArray(n * n) { 1.0 } val time2 = measureTimeMillis { - val target = DoubleArray(n*n) - val res = array.forEachIndexed{index, value -> + val target = DoubleArray(n * n) + val res = array.forEachIndexed { index, value -> target[index] = value + 1 } } println("Array mapping finished in $time2 millis") - val buffer = DoubleBuffer(DoubleArray(n*n){1.0}) + val buffer = DoubleBuffer(DoubleArray(n * n) { 1.0 }) val time3 = measureTimeMillis { - val target = DoubleBuffer(DoubleArray(n*n)) - val res = array.forEachIndexed{index, value -> + val target = DoubleBuffer(DoubleArray(n * n)) + val res = array.forEachIndexed { index, value -> target[index] = value + 1 } } diff --git a/build.gradle.kts b/build.gradle.kts index 409bbe606..5fa89bc1a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 @@ -8,7 +8,7 @@ buildscript { val coroutinesVersion: String by extra repositories { - maven ("https://dl.bintray.com/kotlin/kotlin-eap") + maven("https://dl.bintray.com/kotlin/kotlin-eap") jcenter() } @@ -19,7 +19,7 @@ buildscript { } plugins { - id("com.jfrog.artifactory") version "4.8.1" apply false + id("com.jfrog.artifactory") version "4.8.1" apply false // id("org.jetbrains.kotlin.multiplatform") apply false } @@ -28,14 +28,14 @@ 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") + repositories { + maven("https://dl.bintray.com/kotlin/kotlin-eap") jcenter() } } -if(file("artifactory.gradle").exists()){ +if (file("artifactory.gradle").exists()) { apply(from = "artifactory.gradle") } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index 0a34b536c..c3a6caf58 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -26,23 +26,28 @@ internal class ConstantExpression(val value: T) : Expression { override fun invoke(arguments: Map): T = value } -internal class SumExpression(val context: Space, val first: Expression, val second: Expression) : Expression { +internal class SumExpression(val context: Space, val first: Expression, val second: Expression) : + Expression { override fun invoke(arguments: Map): T = context.add(first.invoke(arguments), second.invoke(arguments)) } -internal class ProductExpression(val context: Field, val first: Expression, val second: Expression) : Expression { - override fun invoke(arguments: Map): T = context.multiply(first.invoke(arguments), second.invoke(arguments)) +internal class ProductExpression(val context: Field, val first: Expression, val second: Expression) : + Expression { + override fun invoke(arguments: Map): T = + context.multiply(first.invoke(arguments), second.invoke(arguments)) } -internal class ConstProductExpession(val context: Field, val expr: Expression, val const: Double) : Expression { +internal class ConstProductExpession(val context: Field, val expr: Expression, val const: Double) : + Expression { override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) } -internal class DivExpession(val context: Field, val expr: Expression, val second: Expression) : Expression { +internal class DivExpession(val context: Field, val expr: Expression, val second: Expression) : + Expression { override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) } -class FieldExpressionContext(val field: Field) : Field>, ExpressionContext { +class ExpressionField(val field: Field) : Field>, ExpressionContext { override val zero: Expression = ConstantExpression(field.zero) @@ -59,4 +64,15 @@ class FieldExpressionContext(val field: Field) : Field>, Exp override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) override fun divide(a: Expression, b: Expression): Expression = DivExpession(field, a, b) + + + operator fun Expression.plus(arg: T) = this + const(arg) + operator fun Expression.minus(arg: T) = this - const(arg) + operator fun Expression.times(arg: T) = this * const(arg) + operator fun Expression.div(arg: T) = this / const(arg) + + operator fun T.plus(arg: Expression) = arg + this + operator fun T.minus(arg: Expression) = arg - this + operator fun T.times(arg: Expression) = arg * this + operator fun T.div(arg: Expression) = arg / this } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt index f6cc1f822..9a470014a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt @@ -5,16 +5,16 @@ package scientifik.kmath.histogram */ -expect class LongCounter(){ - fun decrement() - fun increment() - fun reset() - fun sum(): Long - fun add(l:Long) +expect class LongCounter() { + fun decrement() + fun increment() + fun reset() + fun sum(): Long + fun add(l: Long) } -expect class DoubleCounter(){ - fun reset() - fun sum(): Double +expect class DoubleCounter() { + fun reset() + fun sum(): Double fun add(d: Double) } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt index 705a8e7ca..d9d4da66a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt @@ -6,15 +6,16 @@ import kotlin.math.floor private operator fun RealPoint.minus(other: RealPoint) = ListBuffer((0 until size).map { get(it) - other[it] }) -private inline fun Buffer.mapIndexed(crossinline mapper: (Int, Double) -> T): Sequence = (0 until size).asSequence().map { mapper(it, get(it)) } +private inline fun Buffer.mapIndexed(crossinline mapper: (Int, Double) -> T): Sequence = + (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. */ class FastHistogram( - private val lower: RealPoint, - private val upper: RealPoint, - private val binNums: IntArray = IntArray(lower.size) { 20 } + private val lower: RealPoint, + private val upper: RealPoint, + private val binNums: IntArray = IntArray(lower.size) { 20 } ) : MutableHistogram> { @@ -25,7 +26,8 @@ class FastHistogram( //private val weight: NDStructure = 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 @@ -130,9 +132,9 @@ class FastHistogram( */ fun fromRanges(vararg ranges: Pair, Int>): FastHistogram { return FastHistogram( - ListBuffer(ranges.map { it.first.start }), - ListBuffer(ranges.map { it.first.endInclusive }), - ranges.map { it.second }.toIntArray() + ListBuffer(ranges.map { it.first.start }), + ListBuffer(ranges.map { it.first.endInclusive }), + ranges.map { it.second }.toIntArray() ) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt index 08214142e..a71ab7207 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt @@ -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 = Buffer typealias RealPoint = Buffer @@ -12,7 +12,7 @@ typealias RealPoint = Buffer * A simple geometric domain * TODO move to geometry module */ -interface Domain { +interface Domain { operator fun contains(vector: Point): Boolean val dimension: Int } @@ -20,7 +20,7 @@ interface Domain { /** * The bin in the histogram. The histogram is by definition always done in the real space */ -interface Bin : Domain { +interface Bin : Domain { /** * The value of this bin */ @@ -28,7 +28,7 @@ interface Bin : Domain { val center: Point } -interface Histogram> : Iterable { +interface Histogram> : Iterable { /** * Find existing bin, corresponding to given coordinates @@ -42,7 +42,7 @@ interface Histogram> : Iterable { } -interface MutableHistogram>: Histogram{ +interface MutableHistogram> : Histogram { /** * Increment appropriate bin @@ -50,14 +50,17 @@ interface MutableHistogram>: Histogram{ fun put(point: Point, weight: Double = 1.0) } -fun MutableHistogram.put(vararg point: T) = put(ArrayBuffer(point)) +fun MutableHistogram.put(vararg point: T) = put(ArrayBuffer(point)) -fun MutableHistogram.put(vararg point: Number) = put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray())) -fun MutableHistogram.put(vararg point: Double) = put(DoubleBuffer(point)) +fun MutableHistogram.put(vararg point: Number) = + put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray())) -fun MutableHistogram.fill(sequence: Iterable>) = sequence.forEach { put(it) } +fun MutableHistogram.put(vararg point: Double) = put(DoubleBuffer(point)) + +fun MutableHistogram.fill(sequence: Iterable>) = sequence.forEach { put(it) } /** * Pass a sequence builder into histogram */ -fun MutableHistogram.fill(buider: suspend SequenceScope>.() -> Unit) = fill(sequence(buider).asIterable()) \ No newline at end of file +fun MutableHistogram.fill(buider: suspend SequenceScope>.() -> Unit) = + fill(sequence(buider).asIterable()) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/PhantomHistogram.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/PhantomHistogram.kt index ffffb0d7d..1d04a8c19 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/PhantomHistogram.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/PhantomHistogram.kt @@ -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 @@ -8,8 +9,8 @@ import scientifik.kmath.structures.asSequence data class BinTemplate>(val center: Vector, val sizes: Point) { fun contains(vector: Point): Boolean { if (vector.size != center.size) error("Dimension mismatch for input vector. Expected ${center.size}, but found ${vector.size}") - val upper = center.context.run { center + sizes / 2.0} - val lower = center.context.run {center - sizes / 2.0} + val upper = center.context.run { center + sizes / 2.0 } + val lower = center.context.run { center - sizes / 2.0 } return vector.asSequence().mapIndexed { i, value -> value in lower[i]..upper[i] }.all { it } @@ -44,8 +45,8 @@ class PhantomBin>(val template: BinTemplate, override val v * @param bins map a template into structure index */ class PhantomHistogram>( - val bins: Map, IntArray>, - val data: NDStructure + val bins: Map, IntArray>, + val data: NDStructure ) : Histogram> { override val dimension: Int diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUDecomposition.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUDecomposition.kt index 311e590b6..3db4809be 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUDecomposition.kt @@ -104,7 +104,10 @@ abstract class LUDecomposition, F : Field>(val matrix: Matr val m = matrix.numCols val pivot = IntArray(matrix.numRows) //TODO fix performance - val lu: MutableNDStructure = mutableNdStructure(intArrayOf(matrix.numRows, matrix.numCols), ::boxingMutableBuffer) { index: IntArray -> matrix[index[0], index[1]] } + val lu: MutableNDStructure = 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, F : Field>(val matrix: Matr } -class RealLUDecomposition(matrix: RealMatrix, private val singularityThreshold: Double = DEFAULT_TOO_SMALL) : LUDecomposition(matrix) { +class RealLUDecomposition(matrix: RealMatrix, private val singularityThreshold: Double = DEFAULT_TOO_SMALL) : + LUDecomposition(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 { - fun decompose(mat: Matrix, threshold: Double = 1e-11): RealLUDecomposition = RealLUDecomposition(mat, threshold) + fun decompose(mat: Matrix, threshold: Double = 1e-11): RealLUDecomposition = + RealLUDecomposition(mat, threshold) override fun solve(a: RealMatrix, b: RealMatrix): RealMatrix { val decomposition = decompose(a, a.context.ring.zero) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Matrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Matrix.kt index f5269c74b..e9a45f90a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Matrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Matrix.kt @@ -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> : Space> { val one get() = produce { i, j -> if (i == j) ring.one else ring.zero } - override fun add(a: Matrix, b: Matrix): Matrix = produce(rowNum, colNum) { i, j -> ring.run { a[i, j] + b[i, j] } } + override fun add(a: Matrix, b: Matrix): Matrix = + produce(rowNum, colNum) { i, j -> ring.run { a[i, j] + b[i, j] } } - override fun multiply(a: Matrix, k: Double): Matrix = produce(rowNum, colNum) { i, j -> ring.run { a[i, j] * k } } + override fun multiply(a: Matrix, k: Double): Matrix = + produce(rowNum, colNum) { i, j -> ring.run { a[i, j] * k } } companion object { /** * Non-boxing double matrix */ - fun real(rows: Int, columns: Int): MatrixSpace = StructureMatrixSpace(rows, columns, DoubleField, DoubleBufferFactory) + fun real(rows: Int, columns: Int): MatrixSpace = + StructureMatrixSpace(rows, columns, DoubleField, DoubleBufferFactory) /** * A structured matrix with custom buffer */ - fun > buffered(rows: Int, columns: Int, ring: R, bufferFactory: BufferFactory = ::boxingBuffer): MatrixSpace = StructureMatrixSpace(rows, columns, ring, bufferFactory) + fun > buffered( + rows: Int, + columns: Int, + ring: R, + bufferFactory: BufferFactory = ::boxingBuffer + ): MatrixSpace = StructureMatrixSpace(rows, columns, ring, bufferFactory) /** * Automatic buffered matrix, unboxed if it is possible */ - inline fun > smart(rows: Int, columns: Int, ring: R): MatrixSpace = buffered(rows, columns, ring, ::inlineBuffer) + inline fun > smart(rows: Int, columns: Int, ring: R): MatrixSpace = + buffered(rows, columns, ring, ::inlineBuffer) } } @@ -59,7 +67,7 @@ interface MatrixSpace> : Space> { /** * Specialized 2-d structure */ -interface Matrix> : NDStructure, SpaceElement, MatrixSpace> { +interface Matrix> : NDStructure, SpaceElement, Matrix, MatrixSpace> { operator fun get(i: Int, j: Int): T override fun get(index: IntArray): T = get(index[0], index[1]) @@ -82,7 +90,7 @@ interface Matrix> : NDStructure, SpaceElement Double) = - MatrixSpace.real(rows, columns).produce(rows, columns, initializer) + MatrixSpace.real(rows, columns).produce(rows, columns, initializer) } } @@ -110,10 +118,10 @@ infix fun > Matrix.dot(vector: Point): Point { } data class StructureMatrixSpace>( - override val rowNum: Int, - override val colNum: Int, - override val ring: R, - private val bufferFactory: BufferFactory + override val rowNum: Int, + override val colNum: Int, + override val ring: R, + private val bufferFactory: BufferFactory ) : MatrixSpace { override val shape: IntArray = intArrayOf(rowNum, colNum) @@ -134,15 +142,21 @@ data class StructureMatrixSpace>( override fun point(size: Int, initializer: (Int) -> T): Point = bufferFactory(size, initializer) } -data class StructureMatrix>(override val context: StructureMatrixSpace, val structure: NDStructure) : Matrix { +data class StructureMatrix>( + override val context: StructureMatrixSpace, + val structure: NDStructure +) : Matrix { 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 = this + + override fun Matrix.wrap(): Matrix = this + override val shape: IntArray get() = structure.shape - override val self: Matrix get() = this override fun get(index: IntArray): T = structure[index] @@ -153,4 +167,4 @@ data class StructureMatrix>(override val context: Structure //TODO produce transposed matrix via reference without creating new space and structure fun > Matrix.transpose(): Matrix = - context.produce(numCols, numRows) { i, j -> get(j, i) } \ No newline at end of file + context.produce(numCols, numRows) { i, j -> get(j, i) } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt index 12758d887..818a65733 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt @@ -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 = Buffer + /** * A linear space for vectors. * Could be used on any point-like structure @@ -45,12 +46,17 @@ interface VectorSpace> : Space> { /** * A structured vector space with custom buffer */ - fun > buffered(size: Int, space: S, bufferFactory: BufferFactory = ::boxingBuffer): VectorSpace = BufferVectorSpace(size, space, bufferFactory) + fun > buffered( + size: Int, + space: S, + bufferFactory: BufferFactory = ::boxingBuffer + ): VectorSpace = BufferVectorSpace(size, space, bufferFactory) /** * Automatic buffered vector, unboxed if it is possible */ - inline fun > smart(size: Int, space: S): VectorSpace = buffered(size, space, ::inlineBuffer) + inline fun > smart(size: Int, space: S): VectorSpace = + buffered(size, space, ::inlineBuffer) } } @@ -58,38 +64,42 @@ interface VectorSpace> : Space> { /** * A point coupled to the linear space */ -interface Vector> : SpaceElement, VectorSpace>, Point { +interface Vector> : SpaceElement, Vector, VectorSpace>, Point { override val size: Int get() = context.size - override operator fun plus(b: Point): Vector = context.add(self, b) - override operator fun minus(b: Point): Vector = context.add(self, context.multiply(b, -1.0)) - override operator fun times(k: Number): Vector = context.multiply(self, k.toDouble()) - override operator fun div(k: Number): Vector = context.multiply(self, 1.0 / k.toDouble()) + override operator fun plus(b: Point): Vector = context.add(this, b).wrap() + override operator fun minus(b: Point): Vector = context.add(this, context.multiply(b, -1.0)).wrap() + override operator fun times(k: Number): Vector = context.multiply(this, k.toDouble()).wrap() + override operator fun div(k: Number): Vector = context.multiply(this, 1.0 / k.toDouble()).wrap() companion object { /** * Create vector with custom field */ fun > generic(size: Int, field: S, initializer: (Int) -> T): Vector = - VectorSpace.buffered(size, field).produceElement(initializer) + VectorSpace.buffered(size, field).produceElement(initializer) - fun real(size: Int, initializer: (Int) -> Double): Vector = VectorSpace.real(size).produceElement(initializer) - fun ofReal(vararg elements: Double): Vector = VectorSpace.real(elements.size).produceElement { elements[it] } + fun real(size: Int, initializer: (Int) -> Double): Vector = + VectorSpace.real(size).produceElement(initializer) + + fun ofReal(vararg elements: Double): Vector = + VectorSpace.real(elements.size).produceElement { elements[it] } } } data class BufferVectorSpace>( - override val size: Int, - override val space: S, - val bufferFactory: BufferFactory + override val size: Int, + override val space: S, + val bufferFactory: BufferFactory ) : VectorSpace { override fun produce(initializer: (Int) -> T) = bufferFactory(size, initializer) override fun produceElement(initializer: (Int) -> T): Vector = BufferVector(this, produce(initializer)) } -data class BufferVector>(override val context: VectorSpace, val buffer: Buffer) : Vector { +data class BufferVector>(override val context: VectorSpace, val buffer: Buffer) : + Vector { init { if (context.size != buffer.size) { @@ -101,10 +111,13 @@ data class BufferVector>(override val context: VectorSpace return buffer[index] } - override fun getSelf(): BufferVector = this + + override fun Point.wrap(): Vector = BufferVector(context, this) override fun iterator(): Iterator = (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() } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Cumulative.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Cumulative.kt index 4d4f8ced6..77a1d54bc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Cumulative.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Cumulative.kt @@ -27,33 +27,34 @@ fun Sequence.cumulative(initial: R, operation: (T, R) -> R): Sequence< override fun iterator(): Iterator = this@cumulative.iterator().cumulative(initial, operation) } -fun List.cumulative(initial: R, operation: (T, R) -> R): List = this.iterator().cumulative(initial, operation).asSequence().toList() +fun List.cumulative(initial: R, operation: (T, R) -> R): List = + this.iterator().cumulative(initial, operation).asSequence().toList() //Cumulative sum @JvmName("cumulativeSumOfDouble") -fun Iterable.cumulativeSum() = this.cumulative(0.0){ element, sum -> sum + element} +fun Iterable.cumulativeSum() = this.cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun Iterable.cumulativeSum() = this.cumulative(0){ element, sum -> sum + element} +fun Iterable.cumulativeSum() = this.cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun Iterable.cumulativeSum() = this.cumulative(0L){ element, sum -> sum + element} +fun Iterable.cumulativeSum() = this.cumulative(0L) { element, sum -> sum + element } @JvmName("cumulativeSumOfDouble") -fun Sequence.cumulativeSum() = this.cumulative(0.0){ element, sum -> sum + element} +fun Sequence.cumulativeSum() = this.cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun Sequence.cumulativeSum() = this.cumulative(0){ element, sum -> sum + element} +fun Sequence.cumulativeSum() = this.cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun Sequence.cumulativeSum() = this.cumulative(0L){ element, sum -> sum + element} +fun Sequence.cumulativeSum() = this.cumulative(0L) { element, sum -> sum + element } @JvmName("cumulativeSumOfDouble") -fun List.cumulativeSum() = this.cumulative(0.0){ element, sum -> sum + element} +fun List.cumulativeSum() = this.cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun List.cumulativeSum() = this.cumulative(0){ element, sum -> sum + element} +fun List.cumulativeSum() = this.cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun List.cumulativeSum() = this.cumulative(0L){ element, sum -> sum + element} \ No newline at end of file +fun List.cumulativeSum() = this.cumulative(0L) { element, sum -> sum + element } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt index 659ab9d07..51d0815ec 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -33,13 +33,25 @@ interface Space { 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.sum(): T = fold(zero) { left, right -> left + right } - fun Sequence.sum(): T = fold(zero) { left, right -> left + right } } +abstract class AbstractSpace : Space { + //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.sum(): T = fold(zero) { left, right -> left + right } + + final override fun Sequence.sum(): T = fold(zero) { left, right -> left + right } +} + /** * The same as {@link Space} but with additional multiplication operation */ @@ -56,6 +68,15 @@ interface Ring : Space { 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 : AbstractSpace(), Ring { + final override operator fun T.times(b: T): T = multiply(this, b) } /** @@ -66,10 +87,9 @@ interface Field : Ring { 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 : AbstractRing(), Field { + final override operator fun T.div(b: T): T = divide(this, b) + final override operator fun Number.div(b: T) = this * divide(one, b) } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 6f30e8cfc..a97afdb6b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -14,7 +14,8 @@ object ComplexField : Field { 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 number class */ -data class Complex(val re: Double, val im: Double) : FieldElement { - override val self: Complex get() = this +data class Complex(val re: Double, val im: Double) : FieldElement { + override fun unwrap(): Complex = this + + override fun Complex.wrap(): Complex = this override val context: ComplexField get() = ComplexField diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt index 3cc6ed377..97ff3c6f5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt @@ -6,11 +6,10 @@ import kotlin.math.pow * Advanced Number-like field that implements basic operations */ interface ExtendedField : - Field, - TrigonometricOperations, - PowerOperations, - ExponentialOperations - + Field, + TrigonometricOperations, + PowerOperations, + ExponentialOperations /** @@ -33,7 +32,7 @@ inline class Real(val value: Double) : FieldElement { /** * A field for double without boxing. Does not produce appropriate field element */ -object DoubleField : ExtendedField, Norm { +object DoubleField : AbstractField(),ExtendedField, Norm { 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 diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index 9e5c8a801..7a7866966 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -19,10 +19,10 @@ interface TrigonometricOperations : Field { fun ctg(arg: T): T = cos(arg) / sin(arg) } -fun >> sin(arg: T): T = arg.context.sin(arg) -fun >> cos(arg: T): T = arg.context.cos(arg) -fun >> tg(arg: T): T = arg.context.tg(arg) -fun >> ctg(arg: T): T = arg.context.ctg(arg) +fun >> sin(arg: T): T = arg.context.sin(arg) +fun >> cos(arg: T): T = arg.context.cos(arg) +fun >> tg(arg: T): T = arg.context.tg(arg) +fun >> ctg(arg: T): T = arg.context.ctg(arg) /* Power and roots */ @@ -31,11 +31,12 @@ fun >> ctg(arg: T): T = arg.c */ interface PowerOperations { fun power(arg: T, pow: Double): T + fun sqrt(arg: T) = power(arg, 0.5) } -infix fun >> T.pow(power: Double): T = context.power(this, power) -fun >> sqrt(arg: T): T = arg pow 0.5 -fun >> sqr(arg: T): T = arg pow 2.0 +infix fun >> T.pow(power: Double): T = context.power(this, power) +fun >> sqrt(arg: T): T = arg pow 0.5 +fun >> sqr(arg: T): T = arg pow 2.0 /* Exponential */ @@ -44,11 +45,11 @@ interface ExponentialOperations { fun ln(arg: T): T } -fun >> exp(arg: T): T = arg.context.exp(arg) -fun >> ln(arg: T): T = arg.context.ln(arg) +fun >> exp(arg: T): T = arg.context.exp(arg) +fun >> ln(arg: T): T = arg.context.ln(arg) -interface Norm { +interface Norm { fun norm(arg: T): R } -fun >, R> norm(arg: T): R = arg.context.norm(arg) \ No newline at end of file +fun >, R> norm(arg: T): R = arg.context.norm(arg) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferNDField.kt index 87035b9fa..59fba75df 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferNDField.kt @@ -3,71 +3,120 @@ package scientifik.kmath.structures import scientifik.kmath.operations.Field import scientifik.kmath.operations.FieldElement -open class BufferNDField>(final override val shape: IntArray, final override val field: F, val bufferFactory: BufferFactory) : NDField> { +abstract class StridedNDField>(shape: IntArray, elementField: F) : + AbstractNDField>(shape, elementField) { + + abstract val bufferFactory: BufferFactory 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.map(transform: F.(T) -> T) = - BufferNDElement(this@BufferNDField, bufferFactory(strides.linearSize) { offset -> field.transform(buffer[offset]) }) +class BufferNDField>( + shape: IntArray, + elementField: F, + override val bufferFactory: BufferFactory +) : + StridedNDField(shape, elementField) { - open fun NDBuffer.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) { + if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") + } - open fun combine(a: NDBuffer, b: NDBuffer, 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 = + BufferNDElement( + this, + bufferFactory(strides.linearSize) { offset -> elementField.initializer(strides.index(offset)) }) + + @Suppress("OVERRIDE_BY_INLINE") + override inline fun NDBuffer.map(crossinline transform: F.(T) -> T): BufferNDElement { + check(this) + return BufferNDElement( + this@BufferNDField, + bufferFactory(strides.linearSize) { offset -> elementField.transform(buffer[offset]) }) + } + + @Suppress("OVERRIDE_BY_INLINE") + override inline fun NDBuffer.mapIndexed(crossinline transform: F.(index: IntArray, T) -> T): BufferNDElement { + 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, + b: NDBuffer, + crossinline transform: F.(T, T) -> T + ): BufferNDElement { + 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.toBuffer(): NDBuffer = - this as? NDBuffer ?: produce { index -> get(index) } - - override val zero: NDBuffer by lazy { produce { field.zero } } - - override fun add(a: NDBuffer, b: NDBuffer): NDBuffer = combine(a, b) { aValue, bValue -> add(aValue, bValue) } - - override fun multiply(a: NDBuffer, k: Double): NDBuffer = a.map { it * k } - - override val one: NDBuffer by lazy { produce { field.one } } - - override fun multiply(a: NDBuffer, b: NDBuffer): NDBuffer = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } - - override fun divide(a: NDBuffer, b: NDBuffer): NDBuffer = combine(a, b) { aValue, bValue -> divide(aValue, bValue) } + fun NDStructure.toBuffer(): NDBuffer { + return if (this is NDBuffer && this.strides == this@BufferNDField.strides) { + this + } else { + produce { index -> get(index) } + } + } } -class BufferNDElement>(override val context: BufferNDField, override val buffer: Buffer) : - NDBuffer, - FieldElement, BufferNDElement, BufferNDField>, - NDElement { +class BufferNDElement>(override val context: StridedNDField, override val buffer: Buffer) : + NDBuffer, + FieldElement, BufferNDElement, StridedNDField>, + NDElement { - override val elementField: F get() = context.field + override val elementField: F + get() = context.elementField - override fun unwrap(): NDBuffer = this + override fun unwrap(): NDBuffer = + this - override fun NDBuffer.wrap(): BufferNDElement = BufferNDElement(context, this.buffer) + override fun NDBuffer.wrap(): BufferNDElement = + 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> = strides.indices().map { it to get(it) } + override fun elements(): Sequence> = + strides.indices().map { it to get(it) } - override fun map(action: F.(T) -> T): BufferNDElement = context.run { map(action) } + override fun map(action: F.(T) -> T): BufferNDElement = + context.run { map(action) } - override fun mapIndexed(transform: F.(index: IntArray, T) -> T): BufferNDElement = context.run { mapIndexed(transform) } + override fun mapIndexed(transform: F.(index: IntArray, T) -> T): BufferNDElement = + context.run { mapIndexed(transform) } } - /** * Element by element application of any operation on elements to the whole array. Just like in numpy */ operator fun > Function1.invoke(ndElement: BufferNDElement) = - ndElement.context.run { ndElement.map { invoke(it) } } + ndElement.context.run { ndElement.map { invoke(it) } } /* plus and minus */ @@ -75,13 +124,13 @@ operator fun > Function1.invoke(ndElement: BufferNDE * Summation operation for [BufferNDElement] and single element */ operator fun > BufferNDElement.plus(arg: T) = - context.run { map { it + arg } } + context.run { map { it + arg } } /** * Subtraction operation between [BufferNDElement] and single element */ operator fun > BufferNDElement.minus(arg: T) = - context.run { map { it - arg } } + context.run { map { it - arg } } /* prod and div */ @@ -89,10 +138,10 @@ operator fun > BufferNDElement.minus(arg: T) = * Product operation for [BufferNDElement] and single element */ operator fun > BufferNDElement.times(arg: T) = - context.run { map { it * arg } } + context.run { map { it * arg } } /** * Division operation between [BufferNDElement] and single element */ operator fun > BufferNDElement.div(arg: T) = - context.run { map { it / arg } } \ No newline at end of file + context.run { map { it / arg } } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt index cffc2bea0..e3ca79558 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -12,7 +12,8 @@ interface Buffer { operator fun iterator(): Iterator - 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 Buffer.asSequence(): Sequence = iterator().asSequence() @@ -151,7 +152,8 @@ inline fun inlineBuffer(size: Int, initializer: (Int) -> T): B /** * Create a boxing mutable buffer of given type */ -inline fun boxingMutableBuffer(size: Int, initializer: (Int) -> T): MutableBuffer = MutableListBuffer(MutableList(size, initializer)) +inline fun boxingMutableBuffer(size: Int, initializer: (Int) -> T): MutableBuffer = + MutableListBuffer(MutableList(size, initializer)) /** * Create most appropriate mutable buffer for given type avoiding boxing wherever possible diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt index 7c1b63c23..50ecd3a57 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt @@ -7,40 +7,41 @@ import scientifik.kmath.operations.TrigonometricOperations interface ExtendedNDField, N : NDStructure> : - NDField, - TrigonometricOperations, - PowerOperations, - ExponentialOperations + NDField, + TrigonometricOperations, + PowerOperations, + ExponentialOperations /** * NDField that supports [ExtendedField] operations on its elements */ -class ExtendedNDFieldWrapper, N : NDStructure>(private val ndField: NDField) : ExtendedNDField, NDField by ndField { +class ExtendedNDFieldWrapper, N : NDStructure>(private val ndField: NDField) : + ExtendedNDField, NDField 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]) } } } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt index 7f9f77d4e..223e45b9d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt @@ -18,30 +18,38 @@ object NDElements { * Create a optimized NDArray of doubles */ fun real(shape: IntArray, initializer: DoubleField.(IntArray) -> Double = { 0.0 }) = - NDField.real(shape).produce(initializer) + NDField.real(shape).produce(initializer) fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }) = - real(intArrayOf(dim)) { initializer(it[0]) } + real(intArrayOf(dim)) { initializer(it[0]) } fun real2D(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }) = - real(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } + real(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } fun real3D(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }) = - real(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } + real(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } /** * Simple boxing NDArray */ - fun > generic(shape: IntArray, field: F, initializer: F.(IntArray) -> T): GenericNDElement { + fun > generic( + shape: IntArray, + field: F, + initializer: F.(IntArray) -> T + ): GenericNDElement { val ndField = GenericNDField(shape, field) val structure = ndStructure(shape) { index -> field.initializer(index) } return GenericNDElement(ndField, structure) } - inline fun > inline(shape: IntArray, field: F, noinline initializer: F.(IntArray) -> T): GenericNDElement { + inline fun > inline( + shape: IntArray, + field: F, + noinline initializer: F.(IntArray) -> T + ): GenericNDElement { 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 > Function1.invoke(ndElement: NDElement) = ndElement.map { value -> this@invoke(value) } +operator fun > Function1.invoke(ndElement: NDElement) = + ndElement.map { value -> this@invoke(value) } /* plus and minus */ /** * Summation operation for [NDElements] and single element */ -operator fun > NDElement.plus(arg: T): NDElement = this.map { value -> elementField.run { arg + value } } +operator fun > NDElement.plus(arg: T): NDElement = + this.map { value -> elementField.run { arg + value } } /** * Subtraction operation between [NDElements] and single element */ -operator fun > NDElement.minus(arg: T): NDElement = this.map { value -> elementField.run { arg - value } } +operator fun > NDElement.minus(arg: T): NDElement = + this.map { value -> elementField.run { arg - value } } /* prod and div */ /** * Product operation for [NDElements] and single element */ -operator fun > NDElement.times(arg: T): NDElement = this.map { value -> elementField.run { arg * value } } +operator fun > NDElement.times(arg: T): NDElement = + this.map { value -> elementField.run { arg * value } } /** * Division operation between [NDElements] and single element */ -operator fun > NDElement.div(arg: T): NDElement = this.map { value -> elementField.run { arg / value } } +operator fun > NDElement.div(arg: T): NDElement = + this.map { value -> elementField.run { arg / value } } // /** @@ -111,16 +124,19 @@ operator fun > NDElement.div(arg: T): NDElement = th /** * Read-only [NDStructure] coupled to the context. */ -class GenericNDElement>(override val context: NDField>, private val structure: NDStructure) : - NDStructure by structure, - NDElement, - FieldElement, GenericNDElement, NDField>> { - override val elementField: F get() = context.field +class GenericNDElement>( + override val context: NDField>, + private val structure: NDStructure +) : + NDStructure by structure, + NDElement, + FieldElement, GenericNDElement, NDField>> { + override val elementField: F get() = context.elementField override fun unwrap(): NDStructure = structure override fun NDStructure.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() } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt index e42cebfed..5cda16b25 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt @@ -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, N : NDStructure> : Field { +interface NDField, N : NDStructure> : Field { 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.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, N : NDStructure> : Field { /** * Create a most suitable implementation for nd-field using reified class */ - inline fun > inline(shape: IntArray, field: F) = BufferNDField(shape, field, ::inlineBuffer) + inline fun > buffered(shape: IntArray, field: F) = + BufferNDField(shape, field, ::inlineBuffer) } } -class GenericNDField>(override val shape: IntArray, override val field: F, val bufferFactory: BufferFactory = ::boxingBuffer) : NDField> { - override fun produce(initializer: F.(IntArray) -> T): NDStructure = ndStructure(shape, bufferFactory) { field.initializer(it) } +abstract class AbstractNDField, N : NDStructure>( + override val shape: IntArray, + override val elementField: F +) : AbstractField(), NDField { + override val zero: N by lazy { produce { zero } } - override val zero: NDStructure by lazy { produce { zero } } + override val one: N by lazy { produce { one } } - override val one: NDStructure by lazy { produce { one } } + final override operator fun Function1.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, b: NDStructure): NDStructure { - 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, k: Double): NDStructure { - 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, b: NDStructure): NDStructure { - 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, b: NDStructure): NDStructure { - 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>( + shape: IntArray, + elementField: F, + val bufferFactory: BufferFactory = ::boxingBuffer +) : + AbstractNDField>(shape, elementField) { + + override fun produce(initializer: F.(IntArray) -> T): NDStructure = + ndStructure(shape, bufferFactory) { elementField.initializer(it) } + + override fun NDStructure.map(transform: F.(T) -> T): NDStructure = + produce { index -> transform(get(index)) } + + override fun NDStructure.mapIndexed(transform: F.(index: IntArray, T) -> T): NDStructure = + produce { index -> transform(index, get(index)) } + + override fun combine(a: NDStructure, b: NDStructure, transform: F.(T, T) -> T): NDStructure = + produce { index -> transform(a[index], b[index]) } } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt index 5e55bea78..617c99a9d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -127,8 +127,8 @@ interface NDBuffer : NDStructure { * Boxing generic [NDStructure] */ data class BufferNDStructure( - override val strides: Strides, - override val buffer: Buffer + override val strides: Strides, + override val buffer: Buffer ) : NDBuffer { init { @@ -137,6 +137,12 @@ data class BufferNDStructure( } } + 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( /** * Transform structure to a new structure using provided [BufferFactory] and optimizing if argument is [BufferNDStructure] */ -inline fun NDStructure.mapToBuffer(factory: BufferFactory = ::inlineBuffer, crossinline transform: (T) -> R): BufferNDStructure { +inline fun NDStructure.mapToBuffer( + factory: BufferFactory = ::inlineBuffer, + crossinline transform: (T) -> R +): BufferNDStructure { return if (this is BufferNDStructure) { BufferNDStructure(this.strides, factory.invoke(strides.linearSize) { transform(buffer[it]) }) } else { @@ -171,26 +180,26 @@ inline fun NDStructure.mapToBuffer(factory: BufferFactor * Strides should be reused if possible */ fun ndStructure(strides: Strides, bufferFactory: BufferFactory = ::boxingBuffer, initializer: (IntArray) -> T) = - BufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) }) + BufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) }) /** * Inline create NDStructure with non-boxing buffer implementation if it is possible */ inline fun inlineNDStructure(strides: Strides, crossinline initializer: (IntArray) -> T) = - BufferNDStructure(strides, inlineBuffer(strides.linearSize) { i -> initializer(strides.index(i)) }) + BufferNDStructure(strides, inlineBuffer(strides.linearSize) { i -> initializer(strides.index(i)) }) fun ndStructure(shape: IntArray, bufferFactory: BufferFactory = ::boxingBuffer, initializer: (IntArray) -> T) = - ndStructure(DefaultStrides(shape), bufferFactory, initializer) + ndStructure(DefaultStrides(shape), bufferFactory, initializer) inline fun inlineNdStructure(shape: IntArray, crossinline initializer: (IntArray) -> T) = - inlineNDStructure(DefaultStrides(shape), initializer) + inlineNDStructure(DefaultStrides(shape), initializer) /** * Mutable ND buffer based on linear [inlineBuffer] */ class MutableBufferNDStructure( - override val strides: Strides, - override val buffer: MutableBuffer + override val strides: Strides, + override val buffer: MutableBuffer ) : NDBuffer, MutableNDStructure { init { @@ -205,19 +214,30 @@ class MutableBufferNDStructure( /** * The same as [inlineNDStructure], but mutable */ -fun mutableNdStructure(strides: Strides, bufferFactory: MutableBufferFactory = ::boxingMutableBuffer, initializer: (IntArray) -> T) = - MutableBufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) }) +fun mutableNdStructure( + strides: Strides, + bufferFactory: MutableBufferFactory = ::boxingMutableBuffer, + initializer: (IntArray) -> T +) = + MutableBufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) }) inline fun inlineMutableNdStructure(strides: Strides, crossinline initializer: (IntArray) -> T) = - MutableBufferNDStructure(strides, inlineMutableBuffer(strides.linearSize) { i -> initializer(strides.index(i)) }) + MutableBufferNDStructure(strides, inlineMutableBuffer(strides.linearSize) { i -> initializer(strides.index(i)) }) -fun mutableNdStructure(shape: IntArray, bufferFactory: MutableBufferFactory = ::boxingMutableBuffer, initializer: (IntArray) -> T) = - mutableNdStructure(DefaultStrides(shape), bufferFactory, initializer) +fun mutableNdStructure( + shape: IntArray, + bufferFactory: MutableBufferFactory = ::boxingMutableBuffer, + initializer: (IntArray) -> T +) = + mutableNdStructure(DefaultStrides(shape), bufferFactory, initializer) inline fun inlineMutableNdStructure(shape: IntArray, crossinline initializer: (IntArray) -> T) = - inlineMutableNdStructure(DefaultStrides(shape), initializer) + inlineMutableNdStructure(DefaultStrides(shape), initializer) -inline fun NDStructure.combine(struct: NDStructure, crossinline block: (T, T) -> T): NDStructure { +inline fun NDStructure.combine( + struct: NDStructure, + crossinline block: (T, T) -> T +): NDStructure { if (!this.shape.contentEquals(struct.shape)) error("Shape mismatch in structure combination") return inlineNdStructure(shape) { block(this[it], struct[it]) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt index 3158c38c1..344154ceb 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt @@ -5,47 +5,78 @@ import scientifik.kmath.operations.DoubleField typealias RealNDElement = BufferNDElement class RealNDField(shape: IntArray) : - BufferNDField(shape, DoubleField, DoubleBufferFactory), - ExtendedNDField> { + StridedNDField(shape, DoubleField), + ExtendedNDField> { + + override val bufferFactory: BufferFactory + get() = DoubleBufferFactory /** * Inline map an NDStructure to */ - private inline fun NDBuffer.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.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, pow: Double) = arg.mapInline { power(it, pow) } + @Suppress("OVERRIDE_BY_INLINE") + override inline fun NDBuffer.mapIndexed(crossinline transform: DoubleField.(index: IntArray, Double) -> Double): BufferNDElement { + check(this) + return BufferNDElement( + this@RealNDField, + bufferFactory(strides.linearSize) { offset -> + elementField.transform( + strides.index(offset), + buffer[offset] + ) + }) + } - override fun exp(arg: NDBuffer) = arg.mapInline { exp(it) } + @Suppress("OVERRIDE_BY_INLINE") + override inline fun combine( + a: NDBuffer, + b: NDBuffer, + crossinline transform: DoubleField.(Double, Double) -> Double + ): BufferNDElement { + check(a, b) + return BufferNDElement( + this, + bufferFactory(strides.linearSize) { offset -> elementField.transform(a.buffer[offset], b.buffer[offset]) }) + } - override fun ln(arg: NDBuffer) = arg.mapInline { ln(it) } + override fun power(arg: NDBuffer, pow: Double) = arg.map { power(it, pow) } - override fun sin(arg: NDBuffer) = arg.mapInline { sin(it) } + override fun exp(arg: NDBuffer) = arg.map { exp(it) } - override fun cos(arg: NDBuffer) = arg.mapInline { cos(it) } + override fun ln(arg: NDBuffer) = arg.map { ln(it) } - override fun NDBuffer.times(k: Number) = mapInline { value -> value * k.toDouble() } + override fun sin(arg: NDBuffer) = arg.map { sin(it) } - override fun NDBuffer.div(k: Number) = mapInline { value -> value / k.toDouble() } - - override fun Number.times(b: NDBuffer) = b * this - - override fun Number.div(b: NDBuffer) = b * (1.0 / this.toDouble()) + override fun cos(arg: NDBuffer) = arg.map { cos(it) } +// +// override fun NDBuffer.times(k: Number) = mapInline { value -> value * k.toDouble() } +// +// override fun NDBuffer.div(k: Number) = mapInline { value -> value / k.toDouble() } +// +// override fun Number.times(b: NDBuffer) = b * this +// +// override fun Number.div(b: NDBuffer) = b * (1.0 / this.toDouble()) } + /** * Fast element production using function inlining */ -inline fun BufferNDField.produceInline(crossinline initializer: DoubleField.(Int) -> Double): RealNDElement { - val array = DoubleArray(strides.linearSize) { offset -> field.initializer(offset) } +inline fun StridedNDField.produceInline(crossinline initializer: DoubleField.(Int) -> Double): RealNDElement { + val array = DoubleArray(strides.linearSize) { offset -> elementField.initializer(offset) } return BufferNDElement(this, DoubleBuffer(array)) } @@ -53,7 +84,8 @@ inline fun BufferNDField.produceInline(crossinline initiali * Element by element application of any operation on elements to the whole array. Just like in numpy */ operator fun Function1.invoke(ndElement: RealNDElement) = - ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) } + ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) } + /* plus and minus */ @@ -61,10 +93,10 @@ operator fun Function1.invoke(ndElement: RealNDElement) = * Summation operation for [BufferNDElement] and single element */ operator fun RealNDElement.plus(arg: Double) = - context.produceInline { i -> buffer[i] + arg } + 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 } \ No newline at end of file + context.produceInline { i -> buffer[i] - arg } diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/FieldExpressionContextTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt similarity index 66% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/FieldExpressionContextTest.kt rename to kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt index 8e2845b9e..9266b9394 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/FieldExpressionContextTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt @@ -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 FieldExpressionContext.expression(): Expression{ + fun ExpressionField.expression(): Expression { 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.()->Expression = { + val expressionBuilder: ExpressionField.() -> Expression = { 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) } } \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/histogram/MultivariateHistogramTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/histogram/MultivariateHistogramTest.kt index 842a12ae4..94ec6666c 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/histogram/MultivariateHistogramTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/histogram/MultivariateHistogramTest.kt @@ -11,8 +11,8 @@ class MultivariateHistogramTest { @Test fun testSinglePutHistogram() { val histogram = FastHistogram.fromRanges( - (-1.0..1.0), - (-1.0..1.0) + (-1.0..1.0), + (-1.0..1.0) ) histogram.put(0.55, 0.55) val bin = histogram.find { it.value.toInt() > 0 }!! @@ -22,21 +22,21 @@ class MultivariateHistogramTest { } @Test - fun testSequentialPut(){ + fun testSequentialPut() { val histogram = FastHistogram.fromRanges( - (-1.0..1.0), - (-1.0..1.0), - (-1.0..1.0) + (-1.0..1.0), + (-1.0..1.0), + (-1.0..1.0) ) val random = Random(1234) - fun nextDouble() = random.nextDouble(-1.0,1.0) + fun nextDouble() = random.nextDouble(-1.0, 1.0) val n = 10000 histogram.fill { - repeat(n){ - yield(Vector.ofReal(nextDouble(),nextDouble(),nextDouble())) + repeat(n) { + yield(Vector.ofReal(nextDouble(), nextDouble(), nextDouble())) } } assertEquals(n, histogram.sumBy { it.value.toInt() }) diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt index 87aad3d1d..d02cb2c6f 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt @@ -21,8 +21,8 @@ class MatrixTest { } @Test - fun testTranspose(){ - val matrix = MatrixSpace.real(3,3).one + fun testTranspose() { + val matrix = MatrixSpace.real(3, 3).one val transposed = matrix.transpose() assertEquals(matrix.context, transposed.context) assertEquals((matrix as StructureMatrix).structure, (transposed as StructureMatrix).structure) diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt index bd1eb8147..569b9e450 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt @@ -6,9 +6,9 @@ import kotlin.test.assertEquals class RealLUSolverTest { @Test fun testInvertOne() { - val matrix = MatrixSpace.real(2,2).one + val matrix = MatrixSpace.real(2, 2).one val inverted = RealLUSolver.inverse(matrix) - assertEquals(matrix,inverted) + assertEquals(matrix, inverted) } // @Test diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt index a705fc8f9..df474c53b 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt @@ -6,10 +6,9 @@ import kotlin.test.assertEquals class RealFieldTest { @Test fun testSqrt() { - //fails because KT-27586 - val sqrt = with(RealField) { - sqrt( 25 * one) + val sqrt = with(DoubleField) { + sqrt(25 * one) } - assertEquals(5.0, sqrt.value) + assertEquals(5.0, sqrt) } } \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt index 7c5227293..3bbd620fa 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt @@ -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 diff --git a/kmath-core/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt b/kmath-core/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt index 3c2915587..3765220b9 100644 --- a/kmath-core/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt +++ b/kmath-core/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt @@ -1,16 +1,33 @@ package scientifik.kmath.histogram -actual class LongCounter{ +actual class LongCounter { private var sum: Long = 0 - actual fun decrement() {sum--} - actual fun increment() {sum++} - actual fun reset() {sum = 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 add(l: Long) { + sum += l + } } -actual class DoubleCounter{ + +actual class DoubleCounter { private var sum: Double = 0.0 - actual fun reset() {sum = 0.0} + actual fun reset() { + sum = 0.0 + } + actual fun sum(): Double = sum - actual fun add(d: Double) {sum+=d} + actual fun add(d: Double) { + sum += d + } } \ No newline at end of file diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt index 26ad520e6..4d18b714d 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt @@ -18,7 +18,7 @@ class UnivariateBin(val position: Double, val size: Double, val counter: LongCou override fun contains(vector: Buffer): Boolean = contains(vector[0]) - internal operator fun inc() = this.also { counter.increment()} + internal operator fun inc() = this.also { counter.increment() } override val dimension: Int get() = 1 } @@ -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 { +class UnivariateHistogram private constructor(private val factory: (Double) -> UnivariateBin) : + MutableHistogram { private val bins: TreeMap = TreeMap() diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/BufferSpec.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/BufferSpec.kt index a5cc8c5de..9f95360fd 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/BufferSpec.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/BufferSpec.kt @@ -31,7 +31,7 @@ interface FixedSizeBufferSpec : BufferSpec { */ fun ByteBuffer.readObject(index: Int): T { val dup = duplicate() - dup.position(index*unitSize) + dup.position(index * unitSize) return dup.readObject() } @@ -49,7 +49,7 @@ interface FixedSizeBufferSpec : BufferSpec { */ fun ByteBuffer.writeObject(index: Int, obj: T) { val dup = duplicate() - dup.position(index*unitSize) + dup.position(index * unitSize) dup.writeObject(obj) } } \ No newline at end of file diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/ObjectBuffer.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/ObjectBuffer.kt index b50ed9674..fceda1b25 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/ObjectBuffer.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/ObjectBuffer.kt @@ -2,7 +2,8 @@ package scientifik.kmath.structures import java.nio.ByteBuffer -class ObjectBuffer(private val buffer: ByteBuffer, private val spec: FixedSizeBufferSpec) : MutableBuffer { +class ObjectBuffer(private val buffer: ByteBuffer, private val spec: FixedSizeBufferSpec) : + MutableBuffer { override val size: Int get() = buffer.limit() / spec.unitSize @@ -23,6 +24,6 @@ class ObjectBuffer(private val buffer: ByteBuffer, private val spec: Fi companion object { fun create(spec: FixedSizeBufferSpec, size: Int) = - ObjectBuffer(ByteBuffer.allocate(size * spec.unitSize), spec) + ObjectBuffer(ByteBuffer.allocate(size * spec.unitSize), spec) } } \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/CoroutinesExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/CoroutinesExtra.kt index a837cde01..6bb8682ff 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/CoroutinesExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/CoroutinesExtra.kt @@ -6,6 +6,9 @@ import kotlinx.coroutines.Dispatchers import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -expect fun runBlocking(context: CoroutineContext = EmptyCoroutineContext, function: suspend CoroutineScope.()->R): R +expect fun runBlocking( + context: CoroutineContext = EmptyCoroutineContext, + function: suspend CoroutineScope.() -> R +): R val Dispatchers.Math: CoroutineDispatcher get() = Dispatchers.Default \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/LazyNDField.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/LazyNDField.kt index 8b790da99..f1752b483 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/LazyNDField.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/LazyNDField.kt @@ -3,7 +3,8 @@ package scientifik.kmath.structures import kotlinx.coroutines.* import scientifik.kmath.operations.Field -class LazyNDField>(shape: IntArray, field: F, val scope: CoroutineScope = GlobalScope) : BufferNDField(shape,field, ::boxingBuffer) { +class LazyNDField>(shape: IntArray, field: F, val scope: CoroutineScope = GlobalScope) : + BufferNDField(shape, field, ::boxingBuffer) { override fun add(a: NDStructure, b: NDStructure): NDElements { return LazyNDStructure(this) { index -> @@ -34,13 +35,23 @@ class LazyNDField>(shape: IntArray, field: F, val scope: Corouti } } -class LazyNDStructure>(override val context: LazyNDField, val function: suspend F.(IntArray) -> T) : NDElements, NDStructure { +class LazyNDStructure>( + override val context: LazyNDField, + val function: suspend F.(IntArray) -> T +) : NDElements, NDStructure { override val self: NDElements get() = this override val shape: IntArray get() = context.shape private val cache = HashMap>() - 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>(override val context: LazyNDField, } } -fun NDStructure.deferred(index: IntArray) = if (this is LazyNDStructure) this.deferred(index) else CompletableDeferred(get(index)) +fun NDStructure.deferred(index: IntArray) = + if (this is LazyNDStructure) this.deferred(index) else CompletableDeferred(get(index)) -suspend fun NDStructure.await(index: IntArray) = if (this is LazyNDStructure) this.await(index) else get(index) +suspend fun NDStructure.await(index: IntArray) = + if (this is LazyNDStructure) this.await(index) else get(index) fun > NDElements.lazy(scope: CoroutineScope = GlobalScope): LazyNDStructure { return if (this is LazyNDStructure) { @@ -67,10 +80,12 @@ fun > NDElements.lazy(scope: CoroutineScope = GlobalScope) } } -inline fun > LazyNDStructure.mapIndexed(crossinline action: suspend F.(IntArray, T) -> T) = LazyNDStructure(context) { index -> - action.invoke(this, index, await(index)) -} +inline fun > LazyNDStructure.mapIndexed(crossinline action: suspend F.(IntArray, T) -> T) = + LazyNDStructure(context) { index -> + action.invoke(this, index, await(index)) + } -inline fun > LazyNDStructure.map(crossinline action: suspend F.(T) -> T) = LazyNDStructure(context) { index -> - action.invoke(this, await(index)) -} \ No newline at end of file +inline fun > LazyNDStructure.map(crossinline action: suspend F.(T) -> T) = + LazyNDStructure(context) { index -> + action.invoke(this, await(index)) + } \ No newline at end of file diff --git a/kmath-coroutines/src/commonTest/kotlin/scientifik/kmath/structures/LazyNDFieldTest.kt b/kmath-coroutines/src/commonTest/kotlin/scientifik/kmath/structures/LazyNDFieldTest.kt index 6e712f2a8..1eee26f29 100644 --- a/kmath-coroutines/src/commonTest/kotlin/scientifik/kmath/structures/LazyNDFieldTest.kt +++ b/kmath-coroutines/src/commonTest/kotlin/scientifik/kmath/structures/LazyNDFieldTest.kt @@ -14,7 +14,7 @@ class LazyNDFieldTest { counter++ it * it } - assertEquals(4, result[0,0,0]) + assertEquals(4, result[0, 0, 0]) assertEquals(1, counter) } } \ No newline at end of file diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/_CoroutinesExtra.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/_CoroutinesExtra.kt index 72a764729..0bc3b0dea 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/_CoroutinesExtra.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/_CoroutinesExtra.kt @@ -3,4 +3,5 @@ package scientifik.kmath.structures import kotlinx.coroutines.CoroutineScope import kotlin.coroutines.CoroutineContext -actual fun runBlocking(context: CoroutineContext, function: suspend CoroutineScope.() -> R): R = kotlinx.coroutines.runBlocking(context, function) \ No newline at end of file +actual fun runBlocking(context: CoroutineContext, function: suspend CoroutineScope.() -> R): R = + kotlinx.coroutines.runBlocking(context, function) \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 3b94f15c5..3a8aef730 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,8 +9,8 @@ pluginManagement { rootProject.name = "kmath" include( - ":kmath-core", - ":kmath-io", - ":kmath-coroutines", - ":benchmarks" + ":kmath-core", + ":kmath-io", + ":kmath-coroutines", + ":benchmarks" )